Skip to content

Deploy to staging

For: putting a build in front of yourself on a real deployment, before it goes in front of players.

Staging is the worker named grafted-wars-true-staging. It answers on its workers.dev address and binds no custom domain. Its universe is reset freely and its store carries nothing you need to keep.

Preconditions

  • [ ] The gate is green on the branch being deployed (npm run check).
  • [ ] CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID are set, in the environment or the repository's gitignored .env file.
  • [ ] An owner token has been minted on this machine, if you intend to claim owner (node scripts/owner-claim.mjs mint).

Rehearse it first

The deploy has a dry run that needs no Cloudflare credentials at all. It prints the exact wrangler invocation and stops:

bash
GW_DEPLOY_DRY=1 npm run deploy:staging

Read the variables it reports. Every one it sets is printed, except the owner claim hash and the reset token, whose values are withheld.

Steps

  1. Deploy.

    bash
    npm run deploy:staging
    • Expect: a build, then a wrangler upload, then a one-line summary naming the worker.
    • If instead a RED:-prefixed line appears: the exit code says which. 2 means a missing Cloudflare credential. 1 means the build failed, the wrangler upload failed, or the named starting stocks were not recognised.
  2. On the very first deploy of this worker only, give it its admin secret.

    bash
    npx wrangler secret put ADMIN_EMAIL --name grafted-wars-true-staging
    • Expect: wrangler confirms the secret was set.
    • This is separate from production's secret. Setting one does nothing for the other.
  3. Check the worker answers.

    bash
    curl https://<the workers.dev address>/api/health
    • Expect: a JSON body reporting ok, the worker name and its version.
    • If instead every request is refused: the ADMIN_EMAIL secret has never been set on this worker. That is step 2, not a broken deploy.
  4. Claim owner, if you want the admin surface.

    bash
    node scripts/owner-claim.mjs page <staging base url>
    • Expect: a claim-link page written to ~/rocket-showroom/grafted-wars-enter.html. Open it and follow the staging link once.
  5. Optionally, mint throwaway accounts for a walkthrough.

    bash
    node tools/mint-accounts.mjs --base-url <staging base url>

What staging turns on that production does not

The deploy script sets four variables that differ from the production-safe defaults baked into the configuration file.

VariableStagingWhy
GW_PUBLIC_SIGNUPonso accounts can be made without an invitation
ADMIN_ROUTESonso the admin and export routes answer
GW_DENSE_SEEDonso the home system is packed at boot rather than filling over time
GW_CLOCK_RATE25so a universe's worth of change happens in an evening

Each can be overridden by setting the same name in your environment. Setting one to an empty string falls back to the configuration file's production-safe value for that variable alone, rather than dropping it.

Resetting the universe

Staging is wiped by giving it a reset token it has not seen before. The universe drops every user-state table on its next boot when the value differs from the one already stored:

bash
GW_STAGING_RESET=$(date +%s) npm run deploy:staging

A repeated value does nothing, because the comparison is against the stored one. Each reset needs a new value, which is why the example uses a timestamp.

Rollback

There is no rollback command. Undoing a bad staging deploy means checking out a known-good commit and deploying again.

Staging never touches domain bindings, so a failed or partial deploy leaves no DNS state behind. What remains is the worker at whatever version the last completed upload put there.

Sharp edges

The two worker names are reversed from what they read like. Staging is grafted-wars-true-staging; the worker called grafted-wars-staging is production. Check a deploy by the hostname it answers on rather than by the name in the dashboard.

A densely seeded universe running at 25x under sustained bot load can exceed the Durable Objects free-tier row-read quota. That is a plan limit rather than a defect in the deploy.

An owner claim hash supplied as an environment override must be a 64-character hex digest. A malformed one crashes the script before anything is built, with an uncaught exception rather than the script's own error line.