railiance-platform/docs/cadence-receiver-custody.md
codex 6e24b09eaa
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Record native receiver custody and prepare actual Approval lifecycle readback
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
2026-10-15 21:40:48 +02:00

8.8 KiB

Hourly cadence receiver admission and Qonto sender custody

NK-WP-0035 / RPF-WP-0059 / CCR-2026-0047. User explicitly approved this complete production packet on 2026-10-15 with "Approve packet and proceed", including the new sender grant. The founder's timing-rollout approval did not approve this new bearer or ESO reader. The approved scope binds platform operator, Audit Core, rapp-qonto and qonto-assistant owner roles; native prerequisites still apply.

The prepared change preserves every existing sender credential, source/tenant scope and other registry field. It updates only Approval Engine's canonical cadence and four heartbeat waits (2f=7200s; receiver grace1.5 gives3f=10800s), and adds one new exact sender:

Coordinate Proposed value
Sender rapp-qonto-collector
Source qonto-assistant
Tenant tenant:friendly:binky
Permission write=true, read=false; receiver own-source reconciliation remains contract-defined
Evidence envelope load-bearing/redact; complete source cadence retains each class's kind
Heartbeat one source beat maps once to audit-core.heartbeat, data.class=audit.deny
Custody platform/workloads/rapp-qonto/audit-sender: AUDIT_TOKEN,CUSTODY_REQUEST
Reader policy workload-kv-read-rapp-qonto-audit; exact data path read only, no metadata/list
Kubernetes role external-secrets-rapp-qonto-audit; external-secrets/external-secrets,15m
Delivery namespace-restricted openbao-rapp-qonto-audit store; rapp-qonto/rapp-qonto-audit-sender:bearer

Plan without credentials or network:

python3 scripts/credential-change.py validate CCR-2026-0047
python3 scripts/cadence_receiver_custody.py plan

The plan hashes CCR, exact policy/projection, executable helpers and enforcing receiver parser sources, raw source declarations and Approval metadata proposal. Record owner approvals in the CCR, regenerate the plan, and review its resulting SHA256. Any subsequent source change requires a newly reviewed packet. The runner verifies the exact packet hash; a proposed CCR refuses before native calls. The immutable deployed receiver must support full cadence parsing and per-instance reconciliation, verified with fixture-only native sender parsing before any credential read. A source compatibility test alone cannot satisfy that deployed gate.

Run apply only inside the existing attended Warden platform-admin --exec envelope. Use credential routing first; do not export a token into a shell. Before starting OIDC, remove all three ambient token aliases from the launcher environment without reading or printing their values:

env -u BAO_TOKEN -u VAULT_TOKEN -u OPENBAO_TOKEN \
  warden access openbao-platform-admin-login --exec -- \
  python3 /home/worsch/railiance-platform/scripts/cadence_receiver_custody.py apply <reviewed-arguments>

This is required for the custody and fresh observer-readback launches. The initial native attempt demonstrated that OPENBAO_TOKEN is not scrubbed by the existing Warden wrapper; the contained helper correctly refused it. Session self-revocation was confirmed and no custody mutation occurred in that attempt. Do not inspect, copy or relax the guard for an ambient credential.

The owner wrapper must preserve its independent exit/self-revocation receipt. The contained command is:

python3 scripts/cadence_receiver_custody.py apply \
  --kubeconfig '<owner-selected-kubeconfig>' --server '<verified-primary-server>' \
  --expected-receiver-image 'forgejo.coulomb.social/coulomb/audit-core@sha256:<reviewed-digest>' \
  --confirm-packet-sha256 '<reviewed-post-approval-plan-sha256>' \
  --receipt '<new-private-receipt-path>'

The command is silent; all subprocess outputs are captured, exceptions expose fixed diagnostics only, and the0600 O_EXCL receipt contains metadata. It checks the primary cluster UID, single ready receiver/pinned image, existing registry, exact projection and custody provenance before reader grant writes. It extends the coding-agent deny boundary for only the new data/metadata paths, provisions CAS0 version1 with request provenance, and merges with exact registry-version CAS. A concurrent edit fails; it never retries against an unseen registry. It verifies exact reader path and scope/TTL, denied registry/sibling/metadata/list, wrong service identity, revoked reader session and deny-boundary precedence; then delivers ESO, compares bearer values privately, refreshes the receiver registry secret, restarts the receiver and verifies loaded registry equality. The new store is restricted to rapp-qonto, and source projection shape forbids extra data or arbitrary templates. API-observed default fields are explicitly pinned so a legitimate resumed projection remains equal.

A partial failure retains custody/version1 for investigation. Inspect its nonsecret receipt, confirm unchanged reviewed source, and rerun with a new receipt path plus --resume. The same request's durable token is reused; conflicting versions, provenance, registry scope or role/policy drift refuse. No helper rotation, secret deletion or broad rollback occurs. A restart after successful delivery may be repeated by an explicitly resumed attempt; it preserves the token and registry content.

Success is delivered_pending_native_producer_and_independent_acceptance. It does not prove collector source capture, denial refusal, bearer revocation, independent receiver reads, elapsed-hour heartbeat observation, or profile acceptance. Read-only observer custody must remain independent; do not upgrade the collector bearer. Keep the runtime active using the owner's authorized health polling for the real hour, without changing scale-to-zero. A session lasting15m must close normally; use a fresh attended session for hour-end reads. Source-global missing-heartbeat findings must be interpreted alongside actual active-instance inventory: intended scale-to-zero is not unexpected active process silence. Preserve shutdown/restart scope separately.

Residual native checks include isolated wrong-namespace store denial and former-valid collector bearer revocation, alongside the source/tenant/read refusal and per-instance observer matrix. The runner's wrong-service-identity probes do not separately isolate wrong namespace while preserving the correct service account name. Record that check honestly rather than treating the combined refusal as the complete namespace test.

Independent hour-end custody readback

Use a fresh attended platform-admin envelope after the observation hour; the initial session must not be held open. scripts/cadence_observer_readback.py reuses the existing uniquely selected independent read-only full-tenant operator credential in process memory. It changes no credential or workload and never grants the collector archive read permission. Its only transport is a temporary primary-cluster localhost port-forward that is terminated on exit, with redirects and environment HTTP proxies disabled.

python3 scripts/cadence_observer_readback.py \
  --source qonto-assistant --tenant tenant:friendly:binky \
  --instance '<exact-source-snapshot-instance>' \
  --since '<acceptance-window-start-with-timezone>' \
  --until '<acceptance-window-end-with-timezone>' \
  --event-id '<exact-collector-event-id-1>' \
  --event-id '<exact-collector-event-id-2>' \
  --kubeconfig '<owner-selected-kubeconfig>' --server '<verified-primary-server>' \
  --expected-receiver-image 'forgejo.coulomb.social/coulomb/audit-core@sha256:<reviewed-digest>' \
  --receipt '<new-private-readback-receipt-path>'

Provide all capture event IDs (maximum256, unique). The reader only opens those IDs, the exact source/tenant/instance reconciliation query, and the protected global stream-findings surface. It outputs only findings for the selected source; no unrelated findings or request payload is persisted. Every event must match exact ID/source/tenant/instance and acceptance window. The sanitized event projection includes instance/sequence, action, observed and accepted timestamps, and exact heartbeat coverage. Actual custody accepted_at can distinguish a duplicate delivery receipt from original custody time; a collector duplicate200 is not itself first-acceptance timing proof. Approval Engine source/tenant is also allowed; omit --instance for its instance-free lifecycle records. The helper does not invent process identity or sequence for Approval and labels its conclusion as a bounded source lifecycle acceptance window. Qonto still requires an explicit exact instance. Receiver-normalized timezone spelling is compared by timestamp semantics; source, tenant and actual instance bindings remain exact.

Exit0 reports independent_custody_readback_complete, which proves only the bounded supplied custody reads. The source snapshot, contiguous startup/ periodic capture, process inventory and negative observation matrix remain independent inputs to Kings Guard's assessment.