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
| Secret | Source | Used by |
|---|---|---|
SUPABASE_STAGING_ANON_KEY | Supabase project console | integration tests against staging |
SUPABASE_STAGING_SERVICE_ROLE_KEY | Supabase project console | integration tests + Management API |
SUPABASE_MGMT_API_TOKEN | Supabase organization settings | pre-merge dry-run branches (per ADR-053 D4) |
RENDER_API_KEY | Render dashboard → API keys | deploy workflows (per ADR-046 D12) |
CF_API_TOKEN | Cloudflare → API tokens | edge workflows (per ADR-052 D6) |
GOOGLE_OAUTH_TEST_CLIENT_* | Google Cloud → OAuth credentials | OAuth integration tests |
GITHUB_OAUTH_TEST_CLIENT_* | GitHub OAuth Apps | OAuth 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
| Secret | Source | Used by |
|---|---|---|
LLM_TEST_ANTHROPIC_KEY | Anthropic console (dedicated test account) | nightly live-drift workflow (per ADR-061 D4) |
LLM_TEST_OPENAI_KEY | OpenAI console (dedicated test account) | nightly live-drift workflow |
LLM_TEST_GOOGLE_KEY | Google 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
| Trigger | Secret access | Workflows |
|---|---|---|
pull_request | None | ci.yml (mock-first; testcontainers + cassettes) |
push to main | staging | integration-staging.yml |
schedule (nightly) | test-live | nightly-live-drift.yml |
workflow_dispatch | per-workflow | drift workflow + ops dispatches |
Tag push (prod-*, v*) | production | deploy 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 family | Cadence | Procedure |
|---|---|---|
LLM provider keys (test-live) | Quarterly | Mint new key → update GH Environment → revoke old |
| Render API key | Quarterly | Render dashboard → mint → update staging + production GH Environments → revoke old |
| Cloudflare API token | Quarterly | CF → API tokens → mint scoped token → update GH Environments → revoke old |
| Supabase service-role | Quarterly | Supabase project → settings → API → reset → update GH Environments |
| Supabase Management API token | Quarterly | Supabase organization → settings → tokens → mint → update → revoke |
| OAuth test app secrets | Quarterly | Provider 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)
- Mint new key at the vendor (Anthropic console → keys page).
- Update
test-liveGH Environment secret value via GitHub UI orgh secret set LLM_TEST_ANTHROPIC_KEY --env test-live. - Trigger the nightly drift workflow via
workflow_dispatchto verify the new key works. - Revoke the old key at the vendor.
- Record the rotation event in the operational log (cofounder discipline; private).
Leakage scanning
Defense in depth across four layers:
- GH Actions auto-redaction — registered GitHub secrets are automatically redacted in workflow logs (built-in). Never bypass by echoing transforms of secret values.
- 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.tomlallowlists. - Structlog redaction filter — per ADR-036, the substrate redacts known
sensitive field names (
api_key,authorization,password,token,secret) from structured logs at emission. - Cassette content lint —
tools/quality/check_cassette_redaction.pyblocksAuthorization: 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 insideenv: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.
Related runbooks
secrets-management.md— platform operational secret provisioning and rotationdeployment.md— deploy workflow secret usage (per ADR-053)edge.md— Cloudflare API token usage (per ADR-052)hosting.md— Render API key usage (per ADR-046)