← knowledge.oriz.in

Rotate Cloudflare + npm tokens, set as org-level GH secrets

runbook securitycredentialscloudflarenpmgithub-actions

Rotate Cloudflare + npm tokens

When to run

Prerequisites

Step 1 — Cloudflare token

Path A — Least-privilege (recommended)

Issue ONE token per concern. Less blast radius if any single token leaks.

  1. Go to https://dash.cloudflare.com/profile/api-tokens.
  2. Click Create TokenCustom token.
  3. Add these permissions:
For Permission group Resource
Pages deploys AccountCloudflare PagesEdit Account chirag127
DNS for *.oriz.in ZoneDNSEdit Specific zone oriz.in
Workers (only if you publish workers) AccountWorkers ScriptsEdit Account chirag127
  1. Account Resources: include chirag127 only.
  2. Zone Resources: include oriz.in only.
  3. Client IP Address Filtering: leave blank (CI runners have rotating IPs).
  4. TTL: 1 year.
  5. Click Continue to SummaryCreate Token. Copy the token.

Path B — Max-permission single token (user-overridden)

Use this only if you accept that one leaked token = full account compromise.

  1. Same flow as Path A, but at step 3 click Use TemplateEdit:
    • Account.Account Settings: Read
    • Account.Cloudflare Pages: Edit
    • Account.Workers Scripts: Edit
    • Account.Workers KV Storage: Edit
    • Account.Workers R2 Storage: Edit
    • Account.D1: Edit
    • Account.Cloudflare Tunnel: Edit
    • Zone.Zone: Read
    • Zone.DNS: Edit
    • Zone.Workers Routes: Edit
    • Zone.SSL and Certificates: Edit
    • Zone.Page Rules: Edit
  2. Same Account/Zone scoping as Path A.
  3. Document this token's broad scope in the chirag127/workspace .env.example comment column so future-you remembers it's wide-scoped.

Verify the token

curl -X GET "https://api.cloudflare.com/client/v4/user/tokens/verify" \
  -H "Authorization: Bearer $NEW_TOKEN" \
  -H "Content-Type: application/json"

Expect "status":"active" and the permissions you set. If it fails, the token didn't activate — wait 30 seconds and retry.

Step 2 — npm token

Granular tokens (introduced 2023) replace classic tokens. Use them.

  1. Go to https://www.npmjs.com/settings/chirag127/tokens.
  2. Click Generate New TokenGranular Access Token.
  3. Token name: gh-actions-publish-2026-Q3 (rotate quarterly).
  4. Expiration: 90 days.
  5. Packages and scopes: select scope @chirag127/*. (NOT all-packages.)
  6. Permissions: Read and write.
  7. Allowed IP ranges: leave blank.
  8. Generate. Copy the token (starts with npm_).

Step 3 — Set org-level GH secrets

Per rules/security/github-org-level-secrets.md, secrets are set ONCE at the chirag127 org level and cascade to every repo.

echo "$CF_TOKEN" | gh secret set CLOUDFLARE_API_TOKEN --org chirag127 --visibility all
echo "$CF_ACCOUNT_ID" | gh secret set CLOUDFLARE_ACCOUNT_ID --org chirag127 --visibility all
echo "$NPM_TOKEN" | gh secret set NPM_TOKEN --org chirag127 --visibility all

For repo-narrow secrets (e.g. extension store keys), use --visibility selected --repos repo1,repo2.

Step 4 — Mirror to local .env

Local development needs the same vars. Per rules/security/no-hardcoded-secrets.md, the actual values go in <repo>/.env (git-ignored), NEVER in .env.example (committed, key-names-only).

Workspace .env:

# This file is git-ignored. Do not commit.
CLOUDFLARE_API_TOKEN=<the new token>
CLOUDFLARE_ACCOUNT_ID=<your account id>
NPM_TOKEN=<the new token>

.env.example stays comment-only; scripts/sync-env-example.sh syncs it to every repo per rules/security/env-example-synced-from-master.md.

Step 5 — Update Doppler (if upstream)

If you use Doppler as the upstream truth, update there first and let scripts/set-org-secrets-from-doppler.sh push to GH org secrets.

doppler secrets set CLOUDFLARE_API_TOKEN="$CF_TOKEN" --project oriz --config prd
doppler secrets set NPM_TOKEN="$NPM_TOKEN" --project oriz --config prd
bash scripts/set-org-secrets-from-doppler.sh

Step 6 — Revoke old tokens

After the new tokens are confirmed working in CI:

  1. Cloudflare: revoke old token at the same dashboard URL.
  2. npm: revoke old token at the same settings URL.
  3. Doppler audit log: check for any unexpected access since last rotation.

Step 7 — Trigger a CI run to confirm

Push any small commit to any of the 15 tool repos (or trigger workflow_dispatch if you've added it). The CI logs should show successful CF Pages deploy + (if applicable) npm publish.

Compromised-token checklist

If a token leaked (chat paste, public commit, etc.):

  1. Revoke first. Don't wait for the new token. Old one dies now.
  2. Generate new per Step 1 / Step 2.
  3. Set new at org level per Step 3.
  4. Search for the leaked value across all repos:
    gh search code --owner chirag127 "$LEAKED_TOKEN"
    
  5. If a public repo carries it, force-push history rewrite per runbooks/security/incident/rotate-leaked-secret.md.

(Updated 2026-06-20.)