Skip to content

Secrets

Purpose

The difference between a GitHub Actions secret and a Cloudflare Worker secret, and how to rotate each safely. Sourced primarily from docs/DEPLOYMENT.md §2 and §6.

Two different secret stores - do not confuse them

GitHub Actions secrets Cloudflare Worker secrets
Where set Repo → Settings → Secrets and variables → Actions wrangler secret put <NAME> --config <path>
Who/what reads them Workflow runs only (ci.yml, deploy-cloudflare.yml) The deployed Worker at runtime, via env.<NAME>
Visible in wrangler.jsonc? No - never appear in any committed file Only the name appears (if referenced in vars/bindings docs); value is never in git
Example CLOUDFLARE_API_TOKEN any secret the API Worker reads at request time (none beyond the Cloudflare credentials are currently defined - NOTIFICATION_EMAIL is a plain vars entry, not a secret, see Cloudflare → Workers)

A GitHub Actions secret gets a deploy run; a Cloudflare secret gets a deployed Worker's own code something to read. Setting one does not set the other.

GitHub Actions secrets used in this repository

Secret Purpose Scope needed
CLOUDFLARE_API_TOKEN Auth for every wrangler call in both workflows Workers Scripts: Edit, D1: Edit, R2: Edit, Zone: Read (relevant zone only)
CLOUDFLARE_ACCOUNT_ID Which Cloudflare account to target N/A (identifier, not a credential - low sensitivity, still kept as a secret rather than a plain repo variable)
CLOUDFLARE_D1_DATABASE_ID Used only by deploy-cloudflare.yml's now-no-op sed step, see Deployment N/A (identifier)

Set these at Settings → Secrets and variables → Actions → New repository secret. Never in a workflow file, never in a commit.

Rotating CLOUDFLARE_API_TOKEN

From docs/DEPLOYMENT.md §6, reproduced here:

  1. Cloudflare dashboard → My Profile → API Tokens → create a new token with the same scopes as the one being replaced (see the scope list above).
  2. GitHub repo → Settings → Secrets and variables → Actions → CLOUDFLARE_API_TOKEN → Update → paste the new token value.
  3. Revoke the old token in the Cloudflare dashboard after confirming a workflow run succeeds with the new one - don't revoke first, or an in-flight/queued run fails mid-deploy.
  4. Trigger validate (any push, or workflow_dispatch) to confirm the new token authenticates correctly before relying on it for a real deploy-production run.

Setting a Cloudflare Worker secret

npx wrangler secret put <SECRET_NAME> --config workers/api/wrangler.jsonc   # [MUTATING]
Prompts interactively for the value (not passed as an argument, so it never lands in shell history). Applies to the production Worker immediately - there is no separate secrets store per environment, since this repository has no staging environment (see Infrastructure → Environments).

What is not a secret in this repository

NOTIFICATION_EMAIL and ALLOWED_ORIGINS are plain vars in workers/api/wrangler.jsonc - visible in git, not sensitive. Resource identifiers like a database_id are treated as non-secret per docs/DEPLOYMENT.md's own framing (they identify a resource, they don't authenticate anything) - see Cloudflare → D1 for where one is written literally with a timestamped caveat.

Least privilege

  • Scope CLOUDFLARE_API_TOKEN to only the permissions in the table above - not the account-wide "Edit Cloudflare Workers" template, which grants far more than this repository's workflows use.
  • Only repository Settings → Secrets grants workflow access - branch protection rules don't restrict secret access by branch in this repository's current configuration; treat any contributor with push access to any branch as able to trigger a workflow that reads these secrets (though only a push to main triggers an actual deploy-production, see Deployment).

Security considerations

  • Never place a real secret value in a commit, a workflow log via echo, or this documentation - placeholders (<CLOUDFLARE_API_TOKEN>, <SECRET_VALUE>) only.
  • If a secret value is ever committed by mistake: rotate it immediately (a git revert does not invalidate a leaked value already visible in history/forks/CI logs) - see Operations → Incident Response.