A fully decentralized, self-hosted, open-source solution to save tweets as markdown files in your GitHub repository.
- Privacy First: All credentials stored locally in your browser - never sent to any server
- IPFS Ready: Static frontend can be hosted on IPFS for censorship resistance
- Markdown Output: Tweets saved as clean markdown with full metadata
- Thread Support: Automatically detects and archives all tweets in a thread by the same author
- Article Support: Extracts full content from X/Twitter articles (not just the link)
- Media Downloads: Downloads images, videos, and thumbnails to your repository
- GitHub Actions: Processing happens in your own GitHub repository
- Cost Effective: Uses twitterapi.io (~$0.15 per 1000 tweets)
- Dark/Light Theme: Futuristic cybernetic design with a theme toggle that persists across sessions
- Hosted Version Interest Form: Optional encrypted expression-of-interest form for users who want a managed hosted solution
Option A: Use Template (Recommended)
- Click the green "Use this template" button at the top of this repo
- Name your new repository (e.g.,
my-saved-tweets) - Important: Set visibility to Private to keep your tweets private
Option B: Fork & Make Private
- Fork this repository
- Go to Settings β General β Danger Zone β Change visibility β Make private
- Go to twitterapi.io/dashboard
- Sign up and get your API key (~$0.15 per 1000 tweets)
This is more secure than account-wide tokens!
- Go to GitHub Fine-Grained Tokens
- Token name:
ReviseMachine - my-saved-tweets(or your repo name) - Expiration: Choose your preference (90 days recommended)
- Repository access: Select "Only select repositories"
- Select your ReviseMachine repository from the dropdown
- Permissions β Repository permissions:
- Contents: Read and write
- Secrets: Read and write (for automatic secret creation)
- Actions: Read and write (to trigger workflows)
- Metadata: Read-only (auto-selected)
- Click Generate token and copy it
β οΈ Security Note: This token can ONLY access your ReviseMachine repository, not your other repos!
The frontend can automatically create the GitHub Secret for you:
- Open
frontend/index.htmlin your browser - Enter your repository, PAT, and Twitter API key
- Click "π Save Twitter Key to GitHub Secret"
- The secret
TWITTER_API_KEYwill be securely created in your repository
This uses libsodium encryption in your browser - the key is encrypted before being sent to GitHub.
Open frontend/index.html in your browser (or access via IPFS), then:
- Enter your repository name (e.g.,
username/my-saved-tweets) - Paste your repository-scoped GitHub PAT
- Paste your twitterapi.io API key
- Click "Save Configuration"
Now you can paste any tweet URL and save it!
revisemachine/
βββ .github/
β βββ workflows/
β βββ save-tweet.yml # GitHub Action that fetches and saves tweets
βββ frontend/
β βββ index.html # Static frontend (IPFS-ready, futuristic dark theme)
β βββ interest.html # Expression-of-interest form (encrypted submissions)
βββ scripts/
β βββ process_tweet.js # Tweet processing logic (threads, articles, media)
βββ tweets/ # Your saved tweets will appear here
β βββ media/ # Downloaded images and videos
βββ LICENSE
βββ README.md
Just paste any X/Twitter URL β the system automatically detects and handles:
| Type | Detection | What Gets Saved |
|---|---|---|
| Simple Tweet | Default | Tweet text, media (images/videos), profile pic, engagement stats |
| Thread | Author has self-replies | All tweets by the author in chronological order |
| Article | Tweet links to x.com/i/article/ |
Full article content (title, body, cover image) via API |
No manual selection needed! The backend automatically determines the tweet type and archives accordingly.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your Browser β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Frontend (index.html) β β
β β - Stores credentials in localStorage β β
β β - Triggers GitHub repository_dispatch β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ (repository_dispatch)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Your GitHub Repository β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β GitHub Action (save-tweet.yml) β β
β β 1. Receives tweet URL β β
β β 2. Calls twitterapi.io to fetch tweet β β
β β 3. Generates markdown file β β
β β 4. Commits to tweets/ folder β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Go to fleek.co
- Connect your GitHub repository
- Set build settings:
- Framework: Other
- Build command: (leave empty)
- Publish directory:
frontend
- Deploy!
- Install IPFS Desktop
- Import the
frontendfolder - Copy the CID
- Access via
https://ipfs.io/ipfs/<YOUR_CID>/
- Go to pinata.cloud
- Upload the
frontendfolder - Get your IPFS hash
You can also trigger the workflow directly from GitHub:
- Go to your repository β Actions tab
- Select "Save Tweet to Markdown"
- Click "Run workflow"
- Enter the tweet URL
- Click "Run workflow"
- Fine-Grained PAT: We recommend using a repository-scoped token that can ONLY access your ReviseMachine repo, not your other repositories.
- GitHub PAT: Stored only in your browser's localStorage. Never transmitted to any server except GitHub's API.
- Twitter API Key: Stored as a GitHub Secret. Only accessible by your GitHub Actions.
- Interest Form Encryption: The expression-of-interest form encrypts all data client-side using
libsodium crypto_box_seal(X25519 sealed box) before submission. The server never sees plaintext form data. - No Backend: The self-hosted version has no server component. Everything runs in your browser or GitHub Actions.
- Private Repository: Make your copy private to keep your saved tweets visible only to you.
The frontend files (frontend/index.html and frontend/interest.html) include a Google Analytics tag for the original ReviseMachine project. When you clone this template, you should remove or replace this tag to avoid sending analytics data to the original project owner.
To remove: Delete the following lines from the <head> section of both HTML files:
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-C4WZQDC7BM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-C4WZQDC7BM');
</script>To replace: Change G-C4WZQDC7BM to your own Google Analytics Measurement ID.
When you save a tweet, it creates a file like tweets/2024-01-15-1234567890.md:
---
tweet_id: "1234567890"
type: "tweet"
author: "John Doe"
author_username: "@johndoe"
created_at: "2024-01-15T10:30:00Z"
source_url: "https://x.com/johndoe/status/1234567890"
likes: 42
retweets: 10
is_thread: false
media_count: 1
---
<img src="media/1234567890/profile.jpg" alt="@johndoe" width="48" height="48"> **John Doe** Β· [@johndoe](https://x.com/johndoe)
# Tweet by @johndoe
This is the tweet content with all the text preserved!

## Engagement
| Metric | Count |
|--------|-------|
| Likes | 42 |
| Retweets | 10 |---
type: "thread"
is_thread: true
thread_count: 3
---
# Thread by @johndoe (3 tweets)
### Tweet 1 of 3
First tweet in the thread...
### Tweet 2 of 3
Second tweet continues the story...
### Tweet 3 of 3
Final tweet wraps it up!---
type: "article"
---
## Article Content
**Article URL**: [http://x.com/i/article/...](...)

### Article Title Here
Full article text extracted automatically...Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - feel free to use this for personal or commercial projects.
- twitterapi.io - Twitter API provider
- GitHub Actions - CI/CD platform
- IPFS - Decentralized storage
- libsodium - Client-side encryption
- Tailwind CSS - Styling