From 44314cad3704bda40683383f898b55b1f41491bc Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 11 Sep 2026 09:38:27 +0200 Subject: [PATCH] feat: deliver approved factory audit sender custody Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc --- .../CCR-2026-0021-approval-engine-audit.yaml | 15 +- ...CCR-2026-0022-informed-decision-audit.yaml | 15 +- .../factory-audit-senders-review.md | 47 ++++- .../2026-09-11-factory-audit-sender-seed.json | 63 ++++++ scripts/factory_audit_delivery.py | 192 ++++++++++++++++++ tests/test_factory_audit_delivery.py | 70 +++++++ ...-WP-0035-credential-lane-implementation.md | 17 +- 7 files changed, 411 insertions(+), 8 deletions(-) create mode 100644 docs/evidence/2026-09-11-factory-audit-sender-seed.json create mode 100644 scripts/factory_audit_delivery.py create mode 100644 tests/test_factory_audit_delivery.py diff --git a/credential-change-requests/CCR-2026-0021-approval-engine-audit.yaml b/credential-change-requests/CCR-2026-0021-approval-engine-audit.yaml index 9dcebe9..96c3738 100644 --- a/credential-change-requests/CCR-2026-0021-approval-engine-audit.yaml +++ b/credential-change-requests/CCR-2026-0021-approval-engine-audit.yaml @@ -3,7 +3,7 @@ kind: credential-change-request schema_version: 1 request_type: workload-kv-read title: approval-engine load-bearing audit sender custody and delivery -status: approved +status: applied created: '2026-09-11' updated: '2026-09-11' requester: @@ -107,6 +107,19 @@ verification: blindly rotates or deletes. - Record native positive/negative evidence before declaring verified or active. Custody alone does not admit UI, human approval or factory execution. + evidence: + - at: '2026-09-11T07:32:51+00:00' + actor: codex via attended user platform-admin + kind: factory_audit_initial_custody + result: passed + details: + - Both independently generated sender credentials persisted at version 1 with + exact request provenance. Shared registry CAS advanced from version 7 to 8; + existing senders and registry fields preserved. + - 'Attended retry completed with exit 0 and confirmed self-revocation. Receipt: + docs/evidence/2026-09-11-factory-audit-sender-seed.json.' + - 'Status applied: native ESO delivery, scope verification, receiver reload and + producer acceptance remain.' lifecycle: deactivate: Stop the exact producer; remove only its admitted token from the registry using CAS and reload/verify receiver refusal. Then detach its reader policy and diff --git a/credential-change-requests/CCR-2026-0022-informed-decision-audit.yaml b/credential-change-requests/CCR-2026-0022-informed-decision-audit.yaml index b0c1ba5..8fd5ad6 100644 --- a/credential-change-requests/CCR-2026-0022-informed-decision-audit.yaml +++ b/credential-change-requests/CCR-2026-0022-informed-decision-audit.yaml @@ -3,7 +3,7 @@ kind: credential-change-request schema_version: 1 request_type: workload-kv-read title: informed-decision load-bearing audit sender custody and delivery -status: approved +status: applied created: '2026-09-11' updated: '2026-09-11' requester: @@ -108,6 +108,19 @@ verification: blindly rotates or deletes. - Record native positive/negative evidence before declaring verified or active. Custody alone does not admit UI, human approval or factory execution. + evidence: + - at: '2026-09-11T07:32:51+00:00' + actor: codex via attended user platform-admin + kind: factory_audit_initial_custody + result: passed + details: + - Both independently generated sender credentials persisted at version 1 with + exact request provenance. Shared registry CAS advanced from version 7 to 8; + existing senders and registry fields preserved. + - 'Attended retry completed with exit 0 and confirmed self-revocation. Receipt: + docs/evidence/2026-09-11-factory-audit-sender-seed.json.' + - 'Status applied: native ESO delivery, scope verification, receiver reload and + producer acceptance remain.' lifecycle: deactivate: Stop the exact producer; remove only its admitted token from the registry using CAS and reload/verify receiver refusal. Then detach its reader policy and diff --git a/docs/credential-lane-designs/factory-audit-senders-review.md b/docs/credential-lane-designs/factory-audit-senders-review.md index fa7fd08..5825934 100644 --- a/docs/credential-lane-designs/factory-audit-senders-review.md +++ b/docs/credential-lane-designs/factory-audit-senders-review.md @@ -1,6 +1,6 @@ # Factory audit senders: concrete custody review -CCR-2026-0021 and CCR-2026-0022 are **approved** by the user on 2026-09-11, +CCR-2026-0021 and CCR-2026-0022 were **approved** by the user on 2026-09-11, replying "good, go on" to the explicit question naming the platform operator, Audit Core owner, and each producer owner. The source review comments and the two existing State Hub decisions record that approval. RPF-WP-0035-T08 owns @@ -33,7 +33,7 @@ its observed KV version. That process necessarily reads the existing registry in memory; it never emits its credentials or gives it to a producer. No token passes through Git, a Hub record, command arguments or stdout/stderr. -## Blocking receiver finding +## Historical receiver finding (resolved before custody) On 2026-09-11 the native receiver is 1/1 Ready on `forgejo.coulomb.social/coulomb/audit-core@sha256:c2fe39a0185b99be3fc0cb14d2de69772b8e66e20490097c9d11d90cc39719a6`. @@ -52,6 +52,15 @@ server dry-run in their intended namespaces; its ExternalSecret was refused because that namespace does not exist. This packet creates no namespace, workload or network permission. Its owner must supply that prerequisite. +## Current execution status + +The compatible receiver c82e0442de0f is live and both namespaces exist. The +attended retry seeded both independent version-1 credentials and advanced the +registry with CAS 7→8, preserving existing senders and fields. Both CCRs are +**applied**; Warden exited 0 and confirmed revocation for that session. See the +[seed receipt](../evidence/2026-09-11-factory-audit-sender-seed.json). Delivery, +receiver reload and producer acceptance remain distinct checks. + ## Executable sequence Review the source and exact policies: @@ -102,9 +111,41 @@ exact token equality inside the protected process. Sync the existing registry ExternalSecret, verify its update, and reload Audit Core through its owner runbook. Then run each real producer's accepted/duplicate and wrong-source, wrong-tenant/read-refusal checks and retrieve evidence with the independent -operator reader. The script does not yet automate these owner acceptance steps. +operator reader. The delivery helper automates delivery, scope checks and receiver reload; it +does not deploy producers or automate their outbox acceptance. Do not mark either CCR verified/active until those receipts exist. +## Attended delivery helper + +After the seed receipt exists, run this silent command inside a fresh Warden +attended platform-admin envelope (do not rerun seed or reuse a revoked session): + +```sh +python3 scripts/factory_audit_delivery.py \ + --kubeconfig /operator/railiance-kubeconfig \ + --server https://127.0.0.1:16444 \ + --expected-receiver-image forgejo.coulomb.social/coulomb/audit-core@sha256:c82e0442de0fd181342916ae9cd5d6de41d859e1efda637bd93936c67873afa5 \ + --confirm 'DELIVER CCR-2026-0021 CCR-2026-0022' \ + --receipt /operator/unique-delivery-receipt.json +``` + +The helper pins the reviewed manifest bytes and native cluster/image. It +verifies exact policy, role, version-1 custody and provenance; independently +logs in as each native ESO reader; proves sibling, full-registry, metadata, +listing, wrong-SA and coding-agent-boundary denials; and revokes temporary +reader tokens. It checks exact projected token equality and ESO ownership in +captured memory, forces receiver-registry reconciliation and compares the +complete JSON before restarting the receiver. A uniquely owned temporary +namespace proves wrong-namespace login and explicit ESO namespace restriction +with no Secret created. Cleanup uses the namespace UID precondition. + +The metadata-only receipt advances by phase. Its successful status is +`delivered_and_scope_verified_pending_producer_acceptance`. Failures preserve +completed phases for diagnosis; they do not rotate or delete permanent custody. +Receiver accepted/duplicate and negative HTTP checks, real producer outbox +recovery and bearer revocation remain owner acceptance work. Do not mistake +short-lived ESO-reader revocation for receiver bearer revocation. + ## Revocation and limits ESO login expiry does **not** expire an audit bearer. Stop the affected producer, diff --git a/docs/evidence/2026-09-11-factory-audit-sender-seed.json b/docs/evidence/2026-09-11-factory-audit-sender-seed.json new file mode 100644 index 0000000..70f28ee --- /dev/null +++ b/docs/evidence/2026-09-11-factory-audit-sender-seed.json @@ -0,0 +1,63 @@ +{ + "schema": "platform.factory-audit-custody.v1", + "status": "custody_seeded_pending_delivery_and_receiver_reload", + "credential_values_emitted": false, + "started_at": "2026-09-11T05:37:49.432171+00:00", + "lanes": [ + { + "ccr": "CCR-2026-0021", + "name": "approval-engine", + "kv": "platform/data/workloads/approval-engine/audit-sender", + "store": "openbao-approval-engine-audit", + "secret": "approval-engine-audit", + "secret_key": "audit-token", + "source_sha256": "ba69ecc8227c5bf887c2cd0ac4c50518c019bebfdfb6d49628edee1ce570da7d" + }, + { + "ccr": "CCR-2026-0022", + "name": "informed-decision", + "kv": "platform/data/workloads/informed-decision/audit-sender", + "store": "openbao-informed-decision-audit", + "secret": "informed-decision-audit", + "secret_key": "token", + "source_sha256": "2f7f7f214d1e21fd79275197edd03ee7e154ef451d62a182017926250515a6aa" + } + ], + "receiver": { + "image": "forgejo.coulomb.social/coulomb/audit-core@sha256:c82e0442de0fd181342916ae9cd5d6de41d859e1efda637bd93936c67873afa5", + "deployment_uid": "b85fe3d0-75c9-4e0d-8c34-c6f1df0881bb", + "deployment_resource_version": "59751057", + "pod_uid": "6642c414-a942-4b11-b048-267423e12da8", + "capabilities": { + "load_bearing": true, + "redact": true, + "write_only": true, + "source_exact": true, + "tenant_exact": true + }, + "synthetic_probe_only": true, + "credential_reads": 0 + }, + "phase": "registry_cas", + "registry_version_before": 7, + "seeded": [ + { + "ccr": "CCR-2026-0021", + "version": 1, + "request_id": "cf6dd167-9d37-fbcf-cc3d-6d54c0f988b9" + }, + { + "ccr": "CCR-2026-0022", + "version": 1, + "request_id": "2f373e63-058b-7a6e-07c4-365317dffa73" + } + ], + "registry_write_request_id": "91fdb393-80ba-4e06-2dde-3982763d0ae5", + "registry_version_after": 8, + "unchanged_other_senders": true, + "unchanged_other_registry_fields": true, + "credentials_reused": false, + "attended_wrapper_exit": 0, + "attended_session_self_revocation": "confirmed_by_successful_wrapper", + "prior_failed_login": "not asserted resolved by this new session" +} diff --git a/scripts/factory_audit_delivery.py b/scripts/factory_audit_delivery.py new file mode 100644 index 0000000..91274e3 --- /dev/null +++ b/scripts/factory_audit_delivery.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python3 +"""Attended delivery and native scope checks for the approved factory audit senders. + +No token values leave captured subprocess memory. The receipt is metadata only. +This does not deploy producers or claim their outbox/recovery acceptance. +""" +from __future__ import annotations +import argparse +import base64 +from datetime import datetime, timezone +import hashlib +import json +import os +from pathlib import Path +import time +import yaml + +from factory_audit_custody import contracts, desired, receiver_check, snapshot, require, REGISTRY +from keycape_approval_custody import require_denied, read_optional, role_matches +from state_hub_preflight_lane import ROOT, LaneError, command, bao, data, revoke + +CONFIRM='DELIVER CCR-2026-0021 CCR-2026-0022' +MANIFEST_SHA256='bca0f6b318baf6bccebf7faec48dd5a69d686cfc7ba9f4c769c7a60b9dc72cc9' + + +def custody_value(lane, raw): + require(raw['metadata']['version']==1 and set(raw['data'])=={'AUDIT_TOKEN','CUSTODY_REQUEST'} + and raw['data']['CUSTODY_REQUEST']==lane['ccr'], 'custody_provenance_mismatch') + token=raw['data']['AUDIT_TOKEN'] + import re + require(isinstance(token,str) and re.fullmatch(r'[A-Za-z0-9_-]{64}',token), 'custody_token_shape') + return token + + +def verify_readers(kube, lanes, receipt, save): + values={} + for lane,row in zip(lanes,receipt['lanes']): + receipt['check']=lane['name']+':reader_metadata';save() + require(read_optional('sys/policies/acl/'+lane['policy'])['policy']==lane['hcl'] + and role_matches(read_optional('auth/kubernetes/role/'+lane['role']),lane),'native_metadata_drift') + receipt['check']=lane['name']+':reader_login';save() + jwt=command(kube+['-n','external-secrets','create','token','external-secrets','--duration=10m']).stdout.decode().strip() + auth=data(bao(['write','-format=json','auth/kubernetes/login','-'],payload={'role':lane['role'],'jwt':jwt}))['auth'] + token=auth['client_token'] + try: + require(set(auth['token_policies'])=={lane['policy'],'default'} and 0