diff --git a/.gitea/workflows/sweep-cf-orphans.yml b/.gitea/workflows/sweep-cf-orphans.yml index e7dc50f2a..e5fd2e34f 100644 --- a/.gitea/workflows/sweep-cf-orphans.yml +++ b/.gitea/workflows/sweep-cf-orphans.yml @@ -34,8 +34,10 @@ name: Sweep stale Cloudflare DNS records # scripts/ops/test_sweep_cf_decide.py (#2027) cover the rule # classifier. # -# Secrets: CF_API_TOKEN, CF_ZONE_ID, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY -# are confirmed existing per issue #425 §425 audit. CP_ADMIN_API_TOKEN and +# Secrets: CF_API_TOKEN (preferred CI-scoped name) or CLOUDFLARE_API_TOKEN +# (operator-host canonical name) are accepted — the workflow falls back +# automatically. Same for CF_ZONE_ID / CLOUDFLARE_ZONE_ID. Confirmed +# existing per issue #425 §425 audit. CP_ADMIN_API_TOKEN and # CP_STAGING_ADMIN_API_TOKEN are unconfirmed — if missing, the verify step # (schedule → hard-fail, dispatch → soft-skip) surfaces it clearly. @@ -79,8 +81,8 @@ jobs: # each individually capped at 10s by the script's curl -m flag. timeout-minutes: 3 env: - CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} - CF_ZONE_ID: ${{ secrets.CF_ZONE_ID }} + CF_API_TOKEN: ${{ secrets.CF_API_TOKEN || secrets.CLOUDFLARE_API_TOKEN }} + CF_ZONE_ID: ${{ secrets.CF_ZONE_ID || secrets.CLOUDFLARE_ZONE_ID }} CP_ADMIN_API_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }} CP_STAGING_ADMIN_API_TOKEN: ${{ secrets.CP_STAGING_ADMIN_API_TOKEN }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -129,6 +131,7 @@ jobs: fi echo "::error::sweep cannot run — required secrets missing: ${missing[*]}" echo "::error::set them at Settings → Secrets and Variables → Actions, or disable this workflow." + echo "::error::Cloudflare secrets accept either the CI-scoped name (CF_API_TOKEN / CF_ZONE_ID) or the operator-host canonical name (CLOUDFLARE_API_TOKEN / CLOUDFLARE_ZONE_ID)." echo "::error::a silent skip masked an active CF DNS leak (152/200 zone records) caught only by a manual audit on 2026-04-28; this gate exists to make the gap visible." exit 1 fi diff --git a/.gitea/workflows/sweep-cf-tunnels.yml b/.gitea/workflows/sweep-cf-tunnels.yml index fe7ab099f..4ce8f6d9c 100644 --- a/.gitea/workflows/sweep-cf-tunnels.yml +++ b/.gitea/workflows/sweep-cf-tunnels.yml @@ -29,10 +29,12 @@ name: Sweep stale Cloudflare Tunnels # the DNS sweep's 50% because tenant-shaped tunnels are mostly # orphans by design) refuses to nuke past the threshold. # -# Secrets: CF_API_TOKEN, CF_ACCOUNT_ID are confirmed existing per -# issue #425 §425 audit. CP_ADMIN_API_TOKEN and CP_STAGING_ADMIN_API_TOKEN -# are unconfirmed — if missing, the verify step (schedule → hard-fail, -# dispatch → soft-skip) surfaces it clearly. +# Secrets: CF_API_TOKEN (preferred CI-scoped name) or CLOUDFLARE_API_TOKEN +# (operator-host canonical name) are accepted — the workflow falls back +# automatically. Same for CF_ACCOUNT_ID / CLOUDFLARE_ACCOUNT_ID. Confirmed +# existing per issue #425 §425 audit. CP_ADMIN_API_TOKEN and +# CP_STAGING_ADMIN_API_TOKEN are unconfirmed — if missing, the verify step +# (schedule → hard-fail, dispatch → soft-skip) surfaces it clearly. on: schedule: @@ -74,8 +76,8 @@ jobs: # the sweep-cf-orphans companion job). timeout-minutes: 30 env: - CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} - CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} + CF_API_TOKEN: ${{ secrets.CF_API_TOKEN || secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID || secrets.CLOUDFLARE_ACCOUNT_ID }} CP_ADMIN_API_TOKEN: ${{ secrets.CP_ADMIN_API_TOKEN }} CP_STAGING_ADMIN_API_TOKEN: ${{ secrets.CP_STAGING_ADMIN_API_TOKEN }} MAX_DELETE_PCT: ${{ github.event.inputs.max_delete_pct || '90' }} diff --git a/scripts/ops/sweep-cf-orphans.sh b/scripts/ops/sweep-cf-orphans.sh index b96d7bfbe..9c2fc8906 100755 --- a/scripts/ops/sweep-cf-orphans.sh +++ b/scripts/ops/sweep-cf-orphans.sh @@ -19,7 +19,10 @@ # # Env vars required: # CF_API_TOKEN — Cloudflare token with zone:dns:edit +# (falls back to CLOUDFLARE_API_TOKEN if CF_API_TOKEN is unset; +# the workflow YAML maps both secret names into CF_API_TOKEN) # CF_ZONE_ID — the zone (moleculesai.app) +# (falls back to CLOUDFLARE_ZONE_ID if CF_ZONE_ID is unset) # CP_ADMIN_API_TOKEN — CP admin bearer for api.moleculesai.app # CP_STAGING_ADMIN_API_TOKEN — CP admin bearer for staging-api.moleculesai.app # AWS_* — standard AWS creds (default region us-east-2) @@ -56,6 +59,12 @@ need() { exit 1 fi } +# Fallback: operator-host canonical names → CI-scoped names. +# The workflow YAML already maps both, but direct script invocation +# (e.g. local ops) may only have the canonical names set. +CF_API_TOKEN="${CF_API_TOKEN:-${CLOUDFLARE_API_TOKEN:-}}" +CF_ZONE_ID="${CF_ZONE_ID:-${CLOUDFLARE_ZONE_ID:-}}" + need CF_API_TOKEN need CF_ZONE_ID need CP_ADMIN_API_TOKEN @@ -121,7 +130,7 @@ if not payload.get("success", False) or not isinstance(payload.get("result"), li print(f"ERROR: Cloudflare DNS list failed: {detail}", file=sys.stderr) raise SystemExit(1) '; then - log "Cloudflare DNS list failed; verify CF_API_TOKEN has Zone:DNS:Edit and CF_ZONE_ID is the moleculesai.app zone." + log "Cloudflare DNS list failed; verify CF_API_TOKEN (or CLOUDFLARE_API_TOKEN) has Zone:DNS:Edit and CF_ZONE_ID (or CLOUDFLARE_ZONE_ID) is the moleculesai.app zone." exit 1 fi TOTAL_CF=$(echo "$CF_JSON" | python3 -c "import json,sys; print(len(json.load(sys.stdin)['result']))") diff --git a/scripts/ops/sweep-cf-tunnels.sh b/scripts/ops/sweep-cf-tunnels.sh index 063b989ae..1b0658e3a 100755 --- a/scripts/ops/sweep-cf-tunnels.sh +++ b/scripts/ops/sweep-cf-tunnels.sh @@ -29,8 +29,11 @@ # account:cloudflare_tunnel:edit scope. # (Same secret as sweep-cf-orphans, but the # token must include the tunnel scope.) +# (falls back to CLOUDFLARE_API_TOKEN if CF_API_TOKEN is unset; +# the workflow YAML maps both secret names into CF_API_TOKEN) # CF_ACCOUNT_ID — the account that owns the tunnels (visible # in dash.cloudflare.com URL path) +# (falls back to CLOUDFLARE_ACCOUNT_ID if CF_ACCOUNT_ID is unset) # CP_ADMIN_API_TOKEN — CP admin bearer for api.moleculesai.app # CP_STAGING_ADMIN_API_TOKEN — CP admin bearer for staging-api.moleculesai.app # @@ -70,6 +73,12 @@ need() { exit 1 fi } +# Fallback: operator-host canonical names → CI-scoped names. +# The workflow YAML already maps both, but direct script invocation +# (e.g. local ops) may only have the canonical names set. +CF_API_TOKEN="${CF_API_TOKEN:-${CLOUDFLARE_API_TOKEN:-}}" +CF_ACCOUNT_ID="${CF_ACCOUNT_ID:-${CLOUDFLARE_ACCOUNT_ID:-}}" + need CF_API_TOKEN need CF_ACCOUNT_ID need CP_ADMIN_API_TOKEN