Skip to content

Credentials and access

For: the moment a command asks for a key and you need to know which one, where it comes from, and where it is meant to live.

No value appears on this page, and none should ever be added to it. Every row names a credential and says where to obtain it. If you find yourself pasting a secret into any documentation file, stop: the place for a value is the store the row names.

The accounts behind everything

Five accounts hold everything this product needs. Each is reached at its own console.

AccountConsoleHolds
Cloudflaredash.cloudflare.comthe Workers, the Durable Object, the domain and its DNS
Forgejoforge.tailc53a08.ts.netthe repository, its issues, its pull requests and continuous integration
Expoexpo.devthe mobile project, its build profiles and its over-the-air channels
Apple Developerdeveloper.apple.com and appstoreconnect.apple.comthe iOS signing identity and App Store listing
Google Playplay.google.com/consolethe Android signing key and Play listing

Deploying to Cloudflare

Two values authenticate every deploy and every wrangler command. Both are read from your environment, or from the repository's gitignored .env file.

NameWhere to get itWhich job wants it
CLOUDFLARE_API_TOKENCloudflare dashboard → My Profile → API Tokens → Create Token, choosing "Edit Cloudflare Workers", which covers deploys.every deploy, every wrangler command
CLOUDFLARE_ACCOUNT_IDCloudflare dashboard → Workers & Pages → Overview, right-hand columnevery deploy
CLOUDFLARE_ZONE_IDCloudflare dashboard → Websites → the domain → Overview, right-hand columnproduction deploys only, and only when overriding the built-in zone

The token is the one that matters. Scope it to Workers rather than to the whole account, and rotate it from the same page if it ever reaches a log or a screenshot.

Both deploy scripts exit with code 2 when either of the first two is missing, so a missing credential is named rather than guessed at.

To rehearse a deploy without any credential at all, run the dry form:

bash
GW_DEPLOY_DRY=1 npm run deploy:staging

Secrets held by a deployed worker

These live in Cloudflare, attached to one worker, and never in the repository.

NameSet withNotes
ADMIN_EMAILnpx wrangler secret put ADMIN_EMAIL --name <worker>a worker without it refuses every request
ADMIN_CLAIM_HASHset by the deploy from your local owner tokena 64-character hex digest when overridden by hand
RESET_TOKENset by the staging deploy from GW_STAGING_RESETstaging only; production has no reset

Each worker holds its own copies. The two worker names are grafted-wars-true-staging for staging and grafted-wars-staging for production, and they read as the reverse of what they are.

An ordinary deploy preserves an existing secret. It is lost only if the worker is deleted and recreated, after which the universe refuses every request until it is put back.

Being owner on a deployment

Owner is claimed per deployment, from a token minted on your machine.

ItemWhere it livesHow to get it
owner token~/.grafted-wars/owner-token on your machinenode scripts/owner-claim.mjs mint
claim-link page~/rocket-showroom/grafted-wars-enter.htmlnode scripts/owner-claim.mjs page <base url>
owner sessionyour browser, per deploymentopen the claim page and follow its link once

The token is the root of this. Losing it means minting a new one and redeploying, because the deployed worker checks a claim against the hash of the token that was present at deploy time.

The Forge

Continuous integration and the tracker tools authenticate against the Forge.

NameWhere it comes fromNotes
git credentialyour operating system's credential store, for the Forge remote's hostthe ordinary route; nothing to configure once you have pushed once
FORGE_TOKENForgejo → Settings → Applications → Generate Tokenread only when the credential store has no entry
REVIEW_GATE_TOKENa Forgejo user token, registered as a repository secretlets the review gate read collaborator permissions
GITHUB_TOKENsupplied to each workflow run automaticallynothing to set

A request to a host that is neither the git remote's host nor the configured Forge base carries no credential at all, by construction. Such a request reads back as a 404 rather than an authentication failure.

Mobile

The mobile project is an Expo application, and its build credentials are held by Expo rather than by this repository.

ItemValue or sourceNotes
Expo accountevgeniiartsthe owner recorded in the app configuration
EAS projectrecorded in platforms/mobile/app.config.tsthe project identifier the builds attach to
bundle identifier and packagecom.graftedwars.gamethe same string on both platforms
Expo access tokenexpo.dev → Account Settings → Access Tokensneeded for non-interactive builds

Signing material is generated and stored by Expo when you first build, and you are prompted for it then.

PlatformCredentialWhere it comes from
iOSApple Developer Program membership, a distribution certificate and a provisioning profiledeveloper.apple.com; Expo can generate and manage both
iOSApp Store Connect API key, for automated submissionappstoreconnect.apple.com → Users and Access → Integrations
Androidan upload keystoregenerated by Expo on first build, or supplied by you
Androida Google Play service account JSON keyGoogle Cloud console, granted access in the Play Console

Store none of these in this repository. Expo holds the signing material, and the Play service account key belongs wherever you keep private keys.

Runtime configuration the mobile app reads

These are settings rather than secrets, and they change which server the shell points at.

NameEffectDefault
GRAFTED_WARS_GAME_URLthe server the shell loadshttps://grafted-wars.com
GRAFTED_WARS_ALLOW_HTTP_LOCALHOSTpermits a plain-HTTP localhost target when set to trueoff
GRAFTED_WARS_SUPPORT_EMAILthe support address shown in the appunset, and the entry is hidden

The URL is validated rather than trusted. A value carrying embedded credentials is discarded, and a plain-HTTP value is discarded unless it is localhost and the override above is on.

Where these belong

StoreWhat goes there
your environment, or the gitignored .envCloudflare token and account id
Cloudflare, per workerADMIN_EMAIL and the other worker secrets
your machine, at ~/.grafted-warsthe owner token
your operating system's credential storethe Forge git credential
Expomobile signing material
the repositorynone of the above

A minted test-account store also holds live credentials. It is gitignored, and it belongs in no commit and on no ticket.

If one leaks

Rotate at the source rather than trying to scrub the history. A Cloudflare token is revoked and reissued from the same page it was created on. A Forge token is revoked in Forgejo's application settings. An owner token is re-minted and the deployment redeployed. Mobile signing material is rotated through Expo and the relevant store console.