Secrets Management
Spectral has three secret classes: platform operational, CI, and local-dev. The architecture is a deployer-operated provisioning script that resolves config from infra/environments.toml + 1Password and publishes it to the GitHub Environment; the deploy then applies the Environment to the container (the runtime secret hop). Decision lineage in ADR-037 and ADR-110; CI-specific in ADR-062. Operational runbooks: docs/runbooks/secrets-management.md and docs/runbooks/ci-secrets.md.
Runtime read source
Section titled “Runtime read source”The GitHub Environment carries the rotating key material — one per environment (production today; staging when a Supabase stage project lands). At deploy, deploy.yml reads the matching Environment and sets its values on the container (secrets + non-secret --vars), which the container reads at startup. Code-coupled values — values whose correctness depends on the running image, such as SPECTRAL_GENERATION — are set as per-deploy container vars, never in the shared Environment (per the CD pipeline placement principle).
Provisioning script
Section titled “Provisioning script”tools/provision/provision.sh is the canonical deploy-tier orchestrator (ADR-110). It resolves infra/environments.toml (whose values reference 1Password entries, read via the op CLI) and publishes the result to the GitHub Environment:
provision.sh --env production --dry-run— review the plan.provision.sh --env production— publish (after the 1Password CLI is authenticated withop signin).
The GitHub substrate itself — the production Environment and the production-ff-only ruleset — is bootstrapped once by tools/provision/github_resources.sh. Committed config (infra/environments.toml) is the source of truth; the GitHub Environment is its reconciled projection; nothing is passed to the deploy by hand. Re-running provision.sh after a value changes in 1Password (or a key is added to environments.toml) republishes the Environment.
CI secrets
Section titled “CI secrets”GitHub Environments hold scoped secrets:
production— the deploy contract:CLOUDFLARE_API_TOKEN,DATABASE_URL/SUPABASE_DB_URL,SUPABASE_SECRET_KEY. Gated by the Environment’s protection rule + theproduction-ff-onlyruleset.
Mock-first PR CI; no pull_request_target. Live-secret runs gate to push (merge-to-main), schedule, and workflow_dispatch. Fork PRs never trigger live-secret workflows. See ADR-062.
Leakage scanning
Section titled “Leakage scanning”Four defense-in-depth layers:
- GitHub Actions auto-redaction (built-in).
- gitleaks pre-commit.
- Structlog redaction filter — known sensitive field names auto-redacted in structured logs.
- Cassette content lint (
tools/quality/check_cassette_redaction.py) blocksAuthorization: Bearer ...patterns in committed cassettes; activates with the first cassette commit per ADR-061 D8.
Rotation cadence
Section titled “Rotation cadence”Quarterly + on-incident. Applies to LLM provider API keys, the Cloudflare API token, Supabase keys, and OAuth provider test-app secrets. Rotation updates the 1Password-backed value, then re-runs provision.sh to republish the Environment. The full procedure is in docs/runbooks/secrets-management.md.
Cofounder personal access keys are private and not in system docs.
Composite trail: git history (commits touching tools/provision/ and infra/environments.toml); 1Password item history; the GitHub org audit log + Environment history; Supabase PGAudit. No unified UI; quarterly founder review covers all surfaces.
See also
Section titled “See also”- ADR-037 — decision lineage
- ADR-110 — provisioning + the GitHub Environment
- ADR-062 — CI secrets
- Hosting — configuration placement
- CD pipeline overview — config placement principle
docs/runbooks/secrets-management.md— rotation runbookdocs/runbooks/ci-secrets.md— CI rotation runbook