Skip to content
GitHub
Operator

CI secrets handling runbook

Operational runbook for GitHub Actions secret scoping, fork-PR posture, rotation cadence, and leakage scanning. Implements ADR-062 and composes with ADR-037 secrets-management baseline + ADR-053 GitHub Environment + protection-rule patterns.

This runbook covers CI integration tests and live-provider drift detection. For provisioning + rotation of platform operational secrets see secrets-management.md.

GitHub Environments

Secrets used by CI are scoped to one of three GitHub Environments. Each Environment carries protection rules per ADR-053 D17 (required reviewer = self at alpha; deployment-tags rule with ref type: tag for production).

staging

SecretSourceUsed by
SUPABASE_STAGING_ANON_KEYSupabase project consoleintegration tests against staging
SUPABASE_STAGING_SERVICE_ROLE_KEYSupabase project consoleintegration tests + Management API
SUPABASE_MGMT_API_TOKENSupabase organization settingspre-merge dry-run branches (per ADR-053 D4)
RENDER_API_KEYRender dashboard → API keysdeploy workflows (per ADR-046 D12)
CF_API_TOKENCloudflare → API tokensedge workflows (per ADR-052 D6)
GOOGLE_OAUTH_TEST_CLIENT_*Google Cloud → OAuth credentialsOAuth integration tests
GITHUB_OAUTH_TEST_CLIENT_*GitHub OAuth AppsOAuth integration tests

Required reviewer at alpha: self.

production

Production equivalents of every staging secret. Required reviewer at alpha: self. Deployment-tags rule restricts to ref type: tag references per ADR-053 D17.

test-live

SecretSourceUsed by
LLM_TEST_ANTHROPIC_KEYAnthropic console (dedicated test account)nightly live-drift workflow (per ADR-061 D4)
LLM_TEST_OPENAI_KEYOpenAI console (dedicated test account)nightly live-drift workflow
LLM_TEST_GOOGLE_KEYGoogle AI Studio (dedicated test account)nightly live-drift workflow

Each LLM test-account is daily-capped at the provider level (per ADR-061 D6). Capping prevents runaway cost from a misbehaving cassette regeneration or drift workflow. Required reviewer at alpha: self.

Workflow trigger posture

TriggerSecret accessWorkflows
pull_requestNoneci.yml (mock-first; testcontainers + cassettes)
push to mainstagingintegration-staging.yml
schedule (nightly)test-livenightly-live-drift.yml
workflow_dispatchper-workflowdrift workflow + ops dispatches
Tag push (prod-*, v*)productiondeploy workflows (per ADR-053)

pull_request_target is not used at any workflow at alpha. The blanket posture avoids the privilege-escalation class. If external contributors emerge and a specific PR-time secret-using workflow becomes necessary, the path is a manual-approval gate via GH Environment required-reviewer rule + workflow_run chain — not pull_request_target.

Rotation cadence

Quarterly + on-incident applies to every secret listed above.

Secret familyCadenceProcedure
LLM provider keys (test-live)QuarterlyMint new key → update GH Environment → revoke old
Render API keyQuarterlyRender dashboard → mint → update staging + production GH Environments → revoke old
Cloudflare API tokenQuarterlyCF → API tokens → mint scoped token → update GH Environments → revoke old
Supabase service-roleQuarterlySupabase project → settings → API → reset → update GH Environments
Supabase Management API tokenQuarterlySupabase organization → settings → tokens → mint → update → revoke
OAuth test app secretsQuarterlyProvider console → rotate client secret → update staging GH Environment

Rotation events recorded in operational log (cofounder discipline; outside system docs per ADR-037 D5). Triggered immediately on any leak suspicion.

Rotation procedure (worked example: LLM_TEST_ANTHROPIC_KEY)

  1. Mint new key at the vendor (Anthropic console → keys page).
  2. Update test-live GH Environment secret value via GitHub UI or gh secret set LLM_TEST_ANTHROPIC_KEY --env test-live.
  3. Trigger the nightly drift workflow via workflow_dispatch to verify the new key works.
  4. Revoke the old key at the vendor.
  5. Record the rotation event in the operational log (cofounder discipline; private).

Leakage scanning

Defense in depth across four layers:

  1. GH Actions auto-redaction — registered GitHub secrets are automatically redacted in workflow logs (built-in). Never bypass by echoing transforms of secret values.
  2. gitleaks pre-commit — the pre-commit config (per repo .pre-commit-config.yaml) runs gitleaks on every commit, blocking commits that contain detected secret patterns. False positives are tunable via .gitleaks.toml allowlists.
  3. Structlog redaction filter — per ADR-036, the substrate redacts known sensitive field names (api_key, authorization, password, token, secret) from structured logs at emission.
  4. Cassette content linttools/quality/check_cassette_redaction.py blocks Authorization: Bearer ... and similar patterns from being committed in cassette files. Wired into the pre-push gate per ADR-053; lands with the first cassette commit per ADR-061 D8 (until then a dead lint with no inputs).

Workflow-file discipline

Hard rules every CI workflow file must follow:

  • Reference secrets via ${{ secrets.NAME }} only inside env: blocks of jobs that actually need them. Never at workflow scope when a single job is the consumer.
  • Never echo secrets to logs (echo $SECRET); never include them in error messages or commit metadata.
  • Never pass secrets to third-party actions without explicit allowlist review. Trusted publishers: GitHub-verified actions; org-allowlisted actions only.
  • Pin action versions by SHA or tag. No @main, no @latest. Pinned tags (@v3.1.4) are acceptable; floating refs are not.
  • Workflow files reviewed for these rules before merge. Mechanical lint may land post-alpha.

Auth test credentials

OAuth provider test apps (Google + GitHub per ADR-039 D2) use dedicated test-tenant accounts scoped to test users. Test-app secrets live in the staging GH Environment with manual-approval gate.

JWT signing keys for asymmetric flow (per ADR-039 D4a JWKS-local) follow a strict separation: test fixtures hold dedicated test signing keys; staging and production signing keys are never reused as test fixtures. Test keys are committed to test-fixture directories (private key material is contained because the keys are test-only).

Audit

GitHub’s built-in audit log captures every secret access (workflow runs that access scoped secrets are logged). Founder reviews quarterly via GitHub organization audit log UI.