Rotate Cloudflare + npm tokens, set as org-level GH secrets
Rotate Cloudflare + npm tokens
When to run
- Token leak detected (e.g. accidentally pasted in chat).
- Quarterly rotation cadence per
rules/security/token-rotation-cadence.md. - Initial setup of org-level secrets for a new GH Actions workflow.
Prerequisites
- Logged in to dash.cloudflare.com as the account owner.
- Logged in to npmjs.com as
chirag127. ghCLI authenticated (gh auth statusshows green).- Doppler CLI authenticated (optional — only if Doppler is the upstream
source of truth per
security/secrets-management-doppler.md).
Step 1 — Cloudflare token
Path A — Least-privilege (recommended)
Issue ONE token per concern. Less blast radius if any single token leaks.
- Go to https://dash.cloudflare.com/profile/api-tokens.
- Click Create Token → Custom token.
- Add these permissions:
| For | Permission group | Resource |
|---|---|---|
| Pages deploys | Account → Cloudflare Pages → Edit |
Account chirag127 |
| DNS for *.oriz.in | Zone → DNS → Edit |
Specific zone oriz.in |
| Workers (only if you publish workers) | Account → Workers Scripts → Edit |
Account chirag127 |
- Account Resources: include
chirag127only. - Zone Resources: include
oriz.inonly. - Client IP Address Filtering: leave blank (CI runners have rotating IPs).
- TTL: 1 year.
- Click Continue to Summary → Create 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.
- Same flow as Path A, but at step 3 click Use Template → Edit:
Account.Account Settings: ReadAccount.Cloudflare Pages: EditAccount.Workers Scripts: EditAccount.Workers KV Storage: EditAccount.Workers R2 Storage: EditAccount.D1: EditAccount.Cloudflare Tunnel: EditZone.Zone: ReadZone.DNS: EditZone.Workers Routes: EditZone.SSL and Certificates: EditZone.Page Rules: Edit
- Same Account/Zone scoping as Path A.
- Document this token's broad scope in the chirag127/workspace
.env.examplecomment 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.
- Go to https://www.npmjs.com/settings/chirag127/tokens.
- Click Generate New Token → Granular Access Token.
- Token name:
gh-actions-publish-2026-Q3(rotate quarterly). - Expiration: 90 days.
- Packages and scopes: select scope
@chirag127/*. (NOT all-packages.) - Permissions:
Read and write. - Allowed IP ranges: leave blank.
- 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:
- Cloudflare: revoke old token at the same dashboard URL.
- npm: revoke old token at the same settings URL.
- 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.):
- Revoke first. Don't wait for the new token. Old one dies now.
- Generate new per Step 1 / Step 2.
- Set new at org level per Step 3.
- Search for the leaked value across all repos:
gh search code --owner chirag127 "$LEAKED_TOKEN" - If a public repo carries it, force-push history rewrite per
runbooks/security/incident/rotate-leaked-secret.md.
(Updated 2026-06-20.)