Raxx · internal docs

internal · gated

RCA — BCP vault backup pipeline produced zero backups since inception (silent no-op from 2026-06-30)

Incident ID: 2026-08-29-bcp-vault-backup-silent-noop Date discovered: 2026-08-29 (via prior read-only cross-repo audit, TradeMasterAPI issue #4535) Severity: SEV-3 Duration: From the original GitHub Actions workflow (2026-05-29, #3049) until at least 2026-10-30, the daily vault snapshot has never produced a backup object in s3://raxx-iac-state-prod/vault-exports/. It reported green (exit 0) from 2026-06-30 (soft-skip added to the GHA workflow) until 2026-09-11 (#4598, hard-fail on develop); before 2026-06-30 it failed red at the GPG step with a cryptic error, and the key is still not confirmed provisioned (see Action items). Blast radius: vault.raxx.app (self-hosted Infisical) had zero off-host encrypted backups the entire time. 22 sibling repos/services depend on this vault for secrets. No customer-facing surface affected directly, but a total vault loss during this window (host failure, corruption, compromise) would have had no BCP recovery path beyond the separate Lightsail AutoSnapshot (7-day retention, whole-disk, not app-level export). Author: sre-agent

Timeline of the preflight behaviour (verified against the commit history, UTC)

Date Event
2026-05-29 GHA workflow bcp-vault-snapshot-daily.yml introduced (#3049). GPG_BACKUP_PUBLIC_KEY is never provisioned
2026-05-31 CF Access header fix for the Infisical REST calls; runs still fail red at the GPG fingerprint-parse step
2026-06-30 GHA preflight added: missing key prints a ::warning:: and exit 0. The job turns green while still writing nothing
2026-07-05 Woodpecker port (#4051, issue #4016) carries the exit 0 soft-skip over
2026-08-29 Gap found via issue #4535; this RCA and the runbook refresh are written on fix/vault-backup-pipeline
2026-09-11 exit 1 hard-fail lands on develop through #4598 (the same change as this branch's pipeline edit, made independently)

Summary

The vault-export step of .woodpecker/bcp-vault-snapshot-daily.yaml has a preflight check for GPG_BACKUP_PUBLIC_KEY (required to encrypt the export before it leaves the host). GPG_BACKUP_PUBLIC_KEY was never provisioned at Infisical path /MooseQuest/bcp/ (originally it was expected as a GitHub Actions repo secret). When the preflight found the key empty, it printed a warning and executed exit 0 — a soft skip that reports pipeline success. Every subsequent step (GPG import, export, upload, S3-size verification) was simply never reached, and the notify-on-failure step (Slack/Postmark page) never fired because the pipeline never registered as failed. The result: a backup pipeline that ran daily, reported green from 2026-06-30 onward, and never once wrote a backup — an invisible BCP gap that would only have been discovered at restore time.

Root cause analysis

Detection

Resolution

Action items

Status as of 2026-10-30 UTC (from the repo and issue #4535): #4535 is still open and labelled operator-blocked. Nothing on develop shows the key provisioned or a real object verified, so items 1 and 2 are open and past due. Item 3 is only partly covered: bcp-smoke-monthly Check 5 (.woodpecker/bcp-smoke-monthly.yaml) looks for yesterday's vault-exports/ object, but only monthly, not within 25 hours. There is no evidence of items 4 and 5 on develop.

# Action Owner Due Issue
1 Generate a dedicated BCP backup GPG keypair; store the private key offline (operator-controlled, outside Infisical, per the bootstrap-circularity note in the updated runbook); provision the public key as GPG_BACKUP_PUBLIC_KEY at /MooseQuest/bcp/ via rvault set (admin identity required) operator 2026-09-05 #4535
2 After (1), trigger bcp-vault-snapshot-daily manually in Woodpecker and confirm a .json.gpg object > 1KB lands in s3://raxx-iac-state-prod/vault-exports/ with today's date operator or sre-agent (once key is provisioned) 2026-09-05 #4535
3 Add an independent, pipeline-external alert (e.g. a lightweight cron/CloudWatch check) that fires if no new object has appeared under s3://raxx-iac-state-prod/vault-exports/ in the last 25 hours — so a future failure mode that isn't "step exits early" (e.g. the whole job silently not scheduled) is still caught sre-agent 2026-09-12 —
4 Schedule a periodic (quarterly) restore drill: decrypt a real .json.gpg backup object with the offline private key and confirm the exported JSON is well-formed and non-empty, to catch corruption/format regressions that a size-only check (> 1024 bytes) would miss operator 2026-09-26 —
5 Audit .woodpecker/*.yaml for other VAULT_ALLOW_MISSING=true + preflight patterns that exit 0 on a missing secret in a pageable pipeline; confirm each one's silent-skip behavior is actually intended (e.g. genuinely optional secrets) rather than another instance of this same signaling bug sre-agent 2026-09-19 —

References