railiance-platform/workplans/RPF-WP-0066-openbao-durable-audit-snapshots-conformance.md
codex 05ff9f8678
All checks were successful
CCR intake gate / gate (push) Successful in 1m11s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
RPF-WP-0064, RPF-WP-0066: record hub ids
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4515@bnt-lap001
Assistant-Session: 0bf96127-6e04-41be-bfda-d11717c3f354
2026-10-27 23:33:59 +01:00

8.2 KiB

id type title domain repo status flavor owner created updated related state_hub_workstream_id
RPF-WP-0066 workplan OpenBao durable audit shipping, scheduled Raft snapshots and live conformance receipt financials railiance-platform proposed implementation claude 2026-10-27 2026-10-27
NK-WP-0055
RPF-WP-0036
RPF-WP-0064
AUDIT-WP-0013
AUDIT-WP-0015
ACTIVITY-WP-0042
f1eadbc2-48fd-57de-ab90-e3c24d6e20a5

RPF-WP-0066 — OpenBao durable audit, scheduled snapshots, live conformance

Receiving workplan for NetKingdom NK-WP-0055-T11 (audit sink) and the OpenBao part of NK-WP-0055-T12 (live conformance receipts). Source: net-kingdom docs/evidence/2026-10-27-production-readiness-ledger.md sections 4 (OpenBao) and 9 (Audit sink).

Evidence

  • The declarative audit "file" "file" stanza in helm/openbao-values.yaml writes /openbao/audit/openbao-audit.log on the audit PVC. docs/openbao.md states the PVC "is not a durable audit sink". Durable shipping was deferred when RAIL-PL-WP-0002 closed, and no live workplan owned it until now.
  • make openbao-reboot-snapshot is an attended Raft snapshot -> age -> verify -> offsite path with a receipt. RPF-WP-0064-T07/T08 proved one isolated original restore. Neither is a schedule. RPF-WP-0036-T03 owns recurring backup and restore evidence for all S3 data services and waits on ACTIVITY-WP-0042 (bounded scheduler).
  • make openbao-verify-authenticated already runs non-mutating checks of audit devices, platform/, and the kubernetes/, netkingdom/, keycape/ auth mounts. It does not compare the live state to openbao/policies/ and does not write a dated receipt.

Constraints

  • Source preparation only. Every live step (Helm rollout, enabling an audit device, installing a schedule, the drills) is founder-run, with a guarded helper and the exact command, and keeps its own execution gate.
  • Audit records leave OpenBao with its default HMAC-SHA256 hashing of secret-bearing request and response fields. No log_raw, and no hmac_accessor=false. Any audit_non_hmac_*_keys tuning on a mount needs a separate reviewed change. The shipper never logs record bodies and never holds an OpenBao token.
  • OpenBao blocks requests when every enabled audit device fails. The file device stays enabled as the local, blocking device. Shipping must be asynchronous, so a sink outage cannot stop OpenBao.

Choose the OpenBao audit forwarding design

id: RPF-WP-0066-T01
status: todo
priority: high
state_hub_task_id: "1f2cced8-7851-51bf-9f73-b9504fd69eff"

Compare the options and record the decision in docs/openbao.md (or an ADR if it changes the platform boundary):

  1. File device plus a tailing sidecar shipper (preferred candidate). A shipper such as Vector or Fluent Bit in the openbao pod reads the PVC log, keeps a disk checkpoint, maps records to the audit-core envelope and posts them to the audit-core ingest API as a registered sender. It retries with backoff from the PVC backlog. The PVC is the buffer; size it and set a rotation that cannot drop unshipped lines. Rotation would use SIGHUP to reopen the log.
  2. Socket device to an in-cluster collector. This has lower latency, but a socket failure counts against the "all devices failed" rule. It is acceptable only as a second, non-sole device, and its fallback semantics need checking.
  3. Separate durable store (an encrypted object store with object lock). Use this only if audit-core cannot accept OpenBao volume. It then needs its own retention and its own restore drill.

Done when: one option is chosen with volume and backlog sizing, and its failure modes are recorded: sink down, shipper down, PVC full, rotation.

Agree the audit-core sender contract and the correlation id

id: RPF-WP-0066-T02
status: wait
priority: high
blocked_on: message-from:audit-core
state_hub_task_id: "96f632d0-4a0d-5b1f-9857-4ddee2a33b35"

Needed from audit-core (AUDIT-WP-0013 sender admission):

  • an OpenBao sender registration and its credential route (OpenBao-issued, no secret in Git);
  • an envelope mapping for OpenBao request/response records, with HMAC'd fields passed as opaque;
  • idempotency and dedup keys (OpenBao request.id plus record type);
  • the expected volume and retention class;
  • an acknowledgement and backpressure contract.

Correlation: the NetKingdom-owned cross-stream correlation id (NK-WP-0055-T11) has to reach OpenBao requests. Set a request header such as X-Correlation-Id. If the listener allows it, record it through the auth mount audit_non_hmac_request_keys / passthrough_request_headers or the listener header settings. Otherwise derive it from the OIDC jti/session claim in entity metadata. Verify which of these OpenBao records unhashed without exposing a secret.

Done when: a written contract exists in both repos, and NetKingdom's correlation-id definition is cited.

Implement the shipper source and its tests

id: RPF-WP-0066-T03
status: todo
priority: high
depends_on: [RPF-WP-0066-T01, RPF-WP-0066-T02]
state_hub_task_id: "8dd8ee8a-e94f-5af8-8d80-40f517c230d0"

Add the shipper configuration to helm/openbao-values.yaml, along with the checkpoint volume, log rotation, and the mapping and redaction config. Test against synthetic fixtures:

  • records stay HMAC'd end to end;
  • no token or accessor appears in the clear;
  • dedup holds on replay;
  • the backlog drains after a simulated sink outage;
  • correlation-id propagation works.

Prepare a founder-run rollout helper with a post-rollout check: the audit-core record count rises for a probe request carrying a known correlation id.

Done when: tests pass and the helper and its exact command are documented. The live rollout is a separate attended step.

Set retention and drill audit restore

id: RPF-WP-0066-T04
status: wait
priority: medium
depends_on: [RPF-WP-0066-T03]
state_hub_task_id: "6a87f79d-eddd-5393-b4f0-fc0633557b58"

Record retention per store: the PVC (short, buffer only), audit-core (its retention class), and any separate archive. The recurring audit-core restore drill belongs to audit-core (NK-WP-0055-T11 / AUDIT-WP-0015-T01). This task supplies the OpenBao part: after a restore, an OpenBao record found by correlation id and request.id verifies against the hash chain. If option 3 is chosen in T01, this repo owns the full restore drill of that store.

Done when: a dated restore receipt shows OpenBao records recovered and verified.

Schedule OpenBao Raft snapshots

id: RPF-WP-0066-T05
status: wait
priority: high
depends_on: [RPF-WP-0036-T03, ACTIVITY-WP-0042]
state_hub_task_id: "6098801d-f776-50d8-a4ac-c192b9ceee49"

This is the OpenBao slice of RPF-WP-0036-T03, split out so NK-WP-0055 can track it. Turn scripts/openbao-reboot-snapshot.py (snapshot -> age -> verify -> offsite -> receipt) into an unattended, bounded operation run by the accepted scheduler (ACTIVITY-WP-0042). It needs:

  • a snapshot-only OpenBao policy, sys/storage/raft/snapshot read and nothing more;
  • a scoped credential route;
  • a cadence and expiry that RPF-WP-0036-T02's freshness signal can check;
  • a stale or failed run that reaches a named operator.

A periodic isolated restore, reusing the RPF-WP-0064 isolated-restore path, is the recurring proof. A dated snapshot alone does not pass.

Done when: the schedule is installed through the execution owner, and at least two consecutive scheduled snapshot receipts and one scheduled isolated restore receipt exist.

Record a dated live check of auth methods and mounts against committed policies

id: RPF-WP-0066-T06
status: todo
priority: medium
state_hub_task_id: "797d6df8-9dcf-5252-b91d-909cd12ce202"

Extend make openbao-verify-authenticated, or add a sibling read-only target, to emit a dated, non-secret receipt. The receipt diffs bao auth list, bao secrets list, bao audit list and bao policy list, plus per-policy content hashes, against openbao/policies/*.hcl and the expected mounts list. It lists unexpected mounts, missing mounts and policy drift. The founder runs it; the receipt is committed under docs/evidence/.

Done when: one dated receipt is committed and cited in NK-WP-0055-T12.