Cloudflare Pages deploy
Sadie deploys to Cloudflare Pages as a single worker. OpenNext compiles the Next.js 15 App Router build into a Cloudflare-compatible bundle; wrangler pages deploy pushes assets. The whole thing is one pnpm deploy command.
Prerequisites
Section titled “Prerequisites”- A Cloudflare account with Workers + Pages enabled.
wranglerauthenticated (npx wrangler login).- A Neon project with
DATABASE_URLready. See Neon. - Two R2 buckets (the build references them in
wrangler.jsonc):
npx wrangler r2 bucket create sadie-uploadsnpx wrangler r2 bucket create sadie-opennext-cacheBuild + deploy
Section titled “Build + deploy”From the repo root:
pnpm --filter @sadie/app build:cf # runs `next build && @opennextjs/cloudflare build`pnpm --filter @sadie/app deploy # `wrangler pages deploy .open-next/assets --project-name=sadie --branch=main`The two commands are bundled as one in the app’s deploy script. pnpm --filter @sadie/app deploy does both. Behind the scenes:
next buildproduces the standard Next output.@opennextjs/cloudflare buildreadsapps/app/open-next.config.ts, rewrites the output into.open-next/, wires up R2 incremental cache + Durable Object tag cache + DO queue.wrangler pages deploypushes.open-next/assetswith the worker pointed at.open-next/worker.jsas declared inapps/app/wrangler.jsonc.
Required environment variables (Pages)
Section titled “Required environment variables (Pages)”Set these in the Cloudflare dashboard under Workers & Pages → sadie → Settings → Variables (or via wrangler pages secret put for secrets).
| Var | Note |
|---|---|
DATABASE_URL | Neon pooled connection string. |
SADIE_ENCRYPTION_KEY | 32-byte hex or base64. Required in production. The app refuses to boot without it. |
SADIE_CRON_SECRET | Required if you run the scheduler worker. |
ANTHROPIC_API_KEY / OPENAI_API_KEY | At least one, unless your users supply their own keys in Settings. |
AI_FRONTIER_* (optional) | Pin specific models per tier. Defaults are fine for most deploys. |
The collab websocket: the one out-of-process piece
Section titled “The collab websocket: the one out-of-process piece”Studio multiplayer uses Yjs with a y-websocket server at scripts/collab-server.mjs. Cloudflare Pages is a request/response worker and cannot host a persistent websocket process. Two options:
- Skip Studio multiplayer. Solo editing works without a websocket;
y-indexeddbpersists drafts locally. SetNEXT_PUBLIC_COLLAB_WS_URLto an unreachable host (or leave the default) and the client silently falls back to local-only. - Run the collab server elsewhere. Any VM or container with a public WS endpoint works. Set
NEXT_PUBLIC_COLLAB_WS_URL=wss://your-host:1234and restartwrangler pages deployso the updated client picks it up.
A Cloudflare Durable Object port of the collab server is a reasonable future, but not today’s deploy.
Optional: scheduler worker
Section titled “Optional: scheduler worker”Compile runs, feed refresh, and agent evolution fire on a cron. The scheduler is a separate Cloudflare Worker at scripts/cloudflare/worker.ts.
cd scripts/cloudflarenpx wrangler secret put SADIE_CRON_SECRETnpx wrangler secret put SADIE_APP_URL # e.g. https://sadie.example.comnpx wrangler deployOn its configured cron, the worker POSTs to /api/compile/run and /api/feeds/refresh-all on the app, passing the shared secret as a bearer token. Both endpoints authenticate via SADIE_CRON_SECRET (see apps/app/lib/cron.ts).
Custom domain
Section titled “Custom domain”apps/app/wrangler.jsonc already declares a route for sadie.wiki. Change that block to your own domain:
"routes": [ { "pattern": "your-domain.example", "custom_domain": true }]Then add the domain in the Cloudflare dashboard and DNS-verify.
Verifying a deploy
Section titled “Verifying a deploy”After the first deploy, hit the app from any browser. You should land on /onboarding/frame. If you see a Next.js error, check wrangler tail sadie for runtime logs. The most common misconfiguration is a missing SADIE_ENCRYPTION_KEY.