Author: hermes · Date: 2026-07-28 · Tier: T-deep (decision-grade — every load-bearing claim verified against a primary source this session; source ledger at the end) Requirement, as stated: "a user authentication CMS system where our clients can log in and upload data and add their own data and publish blog posts." Prior arc reviewed:
research/cms-basketball-vercel-handover-2026-07-15/(arm 1, handover) ·research/cms-managed-hosting-model-2026-07-15/(arm 2, managed) ·research/directus-cms-2026-05-22/· decisionhermes#116This is a delta re-read, not a restart. The 13-day-old arc did the landscape work. One thing changed underneath it, and it changes the answer.
The prior CMS verdict stands: Payload v3. Nothing found this session weakens it. Directus is still the better turnkey editor over an existing Postgres; Payload is still the only candidate that runs natively on Cloudflare compute and keeps both business models (handover + managed) open.
What changed: Payload now ships an official Cloudflare D1 adapter. @payloadcms/db-d1-sqlite (3.85.0) plus a first-party with-cloudflare-d1 template and a Deploy to Cloudflare button that provisions the Worker, a D1 database and an R2 bucket, pre-bound. The 2026-07-15 report assumed Payload-on-Workers must reach a Supabase Postgres via Hyperdrive. That assumption is now optional, and an all-Cloudflare path with no Supabase at all is real.
"Cloudflare vs Supabase" is not the fork. Cloudflare is compute + CDN + object storage; Supabase is Postgres + Auth + Storage. They overlap only in data and identity. The real fork is: does the client's data live in D1 (SQLite, inside Cloudflare) or in Supabase Postgres (outside it)?
On the requirement as written, Supabase's strongest card is never played. "Clients log in, upload, publish" is editor-class auth, which Payload's own user store and role system already provides — the 07-15 arc ruled exactly this (T2: do not stand up Supabase Auth for content editors). Supabase Auth only becomes load-bearing if the public site later needs member accounts. Cloudflare has no consumer-identity equivalent — but the claim needs sharpening, and did not have it when first written (re-checked 2026-07-28 under the brief's null-search rule). Cloudflare does now ship a first-party IdP: Cloudflare as identity provider (changelog 2026-05-19), default login method for new Zero Trust organisations since 2026-06-18. It authenticates users with their existing Cloudflare account, and "authentication is restricted to members of your account" — so it is workforce/Access identity, not CIAM for your app's end users. Access remains seat-priced (free ≤50, then $7/user/mo). searched: “Cloudflare identity product for application end users 2026 sign-in customer accounts” · “Cloudflare Workers authentication library D1 sessions users 2026 official” · “Cloudflare 2026 first-party user authentication product Workers better-auth OR OpenAuth OR CIAM end user login D1”. as-of: 2026-07-28.
Cost at fleet scale is not close. All-Cloudflare: $5/month per account (not per site) + usage, with 5 GB D1 storage, 25 B rows read and 50 M rows written included, and 50,000 D1 databases per account. Supabase: $25/month plus roughly $10/month of compute per additional project. At 10 client sites that is ≈$5–15/mo vs ≈$115/mo.
Recommendation — all-Cloudflare becomes the default tier; Supabase becomes the deliberate upgrade. This refines hermes#116 option 3 (tier by complexity) rather than overturning it — same shape, different contents. Trigger to upgrade a client to the Supabase tier: public end-user accounts, relational/reporting workloads, or a contractual portability guarantee that wants pg_dump.
| Prior finding | Status today | Note |
|---|---|---|
| Payload v3 is the CMS pick (both arms) | ✅ holds, strengthened | The D1 adapter removes its last dependency on a second vendor |
| Directus is the most Supabase-data-native, but needs an always-on VM | ✅ holds — re-checked under rule #18 §3 | Two different disqualifiers, previously conflated as "cannot run on Cloudflare compute": (a) Workers — capability-absent, and vendor-documented as to why: Directus needs persistent DB/Redis connections, a local filesystem and persistent memory, none of which the Workers runtime provides (directus/directus discussion #15572); self-hosting requirements are Node 22 + Docker. (b) Containers — cost-disqualified, not capability-disqualified: a Directus image can run there; Containers bill for bursty/sleep-capable workloads, which an always-on CMS is not. Decomposed per the ratified §3 — each half carries its own date because they perish on different clocks: CLAIM A Directus runtime requirements (persistent connections / filesystem / memory; Node 22 + Docker) — SEARCHED: “Directus changelog 2026 hosting requirements Node runtime edge deployment” · AS-OF: 2026-07-28. CLAIM B Cloudflare compute capabilities (Workers runtime; Containers billing shape) — SEARCHED: “Directus deploy Cloudflare Workers Containers 2026 self-host serverless support” · AS-OF: 2026-07-28. A changes on Directus's release cadence; B changes whenever Cloudflare ships or re-prices. |
| Do not stand up Supabase Auth for content editors — the CMS's own invite/role flow is the login | ✅ holds, and is the key to this decision | CMS↔Supabase-Auth SSO reconciliation is paid/enterprise on all three; do not walk into it |
| One isolated CMS instance per client, not shared multi-tenancy | ✅ holds | D1's 50,000-databases-per-account limit makes per-client isolation cheaper, not more expensive |
| Cloudflare Containers are not cost-effective for always-on Directus | ✅ holds | Unchanged since GA 2026-04-13 |
| Media on R2 (no egress fees) | ✅ holds | Now provisioned automatically by the Payload template |
| Managed model, $600–1,200/yr, with a mandatory data-export exit clause | ✅ holds — but see §4 | The exit clause is harder to honour from SQLite than from Postgres. This is the one place the new default costs you something |
hermes#116: Vite+Supabase vs Next+Payload as house default — open, leaning "tier both" |
🔄 this report is the missing input | The Minos build proved the light tier; this proves the heavy tier can be cheaper than assumed |
Reality anchor from hermes#116: the first real client (Minos / Neukölln Minotaurs) was an existing Vite+React+Supabase SPA, and a Supabase-direct in-app CMS was simpler and sufficient. That case is unaffected by anything here — you do not migrate an existing Supabase app to D1. Everything below is about the greenfield default.
@payloadcms/db-d1-sqlite is the official D1 adapter (latest 3.85.0), built on Payload's SQLite/Drizzle path.payloadcms/payload/templates/with-cloudflare-d1, with a Deploy to Cloudflare button that "generates a fully functional Payload instance, including a D1 database and R2 bucket automatically bound to your worker."Agents Week 2026 (13–17 April) was an agent-infrastructure event: Dynamic Workers, Sandboxes GA, Mesh, AI Gateway as a unified inference layer, Artifacts (git-compatible versioned storage), Email Service beta, and a new unified cf CLI. None of it changes a CMS build. The one adjacent item is Managed OAuth for Access (open beta, changelog 2026-03-20): it lets non-browser clients — CLIs, agents, SDKs — authenticate to Access-protected apps via standard OAuth 2.0. Per Cloudflare's own docs it is "a new transport mechanism, not a separate authentication path" — Access policies still govern, and the identity still comes from an IdP. It is not consumer login.
June 2026: a $500 M Series F, Multigres 0.1, passkey auth (WebAuthn; only the public key is stored server-side), a ChatGPT integration with 29 management tools, and token-based database access. Pricing is unchanged in shape: Free (paused after 1 week idle, 2 active projects), Pro $25/mo (100,000 MAU included, 8 GB disk/project, 100 GB storage, 250 GB egress, one Micro instance via a $10/mo credit), Team $599/mo.
Read against our requirement: none of it is needed for editor login — all of it matters the moment the public site needs member accounts. That is precisely the upgrade trigger in §4.
| Axis | All-Cloudflare (Workers + D1 + R2 + Payload) | Cloudflare + Supabase (Workers + Hyperdrive→Postgres + R2/Supabase Storage) |
|---|---|---|
| Client/editor login ("log in… publish") | Payload's own user store, invites, roles | Identical — Payload's own auth. Supabase Auth adds nothing here |
| Public member accounts (not asked for, plausible later) | ❌ no first-party CIAM. Cloudflare's own IdP (2026-05-19, default since 06-18) is Access-scoped and restricted to account members. The real path is third-party on Workers — Better Auth has a maintained Cloudflare integration (better-auth-cloudflare: D1, KV, Hyperdrive, migrations), which is a build you own and patch |
✅✅ Supabase Auth — 100,000 MAU on Pro, passkeys, MFA, OAuth providers, and RLS wired to the same identity |
| Data store | D1 (SQLite): 10 GB/database (Paid), 5 GB storage and 50,000 databases included per account, unlimited rows per table, single-threaded per database, read replication in beta | Postgres: 8 GB disk/project included then $0.125/GB, RLS, extensions, full-text, pgvector, realtime, branching |
| Write concurrency | One writer per database, serialized. Irrelevant for a blog/CMS; disqualifying for a write-heavy app | Real concurrency |
| Uploads / media | R2, auto-bound by the template, no egress fees | R2 the same, or Supabase Storage (RLS-aware, image transforms) with 100 GB included and $0.09/GB egress beyond 250 GB |
| Backups | Time Travel: always on, no extra cost, restore to any minute in the last 30 days (7 on Free) | Pro: automated daily backups (14-day retention on Team); pg_dump any time |
| Moving parts per site | 1 vendor, 1 compute, 0 extra hosts | 2 vendors |
| Cost, 10 client sites | ≈$5–15/mo total ($5/account minimum + usage; per-site marginal cost ≈ $0 at this scale) | ≈$115/mo ($25 + ~$10/mo compute × 9 extra projects) |
| Exit / portability (contractual clause) | SQLite export — workable, but "here is your database" means a .sqlite file plus R2 objects |
pg_dump to any Postgres host. Strictly the more credible promise to write into a contract |
| Vendor concentration | DNS + CDN + compute + data + storage all Cloudflare. Operationally simple; a single blast radius | Data survives a Cloudflare-side decision; a second bill and a second status page |
| Known blockers | Paid Workers required; GraphQL not guaranteed on Workers | Hyperdrive hop adds a moving part; free-tier projects pause, so Pro is the floor for live sites |
The ~$100/mo difference at 10 sites is not the interesting part — it is ~$1,400/yr against upkeep revenue the prior arc priced at $600–1,200/yr per client. At 10 clients that is $6,000–12,000/yr of revenue, so infra is a rounding error in both models. The gap matters for a different reason: it is the difference between "a new client site costs me nothing to stand up" and "a new client site costs me $10/mo before anyone has visited it." That changes how freely you can take on small clients, pilots and unpaid prospects — which is a business-shape question, not an accounting one.
For the requirement as stated — clients log in, upload data, add their own data, publish blog posts — go all-Cloudflare: Payload v3 on Workers + D1 + R2.
Reasoning, in order of weight:
Upgrade a client to the Supabase tier when any of these is true (each is a real trigger, not a hypothetical):
pg_dump rather than a SQLite file.What this does to hermes#116: it keeps option 3 (tier by complexity) but rewrites both tiers. The light tier is Supabase-direct in-app CMS for clients who already have a Supabase app (proven by Minos). The default greenfield tier becomes Payload on Workers + D1 + R2. The Supabase-Postgres-under-Payload combination — the 07-15 recommendation — survives only as the upgrade configuration, not the default.
| # | Claim | Source | Tier |
|---|---|---|---|
| 1 | Official Payload D1 adapter @payloadcms/db-d1-sqlite 3.85.0; template provisions Worker + D1 + R2; Paid Workers required; GraphQL not guaranteed |
payloadcms/payload templates/with-cloudflare-d1/README.md (fetched) + npm listing |
deep-read |
| 2 | D1 limits: 10 GB/db Paid, 500 MB Free; 50,000 dbs/account; 1,000 queries per invocation; single-threaded per database; read replication beta | developers.cloudflare.com/d1/platform/limits/ (fetched) |
deep-read |
| 3 | D1 pricing: 5 GB storage, 25 B rows read, 50 M rows written included on Paid; $0.75/GB-mo beyond | developers.cloudflare.com/d1/platform/pricing/ (fetched) |
deep-read |
| 4 | D1 Time Travel: always on, no extra cost, any minute within 30 days (7 on Free) | developers.cloudflare.com/d1/reference/time-travel/ (fetched) |
deep-read |
| 5 | Workers Paid: "minimum charge of $5 USD per month for an account"; 10 M requests + 30 M CPU-ms included | developers.cloudflare.com/workers/platform/pricing/ (fetched) |
deep-read |
| 6 | Supabase pricing/limits: Free (2 projects, pauses after 1 week), Pro $25 (100 k MAU, 8 GB disk, 100 GB storage, 250 GB egress, one Micro via $10 credit), Team $599 | supabase.com/pricing (fetched) |
deep-read |
| 7 | Managed OAuth for Access — open beta, OAuth 2.0 transport over existing Access policies, RFC 9728 discovery; not a separate auth path | Cloudflare changelog 2026-03-20 + Cloudflare One docs (search-surfaced summary) | snippet |
| 8 | Cloudflare Access seat model: free ≤50 users, $7/user/mo beyond, one seat per authenticating identity | multiple 2026 pricing trackers; not confirmed against Cloudflare's own pricing page this session | snippet ⚠ |
| 8b | Negative claim — no first-party Cloudflare CIAM for app end users. Re-checked under the null-search rule: Cloudflare's own IdP exists but is Access-scoped/account-member-restricted; the CIAM path on Workers is third-party (Better Auth + D1/KV) | CF changelog 2026-05-19 + 2026-06-18; better-auth-cloudflare. searched: three query strings, listed inline at the claim |
snippet, terms named |
| 9 | Agents Week 2026 releases (Dynamic Workers, Sandboxes GA, Mesh, AI Gateway, Artifacts, Email Service, cf CLI) |
cloudflare.com/agents-week/updates + secondary recaps | snippet |
| 10 | Supabase June 2026: $500 M Series F, Multigres 0.1, passkey auth, ChatGPT integration | secondary tech press | snippet ⚠ |
| 10b | Absence-based disqualifier — Directus has no Workers-runtime path (capability), and Containers is a cost disqualifier rather than a capability one. Re-checked under rule #18 §3 as ratified 2026-07-28 | Directus self-hosting requirements (Node 22/Docker) + directus/directus discussion #15572 + CF Containers docs. SEARCHED: two query strings at the claim · AS-OF: 2026-07-28 |
snippet, terms named |
| 11 | Prior-arc verdicts (Payload v3, Directus VM requirement, T2 auth ruling, per-client isolation, pricing model) | research/cms-managed-hosting-model-2026-07-15/REPORT.md, hermes#116 |
file-sourced |
Load-bearing rows are 1–6, all deep-read. Rows 8 and 10 are flagged: the Access seat price and the Supabase funding/passkey details come from secondary sources, and no verdict above rests on either — Access is ruled out on shape (workforce ZTNA, not consumer identity), which its own product docs establish, not on price.