# X Bookmarks → RSS A tiny [Hono](https://hono.dev) app on Cloudflare Workers that turns your X (Twitter) bookmarks into a live RSS 2.0 feed. Uses the official X API v2 (OAuth 2.0 user context). State lives in a single **D1** database. Personal/single-user: the feed is unguarded (`/feed.xml`, no key). Bookmarks are stored in D1 and accumulate, so tweets stay in your feed even after they scroll out of X's 100-item bookmarks API window. ## How it works - There is **no login route**. You seed one OAuth2 refresh token into D1 once; from then on the app refreshes the access token itself on every sync (refresh tokens rotate and the new one is written straight back to D1). - `GET /feed.xml` — if the last sync is older than `SYNC_TTL_SECONDS`, it refreshes the access token, pages through your bookmarks, and upserts them into D1; then it renders RSS from **all** stored rows (newest first). Sync failures are swallowed — the feed still serves what's already stored. ## Setup ### 1. Create the D1 database ```sh npm install wrangler d1 create x-bookmarks-db # paste the printed database_id into wrangler.jsonc npm run db:init # applies schema.sql to the remote DB ``` ### 2. Set secrets (used to refresh the token) ```sh wrangler secret put X_CLIENT_ID wrangler secret put X_CLIENT_SECRET ``` `SYNC_TTL_SECONDS` (default 900) and `MAX_ITEMS` (default 100, items pulled per sync) are plain vars in `wrangler.jsonc`. ### 3. Seed your refresh token You need one OAuth2 refresh token with the `tweet.read users.read bookmark.read offline.access` scopes (this is the only step that requires a one-time authorization — every sync afterward is automatic). Once you have it: ```sh export X_CLIENT_ID=... # same values as the secrets above export X_CLIENT_SECRET=... scripts/seed-token.sh "" ``` The script validates the token, captures the rotated token, and writes `refresh_token` + `user_id` into the D1 `meta` table. ### 4. Deploy & subscribe ```sh wrangler deploy ``` Add `https://.workers.dev/feed.xml` to your RSS reader. ## Notes - Refresh tokens rotate on every use and are written back to D1 immediately. - If syncs start failing (token revoked), re-run `scripts/seed-token.sh` with a fresh token. - The feed is unguarded by design (personal use). Don't host it where you'd mind it being readable, or re-add a key check.