Finish AUDIT-WP-0005: walk restore and close T06
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Logical dump of live audit_core restored locally in 4.1s; counts and
sample triples match the rapp-postgres drill. Retention is platform
backup, not an audit-core deletion window. Workplan finished.
This commit is contained in:
tegwick 2026-08-13 14:14:22 +02:00
parent f30faec660
commit c4dbb92a8a
4 changed files with 85 additions and 48 deletions

View file

@ -142,52 +142,48 @@ behind the read privilege). Watch:
## Restore
audit-core declares `custody_class=archive` and `immutable=True` (the
append-only trigger). It does **not** declare a retention window shorter
than the platform's, because it currently declares none (`retention_days`
unset = keep). That is only honest if the platform's backup retention is
also unbounded or is an explicit accepted loss.
audit-core does **not** expire events (`retention_days` unset). Recovery is
bounded by what rapp-postgres can restore, not by an audit-core deletion
window. The platform's planned Barman window is 30 days. Production Barman
is still fail-closed (no governed off-host target). Do not promise an RPO
until that target exists. Local WAL on the node is not an off-host copy.
As of 2026-08-13, `RAPP-POSTGRES-WP-0002-T06` is `wait`. Production backup
is fail-closed until an S3-compatible Barman target exists. The governed
Nextcloud logical-dump lane cannot provide WAL archiving or PITR. Do not
claim an RPO/RTO for audit-core until those drills have been recorded in
rapp-postgres.
Physical restore is instance-wide. A consumer-only restore is a logical
export of `audit_core` from a scratch physical restore, then a controlled
import. Never recover in place. Procedure: `rapp-postgres/docs/restore.md`.
When the platform restore path is open, the audit-core walk is:
Walked 2026-08-13:
1. Restore `platform-pg` to a scratch cluster (`platform-pg-restore-full` or
PITR). Never recover in place.
2. Physical restore is instance-wide. A single-consumer restore is a logical
dump of `audit_core` from the scratch cluster, then a controlled import.
3. Verify: event counts, a sample of `event_id`/`payload_hash`/`accepted_at`
triples, and that the append-only trigger is still installed
(`SELECT tgname FROM pg_trigger WHERE tgname = 'events_append_only'`).
4. Record elapsed time. That number is what this service can promise.
| Path | Evidence | Elapsed | Result |
| --- | --- | --- | --- |
| Platform full restore + PITR of live `audit_core` (21 events) | `rapp-postgres/docs/evidence/backup-restore-20260813T111651Z-remote.json` | 51.0s / 52.5s | triples + `events_append_only` |
| Platform logical single-consumer export/import | same | 3.5s | 21 events |
| audit-core logical dump of live `audit_core` → local PG 16 | `evidence/restore-walk-20260813T121200Z.json` | 4.1s | 21/3/1 counts, 5 triples, trigger present |
Until that walk exists, a workstation-side logical dump of an empty
`audit_core` is not evidence of recovery.
Verify after any restore:
```sql
SELECT count(*) FROM audit_core.events;
SELECT event_id, payload_hash, accepted_at FROM audit_core.events
WHERE event_id IN (/* sample from pre-restore */);
SELECT tgname FROM pg_trigger WHERE tgname = 'events_append_only';
```
## Rollback
The Deployment annotation `audit-core.railiance.io/rollback-note` is the
source of truth. Migrations 0001-0004 are additive; `kubectl -n audit-core
source of truth. Migrations 0001-0005 are additive; `kubectl -n audit-core
rollout undo deploy/audit-core` returns to the previous digest and an older
image runs against the newer schema. A future migration that drops or
narrows a column must replace that note before release.
## Deploy order
1. Image published, digest pinned in `deploy/audit-core.yaml` and
`deploy/migrate-job.yaml`.
2. Attended `scripts/openbao-eso-token-apply.sh`.
3. `bao kv put platform/workloads/audit-core/senders senders.json=@file`
from a mode-0600 file; shred the file.
4. Apply namespace (carries `railiance.io/postgres-client: platform-pg`),
NetworkPolicies, ClusterSecretStore, VaultDynamicSecrets, ExternalSecrets.
5. Wait until Secrets `audit-core-database`, `audit-core-database-migrate`,
and `audit-core-senders` exist.
6. Apply and wait for Job `audit-core-migrate`.
7. Apply the Deployment. Confirm `/readyz` reports `custody_class=archive`.
8. Run `MODE=remote BASE_URL=… make failure-matrix` and hand the JSON to
NK-WP-0024.
1. Image published and digest-pinned.
2. Namespace (label `railiance.io/postgres-client: platform-pg`),
NetworkPolicies, ClusterSecretStores, ExternalSecrets.
3. Sender registry is Secret `audit-core-senders` (in-cluster mint).
Database leases come from `openbao-audit-core-database`.
4. Job `audit-core-migrate` with `AUDIT_CORE_MIGRATE_ROLE=audit_core_migrate`.
5. Deployment. `/readyz` must report `custody_class=archive`.
6. In-pod `MODE=remote DISRUPT=0` failure matrix. Evidence goes to NK-WP-0024.