birdclaw: a local SQLite mirror of an X archive, with four surfaces over it
Stores all your tweets nicely claw-able for agents. It makes no network requests; run birdclaw serve afterward to browse the demo.
At a glance
- What is it?
- steipete/birdclaw imports Twitter/X archives into SQLite on your own machine and exposes them through a web app, a CLI, a backup format and an optional read-only MCP server. Live reads are delegated to xurl, every surface is switchable off by environment variable, and no read touches the network by default.
- Who is it for?
- birdclaw is a good fit for one specific job: keeping a searchable copy of an account's own history, DMs, saved posts and follow graph on a disk you control, with no cloud account in the middle. It is a poor fit as a live client, because the parts that talk to X are handed to xurl or to a browser session cookie pair, and the parts that serve data are individually switchable, so a deployment has to be configured rather than assumed.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- steipete/birdclaw1,678 ★ · +68
| Repository | 2026-08-02 | 2026-11-14 |
|---|---|---|
| steipete/birdclaw | 1.6k | 1.7k |
68 Stars in the last 104 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Two install paths, one Node contract, and a canary for source work
Homebrew is described as the shortest path on macOS and Linux:
brew install steipete/tap/birdclawThe same tool is on npm:
npm install -g birdclawBoth published channels keep the public Node.js contract `>=26.5.1 <27`, and source development deliberately does not use that runtime. It pins one checksum verified Bun build, `1.4.0-canary.1`, a Rust port of the runtime, and keeps Node as a tested compatibility lane beside it. That split is unusual enough to notice: the package you install runs on a stable Node contract, while the repository you clone develops against a canary whose checksum is recorded in the tree. Two documentation pages exist for exactly this, one for installation and one for the Bun canary with its checksums, constraints and rollback boundaries. The project is MIT licensed, created by Peter Steinberger, and states plainly that it is not affiliated with X Corp.
SQLite is canonical, FTS5 does the searching, and four surfaces sit on top
The store is local SQLite and it is described as canonical, meaning archive imports and live transports converge on the same tables rather than living in separate caches. Full text search over tweets and DMs is FTS5 inside that database. Over it sit four surfaces. The web app covers home, mentions, saved posts, DMs, an inbox, moderation and network views. The CLI covers search, sync, moderation, research, JSON output and scheduled jobs. Backup produces deterministic JSONL shards that are designed to round trip through Git, which is the only export format described. The MCP server offers read-only cached tweet search and thread tools, and it stays off until a token and a public URL are configured. Version 0.15.0 is current, and the release titles show how fast the surface is still moving: v0.13.0 added full Note Tweets, direct X links and a faster CLI on 2026-09-13, v0.14.0 added feed video playback and faster follower maps on 2026-09-14, and v0.15.0 followed on 2026-09-22.
Live sync is delegated to xurl and only runs when you ask
Archive import and local search need no X login at all. Live data is the part that does, and birdclaw does not implement its own transport. Sync delegates to xurl or to an existing private bird installation, and it runs only when requested:
birdclaw sync timeline --limit 100 --refresh --json
birdclaw sync bookmarks --mode auto --limit 100 --refresh --jsonThe archive path is the entry point for a real database:
birdclaw import archive ~/Downloads/twitter-archive.zip --jsonThat import establishes the account identity for the database and brings in tweets, DMs, likes, bookmarks, profiles, media and follow edges. Imports are stated to be idempotent, merging destination only rows by default, with selective re-import and exact replacement handled separately in the archive documentation. The ordering rule is explicit: import an archive before the first live sync on a new database, so identity exists before the network is consulted. Nothing here happens until you run it, which is the design choice the rest of the project is built around.
Four environment variables decide what the server may do
What the surfaces are allowed to do is controlled by switches rather than by a config wizard. The web server listens on loopback. Live writes can be turned off with `BIRDCLAW_DISABLE_LIVE_WRITES=1`. A deployment meant to serve an archive and nothing else sets `BIRDCLAW_DEPLOYMENT_READ_ONLY=1` before `birdclaw serve`, which serves an initialized archive through strict readers, disables web mutations and automatic sync, and hides controls that would not work. The MCP endpoint stays off until both its token and its public URL are configured, so an agent cannot reach it by accident. Local reads trigger no network traffic by default. Storage is not negotiable in the same way: the database, configuration and media all live under `~/.birdclaw`, and `BIRDCLAW_HOME` moves the root when you want them elsewhere. The precedence chain is short and worth remembering, because it decides who wins: command flags override environment variables, which override the config file.
DM reads authenticate with AUTH_TOKEN and CT0 cookies
The security surface of this project is concentrated in one paragraph about an optional dependency. Bird is optional: xurl is said to support ordinary live workflows and moderation, while native `web` access handles DM requests using `AUTH_TOKEN` and `CT0` session cookies. Neither native request access nor xurl moderation needs a browser process running. That last sentence cuts both ways. No browser means no browser profile to inspect, but it also means the credentials are two values sitting in your environment that stand in for a logged in X session, and anyone who can read them can act as that session. The rest of the documentation treats them as configuration, and the configuration page is where the storage and transport details live. Anyone running this on a shared machine, or feeding an agent access to it, should decide deliberately which of these two paths is even enabled, because read-only deployment mode covers the served data and not the credentials you handed the process.
The canary is pinned by checksum and the wrapper refuses a mismatch
Development never calls Bun directly. Four wrapper commands cover it:
./scripts/bun-canary.sh install --frozen-lockfile
./scripts/bun-canary.sh run --bun check
./scripts/bun-canary.sh run --bun test
./scripts/bun-canary.sh run --bun buildThe wrapper installs and verifies the exact Rust port canary recorded in `toolchains/bun-canary.conf`, and it refuses a newer rolling canary when the checksum or the revision differs. That refusal is the point: a rolling tag would let the runtime change underneath a lockfile that was written for a different build. Node stays in the picture as a second lane, since the manifest carries parallel `dev:node`, `build:node`, `test:node`, `coverage:node` and `e2e:node` scripts beside their Bun counterparts. Coverage is configured twice as well, once through Istanbul on Bun and once through V8 on Node. CI adds a dual runtime smoke test of the installed package and Playwright runs against the Bun production server, so the same code is exercised on both runtimes and then again as a built artifact.
A favicon is generated at build time and the CLI bundles Effect to start faster
Two build details explain the shape of the published package. The first is the brand asset: Vite generates the compact app and favicon logo from the original artwork during configuration, the derivative is written to `.generated/birdclaw-brand/` with a content hash, and the full resolution documentation image is left untouched. Sharp is a build-only dependency, so the image pipeline costs nothing at runtime and the hashed filename means the derivative can be cached without a version query. The second is startup cost: the CLI build bundles the Effect modules it uses to reduce process startup work, while other direct dependencies stay external and the installed package keeps the same Node and Bun runtime contracts. The manifest ships a single binary entry, `bin/birdclaw.mjs`, and a files list limited to bin/, dist/cli/, dist/client/, dist/server/, CHANGELOG.md, LICENSE, README.md and package.json, with `#/*` mapped to `./src/*` for internal imports. CNAME at the root ties the documentation to the custom domain, and VISION.md holds the product boundaries the docs point at.
Editorial conclusion
birdclaw is a good fit for one specific job: keeping a searchable copy of an account's own history, DMs, saved posts and follow graph on a disk you control, with no cloud account in the middle. It is a poor fit as a live client, because the parts that talk to X are handed to xurl or to a browser session cookie pair, and the parts that serve data are individually switchable, so a deployment has to be configured rather than assumed. Before pointing it at anything real, check four things. Which X session credentials end up in your environment, since DM reads run on AUTH_TOKEN and CT0. Whether the read-only deployment mode is on, since the default server will accept web mutations and sync. What your rollback looks like for a checksum pinned Bun canary, because development will refuse a build that does not match. And whether JSONL backup shards are in your backup plan, since that format is the only export the project describes.
Frequently asked questions
Does birdclaw need an X login to work?
Not for archive import or local search. Live sync is the part that needs credentials, and birdclaw delegates it to xurl or to an existing private bird installation, running only when requested. Native `web` access handles DM requests with AUTH_TOKEN and CT0 session cookies, without a browser process.
Where does birdclaw keep its data?
The database, configuration and media all live under `~/.birdclaw`, and setting `BIRDCLAW_HOME` moves that root. SQLite is the canonical store, with archive imports and live transports converging on the same tables and FTS5 powering local tweet and DM search.
Can birdclaw serve an archive without allowing changes?
Yes. Setting `BIRDCLAW_DEPLOYMENT_READ_ONLY=1` before `birdclaw serve` serves an initialized archive through strict readers, disables web mutations and automatic sync, and hides the controls that would not work. Live writes can also be disabled on their own with `BIRDCLAW_DISABLE_LIVE_WRITES=1`.
What can the birdclaw MCP server do?
It offers read-only cached tweet search and thread tools behind a dedicated token, and it stays off until both that token and a public URL are configured. The other surfaces are a web app for home, mentions, saved posts, DMs, inbox, moderation and network views, plus a CLI for search, sync, moderation, research, JSON output and scheduled jobs.
How is birdclaw installed and which runtime does it use?
With `brew install steipete/tap/birdclaw` on macOS and Linux, or `npm install -g birdclaw`. Both published channels keep the public Node.js contract `>=26.5.1 <27`, while source development pins a checksum verified Bun `1.4.0-canary.1` build and keeps Node as a tested compatibility lane.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/steipete-birdclaw)Star history chart for your README
[](https://hysenlabs.com/projects/steipete-birdclaw)Was this useful?