Appearance
Deploy to production
For: putting a build in front of real players, on grafted-wars.com.
Production is the worker named grafted-wars-staging. That name is the reverse of what it reads like, and the actual staging deployment is grafted-wars-true-staging. Check which is which by the hostname it answers on, never by the name in the dashboard.
Production is never wiped. It has no reset switch, and schema changes reach it as migrations.
Preconditions
- [ ] The build has been through staging and looked right there.
- [ ] The gate is green on the commit being deployed (
npm run check). - [ ]
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_IDare set, in the environment or the repository's gitignored.envfile. - [ ] An owner token exists at
~/.grafted-wars/owner-token, if the claim route is to work.
The two arming signals
An ordinary npm run deploy:prod never touches production. The script refuses unless both arming signals are present, and exits 3 when the flag is absent.
bash
GW_FLIP_ACK=grafted-wars.com npm run deploy:prod -- --flipThe environment variable must match the domain. The flag must be passed after the double dash so npm forwards it to the script rather than consuming it.
Steps
Deploy.
bashGW_FLIP_ACK=grafted-wars.com npm run deploy:prod -- --flip- Expect: a build, a wrangler upload, then one domain-binding call per hostname, then a summary naming the worker and its bindings.
- Expect: a
curlcommand printed at the end. The script names it and does not run it, because local DNS can answer the old address for a while.
On the very first deploy of this worker only, give it its admin secret.
bashnpx wrangler secret put ADMIN_EMAIL --name grafted-wars-staging- An ordinary deploy preserves an existing secret. It is lost only if the worker itself is deleted and recreated.
Check the live edge with the command the script printed.
bashcurl https://grafted-wars.com/api/health- Expect: a JSON body reporting
ok, the worker name and the version it stamped. - If instead every request is refused: the
ADMIN_EMAILsecret is missing on this worker.
- Expect: a JSON body reporting
Check
wwwanswers too, since it is a separate binding.bashcurl https://www.grafted-wars.com/api/health
What production leaves off
The configuration file's defaults are the production-safe ones, and the production deploy does not override them.
| Setting | Production |
|---|---|
| Public signup | on, narrowed to the addresses GW_SIGNUP_ALLOW names |
| Admin routes | off |
| Dense seed | off, so the universe fills as accounts join |
| Clock rate | real time |
| Reset switch | none exists |
Because admin routes are off, the export and overview endpoints do not answer in production. Data and stores covers what that means for reading live state.
Who may sign up
Production answers /api/auth/signup, so a player joins by making an account rather than by being minted one. Which addresses it will seat is a separate question, answered by the GW_SIGNUP_ALLOW secret: a comma-separated list of email addresses, matched trimmed and case-folded the way every other address is.
bash
npx wrangler secret put GW_SIGNUP_ALLOW --name grafted-wars-staging- A secret rather than a variable in
wrangler.jsonc, because the list is made of real people's addresses and nothing commits those. - With the secret unset, an open signup accepts anyone who finds the domain. Narrowing it is the only thing standing between a public hostname and a stranger's account.
- The list narrows an open signup and never opens a closed one. To close the route entirely, deploy with
GW_PUBLIC_SIGNUP=0in the deploying environment. - A refused address gets a 403 and
EMAIL_NOT_ALLOWED; the sign-up screen states it plainly.
What is irreversible
The domain binding is the part that reaches players. Once grafted-wars.com points at the new worker, everyone is on it.
There is no rollback command in this tooling. Undoing a bad deploy means checking out a known-good commit and running the same deploy command again. Plan for that being the only route before you flip.
Rerunning the same command is safe. The upload re-sends the same build, and each binding call overrides an existing origin, so repeating a correct deploy changes nothing.
When a deploy half-lands
The upload and the domain bindings are separate calls. When the upload succeeds and a binding call then fails, one hostname can be bound while the other is not.
The script exits 1 in that case and does not undo the binding that already succeeded. The recovery is to fix the cause and run the same command again, which re-issues both bindings.
The zone
The script reads CLOUDFLARE_ZONE_ID when it is set, and otherwise falls back to a zone identifier built into it. Setting it is only needed when the zone changes.