railiance-platform/docs/cadence-receiver-custody.md

155 lines
8.8 KiB
Markdown
Raw Normal View History

# 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:
```bash
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:
```bash
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:
```bash
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.
```bash
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.