Establish pre-production record disposition and add the migration tool
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

AUDIT-WP-0005-T04.

Disposition: there are no pre-production records. No audit-core SQLite store
on this host, no mock-file-backend output, and no audit-core pod, deployment
or PVC on railiance01 - the only audit-* PVC there is OpenBao's own audit
device. Consistent with the history: WP-0003-T03 was cancelled before the
receiver was ever deployed, so every SQLite store that has existed was a test
fixture. Nothing is being discarded because nothing was ever accepted outside
tests.

The tool is built anyway because the SQLite path stays reachable - the
entrypoint falls back to it when AUDIT_CORE_DATABASE_URL is unset. If that
fallback is ever used in anger the records are audit records, and writing the
migration afterwards under pressure is the wrong time.

audit_core.migrate_store and `python -m audit_core migrate-store` transfer
events, dead letters and secret-finding counters. Records keep their original
event_id, payload_hash and accepted_at, which is why this bypasses accept():
that stamps acceptance with the current time, and a migration that rewrote
acceptance times would destroy the evidence it exists to preserve.

Idempotent, and verification reads back from the destination rather than
trusting the write path. A destination record with a differing payload hash is
reported as a conflict and left untouched - silently overwriting a stored audit
record is the same class of failure as losing it. Conflicts and failed
verification exit non-zero; a partial migration is not a success.

Tests 71 -> 77.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-10 17:31:18 +02:00
parent bd274f6269
commit 88d16847ff
5 changed files with 455 additions and 2 deletions

View file

@ -166,7 +166,7 @@ previous version.
```task
id: AUDIT-WP-0005-T04
status: todo
status: done
priority: medium
state_hub_task_id: "9010fb4a-a1b8-4ef7-b143-e33ca7cc0619"
```
@ -182,6 +182,42 @@ events is the exact failure this service is meant to make impossible.
Done when the disposition of every pre-production record is either migrated
and verified, or explicitly and justifiably discarded.
**Disposition, established 2026-08-10: there are no pre-production records.**
Checked and found empty: no `/data/audit-core.db` or any other audit-core
SQLite store on this host, no mock-file-backend output under `/tmp/audit-core`,
and no audit-core pod, deployment, or PVC on railiance01. (The only `audit-*`
PVC there is `audit-openbao-0`, which is OpenBao's own audit device and
unrelated to this service.)
That is consistent with the history rather than surprising: WP-0003-T03 was
cancelled before the receiver was ever deployed, so the only SQLite stores that
have ever existed were test fixtures with no custody value. Nothing is being
discarded, because nothing was ever accepted outside tests.
The migration tool was built regardless, because the SQLite path remains
reachable: the entrypoint falls back to it when `AUDIT_CORE_DATABASE_URL` is
unset. If that fallback is ever used in anger, the records it accumulates are
audit records and cannot simply be dropped — and building the tool afterwards,
under pressure, is the wrong time.
`audit_core.migrate_store` plus `python -m audit_core migrate-store` transfer
events, dead letters, and secret-finding counters. Fidelity is the point:
records keep their original `event_id`, `payload_hash`, and `accepted_at`,
which is why the tool bypasses `accept()` — that stamps acceptance with the
current time, and a migration that rewrote acceptance times would destroy the
evidence it exists to preserve.
Migration is idempotent, and verification reads back from the destination
rather than trusting the write path. Where the destination already holds an
event with a *different* payload hash, the tool reports a conflict and leaves
the destination untouched: silently overwriting a stored audit record with a
differing one is the same class of failure as losing it. A conflict or failed
verification exits non-zero — a partial migration is not a success.
Six tests cover fidelity, dead letters and counters, idempotency, conflict
handling, post-migration readability under the append-only trigger, and the
empty-source case that is today's actual situation.
## T05 - Run the live failure matrix
```task