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
|
|
@ -117,20 +117,26 @@ Compatibility rules (not yet implemented):
|
|||
|
||||
| Field | Meaning |
|
||||
| --- | --- |
|
||||
| `custody_class` | `development`, `archive`, or `hot_search` |
|
||||
| `retention_days` | Maximum age before eligible deletion; `None` means indefinite |
|
||||
| `custody_class` | `development`, `operational`, `archive`, or `hot_search` |
|
||||
| `retention_days` | Maximum age before eligible deletion; `None` is a lifecycle statement (no expiry), not a recovery guarantee |
|
||||
| `immutable` | Whether stored records are protected from in-place alteration |
|
||||
| `tamper_evidence` | Whether manifests, hash chains, or signatures exist |
|
||||
| `durable` | Whether survival is expected across process restarts and host reboots |
|
||||
| `recoverable_days` | Cited platform backup window; `None` if not declared |
|
||||
| `recoverable_source` | Where the recoverable window is cited from |
|
||||
| `recoverable_basis` | ITC-GOV EvidenceBasis of that citation (`measured`, `quoted`, …) |
|
||||
|
||||
### Custody classes
|
||||
|
||||
| Class | Purpose | Guarantees |
|
||||
| --- | --- | --- |
|
||||
| `development` | Local integration and bootstrap wiring | Ephemeral local files; best-effort cleanup; **not audit custody** |
|
||||
| `archive` | Long-term evidence (planned) | Durable object storage, batch manifests, explicit retention |
|
||||
| `operational` | Durable production custody | Append-only Postgres; recoverable through the platform `data.backup` provision; not ITC-CAP `data.archive` |
|
||||
| `archive` | Long-term evidence (future sink) | Reserved for a backend that can satisfy `data.archive` hooks (retention policy, integrity verification, retrieval test) |
|
||||
| `hot_search` | Operational investigation (planned) | Shorter retention; searchable; not the evidence record |
|
||||
|
||||
`AUDIT_CORE_REQUIRE_CUSTODY_CLASS=operational` is the production fail-closed gate. `archive` is accepted as an alias of `operational` for one mixed rollout so an old manifest cannot refuse a new image. A `development` backend satisfies neither.
|
||||
|
||||
### Mock file backend policy
|
||||
|
||||
`MockFileAuditBackend.retention_policy`:
|
||||
|
|
@ -152,15 +158,20 @@ Enforcement:
|
|||
**Not guaranteed:** crash-safe writes, replication, encryption, tenant isolation,
|
||||
integrity proofs, or survival of `/tmp` across reboots.
|
||||
|
||||
### Production archive policy (planned)
|
||||
### Production operational policy (Postgres, live)
|
||||
|
||||
Target guarantees for the first durable backend:
|
||||
`PostgresAuditBackend.retention_policy`:
|
||||
|
||||
- `custody_class`: `archive`
|
||||
- `retention_days`: scope policy (often years, sometimes indefinite)
|
||||
- `immutable`: true (WORM / object lock where available)
|
||||
- `tamper_evidence`: true (batch manifests with content hashes)
|
||||
- `custody_class`: `operational`
|
||||
- `retention_days`: unset in production (the service does not expire rows)
|
||||
- `immutable`: true (trigger `events_append_only`; not a claim against the database owner)
|
||||
- `tamper_evidence`: false (a superuser can drop the trigger; no hash-chain)
|
||||
- `durable`: true
|
||||
- `recoverable_days`: 30, cited from the platform `data.backup` provision
|
||||
- `recoverable_source`: `resource-control/data/capability/platform-audit-storage.json#provisions[capability=data.backup]`
|
||||
- `recoverable_basis`: `measured`
|
||||
|
||||
`None` retention is a lifecycle statement, not unbounded archive. Rows older than the recoverable window are not promised after a restore. A future `archive` backend that satisfies ITC-CAP `data.archive` is not implemented.
|
||||
|
||||
## Migration path: mock file → durable backend
|
||||
|
||||
|
|
@ -189,15 +200,15 @@ backend.emit(AuditEvent(source="...", action="...", resource="...", outcome="suc
|
|||
|
||||
1. Register a durable archive backend implementing `AuditBackend`.
|
||||
2. Configure routing: development scopes may keep mock; production scopes require
|
||||
`custody_class=archive`.
|
||||
`custody_class=operational` (the live Postgres backend).
|
||||
3. Readiness checks compare `backend.retention_policy` against scope policy and
|
||||
fail closed when custody is insufficient.
|
||||
|
||||
### Phase 2 — archive primary (planned)
|
||||
### Phase 2 — operational primary (live)
|
||||
|
||||
1. Point `emit` calls (or HTTP ingestion) at the archive backend.
|
||||
1. HTTP ingestion writes through `PostgresAuditBackend` (`custody_class=operational`).
|
||||
2. Retain mock only for local `make mock-audit-smoke` and unit tests.
|
||||
3. Export historical mock JSONL into archive batches with manifest generation.
|
||||
3. A future `data.archive` sink is a separate backend, not a rename of Postgres.
|
||||
|
||||
### Phase 3 — hot search adjunct (planned)
|
||||
|
||||
|
|
@ -220,7 +231,9 @@ Archive remains the evidence record; hot search may use shorter `retention_days`
|
|||
| Backend | Module | Custody class |
|
||||
| --- | --- | --- |
|
||||
| Mock file JSONL | `audit_core.mock_file_backend.MockFileAuditBackend` | `development` |
|
||||
| Archive (planned) | TBD | `archive` |
|
||||
| SQLite (local / test) | `audit_core.sqlite_backend.SQLiteAuditBackend` | `development` |
|
||||
| PostgreSQL (production) | `audit_core.postgres_backend.PostgresAuditBackend` | `operational` |
|
||||
| Archive (planned `data.archive` sink) | TBD | `archive` |
|
||||
| Hot search (planned) | TBD | `hot_search` |
|
||||
|
||||
## Related documents
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue