DEPLOY-PAGES.md — Cloudflare Pages (trading-tyno.pages.dev)
Production URL: https://trading-tyno.pages.dev — the only host since
2026-11-03, when trading.tynolabs.com, dsh.tynolabs.com and
terminal.tynolabs.com were removed from the Cloudflare tunnel and their DNS
records deleted (only ssh.tynolabs.com remains on the tunnel). No custom
domain is attached. Same source tree, SvelteKit SSR on Workers + Turso (libSQL);
the default bun run build (Bun server) still exists for a self-hosted checkout.
Build / deploy
master is connected to the Pages project through the Cloudflare GitHub App:
a push deploys. Any other branch produces a preview deployment.
git push origin master # → Pages build → production
bun run build # Bun server → build/ (self-hosted only)
ADAPTER=cloudflare bun run build # what Pages runs → .svelte-kit/cloudflare
bunx wrangler pages deploy --project-name trading-tyno # manual fallback
Pages dashboard: build command ADAPTER=cloudflare bun run build, output
.svelte-kit/cloudflare, BUN_VERSION=1.4.4 (matches .bun-version).
wrangler.jsonc holds the project name, compatibility flags and the plain
vars. TURSO_DATABASE_URL must stay in vars: wrangler pages deploy
replaces the project's plain-text vars with that block, so a value set only in
the dashboard is silently dropped on the next deploy (secrets survive). Without
it db.ts falls back to file:, which the Workers client rejects — every route
returns an empty 500. .dev.vars (gitignored) is the local equivalent for
wrangler pages dev.
Secrets (set once, never committed)
bunx wrangler pages secret put TURSO_AUTH_TOKEN --project-name trading-tyno
bunx wrangler pages secret put BETTER_AUTH_SECRET --project-name trading-tyno
bunx wrangler pages secret put CAPTURE_LLM_KEY --project-name trading-tyno
TURSO_DATABASE_URL is not a secret here: it is a plain var in
wrangler.jsonc (see above), which is what makes it survive a wrangler deploy.
ORIGIN must be exactly https://trading-tyno.pages.dev. If a custom domain is
added later, ORIGIN moves with it. Better Auth rejects /api/auth/* whose request origin differs
from baseURL, and SvelteKit's CSRF check 403s every form POST — a mismatch
shows up as a 404 on sign-in, not an obvious error.
Data
- Turso
aws-ap-south-1(Mumbai, ~16 ms/query) —libsql://trading-skpawar1305.aws-ap-south-1.turso.io. The olderaws-eu-west-1DB is ~145 ms/query from India and is not used. - Schema: the live SQLite schema copied verbatim (
sqlite_master), because the committed drizzle chain is not replayable (0016writesREFERENCES order(id)unquoted).drizzle/is still the source of truth for new migrations. - Import is FK-safe: orphan rows are skipped and reported. Production carries
test residue (
quote.party_id = 'c67-save-party', 1340 rows from the suite running againstdata.db), which Turso rejects. - Uploads are rows in
upload_file(bin, name, mime, size, base64 bytes) in the same database — no object store and no filesystem. The client shrinks images to ~1600 px / q0.75 before sending (src/lib/imageShrink.ts), because the Worker must base64-encode the body inside its 10 ms CPU budget; the server caps every bin at 2 MB.
Known gaps on Workers
- OCR / PDF capture (
/capture,/api/capture-ocr):tesseract,magickandmutoolare host CLIs; there is no equivalent on Workers. The LLM gateway path works, the local fallback and PDF rasterising do not. - No filesystem: anything that reaches for
node:fsat request time must go through a bundled copy (import.meta.glob) or theupload_filetable. - Worker CPU/body limits: uploads are capped at 2 MB per file because the
base64 encode happens inside the 10 ms budget — raising the cap without
re-measuring will reintroduce
error code: 1102. - Worker CPU: the free plan allows 10 ms of CPU per request. It first bit five
screens (
error code: 1102); both causes were N+1 reads, not SSR —/hishob's per-contract legs preview and/weft-issue's full-tablebeam/orderselects. Batched, all 81 screens fit again (86/86 live-check), so no plan upgrade is required. If a future screen trips 1102, fix the query shape before paying.
Backups
Production's only durable copy is Turso, so the restore point is the db-backup
GitHub workflow: nightly at 20:30 UTC (02:00 IST) it dumps every table to one
gzipped JSON per table plus a manifest (~1.3 MB, 58 tables, 33k rows) and keeps
it as a workflow artifact for 14 days. Run it by hand from the Actions tab before
a risky change. The phone's backup unit snapshots a file production no longer
reads, so it protects nothing.
# restore into whatever TURSO_* points at: wipes, then inserts parents-first
export TURSO_DATABASE_URL=libsql://… TURSO_AUTH_TOKEN=…
bun scripts/db-snapshot.ts # dump → ./snapshot/<day>/
bun scripts/db-restore.ts snapshot/<day> # restore that snapshot
A Cloudflare Worker cron was tried first and abandoned: serialising the dump exceeds the 10 ms CPU budget, and a Pages project has no cron triggers.