Stored in Git
Service names, D1, R2 and KV bindings, rate-limit namespaces, Cron expressions, routes, compatibility dates, and other public settings.
How each repo is tested and deployed, where secrets are stored, and what health checks tell us.
Each repository tests and deploys its own code. Changing the wiki does not deploy the API. Fixing email does not rebuild the website.
When a change affects more than one service, both sides must work while they are deployed at different times.
System flow
Each repository runs its own checks. Most services deploy from main to Cloudflare. The VPS Gateway deploys through Git and systemd.
Deploy a Cloudflare Worker
Deploy the API
Deploy the wiki
Deploy the VPS Gateway
| Repository | Checks | Deploys to | Extra step |
|---|---|---|---|
website | Tests, Astro build, TypeScript, React Doctor, deploy artifact check | Cloudflare Worker | Uses the Astro Cloudflare server entrypoint and API service binding. |
api | Tests, TypeScript, dry-run Worker build, React Doctor | Cloudflare Worker | Applies D1 migrations before the normal deploy. |
discord | Worker tests, TypeScript, ESLint, Worker and Gateway builds | Cloudflare Worker | This workflow does not deploy the VPS Gateway. |
email-worker | Parser tests, TypeScript, dry-run Worker build, React Doctor | Cloudflare Worker | Keeps the five-minute receipt retry Cron and KV binding. |
scheduler-worker | TypeScript, dry-run Worker build, React Doctor | Cloudflare Worker | Keeps the thirty-minute Cron and API binding. |
wiki | Content/UI tests and Astro static build | Cloudflare Pages | Builds Pagefind search data into the static output. |
The Worker workflows use Node 22. The wiki Pages workflow uses Node 24. Each Wrangler file sets its own runtime compatibility date and flags. The CI Node version does not set them.
main deploysMost repositories follow these steps:
main, sign in to Cloudflare and deploy.Some pipelines also audit dependencies and check for secrets. These checks can catch common mistakes, but reviewers must still check authorization, input validation, and new trust boundaries.
The normal API deployment command applies remote D1 migrations before it publishes the new Worker.
This gives new code the tables and columns it needs. It also means the migration must work with the old Worker until the new Worker is live.
For a safe schema change:
The API normally binds to the Discord Worker. In a new environment, the API expects the ppc-bot service, but the Discord Worker also expects the API. Neither service exists yet.
The API has a bootstrap deployment that creates a temporary Wrangler file without the Discord binding. This lets the API deploy first. The Discord Worker deploys next, and then the API deploys normally with the binding restored.
Use bootstrap only to create an environment. It is not the normal production setup.
The bootstrap setup cannot send Discord events. Check that the normal binding is restored after both services exist.
The Discord repository builds the Worker and the Gateway. Its Cloudflare workflow deploys only the Worker.
The Gateway uses these steps:
dist folder into a small deployment repository.A successful Worker deployment does not prove that the VPS process, websocket, forwarding allowlist, Tunnel/VPC route, or systemd service works.
A healthy Gateway can also send an event that the Worker rejects because its secret, timestamp, nonce, or event contract is wrong.
Public settings can stay in the repository. Secrets cannot.
Stored in Git
Service names, D1, R2 and KV bindings, rate-limit namespaces, Cron expressions, routes, compatibility dates, and other public settings.
Stored as secrets
Auth secrets, OAuth credentials, internal tokens, signing secrets, Discord tokens, Resend keys, Turnstile keys, and external API credentials.
Set when deployed
Service bindings, custom domains, VPC routes, Email Routing, and VPS environment values that connect the services.
Secrets are set through Wrangler or the server environment. They are never hardcoded in TypeScript. Shared values must match in both services, but must never appear in a public variable or browser bundle.
A feature may cross several service boundaries. Check each boundary it uses.
All five Worker services enable Cloudflare observability with a 10% head-sampling rate. The API and Discord Worker use structured logs that hide common credential fields.
Useful log fields include:
Sampling means that not every request gets a trace. Error logs should include enough limited context to investigate, but never cookies, authorization headers, raw secrets, full receipt bodies, or private user data.
There is no single trace that follows a request through every service. To investigate a problem, you may need to match timestamps and safe IDs across Cloudflare and VPS logs.
A health endpoint tests one service, not the full flow. It does not test every dependency or allowlist.
For example:
Test the same path that the user action takes.
The code and Wrangler files show the planned setup. They do not show live secret values, Email Routing rules, the VPS environment, Tunnel/VPC state, or domain changes made outside Git.
Check the live resource when you need those details. A route in the repository does not prove that the deployed route works.