Skip to content

Operator tooling

Operator tooling builds a release, deploys it, and holds the credentials a live grafted-wars deployment needs. It is written for a solo operator running staging and production alone, who is not a Cloudflare expert and is reading this while deploying.

From a green gate to a live deployment

  1. Start from a green gate (npm run check) on the branch being deployed.
  2. If no owner token exists yet on this machine, mint one: node scripts/owner-claim.mjs mint.
  3. Deploy to true staging: npm run deploy:staging. This needs CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID, from the environment or the repository's gitignored .env.
  4. The first time the staging worker grafted-wars-true-staging is deployed, give it its ADMIN_EMAIL secret — the worker refuses every request without one: npx wrangler secret put ADMIN_EMAIL --name grafted-wars-true-staging.
  5. Build the claim-link page and claim owner on staging: node scripts/owner-claim.mjs page <staging base url>, then open ~/rocket-showroom/grafted-wars-enter.html and follow the staging link once.
  6. Optionally, mint test accounts for a walkthrough: node tools/mint-accounts.mjs --base-url <staging base url>.
  7. When staging looks right, deploy to production: GW_FLIP_ACK=grafted-wars.com npm run deploy:prod -- --flip.
  8. The first time the production worker grafted-wars-staging is deployed, give it its own ADMIN_EMAIL secret too — it is a separate worker with a separate secret: npx wrangler secret put ADMIN_EMAIL --name grafted-wars-staging.
  9. Build a claim-link page for production (or reuse the one from step 5, adding the production URL) and claim owner there the same way.
  10. Verify the deploy — see "After a deploy" below.

What differs between staging and production

AspectStagingProduction
Commandnpm run deploy:stagingnpm run deploy:prod
Worker namegrafted-wars-true-staginggrafted-wars-staging — the two names are confusable at a glance
ArmingNone — runs on any invocationDouble-armed: needs both --flip and GW_FLIP_ACK=grafted-wars.com
Domainworkers.dev onlygrafted-wars.com and www.grafted-wars.com, bound through the Cloudflare API
Public signupOn (GW_PUBLIC_SIGNUP=1)On (GW_PUBLIC_SIGNUP=1), narrowed to the addresses named in the GW_SIGNUP_ALLOW secret
Admin surfaceReachable (ADMIN_ROUTES=1)Off (not set)
Starting seedPacked to capacity (GW_DENSE_SEED=1)Empty, fills as accounts join
Clock25x (GW_CLOCK_RATE=25)Real time (GW_CLOCK_RATE=1)
ResetGW_STAGING_RESET=<new value> wipes all user state on the next bootNo such switch

After a deploy

Both deploy scripts print a RED:-prefixed line and exit with a nonzero code on failure. A successful run ends with a one-line summary naming the worker, and for production, the domain bindings.

After a production deploy, run the curl command the script prints, or hit /api/health directly on the live hostname. The script names that command but does not run it, since local DNS can still answer the old address for a while.

A freshly deployed worker that has never had its ADMIN_EMAIL secret set answers every request with a refusal rather than a working page. Check for that before suspecting the deploy itself went wrong.

Units in this area

Every unit in operator tooling, with the page that documents it.

UnitWhat it does
deploy-prod.mjsBuilds the site, deploys the production Cloudflare Worker, and binds grafted-wars.com and its www alias to it.
deploy-staging.mjsDeploys the true staging Cloudflare Worker with the spectator-testing environment: public signup on, the admin surface reachable, the home system packed at boot, and a 25x clock.
owner-claim.mjsMints, hashes and reads the local owner admin token, and builds a one-click claim-link page for entering owner on a deployment.
mint-accounts.mjsMints throwaway grafted-wars accounts against a running deployment, one real signup-login-session cycle per account.