Architecture: server-first by design
Read this before you write code, especially if you're coming from Supabase. The flarelink.from(…) builder looks familiar, but the trust model is deliberately different.
flarelink.auth.* only. Database and storage require the service key and run on your server. There is no row-level security yet — your server route is the authorization boundary, so scope every query to the signed-in user (where: { author_id: me.id }). If you've been leaning on Supabase RLS to let the browser query the DB directly, that pattern doesn't exist here.Two planes: control vs. data
Flarelink is a control plane. The dashboard talks to Cloudflare's REST API with your token to create and configure resources — a D1 database, a KV namespace, an R2 bucket, your auth Worker — and then walks away. Everything it provisions lives on your Cloudflare account.
Your app's runtime traffic is the data plane, and Flarelink is never in it. Your users hit your auth Worker (on your account) for sign-in; your app reads its own D1; browsers upload straight to your R2. If Flarelink disappeared tomorrow, your deployed app keeps running unchanged. (This is also why "leaving is a non-event" — see Trust & verification.)
- Control plane (Flarelink): provisioning, the table editor, the SQL console, config writes. Uses your CF API token.
- Data plane (your account): the auth Worker, D1, KV sessions, R2. Flarelink never proxies this.
Who can call what
| Surface | Browser | Server (with service key) |
|---|---|---|
flarelink.auth.* | ✓ yes | ✓ yes (forward cookies) |
flarelink.from(…) / .sql\`…\` | ✗ no | ✓ yes |
flarelink.storage.* | ✗ no (bytes go browser → R2 via presigned URL) | ✓ yes |
The service key grants full DB + R2 access for the project. Constructing a client with it in the browser would leak it — so flarelink.from(…) / flarelink.storage.* throw MissingServiceKeyError if you forget the key, and you should never put the key in a client bundle. Auth is the exception: it's safe everywhere because the session cookie, not a static secret, is the credential.
No row-level security (yet)
Supabase lets the browser query Postgres directly because RLS policies enforce per-row access in the database. Flarelink has no equivalent today, which is exactly why database access is server-only: with no in-database policy layer, the only safe place to enforce "user A can't read user B's rows" is your own server code.
The practical rule:
Browser-side queries with row-level security policies are on the roadmap; until then, treat the server route as the boundary. Each framework's protect-a-route recipe shows the pattern.
Sessions live in KV
The auth Worker stores sessions in KV, never D1 — auth checks happen on every request, and KV reads are sub-millisecond and cheap, whereas a D1 row-read per request adds up. One consequence: KV is eventually consistent, so a just-signed-out session can validate for a short window at a far edge. See Limits & semantics for the details.
What this buys you
- No lock-in. Everything runs on your Cloudflare account at Cloudflare's prices. Flarelink takes no cut and isn't a dependency at runtime.
- No data-processor relationship. Your users' data never touches Flarelink's servers — it's in your D1 and your R2.
- Verifiable auth. The auth Worker is source-available and you can confirm the deployed bundle byte-for-byte — see Trust & verification.