A Ruby tool to selectively delete content from your Bluesky, Mastodon, or X (Twitter) account.
- 🗑️ Selective Deletion: Choose what to delete - posts, replies, likes, reposts, or any combination
- 🌐 Multi-platform: Supports Bluesky, Mastodon, and X (Twitter)
- ⏰ Time-based: Delete content older than a specified number of days (default: 90 days)
- 🎯 ID-based: Target specific posts by their IDs
- 🚫 Exclude Lists: Protect specific posts from deletion with exclude-ids
- 🔖 Keeps Favourites: Posts and replies you've bookmarked or liked yourself are kept by default
- 🔍 Dry-run Mode: Preview what would be deleted before committing (enabled by default)
- 📦 Archive Support: Import your X data archive to find and delete old tweets
- 🖥️ Browser Console Script: Clean up X without API access by pasting
x_console.jsinto the x.com console - 🔐 Secure: Uses app passwords (Bluesky), access tokens (Mastodon), or OAuth 1.0a (X) for authentication
- 📊 Detailed Output: Progress tracking and comprehensive logging
- Ruby 3.0 or higher
- A Bluesky, Mastodon, and/or X account
- For the X CLI: a downloaded data archive (request at x.com/settings/download_your_data). The X browser console script needs neither Ruby nor an archive.
-
Clone or download this repository
-
Install dependencies:
bundle install
-
Make the script executable:
chmod +x vanillasky.rb
VanillySky uses a command-based syntax where you specify what types of content to delete:
./vanillasky.rb --help
./vanillasky.rb --versionDelete posts older than 90 days (preview only):
./vanillasky.rb postsDelete both posts and likes (preview only):
./vanillasky.rb posts likesDelete only replies (preview only):
./vanillasky.rb repliesBy default VanillaSky uses Bluesky. Use --platform / -p to switch:
./vanillasky.rb posts -p mastodon
./vanillasky.rb posts likes -p mastodon --days 30
./vanillasky.rb posts -p x --archive ~/path/to/x-archiveThe X platform requires the --archive flag pointing to your extracted X data archive directory.
Add --force to perform actual deletions:
./vanillasky.rb posts --force
./vanillasky.rb posts replies likes --force
./vanillasky.rb posts -p mastodon --force
./vanillasky.rb posts likes -p x --archive ~/x-archive --forceDelete content older than 30 days:
./vanillasky.rb posts --days 30 --forceDelete everything (including today's content):
./vanillasky.rb posts likes reposts --days 0 --forceDelete only specific posts by their IDs:
./vanillasky.rb posts --ids abc123,def456,ghi789Delete posts but protect certain ones:
./vanillasky.rb posts --exclude-ids important123,keepme456 --forcePosts and replies you have bookmarked, or liked yourself, are kept by default. Each one kept is listed in the output. To delete them as well:
./vanillasky.rb posts replies --no-keep-bookmarked --no-keep-liked --forceOn X the archive has no bookmarks, so only self-liked tweets are kept (the x_console.js script skips both). If VanillaSky can't fetch your Bluesky bookmarks or likes, it stops without deleting anything.
posts: Top-level posts (not replies to other posts)replies: Your replies to other people's postslikes: Posts you've liked (Bluesky likes / Mastodon favourites / X likes)reposts: Posts you've reposted/shared (Bluesky reposts / Mastodon boosts / X retweets)
You can specify multiple types in one command:
./vanillasky.rb posts replies likes reposts --forceCreate a .env file:
BLUESKY_HANDLE=your-handle.bsky.social
BLUESKY_APP_PASSWORD=your-app-passwordGenerate an app password at bsky.app/settings/app-passwords.
If no environment variables are set, you'll be prompted for your handle and app password.
Important: Use an app password, not your main account password.
- Log in to your Mastodon instance in a web browser
- Go to Preferences → Development → New application
(or visit
https://your-instance/settings/applications/newdirectly) - Fill in the application name (e.g. "VanillaSky")
- Under Scopes, select:
read:statuses— to fetch your posts, replies and boostsread:favourites— to fetch your favouriteswrite:statuses— to delete posts, replies and boostswrite:favourites— to unfavourite posts
- Click Submit
- On the application page, copy Your access token
Add to your .env file:
MASTODON_INSTANCE=mastodon.social
MASTODON_ACCESS_TOKEN=your-access-token-hereIf no environment variables are set, you'll be prompted for your instance URL and access token.
There are two ways to clean up X. The browser console script is the easiest and needs no API access or archive. The CLI works like the other platforms but needs API credentials and a data archive.
x_console.js drives the x.com web UI for you: it scrolls your profile, finds content older than the cutoff and clicks Delete / Undo repost / Unlike exactly as you would by hand. No API keys, no archive, and it can remove likes (which the free API tier cannot).
- Log in to x.com and open the tab you want to clean up:
https://x.com/<you>— posts (and reposts, if enabled)https://x.com/<you>/with_replies— posts and replieshttps://x.com/<you>/reposts— reposts only, including reposts of your own posts (needsREPOSTS: true)https://x.com/<you>/likes— likes
- Open the developer console (
Cmd+Option+Jon Mac,Ctrl+Shift+Jelsewhere). Chrome will ask you to typeallow pastingthe first time. - Open
x_console.js, edit theCONFIGblock at the top (days, exclude IDs, which types, andDRY_RUN), then paste the whole file into the console and press Enter. - Watch the log. Run
vanillasky.stop()in the console to halt early.
const CONFIG = {
DRY_RUN: true, // false to really delete
DAYS: 60, // delete content older than this many days
START_DATE: null, // 'YYYY-MM-DD' — never delete anything before this date
EXCLUDE_IDS: [], // tweet IDs to keep
POSTS: true, // your own tweets (and replies on the with_replies tab)
REPOSTS: false, // undo your reposts
LIKES: true, // unlike (only acts on the /likes tab)
SKIP_PINNED: true, // leave your pinned post alone
SKIP_BOOKMARKED: true, // leave anything you have bookmarked alone
SKIP_LIKED: true, // leave your own posts that you have liked alone
MAX_ACTIONS: Infinity, // stop after this many deletions
DELAY_MS: 1500, // pause between deletions
// ...
};Bookmarking or liking one of your own posts is a quick way to protect it without editing EXCLUDE_IDS: SKIP_BOOKMARKED and SKIP_LIKED are on by default. (SKIP_LIKED only applies to your own posts, so it does nothing on the likes tab.)
Dry run is the default, so the first paste only lists what would go. Set DRY_RUN: false and paste again to delete for real. Keep the tab in the foreground while it runs; the X UI only renders tweets that are near the viewport.
Caveats:
- The web UI shows the original post's date for reposts and likes, so that is the date used for the cutoff (the archive-based CLI has the same limitation for likes).
- The UI cannot reliably tell a reply from a post, so
POSTSmeans "your own tweets on the current tab". Use the Posts & replies tab to include replies. - Menu items are matched by their text. If your X UI is not in English, set
DELETE_LABELto the label of the Delete menu item. - X changes its markup from time to time. The script relies on
data-testidattributes (tweet,caret,unretweet,unlike,confirmationSheetConfirm) which have been stable for years, but if it stops finding anything, that is the first place to look.
The CLI requires two things: API credentials for deleting content, and a data archive for finding your old content (since the X API no longer allows free listing of tweets). Note that the free API tier cannot delete likes.
- Go to x.com/settings/download_your_data
- Request your archive and wait for the email (can take 24+ hours)
- Download and extract the ZIP file
- Pass the extracted directory to VanillaSky with
--archive
The archive should contain a data/ directory with files like tweets.js and like.js.
- Go to the X Developer Portal
- Create a project and app (the free tier works for deletion)
- On the app creation screen you'll see your API Key, API Key Secret, and Bearer Token — save the first two (Bearer Token is not needed)
- Under User authentication settings, enable OAuth 1.0a with Read and write permissions
- Go to your app's Keys and tokens tab
- Under Authentication Tokens, click Generate to create your Access Token and Secret — save both
Add to your .env file:
X_API_KEY=your-api-key-here
X_API_KEY_SECRET=your-api-key-secret-here
X_ACCESS_TOKEN=your-access-token-here
X_ACCESS_TOKEN_SECRET=your-access-token-secret-hereIf no environment variables are set, you'll be prompted for all four credentials.
posts- Delete posts (not replies)replies- Delete replies to other postslikes- Delete likes / favouritesreposts- Delete reposts / boosts
-p, --platform PLATFORM- Platform to use:bluesky(default),mastodon, orx-a, --archive PATH- Path to extracted archive directory (required for X)-d, --days DAYS- Delete content older than DAYS (default: 90)-s, --start-date DATE- Don't delete anything before this date (YYYY-MM-DD)-n, --dry-run- Show what would be deleted without deleting (default)-f, --force- Actually delete content (required to disable dry-run)--ids ID1,ID2,ID3- Delete only specific IDs of the specified type--exclude-ids ID1,ID2,ID3- Exclude specific IDs from deletion--no-keep-bookmarked- Also delete posts and replies you have bookmarked--no-keep-liked- Also delete posts and replies you have liked yourself-h, --help- Show help message-v, --version- Show version
# Preview
./vanillasky.rb posts --days 60
# Execute
./vanillasky.rb posts --days 60 --force./vanillasky.rb posts replies --exclude-ids important1,important2 --force./vanillasky.rb likes -p mastodon --days 30 --force./vanillasky.rb reposts -p mastodon --days 14 --forceOpen https://x.com/<you>, paste x_console.js into the console with DRY_RUN: true, check the list, then paste again with DRY_RUN: false. For likes, do the same on https://x.com/<you>/likes.
# Preview
./vanillasky.rb posts -p x --archive ~/x-archive --days 60
# Execute
./vanillasky.rb posts -p x --archive ~/x-archive --days 60 --force./vanillasky.rb likes reposts -p x --archive ~/x-archive --force- 🔒 Secure authentication with app passwords / access tokens
- 🛡️ Dry-run mode by default - requires
--forceto delete ⚠️ Confirmation prompt before actual deletion- 🔍 Detailed preview of what will be deleted
- ⏱️ Rate limiting to avoid API issues
- 📋 Comprehensive logging
- 🚫 Exclude lists to protect important content
- ⛔ Mutual exclusion prevents conflicting options
🌌 VanillaSky - Bluesky Auto-Delete
=========================================
✅ Successfully authenticated as alice.bsky.social
🔍 DRY RUN MODE - Nothing will be deleted
🚫 Excluding 2 IDs from deletion: important123, keepme456
🔍 Scanning for posts older than 2024-05-25...
....
📋 Found 23 posts older than 90 days
💬 Found 15 replies older than 90 days
[DRY RUN] ID: abc123 | 2024-03-15 - Just tried that new coffee shop...
[DRY RUN] ID: def456 | 2024-03-10 - Beautiful sunset today! 📸
...
🔍 DRY RUN: Would have deleted 23 posts
🔍 DRY RUN: Would have deleted 15 replies
- Platform Selection: Picks Bluesky, Mastodon, or X based on
--platformflag - Authentication: Connects using your credentials
- Command Parsing: Determines what content types to process
- Scanning: Fetches your content via API (Bluesky/Mastodon) or from your data archive (X)
- Filtering: Applies time thresholds and exclude lists
- Preview/Execution: Shows what would be deleted or performs deletions
- Rate Limiting: Processes deletions with delays to respect API limits
- v0.5.0: X browser console script (
x_console.js) — no API or archive needed - v0.4.0: X (Twitter) support via data archive + API
- v0.3.0: Mastodon support, platform abstraction
- v0.2.1: Fix SSL certificate CRL verification errors on Ruby 3.4+
- v0.2: Command-based interface, replies support, exclude-ids functionality
- v0.1: Original flag-based interface
- Credentials are only used for the current session and never stored
- All API communications use HTTPS
- Bluesky app passwords have limited scope and can be revoked anytime
- Mastodon access tokens can be revoked from your instance's Development settings
- X OAuth tokens can be revoked from the Developer Portal
- X data archives are read locally and never uploaded anywhere
- Processes content sequentially to respect rate limits
- Cannot recover deleted content
- Some very old content might not be accessible via the API
- X requires a data archive since the API no longer supports free listing of tweets
- X like dates are estimated from tweet IDs (snowflake timestamps), not the date you liked them
- X API free tier has strict rate limits (50 tweet deletions per 15 minutes) and cannot delete likes — use
x_console.jsfor those x_console.jsdepends on the x.com markup and may need updating when X changes its UI
Feel free to open issues or submit pull requests for bugs or feature suggestions.
This project is licensed under the MIT License - see the LICENSE file for details.