{ "$schema": "node_modules/wrangler/config-schema.json", // ───────────────────────────────────────────────────────────────────────── // TOP LEVEL = the generic self-host / fork target. Deploying this as-is // stands up a standalone Goldroad on your own workers.dev subdomain with your // own D1. It carries NO custom routes and only a placeholder database_id, so // a fork never inherits the hosted instance's domain or database. // • Self-hosting: replace `database_id` with your own D1 id (the Deploy to // Cloudflare button provisions one and rewrites it for you), set the // secrets from `.dev.vars.example`, and set VITE_PUBLIC_ORIGIN to your // public origin (see SELF_HOSTING.md). // Environment-specific deploys live in the `env.*` blocks below. Bindings are // non-inheritable, so each env repeats its own `d1_databases`. // ───────────────────────────────────────────────────────────────────────── "name": "goldroad", "workers_dev": true, // Off: each uploaded version would get a public URL on your live D1. Turn it // on to review PRs on preview URLs (SELF_HOSTING.md). "preview_urls": false, "compatibility_date": "2025-09-02", // NOTE: global_fetch_strictly_public was tried here as defense-in-depth for // /api/import (writer-supplied feed URLs; same-zone fetches otherwise skip // the platform front door). It broke outbound fetches to Cloudflare-fronted // APIs on the deployed worker (handle resolution against public.api.bsky.app // started failing), so it was removed — the app-level guard that refuses our // own hostnames is the SSRF defense. "compatibility_flags": ["nodejs_compat"], "observability": { "enabled": true, "head_sampling_rate": 1 }, // Custom entry: canonical-hostname 301s in front of the Start handler. "main": "src/server.ts", // ONE hourly cron (free tier: ≤5 triggers/account) multiplexing every // scheduled job: publish due scheduled posts, purge expired oauth_kv state // rows, sample follower counts, check the backup heartbeat, alert on new // abuse reports, and self-check core invariants — alerting via the optional // WEBHOOK_URL secret. Adding a job means adding it to the handler, NOT // adding a trigger. // Handler: scheduled() in src/server.ts → runScheduled() in ~/lib/scheduled. "triggers": { "crons": ["0 * * * *"] }, "d1_databases": [ { "binding": "DB", "database_name": "goldroad-db", // Placeholder — replace with your own D1 database id when self-hosting. // NOTHING REAL USES THIS. Both envs below carry their own id (d1 // bindings are non-inheritable), so any wrangler d1 command WITHOUT // `--env` resolves to this placeholder and fails with "database // 00000000-... could not be found". That is the intended outcome — it is // a loud failure rather than a migration applied to the wrong database — // but it means a hosted d1 command needs its env: see db:migrate:prod and // db:migrate:staging in package.json. db:migrate:selfhost is the one that // deliberately omits `--env` and so lands here, on the id a self-hoster // has replaced with their own. "database_id": "00000000-0000-0000-0000-000000000000", "migrations_dir": "drizzle" } ], "env": { // Staging: main-branch channel, Access-gated workers.dev URL, own D1. // The staging database is provisioned by the operator; until then this id // is a placeholder. "staging": { "name": "goldroad-staging", "workers_dev": true, "preview_urls": true, "observability": { "enabled": true, "head_sampling_rate": 1 }, "triggers": { "crons": ["0 * * * *"] }, "d1_databases": [ { "binding": "DB", "database_name": "goldroad-db-staging", "database_id": "3d671556-7602-462a-a327-b0da05344972", "migrations_dir": "drizzle" } ] }, // Production: release-branch channel, serves the canonical zone route. "production": { "name": "goldroad", "workers_dev": true, // Off: each version would get a public URL on the production D1. PR previews are staging's. "preview_urls": false, // Both hostnames are routed on purpose. `www` resolves at the edge // already, so without a route here Cloudflare matched nothing and served // its own 404 — the request never reached the Worker, and every shared // link carrying `www.` was dead. With the route, `canonicalRedirect` // (~/lib/origin) 301s it to the apex like any other non-canonical host. "routes": [ { "pattern": "trygoldroad.com/*", "zone_name": "trygoldroad.com" }, { "pattern": "www.trygoldroad.com/*", "zone_name": "trygoldroad.com" } ], "observability": { "enabled": true, "head_sampling_rate": 1 }, "triggers": { "crons": ["0 * * * *"] }, "d1_databases": [ { "binding": "DB", "database_name": "goldroad-db", "database_id": "7cf27d45-076c-4cda-8057-ddae78160b1a", "migrations_dir": "drizzle" } ] } } }