Skip to content

dynchen/recall-inbox

Repository files navigation

Recall Inbox

A self-hosted inbox for X bookmarks, GitHub stars, and other saved items. Recall Inbox turns scattered saves into a reviewable workflow: sync -> review -> tag/note -> export.

Recall Inbox screenshot

Why

Bookmarks and stars are easy to collect and hard to revisit. Recall Inbox gives them a lightweight processing queue with review states, tags, notes, daily sync, and Markdown export, while keeping the data in infrastructure you control.

Who It Is For

  • People who save useful posts and repositories but rarely process them later.
  • Developers who want GitHub Stars and X bookmarks in one searchable review UI.
  • Personal knowledge management users who prefer local Markdown and self-hosted data over another hosted read-it-later service.

One-Minute Demo

Try the hosted demo without creating API tokens:

https://recall-inbox-demo.kapler.workers.dev

Features

  • Sync X bookmarks through OAuth 2.0.
  • Sync GitHub starred repositories with a personal access token.
  • Review saved items with inbox / keep / action / dismiss, tags, and notes.
  • Export Markdown files to outputs/daily/YYYY-MM-DD.md.
  • Deploy the same review UI and sync job to Cloudflare Workers with D1, or to Vercel with Postgres.

Deployment Options

  • Local only: use .data/items.json, yarn sync:x, yarn sync:github, and yarn view.
  • Cloudflare: deploy the review UI, API, D1 database, and scheduled sync with yarn cf:release.
  • Vercel: deploy the same UI and API routes with Postgres and Vercel Cron.

Project Direction

See Roadmap for planned source adapters, review workflow improvements, action exports, GitHub Stars intelligence, and deployment polish.

Source Configuration

Configure one or more sources.

  • X bookmarks require X_CLIENT_ID and X_CLIENT_SECRET.
  • GitHub stars require GITHUB_TOKEN.

Credential Guide

For X bookmarks:

  1. Create an X Developer Project and App.
  2. Enable OAuth 2.0 user authentication for the App.
  3. Use the callback URL that matches your runtime exactly.
  4. Copy the App Client ID and Client Secret into .env, Wrangler secrets, or Vercel environment variables.
  5. Run the one-time X authorization flow. Local mode uses yarn auth:x; deployed mode uses Admin -> Authorize X in the review UI.

Recall Inbox requests tweet.read, users.read, bookmark.read, and offline.access. offline.access lets the app refresh the X token after the one-time authorization, so scheduled syncs can continue without asking you to authorize again.

For GitHub stars:

  1. Create a GitHub personal access token for the account whose stars you want to sync.
  2. Prefer a fine-grained token with Starring user permission set to read.
  3. Store it as GITHUB_TOKEN in .env, Wrangler secrets, or Vercel environment variables.

The GitHub source only reads starred repositories through the authenticated user's starring endpoint. It does not need repository write permissions.

X callback URLs depend on where you run authorization:

  • Local CLI: http://127.0.0.1:17863/callback
  • Cloudflare Worker: https://<your-worker-url>/api/auth/x/callback
  • Vercel: https://<your-vercel-domain>/api/auth/x/callback

Local Setup

Install dependencies:

yarn install

Copy the environment template:

cp .env.example .env

Set ADMIN_SECRET, then fill in the source credentials you need. The review UI uses ADMIN_SECRET to load and edit real saved items. For local X authorization, use the local callback URL in both the X Developer Portal and .env:

http://127.0.0.1:17863/callback

Build:

yarn build

Authorize X only if you use the local X source:

yarn auth:x

Run the sync command for each source you configured:

yarn sync:x
yarn sync:github

sync:x is X-only and requires yarn auth:x first. sync:github is GitHub-only and requires GITHUB_TOKEN. A fine-grained GitHub token with read-only Starring user permission is enough for personal use.

Review stored items locally:

yarn view

Open http://127.0.0.1:17864. Enter ADMIN_SECRET in Sources to unlock stored items and run protected source actions.

Regenerate Markdown from already stored local data without calling remote APIs:

yarn export:md

Data Rules

Local data is stored under .data/. Markdown files are written to outputs/daily/YYYY-MM-DD.md.

Daily Markdown files are grouped by item creation date:

  • X bookmarks use the original post created_at when available.
  • GitHub stars use starred_at.
  • discoveredAt records when the item was first seen by this app.

Sync Limits

The local CLI sync can walk available pages until already-known items are found. The Cloudflare Worker uses SYNC_MAX_PAGES_PER_SOURCE to avoid Worker request limits. The default is 2.

First large sync: use First sync in the deployed Admin dialog. It performs a deeper full scan and does not stop just because the newest page is already in the database. Daily scheduled syncs keep SYNC_MAX_PAGES_PER_SOURCE conservative and stop once a fully known page is reached.

Cloudflare Deployment

Run the setup script:

yarn install
yarn cf:setup

The script copies wrangler.example.toml to wrangler.toml when needed, creates the inbox D1 database, and writes its id back to wrangler.toml.

If you are starting over from an existing wrangler.toml, run:

yarn cf:setup -- --force

If you are not logged in to Cloudflare yet, run this first:

yarn wrangler login

Set ADMIN_SECRET, plus the source secrets you use:

yarn wrangler secret put ADMIN_SECRET
yarn wrangler secret put X_CLIENT_ID
yarn wrangler secret put X_CLIENT_SECRET
yarn wrangler secret put GITHUB_TOKEN

ADMIN_SECRET protects real saved-item reads and writes, manual sync, and one-time X authorization. X_CLIENT_ID and X_CLIENT_SECRET are only needed for X bookmarks. GITHUB_TOKEN is only needed for GitHub stars. Scheduled cron runs do not need an authorization header.

Build, migrate, and deploy:

yarn cf:release

Manual setup is also possible with Wrangler:

cp wrangler.example.toml wrangler.toml
yarn wrangler d1 create inbox
yarn wrangler d1 migrations apply DB --remote
yarn build
yarn wrangler deploy

To deploy a public demo Worker without D1 or source credentials:

yarn cf:deploy-demo

The demo serves built-in sample items from DEMO_MODE and does not persist visitor changes.

If you use X on Cloudflare, set the X Developer Portal callback URL to:

https://<your-worker-url>/api/auth/x/callback

Then open the deployed review page, click Admin, enter ADMIN_SECRET, and use Authorize X. The page starts the protected one-time OAuth flow without putting the secret in the authorization URL. The Worker stores the resulting X token in D1 and refreshes it during later syncs.

The default cron is 0 21 * * *, which runs at 21:00 UTC and 05:00 in UTC+8. Adjust wrangler.toml if you want another time.

Run a manual sync:

curl -X POST https://<your-worker-url>/api/sync \
  -H "Authorization: Bearer <ADMIN_SECRET>"

The deployed review page also has protected admin controls for:

  • Authorize X: start the one-time X OAuth flow.
  • Sync all: sync all configured sources with the normal page limit.
  • Sync X / Sync GitHub: sync one source.
  • First sync: sync all configured sources with a deeper full-scan page limit.

The same behavior is available through query parameters:

curl -X POST "https://<your-worker-url>/api/sync?source=github&maxPages=50&fullScan=true" \
  -H "Authorization: Bearer <ADMIN_SECRET>"

source can be all, x, or github. maxPages controls how many pages each source may fetch for that run. fullScan=true is useful for backfilling older items when the newest page has already been synced.

Vercel Deployment

Vercel uses the same Vite review UI and /api/* endpoints, backed by Postgres instead of Cloudflare D1.

Create a Postgres database through Vercel, Neon, Supabase, or another hosted provider, then set these environment variables in Vercel:

POSTGRES_URL=<postgres connection string>
ADMIN_SECRET=<admin password for protected UI actions>
GITHUB_TOKEN=<optional GitHub token>
X_CLIENT_ID=<optional X app client id>
X_CLIENT_SECRET=<optional X app client secret>
SYNC_MAX_PAGES_PER_SOURCE=2

If you use X on Vercel, set the X Developer Portal callback URL to:

https://<your-vercel-domain>/api/auth/x/callback

Apply the SQL files in migrations/ in filename order to initialize or upgrade the Postgres database. The table and index names are intentionally shared with D1 so the app logic can run against either store.

The Vercel build command is:

yarn build

The included vercel.json publishes dist/view and schedules daily sync via:

/api/cron/sync

Manual sync uses the same API shape as Cloudflare:

curl -X POST "https://<your-vercel-domain>/api/sync?source=github&maxPages=50&fullScan=true" \
  -H "Authorization: Bearer <ADMIN_SECRET>"

Open Source Checklist

  • Keep .env, .data/, outputs/, and wrangler.toml private.
  • Publish wrangler.example.toml, not your real Wrangler config.
  • Use Source Adapter Guide when adding more saved-item sources.
  • Rotate X_CLIENT_SECRET, GITHUB_TOKEN, and ADMIN_SECRET if they are ever exposed.
  • The project is licensed under MIT. Keep LICENSE in published copies.

Optional AI Summary

Set SUMMARY_API_KEY in .env to enable daily summary and action item generation. SUMMARY_MODEL and SUMMARY_BASE_URL configure the model and provider-compatible endpoint. If the key is absent, sync still writes the raw Markdown list.