Implement AUDIT-WP-0006 honest operational custody.
Postgres now reports custody_class=operational with a cited 30-day recoverable window. Join ITC-CAP operations.audit at D4, publish the interface card, and overlay user-engine tenants [*] from Git so an ExternalSecret refresh cannot shrink it.
This commit is contained in:
parent
0a3d05ff1c
commit
ded432a63f
25 changed files with 832 additions and 94 deletions
|
|
@ -29,12 +29,13 @@ warden route show database-dynamic-credentials --json
|
|||
| Check | Meaning |
|
||||
| --- | --- |
|
||||
| `GET /healthz` | Process is up. Liveness uses this. A database outage must **not** restart the pod. |
|
||||
| `GET /readyz` | Custody is reachable and `custody_class=archive`. Readiness uses this; the pod leaves the Service rather than accept events it cannot store. |
|
||||
| `GET /readyz` | Custody is reachable and `custody_class=operational`. Also reports `recoverable_days` (cited platform backup window). Readiness uses this; the pod leaves the Service rather than accept events it cannot store. |
|
||||
| `GET /v1/stats` | In-process counters since start (`accepted`, `duplicate`, `conflict`, `rejected`, `unauthorized`, `forbidden`, `unavailable`, `error`). Resets on restart. Requires `may_read`. |
|
||||
|
||||
A missing `AUDIT_CORE_DATABASE_URL` / credential directory is a startup
|
||||
failure (`AUDIT_CORE_REQUIRE_CUSTODY_CLASS=archive`), not a silent downgrade
|
||||
to SQLite.
|
||||
failure (`AUDIT_CORE_REQUIRE_CUSTODY_CLASS=operational`), not a silent
|
||||
downgrade to SQLite. An older manifest that still requires `archive` is
|
||||
accepted as an alias for one mixed rollout.
|
||||
|
||||
## Lookup
|
||||
|
||||
|
|
@ -96,9 +97,14 @@ replace that path with a founder paste. Rotation is overlap-first:
|
|||
5. Either wait for the 1h refresh or annotate the ExternalSecret to force a
|
||||
sync, then restart the pod so it re-reads `AUDIT_CORE_SENDERS`.
|
||||
|
||||
The write token is bound to `source=user-engine` and the tenants that
|
||||
identity may claim. The operator token is a separate identity with
|
||||
`may_read: true`. Do not reuse one token for both.
|
||||
The write token is bound to `source=user-engine`. Tenant scope for that
|
||||
identity is **not a secret**: `deploy/senders-scope.json` (ConfigMap
|
||||
`audit-core-senders-scope`) overlays `tenants: ["*"]` onto the Secret
|
||||
at start. An ExternalSecret refresh cannot revert it to a single tenant.
|
||||
Tokens stay in Secret `audit-core-senders` / OpenBao KV.
|
||||
|
||||
The operator token is a separate identity with `may_read: true`. Do not
|
||||
reuse one token for both.
|
||||
|
||||
A shape (values are placeholders) is in `docs/senders.example.json`.
|
||||
|
||||
|
|
@ -142,17 +148,34 @@ behind the read privilege). Watch:
|
|||
|
||||
## Restore
|
||||
|
||||
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.
|
||||
audit-core does **not** expire events (`retention_days` unset). That is a
|
||||
lifecycle policy statement, not a recovery guarantee. Recoverable history
|
||||
is the platform `data.backup` window: **30 days**, prefix `platform-pg/`,
|
||||
bucket owned by `resource:platform:audit-storage`. Cite, do not copy:
|
||||
|
||||
- `resource-control/data/capability/platform-audit-storage.json`
|
||||
- `rapp-postgres/docs/restore.md`
|
||||
|
||||
The platform requirement is `data.backup` profile `database` at D5
|
||||
(RPO 5 min, RTO 60 min, 30-day retention, not in the railiance01 /
|
||||
host-europe failure domain). The live provision is **D4**, not D5:
|
||||
resource-control scored one backup, one full restore, one PITR, and
|
||||
does not yet emit `wal_archive_gap_minutes`. RPO/RTO numbers are
|
||||
theirs (`measured`, single observation). audit-core does not claim a
|
||||
better grade.
|
||||
|
||||
Rows older than the 30-day window are not promised after a restore.
|
||||
Local WAL on the node is not a second copy.
|
||||
|
||||
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`.
|
||||
|
||||
Walked 2026-08-13:
|
||||
The 2026-08-13 fail-closed sentence is superseded by RESOURCE-WP-0002-T05
|
||||
(2026-08-14): production Barman to Scaleway, `audit_core.events` 30=30,
|
||||
full restore 65 s, PITR 65 s.
|
||||
|
||||
Walked 2026-08-13 (pre-commissioning historical evidence):
|
||||
|
||||
| Path | Evidence | Elapsed | Result |
|
||||
| --- | --- | --- | --- |
|
||||
|
|
@ -185,5 +208,6 @@ narrows a column must replace that note before release.
|
|||
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`.
|
||||
5. Deployment. `/readyz` must report `custody_class=operational` and
|
||||
`recoverable_days=30`.
|
||||
6. In-pod `MODE=remote DISRUPT=0` failure matrix. Evidence goes to NK-WP-0024.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue