Record native receiver custody and prepare actual Approval lifecycle readback
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
This commit is contained in:
codex 2026-10-15 21:40:48 +02:00
parent ce5344da69
commit 6e24b09eaa
8 changed files with 372 additions and 40 deletions

View file

@ -3,7 +3,7 @@ kind: credential-change-request
schema_version: 1
request_type: workload-kv-read
title: rapp-qonto collector exact audit sender custody and delivery
status: approved
status: applied
created: '2026-10-15'
updated: '2026-10-15'
requester:
@ -104,7 +104,24 @@ verification:
- Apply only reviewed exact policy/role/projection; retain custody on failure and resume explicitly;
no destructive rollback or blind rotation.
- No claim of operational completion until native positive/negative and independent observer receipts.
evidence: []
evidence:
- at: '2026-10-15T19:40:23.546597+00:00'
actor: codex via attended user platform-admin (root execution)
kind: cadence_receiver_custody_and_native_sender
result: passed
details:
- "Native custody receipt receiver-custody-live.json: protected registry CAS15\u219216, version1\
\ new sender custody, Approval credentials and all other senders preserved; exact readers/negative\
\ checks/revocation, ESO values and loaded receiver registry matched."
- Independent attended wrapper exit0/session self-revocation confirmed by root; initial rejected
OPENBAO_TOKEN-ambient attempt made no credential mutation and session self-revocation was confirmed.
Retry launch removed all three token aliases before OIDC.
- 'Native collector source/tenant admission: heartbeat1 and deny1 reconciled for one exact instance;
duplicate200, wrong-source400, wrong-tenant400, archive-read403, invalid-bearer401; pending0.
Original202 receipts persisted across actual collector pod restart.'
- Source receipts copied unchanged to docs/evidence/2026-10-15-cadence-receiver/. Status applied
only; elapsed-hour periodic observation, independent protected observer readback and complete
native matrix remain pending.
lifecycle:
deactivate: Stop only collector; remove only its bearer from receiver via CAS and prove refusal; detach
exact reader role and ESO projection; retain custody versions and evidence.

View file

@ -42,6 +42,21 @@ 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:
@ -126,9 +141,12 @@ 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, but this helper requires an
instance-bearing source record and therefore cannot substitute for its
historical instance-free lifecycle acceptance path.
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/

View file

@ -0,0 +1,59 @@
{
"own_reconciliation_status": 200,
"own_reconciliation": {
"source": "qonto-assistant",
"tenant": "tenant:friendly:binky",
"since": "2026-10-15T19:36:00+00:00",
"until": "2026-10-15T19:37:05.369346+00:00",
"counts": [
{
"class": "audit-core.heartbeat",
"count": 1
},
{
"class": "audit.deny",
"count": 1
}
],
"stream_instance_id": "b351fa6b-e0d6-4f88-b7c9-6bacb1a112b7",
"means": "the count of events audit-core accepted and stored in this window. Divergence from the source's own count is a finding for the source; agreement proves neither completeness nor that any event occurred."
},
"duplicate_status": 200,
"wrong_source_status": 400,
"wrong_tenant_status": 400,
"read_refusal_status": 403,
"invalid_bearer_status": 401,
"global_pending_count": 0,
"instance_receipts": [
{
"accepted_at": "2026-10-15T19:36:30.769604+00:00",
"covered_event_classes": [
"audit.deny"
],
"event_id": "28dad6de11fb25fe6e0e145351f047360d6898afb53afd4cd677e560ec69132a",
"source": "qonto-assistant",
"status_code": 202,
"stream_instance_id": "b351fa6b-e0d6-4f88-b7c9-6bacb1a112b7",
"stream_sequence": 1,
"tenant": "tenant:friendly:binky",
"type": "audit-core.heartbeat"
},
{
"accepted_at": "2026-10-15T19:36:30.861014+00:00",
"covered_event_classes": [],
"event_id": "6a9f34445e13062e38ab309a6fc800dc9a2fa24cb8a46ecd919ae18fe9409874",
"source": "qonto-assistant",
"status_code": 202,
"stream_instance_id": "b351fa6b-e0d6-4f88-b7c9-6bacb1a112b7",
"stream_sequence": 2,
"tenant": "tenant:friendly:binky",
"type": "audit.deny"
}
],
"observed_at": "2026-10-15T19:37:03.422744+00:00",
"credential_values_emitted": false,
"source": "qonto-assistant",
"tenant": "tenant:friendly:binky",
"stream_instance_id": "b351fa6b-e0d6-4f88-b7c9-6bacb1a112b7",
"independent_readback": false
}

View file

@ -0,0 +1,37 @@
{
"receipts": [
{
"accepted_at": "2026-10-15T19:36:30.769604+00:00",
"covered_event_classes": [
"audit.deny"
],
"event_id": "28dad6de11fb25fe6e0e145351f047360d6898afb53afd4cd677e560ec69132a",
"source": "qonto-assistant",
"status_code": 202,
"stream_instance_id": "b351fa6b-e0d6-4f88-b7c9-6bacb1a112b7",
"stream_sequence": 1,
"tenant": "tenant:friendly:binky",
"type": "audit-core.heartbeat"
},
{
"accepted_at": "2026-10-15T19:36:30.861014+00:00",
"covered_event_classes": [],
"event_id": "6a9f34445e13062e38ab309a6fc800dc9a2fa24cb8a46ecd919ae18fe9409874",
"source": "qonto-assistant",
"status_code": 202,
"stream_instance_id": "b351fa6b-e0d6-4f88-b7c9-6bacb1a112b7",
"stream_sequence": 2,
"tenant": "tenant:friendly:binky",
"type": "audit.deny"
}
],
"pending_count": 0,
"started_at": "2026-10-15T19:37:41.503622+00:00",
"pod_uid_before": "1a39bd4b-19dc-4c1f-adaa-56a11423d945",
"native_admission_receipt": "qonto-collector-native-admission.json",
"pod_uid_after": "92ef9104-469c-4b28-a0cc-f781f53bb26e",
"observed_at": "2026-10-15T19:38:35.874453+00:00",
"first_202_receipts_preserved": true,
"pvc": "rapp-qonto-audit-outbox",
"image": "forgejo.coulomb.social/coulomb/rapp-qonto-audit-collector@sha256:82bfd4100e14ddb2cefc4acc02b6b74609846b4a26ad40d9089ed564693f250a"
}

View file

@ -0,0 +1,87 @@
{
"schema_version": "platform.cadence-custody/v1",
"status": "delivered_pending_native_producer_and_independent_acceptance",
"credential_values_emitted": false,
"started_at": "2026-10-15T19:35:14.571483+00:00",
"packet_sha256": "81478f43abd2dbcb924966e9a3404e26bc226c91f2921b493ef0d5e2c23d4729",
"receiver_before": {
"image": "forgejo.coulomb.social/coulomb/audit-core@sha256:b479801b0bc76df66a36a85376c45aaf399c6d82feade9ba81430b774c4a8271",
"deployment_uid": "b85fe3d0-75c9-4e0d-8c34-c6f1df0881bb",
"deployment_resource_version": "75784583",
"pod_uid": "22e4df05-dfb0-4e70-8d5a-5a5950c860bd",
"capabilities": {
"load_bearing": true,
"redact": true,
"write_only": true,
"source_exact": true,
"tenant_exact": true
},
"synthetic_probe_only": true,
"credential_reads": 0,
"cadence_instance_capabilities": {
"cadence_registration": true,
"sqlite_instance_filter": true,
"postgres_instance_filter": true,
"synthetic_cadence_parse": true,
"synthetic_heartbeat_parse": true,
"synthetic_exact_scope": true
}
},
"phase": "delivery",
"registry_version_before": 15,
"custody_version": 1,
"custody_request_id": "77a526ef-dbcc-147d-b98b-a339f4efcd62",
"approval": {
"sender": "approval-engine",
"source": "approval-engine",
"tenant": "tenant:platform",
"may_read": false,
"emission_cadence_sha256": "cc58c8d53ebf50acf540d73b7607d579942bda7734c3e8722108e9c9204af655",
"heartbeat_classes": {
"issuance": 7200,
"use": 7200,
"supersession": 7200,
"revocation": 7200
},
"effective_deadline_seconds": 10800,
"changed": true,
"credentials_preserved": true,
"other_registry_fields_preserved": true
},
"registry_request_id": "47ca933d-4a5d-699b-c474-094947f78fb9",
"registry_version_after": 16,
"registry_readback_matches": true,
"other_senders_preserved": true,
"approval_credentials_preserved": true,
"reader_exact_path_negatives_and_revocation": true,
"wrong_identity_denied": true,
"coding_agent_deny_wins": true,
"projection_matches": true,
"receiver_registry_projection_matches": true,
"receiver_after": {
"image": "forgejo.coulomb.social/coulomb/audit-core@sha256:b479801b0bc76df66a36a85376c45aaf399c6d82feade9ba81430b774c4a8271",
"deployment_uid": "b85fe3d0-75c9-4e0d-8c34-c6f1df0881bb",
"deployment_resource_version": "75785764",
"pod_uid": "8333754a-eca0-4dd0-9230-72e41bb1e632",
"capabilities": {
"load_bearing": true,
"redact": true,
"write_only": true,
"source_exact": true,
"tenant_exact": true
},
"synthetic_probe_only": true,
"credential_reads": 0,
"cadence_instance_capabilities": {
"cadence_registration": true,
"sqlite_instance_filter": true,
"postgres_instance_filter": true,
"synthetic_cadence_parse": true,
"synthetic_heartbeat_parse": true,
"synthetic_exact_scope": true
}
},
"receiver_reloaded": true,
"receiver_runtime_registry_matches": true,
"completed_at": "2026-10-15T19:35:47.205117+00:00"
}

View file

@ -36,12 +36,15 @@ def stamp(value):
def paths(source, tenant, instance, since, until, event_ids):
require(SCOPES.get(source) == tenant, "exact_source_tenant_required")
require(
isinstance(instance, str)
and 1 <= len(instance) <= 256
and instance.strip() == instance,
"exact_instance_required",
)
if source == "qonto-assistant":
require(
isinstance(instance, str)
and 1 <= len(instance) <= 256
and instance.strip() == instance,
"exact_instance_required",
)
else:
require(instance is None, "approval_has_no_declared_instance_filter")
require(
stamp(since) <= stamp(until) <= datetime.now(timezone.utc),
"invalid_acceptance_window",
@ -57,15 +60,10 @@ def paths(source, tenant, instance, since, until, event_ids):
),
"invalid_event_id",
)
query = urlencode(
dict(
source=source,
tenant=tenant,
stream_instance_id=instance,
since=since,
until=until,
)
)
query_values = dict(source=source, tenant=tenant, since=since, until=until)
if instance is not None:
query_values["stream_instance_id"] = instance
query = urlencode(query_values)
return ["/v1/reconciliation?" + query, "/v1/stream-findings"] + [
"/v1/events/" + x for x in event_ids
]
@ -95,11 +93,15 @@ def sanitize_record(record, source, tenant, instance, event_id, since, until):
"record_source_tenant_id_mismatch",
)
detail = record["details"]["data"]
require(detail.get("stream_instance_id") == instance, "record_instance_mismatch")
require(
type(detail.get("stream_sequence")) is int and detail["stream_sequence"] > 0,
"record_sequence_missing",
)
if source == "qonto-assistant":
require(
detail.get("stream_instance_id") == instance, "record_instance_mismatch"
)
require(
type(detail.get("stream_sequence")) is int
and detail["stream_sequence"] > 0,
"record_sequence_missing",
)
accepted = record["accepted_at"]
require(
stamp(since) <= stamp(accepted) < stamp(until),
@ -118,12 +120,25 @@ def sanitize_record(record, source, tenant, instance, event_id, since, until):
)
if action == "audit-core.heartbeat":
require(coverage == ["audit.deny"], "record_heartbeat_scope_mismatch")
if source == "approval-engine":
names = {"issuance", "use", "supersession", "revocation"}
require(
action in {"approval." + name for name in names} | {"audit-core.heartbeat"},
"record_undeclared_class",
)
if action == "audit-core.heartbeat":
require(
len(coverage) == 1 and coverage[0] in names,
"record_heartbeat_scope_mismatch",
)
return {
"event_id": event_id,
"source": source,
"tenant": tenant,
"stream_instance_id": instance,
"stream_sequence": detail["stream_sequence"],
"stream_sequence": detail.get("stream_sequence")
if instance is not None
else None,
"type": action,
"accepted_at": accepted,
"observed_at": record["observed_at"],
@ -136,16 +151,11 @@ def readback(origin, token, source, tenant, instance, since, until, event_ids):
allowed = paths(source, tenant, instance, since, until, event_ids)
reconciliation = read_http(origin, token, allowed[0], allowed)
require(
all(
reconciliation.get(k) == v
for k, v in (
("source", source),
("tenant", tenant),
("stream_instance_id", instance),
("since", since),
("until", until),
)
),
reconciliation.get("source") == source
and reconciliation.get("tenant") == tenant
and reconciliation.get("stream_instance_id") == instance
and stamp(reconciliation["since"]) == stamp(since)
and stamp(reconciliation["until"]) == stamp(until),
"reconciliation_scope_window_mismatch",
)
raw_findings = read_http(origin, token, allowed[1], allowed)
@ -183,7 +193,11 @@ def readback(origin, token, source, tenant, instance, since, until, event_ids):
"reconciliation": reconciliation,
"stream_findings": safe_findings,
"events": records,
"scope": "bounded same-instance accepted custody; source-global findings require active-instance interpretation",
"scope": (
"bounded same-instance accepted custody; source-global findings require active-instance interpretation"
if instance is not None
else "bounded source lifecycle acceptance window; no process-instance completeness claim"
),
}
@ -192,7 +206,6 @@ def main():
p.add_argument("--source", choices=SCOPES, required=True)
for key in (
"tenant",
"instance",
"since",
"until",
"kubeconfig",
@ -200,6 +213,10 @@ def main():
"expected-receiver-image",
):
p.add_argument("--" + key, required=True)
p.add_argument(
"--instance",
help="Required only for Qonto; Approval has no declared instance field",
)
p.add_argument("--event-id", action="append", required=True)
p.add_argument("--receipt", type=Path, required=True)
a = p.parse_args()

View file

@ -156,5 +156,82 @@ class ReadbackBounds(unittest.TestCase):
)
class ApprovalReadback(unittest.TestCase):
def test_approval_omits_instance_filter_without_inventing_one(self):
paths = lane.paths(
"approval-engine", "tenant:platform", None, SINCE, UNTIL, ["approval-1"]
)
self.assertNotIn("stream_instance_id", paths[0])
with self.assertRaisesRegex(lane.LaneError, "no_declared_instance"):
lane.paths(
"approval-engine",
"tenant:platform",
"invented",
SINCE,
UNTIL,
["approval-1"],
)
with self.assertRaisesRegex(lane.LaneError, "exact_instance_required"):
lane.paths(
"qonto-assistant", "tenant:friendly:binky", None, SINCE, UNTIL, ["q-1"]
)
def test_actual_approval_record_needs_no_instance_sequence(self):
record = dict(
event_id="approval-1",
source="approval-engine",
tenant="tenant:platform",
action="approval.issuance",
accepted_at="2026-10-15T10:30:00Z",
observed_at="2026-10-15T10:29:59Z",
details={"data": {"transition": "issuance"}},
)
result = lane.sanitize_record(
record,
"approval-engine",
"tenant:platform",
None,
"approval-1",
SINCE,
UNTIL,
)
self.assertIsNone(result["stream_instance_id"])
self.assertIsNone(result["stream_sequence"])
self.assertEqual(result["type"], "approval.issuance")
def test_reconciliation_compares_semantic_window_times(self):
response = dict(
source="approval-engine",
tenant="tenant:platform",
since="2026-10-15T10:00:00+00:00",
until="2026-10-15T11:00:00+00:00",
counts=[],
)
record = dict(
event_id="approval-1",
source="approval-engine",
tenant="tenant:platform",
action="audit-core.heartbeat",
accepted_at="2026-10-15T10:30:00Z",
observed_at="2026-10-15T10:29:59Z",
details={"data": {"class": "revocation"}},
)
with patch.object(
lane, "read_http", side_effect=[response, {"stream_findings": []}, record]
):
result = lane.readback(
"http://127.0.0.1:1",
"fixture-only",
"approval-engine",
"tenant:platform",
None,
SINCE,
UNTIL,
["approval-1"],
)
self.assertEqual(result["events"][0]["covered_event_classes"], ["revocation"])
self.assertIn("no process-instance completeness claim", result["scope"])
if __name__ == "__main__":
unittest.main()

View file

@ -50,7 +50,7 @@ immutable compatible receiver image before attended execution.
```task
id: RPF-WP-0059-T03
status: progress
status: done
priority: high
state_hub_task_id: "c4e69d60-030e-592b-ac56-9f77468f7da9"
```
@ -64,7 +64,7 @@ resume on interruption. No live effect or secret read occurred in preparation.
```task
id: RPF-WP-0059-T04
status: wait
status: progress
priority: high
state_hub_task_id: "7d8e5315-9470-5596-a865-d451284f6f50"
```
@ -74,3 +74,23 @@ source/tenant/instance scope and real elapsed-hour periodic evidence, isolated
wrong-namespace denial, actual formerly-valid bearer revocation, independent
protected readback, missing-class/lag/divergence, restart/scale-zero evidence.
Never infer profile acceptance from a model test or registry projection.
### Native admission progress — 2026-10-15
Root executed the explicit approved packet in an attended platform-admin
session. Wrapper exit0 and self-revocation confirmed; receiver-custody-live.json
records registry15→16 CAS, durable version1 custody, preserved Approval
credentials/other senders, reader scope/negative/revoke checks, ESO projection
and actual loaded registry equality, and Ready receiver reload. CCR0047 is
applied, not verified/active. An initial ambient OPENBAO_TOKEN attempt was
correctly refused before custody mutation; that session also self-revoked.
Retry removed token aliases before launching OIDC, without relaxing guards.
Collector native receipt proves heartbeat1/deny1 own-instance reconciliation,
duplicate200, wrong-source400/wrong-tenant400/archive-read403/invalid-bearer401,
pending0; actual collector pod restart preserved first202 receipts and pending0.
Exact root receipts copied to docs/evidence/2026-10-15-cadence-receiver/.
The real hourly observation started19:36 UTC with authorized health polling;
it is still running. No elapsed-hour or independent observer acceptance is
claimed. T04 remains progress for actual timed capture and protected readback.