Skip to content

Where your data lives

All your links live in a single Cloudflare D1 database (SQLite) named linkbrain, bound to the Worker as env.linkbrain. There’s no other state.

The source of truth for the data shape is the migrations/ directory — 0001_init.sql for the core columns and 0003_metadata.sql for the metadata columns below. A saved link is one row:

| Column | Notes | |--------|-------| | id | Integer primary key | | url | The link — unique (this is how duplicates are rejected) | | title | Resolved title (OG title, <title>, or GitHub full name) | | summary | AI-written summary, or scraped description as fallback | | tags | Space-delimited, single-token tags (e.g. #api-gateway #backend) | | note | Your personal note, if you added one | | kind | github or web | | saved_at | Timestamp, defaults to insert time |

Plus metadata columns (all default-valued, so old rows stay valid):

| Column | Tier | Notes | |--------|------|-------| | domain | auto | Hostname of the URL | | image, site_name, author, published_at | auto | Scraped OpenGraph/article metadata (web links) | | stars, language, license | auto | GitHub repo metadata | | category | AI | Single bucket (article/video/repo/tool/paper/…) | | key_points | AI | JSON array of 2–4 takeaways | | details | AI | 3–5 sentence synopsis from the full content (article body / GitHub README) | | status | you | unread / reading / read / archived | | favorite | you | 0 / 1 | | collection | you | Free-form grouping name | | remind_at | you | YYYY-MM-DD to resurface (see /due) | | shareable | you | 0 / 11 publishes the link to your List (/list) |

Alongside links is an FTS5 virtual table, links_fts, indexing the URL, title, summary, tags, and note. It’s an external-content table kept in sync by an AFTER INSERT trigger on links. /find runs a MATCH query against it ordered by relevance.

Schema and data changes ship as checked-in SQL files in migrations/, applied with wrangler d1 migrations apply linkbrain — never a manual “run this once” step. 0001_init.sql creates the tables and trigger; 0002_seed.sql carries over the links that existed under the original local SQLite bot, using INSERT OR IGNORE so re-runs are no-ops.

Query the database directly with wrangler:

Terminal window
# remote (production)
npx wrangler d1 execute linkbrain --remote --command "SELECT COUNT(*) FROM links"
# local dev database
npx wrangler d1 execute linkbrain --local --command "SELECT url, tags FROM links"