Prepare scoped factory audit custody and enforce receiver compatibility

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
codex 2026-09-11 02:01:57 +02:00
parent d3a502b45c
commit bfe65a5b05
11 changed files with 1053 additions and 1 deletions

View file

@ -0,0 +1,121 @@
# Factory audit senders: concrete custody review
CCR-2026-0021 and CCR-2026-0022 are **proposed**. RPF-WP-0035-T08 owns the
platform work; AUDIT-WP-0009-T09/T11 retain receiver admission. This packet
requires the named platform operator, Audit Core owner and each producer owner
to approve its request before native credential mutation.
## Scope to approve
| Request | Producer / exact source | OpenBao path | Producer Secret / key |
| --- | --- | --- | --- |
| CCR-2026-0021 | approval-engine | platform/workloads/approval-engine/audit-sender | approval-engine/approval-engine-audit / audit-token |
| CCR-2026-0022 | informed-decision | platform/workloads/informed-decision/audit-sender | informed-decision/informed-decision-audit / token |
Each sender is limited to `tenant:platform`, write true, read false,
`evidence_kind: load-bearing`, `secret_policy: redact`. The producer receives
only its own `AUDIT_TOKEN`. `CUSTODY_REQUEST` is a non-secret provenance marker
stored beside that token, enabling an interrupted first provision to identify
its own version-1 value. It is not projected to the producer.
Each lane has its own exact-path policy, Kubernetes auth role and namespace-
restricted ClusterSecretStore. Only `external-secrets/external-secrets` may
log in to the reader role (15-minute session). Neither producer can read the
full sender registry or its sibling's token. The coding-agent boundary gains
exact denies on the two new data/metadata paths; existing rules are preserved.
The approved first-provision operation would generate two independent values
in the attended platform process, store them with CAS=0, and append the exact
identities to `platform/workloads/audit-core/senders` with compare-and-set on
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
On 2026-09-11 the native receiver is 1/1 Ready on
`forgejo.coulomb.social/coulomb/audit-core@sha256:c2fe39a0185b99be3fc0cb14d2de69772b8e66e20490097c9d11d90cc39719a6`.
Its `SenderIdentity` has **no evidence_kind support**. A synthetic registration
probe in the native pod fails the load-bearing contract; the same probe against
current Audit Core source passes. Readiness did not detect this contract gap.
`factory_audit_custody.py receiver-check` now enforces that contract before any
credential access. An approved CCR alone cannot bypass it. Audit Core must
build, publish, admit and roll out its current implementation and source scope
first. Its existing AUDIT-WP-0009-T09/T11 tasks retain that work; no completed
source task is reopened. No receiver restart or deployment was done here.
Approval Engine's namespace is also absent. Three of four ESO objects passed
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.
## Executable sequence
Review the source and exact policies:
```sh
python3 scripts/credential-change.py render CCR-2026-0021
python3 scripts/credential-change.py render CCR-2026-0022
python3 scripts/factory_audit_custody.py plan
```
After a compatible receiver release, the credential-free check is:
```sh
python3 scripts/factory_audit_custody.py receiver-check \
--kubeconfig /operator/railiance-kubeconfig \
--expected-receiver-image forgejo.coulomb.social/coulomb/audit-core@sha256:ADMITTED_DIGEST \
--receipt /operator/unique-receiver-receipt.json
```
The seed command requires approved CCR files with all named owner reviews,
the expected image, exact Railiance cluster UID, ready/current pod, both target
namespaces and the Warden attended platform-admin login envelope. Route via
`warden route show openbao-platform-admin-login --json` before arranging access.
Use its installed attended wrapper; do not copy a token into the command.
Inside that envelope, invoke:
```sh
python3 scripts/factory_audit_custody.py seed \
--kubeconfig /operator/railiance-kubeconfig \
--expected-receiver-image forgejo.coulomb.social/coulomb/audit-core@sha256:ADMITTED_DIGEST \
--confirm 'PROVISION CCR-2026-0021 CCR-2026-0022' \
--receipt /operator/unique-custody-receipt.json
```
A successful seed deliberately reports
`custody_seeded_pending_delivery_and_receiver_reload`. It applies the exact
reader metadata and seeds custody, but does not apply ESO objects, reload the
shared receiver or claim live ingestion. Preserve that receipt. After an
interruption, review it and use `--resume` with a new receipt: only exact
request-owned version-1 custody is reused. Changed tokens, source scopes,
provenance, unknown duplicate identities and concurrent registry writes refuse.
No automatic deletion or rotation is attempted on failure.
Under the same reviewed lane, the subsequent attended delivery uses
`manifests/factory-audit-senders.yaml`; verify both stores/ExternalSecrets and
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.
Do not mark either CCR verified/active until those receipts exist.
## Revocation and limits
ESO login expiry does **not** expire an audit bearer. Stop the affected producer,
remove only its token from the receiver registry using CAS, reload the receiver
and prove refusal, then disable its reader/projection. Retain stored events and
KV provenance. Rotation is separately reviewed and overlap-first. Existing
senders and reader permissions remain intact.
`warden route show audit-core-senders` still points to the older Mason bootstrap.
The receiver runbook records the later OpenBao migration; this packet uses that
current authority and the platform's existing attended custody mechanics. It
creates no second registry or generic credential broker.
Factory policy/caller admission, registered human binding, CCR-2026-0019's
operator group, native runtime delivery and spending/operating acceptance stay
with their existing records. Sender approval does not admit any of those acts.

View file

@ -0,0 +1,81 @@
{
"schema": "platform.factory-audit-custody-preparation.v1",
"observed_at": "2026-09-11T00:00:15.014528+00:00",
"platform_base_commit": "d3a502b45cebc0a658d0ee7499f1cfcf2a5902a4",
"owner_task": "RPF-WP-0035-T08",
"source_records": [
"CCR-2026-0021",
"CCR-2026-0022",
"AUDIT-WP-0009-T09",
"AUDIT-WP-0009-T11"
],
"status": "proposed_custody_with_receiver_release_prerequisite",
"verification": {
"new_tests_passed": 17,
"existing_credential_change_tests_passed": 53,
"tests_skipped": 0,
"local_openbao_test_cases": 11,
"actual_receiver_source_contract_passed": true,
"all_22_ccrs_valid": true,
"server_dry_run": {
"objects": 4,
"exact_namespace_passed": 3,
"refusal": "approval-engine namespace absent",
"cluster_changes_applied": 0
}
},
"native_receiver": {
"image": "forgejo.coulomb.social/coulomb/audit-core@sha256:c2fe39a0185b99be3fc0cb14d2de69772b8e66e20490097c9d11d90cc39719a6",
"ready_replicas": 1,
"replicas": 1,
"evidence_kind_supported": false,
"probe": "synthetic SenderRegistry contract in exact deployed pod; no environment registry read",
"entry_refusal": "receiver_lacks_sender_contract",
"credential_reads": 0,
"source_contract_commit": "5c0ad522fb36092aa7ec2e8d72f63a5a91853b5b"
},
"recovery_proven": [
"interruption after first token write reuses the same value",
"lost registry write reply reconciles without another write",
"concurrent registry change is preserved by CAS refusal then deliberate resume",
"wrong provenance/scope/duplicate names/token collisions refuse",
"each exact read policy denies sibling data, full registry and metadata",
"all named approvals and exact credential coordinates are enforced"
],
"routing_finding": {
"catalog": "audit-core-senders",
"status": "draft",
"catalog_points_to": "ops-mason bootstrap",
"current_authority": "platform/workloads/audit-core/senders per audit-core operator runbook",
"execution_owner": "railiance-platform attended custody; existing platform helper pattern"
},
"pending_decisions": [
{
"ccr": "CCR-2026-0021",
"status": 201,
"decision_id": "2c9fe9f0-034a-41d7-9d49-b99df488fdc8"
},
{
"ccr": "CCR-2026-0022",
"status": 201,
"decision_id": "ee4ff001-256a-4406-9cb8-51be2cd5d31b"
}
],
"source_sha256": {
"scripts/factory_audit_custody.py": "77b5bd7bd1b27a642d87d73d799b8896ebca1808b7c95fc47dd603ba74d8d30e",
"tests/test_factory_audit_custody.py": "aef55cb98862dd62e2202fe4d2348aa8915f23d059331fd07d5644598452dc9f",
"manifests/factory-audit-senders.yaml": "3d682dfd517b516dcb20c40373aae2557e4d8d2b49b3392d75c2cb36cd80785d",
"openbao/policies/agent-high-risk-boundary.hcl": "9d863886c815740e65458417949c73a616b6e038594979b343ea07d08c585f64",
"openbao/policies/workload-kv-read-approval-engine-audit.hcl": "a527c3bdfe7bf356fcd19f499ebc3ad75a71256a22c19882ad0dfdd09e0f0eb9",
"openbao/policies/workload-kv-read-informed-decision-audit.hcl": "389e952546d32692e87b4f249752a9bee0fafa9fc58bf50ab8904110bf6b3780",
"credential-change-requests/CCR-2026-0021-approval-engine-audit.yaml": "33f3bb5d32f2dbada864e1b89cdb47fcb9742dfe313eb391ca2f342be6bc119a",
"credential-change-requests/CCR-2026-0022-informed-decision-audit.yaml": "7656519a7be847e6cf1b4e3e03200c68f5a36d87db8c81b84a16511b5dfe5d3a",
"docs/credential-lane-designs/factory-audit-senders-review.md": "70e7a3568821672c84e0ce0388f7b8bc7d1b920354b38153995ec45022c53e52"
},
"native_credentials_created": 0,
"native_secret_reads": 0,
"native_policy_writes": 0,
"native_deployments": 0,
"factory_attempts": 0,
"paid_model_calls": 0
}