187 lines
6.6 KiB
Markdown
187 lines
6.6 KiB
Markdown
|
|
---
|
||
|
|
id: AUDIT-WP-0005
|
||
|
|
type: workplan
|
||
|
|
title: "Deploy audit-core on Railiance with durable Postgres custody"
|
||
|
|
domain: infotech
|
||
|
|
repo: audit-core
|
||
|
|
status: proposed
|
||
|
|
owner: codex
|
||
|
|
topic_slug: netkingdom
|
||
|
|
created: "2026-08-10"
|
||
|
|
updated: "2026-08-10"
|
||
|
|
depends_on:
|
||
|
|
- AUDIT-WP-0004
|
||
|
|
- RAPP-POSTGRES-WP-0002
|
||
|
|
- NK-WP-0024
|
||
|
|
---
|
||
|
|
|
||
|
|
# AUDIT-WP-0005 - Postgres store and production deployment
|
||
|
|
|
||
|
|
## Goal
|
||
|
|
|
||
|
|
Put audit-core into production on Railiance with PostgreSQL as the custody
|
||
|
|
store, and prove the delivery path end to end.
|
||
|
|
|
||
|
|
This is first on the critical path: email-connect delivery, OpenBao sender
|
||
|
|
credentials, the user-engine runtime integrations, and the live failure matrix
|
||
|
|
all sit downstream of a receiver that actually holds evidence.
|
||
|
|
|
||
|
|
## Dependencies
|
||
|
|
|
||
|
|
- **AUDIT-WP-0004** — the receiver must route through the backend contract and
|
||
|
|
return deliberate status codes before a Postgres backend is worth writing
|
||
|
|
behind it, and before a failure matrix can distinguish a correct refusal
|
||
|
|
from a crash.
|
||
|
|
- **RAPP-POSTGRES-WP-0002** — the database, the tenancy model, and the
|
||
|
|
credential lane. T01 below can be drafted against the model as soon as
|
||
|
|
RAPP-POSTGRES-WP-0002-T01 settles it; T02 needs a provisioned database.
|
||
|
|
|
||
|
|
## Boundaries
|
||
|
|
|
||
|
|
This workplan owns the Postgres backend implementation, audit-core's
|
||
|
|
deployment, and the live verification. It does not own the database platform,
|
||
|
|
its tenancy model, or its backup machinery — those belong to rapp-postgres.
|
||
|
|
Where audit-core needs a guarantee from the platform, it states the
|
||
|
|
requirement and consumes it; it does not implement it here.
|
||
|
|
|
||
|
|
## T01 - Implement the Postgres audit backend
|
||
|
|
|
||
|
|
```task
|
||
|
|
id: AUDIT-WP-0005-T01
|
||
|
|
status: todo
|
||
|
|
priority: high
|
||
|
|
```
|
||
|
|
|
||
|
|
Implement `AuditBackend` against PostgreSQL, declaring an honest
|
||
|
|
`RetentionPolicy` — the custody class, retention window, and whether
|
||
|
|
immutability and tamper evidence are genuinely provided rather than aspired
|
||
|
|
to. If the schema does not prevent an operator from silently editing a
|
||
|
|
recorded event, the policy must not claim `immutable`.
|
||
|
|
|
||
|
|
Push idempotency into the database rather than a read-then-write in
|
||
|
|
application code: an insert conflicting on event ID must resolve atomically
|
||
|
|
into duplicate-accepted or conflict, with no window where two concurrent
|
||
|
|
identical events both write. Store the payload hash for conflict detection.
|
||
|
|
|
||
|
|
Handle connection lifecycle properly — pooling, reconnection after a database
|
||
|
|
restart, and a bounded statement timeout so a stalled write surfaces as
|
||
|
|
unavailable instead of hanging the request.
|
||
|
|
|
||
|
|
Own the migrations. The consuming service owns its schema; rapp-postgres owns
|
||
|
|
the space it runs in.
|
||
|
|
|
||
|
|
Done when the backend passes the same contract tests as the existing backends,
|
||
|
|
concurrent duplicate submissions produce exactly one record, and a database
|
||
|
|
restart mid-write does not produce an acknowledged-but-absent event.
|
||
|
|
|
||
|
|
## T02 - Provision storage through the platform lane
|
||
|
|
|
||
|
|
```task
|
||
|
|
id: AUDIT-WP-0005-T02
|
||
|
|
status: todo
|
||
|
|
priority: high
|
||
|
|
```
|
||
|
|
|
||
|
|
Declare audit-core's database requirement against rapp-postgres and take
|
||
|
|
delivery of a least-privilege runtime role through the OpenBao lane. The
|
||
|
|
runtime role connects; a separate role runs migrations; neither owns more than
|
||
|
|
it needs.
|
||
|
|
|
||
|
|
Verify audit-core's side of the isolation model: the runtime credential
|
||
|
|
reaches audit-core's data and nothing else, and rotation completes without a
|
||
|
|
delivery gap.
|
||
|
|
|
||
|
|
Done when audit-core runs against a provisioned database using a credential it
|
||
|
|
never received as a literal, and rotating that credential does not drop
|
||
|
|
events.
|
||
|
|
|
||
|
|
## T03 - Deploy the receiver
|
||
|
|
|
||
|
|
```task
|
||
|
|
id: AUDIT-WP-0005-T03
|
||
|
|
status: todo
|
||
|
|
priority: high
|
||
|
|
```
|
||
|
|
|
||
|
|
Publish an immutable image — base pinned by digest, not a mutable tag, and
|
||
|
|
labelled with the build commit — and deploy on railiance01 with a Service,
|
||
|
|
health and readiness probes wired to the checks from WP-0004-T01, resource
|
||
|
|
requests and limits, a restricted security context, and a default-deny
|
||
|
|
NetworkPolicy admitting only user-engine as sender and the operator path for
|
||
|
|
reads.
|
||
|
|
|
||
|
|
State the rollback position: which image digest and which schema version the
|
||
|
|
deployment can return to, and whether the migration in T01 is reversible. A
|
||
|
|
rollback plan that assumes reversible migrations without checking is not a
|
||
|
|
plan.
|
||
|
|
|
||
|
|
The container currently runs as uid 10001 and expects a writable `/data`; once
|
||
|
|
custody is in Postgres that path should carry no durable state at all. Confirm
|
||
|
|
nothing of value is left on the pod filesystem.
|
||
|
|
|
||
|
|
Done when the receiver is reachable only by its declared peers, survives pod
|
||
|
|
restart and rescheduling without loss, and has a tested path back to the
|
||
|
|
previous version.
|
||
|
|
|
||
|
|
## T04 - Migrate existing SQLite records
|
||
|
|
|
||
|
|
```task
|
||
|
|
id: AUDIT-WP-0005-T04
|
||
|
|
status: todo
|
||
|
|
priority: medium
|
||
|
|
```
|
||
|
|
|
||
|
|
Any events accepted by the pre-production SQLite receiver are audit records
|
||
|
|
and cannot simply be dropped. Move them into the Postgres store with their
|
||
|
|
original identifiers, timestamps, and payload hashes intact, or record an
|
||
|
|
explicit decision that they are development artifacts with no custody value.
|
||
|
|
|
||
|
|
Whichever holds, it must be written down — silently discarding accepted audit
|
||
|
|
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.
|
||
|
|
|
||
|
|
## T05 - Run the live failure matrix
|
||
|
|
|
||
|
|
```task
|
||
|
|
id: AUDIT-WP-0005-T05
|
||
|
|
status: todo
|
||
|
|
priority: high
|
||
|
|
```
|
||
|
|
|
||
|
|
Exercise the deployed path: successful delivery; receiver timeout and
|
||
|
|
unavailability; bounded user-engine retry against each documented status code;
|
||
|
|
dead-letter visibility; operator replay and duplicate replay; redaction; and
|
||
|
|
correlation lookup. Include a database failover or restart during active
|
||
|
|
ingestion, and a credential rotation during active ingestion.
|
||
|
|
|
||
|
|
The controlling assertion is that one source outbox event produces exactly one
|
||
|
|
durable normalized event across retries, replay, and infrastructure
|
||
|
|
disruption — no loss, no duplication.
|
||
|
|
|
||
|
|
Hand non-secret evidence back to NK-WP-0024.
|
||
|
|
|
||
|
|
Done when the matrix has been run against the deployed system and each
|
||
|
|
outcome recorded, including any case where behaviour differed from the
|
||
|
|
documented contract.
|
||
|
|
|
||
|
|
## T06 - Operational handover
|
||
|
|
|
||
|
|
```task
|
||
|
|
id: AUDIT-WP-0005-T06
|
||
|
|
status: todo
|
||
|
|
priority: medium
|
||
|
|
```
|
||
|
|
|
||
|
|
Document what an operator needs: how to look up an event by correlation ID,
|
||
|
|
how to inspect and replay dead-lettered events, how to rotate the sender
|
||
|
|
credential, what the alert conditions mean, and how to restore audit data from
|
||
|
|
a rapp-postgres backup.
|
||
|
|
|
||
|
|
Verify audit-core's recovery requirement against what rapp-postgres actually
|
||
|
|
provides — the retention window audit-core declares must not exceed the
|
||
|
|
retention the platform guarantees.
|
||
|
|
|
||
|
|
Done when the runbook exists and the restore path has been walked once.
|