feat: deliver approved factory audit sender custody
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: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
codex 2026-09-11 09:38:27 +02:00
parent fe1b1c5fe2
commit 44314cad37
7 changed files with 411 additions and 8 deletions

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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"
}

View file

@ -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<auth['lease_duration']<=900,'reader_scope_or_ttl_mismatch')
raw=data(bao(['read','-format=json',lane['kv']],token=token))['data']
values[lane['name']]=custody_value(lane,raw)
for path in [REGISTRY,lane['metadata'],next(x['kv'] for x in lanes if x!=lane)]:
require_denied(bao(['read','-format=json',path],token=token,allow_failure=True))
require_denied(bao(['list','-format=json','platform/metadata/workloads'],token=token,allow_failure=True))
row.update(reader_exact_path=True,registry_and_sibling_denied=True,metadata_and_listing_denied=True,reader_ttl=auth['lease_duration'])
finally: revoke(token)
require_denied(bao(['token','lookup','-format=json'],token=token,allow_failure=True))
row['reader_revocation_verified']=True
receipt['check']=lane['name']+':wrong_service_account';save()
bad_jwt=command(kube+['-n','external-secrets','create','token','default','--duration=10m']).stdout.decode().strip()
bad=bao(['write','-format=json','auth/kubernetes/login','-'],payload={'role':lane['role'],'jwt':bad_jwt},allow_failure=True)
if bad.returncode==0:
revoke(data(bad)['auth']['client_token']);raise LaneError('wrong_service_account_authenticated')
require(b'403' in bad.stderr and b'service account' in bad.stderr.lower(),'service_account_denial_inconclusive')
row['wrong_service_account_denied']=True
receipt['check']=lane['name']+':coding_agent_boundary';save()
child=data(bao(['token','create','-format=json','-policy='+lane['policy'],'-policy=agent-high-risk-boundary','-no-default-policy','-ttl=60s']))['auth']['client_token']
try:
for path in [lane['kv'],lane['metadata']]:require_denied(bao(['read','-format=json',path],token=child,allow_failure=True))
finally:revoke(child)
row['coding_agent_deny_wins']=True;save()
require(len(set(values.values()))==len(lanes),'sender_token_collision')
return values
def projection_matches(secret, external, lane, value):
require(set(secret['data'])=={lane['secret_key']}
and base64.b64decode(secret['data'][lane['secret_key']],validate=True).decode()==value,'projection_value_mismatch')
require(any(x['uid']==external['metadata']['uid'] and x.get('controller') is True
for x in secret['metadata'].get('ownerReferences',[])),'projection_owner_mismatch')
def guarded_manifest():
path=ROOT/'manifests/factory-audit-senders.yaml'
require(hashlib.sha256(path.read_bytes()).hexdigest()==MANIFEST_SHA256,'reviewed_manifest_changed')
return path,list(yaml.safe_load_all(path.read_text()))
def preflight_projection(kube, objects):
for obj in objects:
meta=obj['metadata'];cmd=kube+(['-n',meta['namespace']] if 'namespace' in meta else [])
result=command(cmd+['get',obj['kind'],meta['name'],'-o','json'],allow_failure=True)
if result.returncode:
require(b'NotFound' in result.stderr,'projection_presence_unknown');continue
live=data(result)
# Ignore API default fields, but never redirect an existing delivery lane.
require(all(live['spec'].get(k)==v for k,v in obj['spec'].items()),'existing_projection_drift')
require(not live['spec'].get('dataFrom') and not live['spec'].get('target',{}).get('template'),'unexpected_projection_template')
def deliver(kube, lanes, values, receipt, save):
path,objects=guarded_manifest();preflight_projection(kube,objects)
receipt['check']='apply_reviewed_projection';save()
command(kube+['apply','-f',str(path)])
for lane,row in zip(lanes,receipt['lanes']):
receipt['check']=lane['name']+':projection_readiness';save()
command(kube+['wait','--for=condition=Ready','clustersecretstore/'+lane['store'],'--timeout=45s'])
command(kube+['-n',lane['name'],'wait','--for=condition=Ready','externalsecret/'+lane['secret'],'--timeout=45s'])
es=data(command(kube+['-n',lane['name'],'get','externalsecret',lane['secret'],'-o','json']))
secret=data(command(kube+['-n',lane['name'],'get','secret',lane['secret'],'-o','json']))
projection_matches(secret,es,lane,values[lane['name']])
row.update(external_secret_ready=True,projection_matches=True,secret_uid=secret['metadata']['uid'],secret_resource_version=secret['metadata']['resourceVersion']);save()
receipt['check']='receiver_registry_delivery';save()
version,body,rows,_=snapshot()
require(all(desired(l,values[l['name']]) in rows for l in lanes),'registry_scope_changed')
command(kube+['-n','audit-core','annotate','externalsecret','audit-core-senders','force-sync='+str(time.time_ns()),'--overwrite'])
for _ in range(25):
secret=data(command(kube+['-n','audit-core','get','secret','audit-core-senders','-o','json']))
if json.loads(base64.b64decode(secret['data']['senders.json']))==json.loads(body['senders.json']):break
time.sleep(2)
else:raise LaneError('receiver_registry_delivery_mismatch')
receipt.update(registry_version=version,receiver_registry_matches=True,receiver_secret_resource_version=secret['metadata']['resourceVersion']);save()
receipt['check']='receiver_reload';save()
command(kube+['-n','audit-core','rollout','restart','deployment/audit-core'])
command(kube+['-n','audit-core','rollout','status','deployment/audit-core','--timeout=50s'])
for _ in range(20):
try:
receipt['receiver_after']=receiver_check(kube,receipt['receiver_before']['image']);break
except LaneError as e:
if str(e)!='receiver_rollout_in_progress':raise
time.sleep(2)
else:raise LaneError('receiver_rollout_in_progress')
receipt['receiver_reloaded']=True;save()
def namespace_checks(kube, lanes, receipt, save):
import secrets
ns='factory-audit-check-'+secrets.token_hex(5)
created=data(command(kube+['create','namespace',ns,'-o','json']))
try:
receipt['temporary_namespace']={'name':ns,'uid':created['metadata']['uid']};save()
command(kube+['-n',ns,'create','serviceaccount','external-secrets'])
jwt=command(kube+['-n',ns,'create','token','external-secrets','--duration=10m']).stdout.decode().strip()
for lane,row in zip(lanes,receipt['lanes']):
receipt['check']=lane['name']+':wrong_namespace';save()
result=bao(['write','-format=json','auth/kubernetes/login','-'],payload={'role':lane['role'],'jwt':jwt},allow_failure=True)
if result.returncode==0:
revoke(data(result)['auth']['client_token']);raise LaneError('wrong_namespace_authenticated')
require(b'403' in result.stderr and b'namespace' in result.stderr.lower(),'namespace_denial_inconclusive')
obj={'apiVersion':'external-secrets.io/v1','kind':'ExternalSecret','metadata':{'name':lane['secret'],'namespace':ns},'spec':{'refreshInterval':'15s','secretStoreRef':{'kind':'ClusterSecretStore','name':lane['store']},'target':{'name':lane['secret'],'creationPolicy':'Owner'},'data':[{'secretKey':lane['secret_key'],'remoteRef':{'key':lane['kv'].removeprefix('platform/data/'),'property':'AUDIT_TOKEN'}}]}}
command(kube+['create','-f','-'],payload=obj)
for _ in range(20):
live=data(command(kube+['-n',ns,'get','externalsecret',lane['secret'],'-o','json']))
events=data(command(kube+['-n',ns,'get','events','--field-selector=involvedObject.uid='+live['metadata']['uid'],'-o','json']))
if any('not allowed' in e.get('message','').lower() and 'namespace' in e.get('message','').lower() for e in events['items']):break
time.sleep(2)
else:raise LaneError('store_namespace_denial_inconclusive')
absent=command(kube+['-n',ns,'get','secret',lane['secret'],'-o','json'],allow_failure=True)
require(absent.returncode!=0 and b'NotFound' in absent.stderr,'outside_namespace_secret_not_absent')
row.update(wrong_namespace_denied=True,store_namespace_denied=True);save()
finally:
command(kube+['delete','--raw=/api/v1/namespaces/'+ns,'-f','/dev/stdin'],payload={'apiVersion':'v1','kind':'DeleteOptions','preconditions':{'uid':created['metadata']['uid']}})
receipt['namespace_cleanup_requested']=True;save()
def main():
p=argparse.ArgumentParser(description=__doc__)
p.add_argument('--kubeconfig',required=True);p.add_argument('--server',required=True)
p.add_argument('--expected-receiver-image',required=True);p.add_argument('--confirm',required=True)
p.add_argument('--receipt',type=Path,required=True)
a=p.parse_args();receipt={'schema':'platform.factory-audit-delivery.v1','status':'refused','started_at':datetime.now(timezone.utc).isoformat(),'credential_values_emitted':False};fd=None
try:
lanes=contracts(approved=True);guarded_manifest();require(a.confirm==CONFIRM,'exact_confirmation_required')
require(Path.home().parent.name=='.warden-attended-login' and not os.getenv('BAO_TOKEN') and not os.getenv('VAULT_TOKEN'),'attended_warden_envelope_required')
fd=os.open(a.receipt,os.O_RDWR|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
def save():
os.lseek(fd,0,os.SEEK_SET);os.ftruncate(fd,0);os.write(fd,(json.dumps(receipt,indent=2)+'\n').encode());os.fsync(fd)
kube=['kubectl','--kubeconfig',a.kubeconfig,'--server',a.server,'--request-timeout=20s']
receipt['receiver_before']=receiver_check(kube,a.expected_receiver_image)
identity=data(bao(['token','lookup','-format=json']))['data']
require('platform-admin' in identity['policies'] and 'root' not in identity['policies'],'attended_platform_admin_required')
receipt['lanes']=[{k:x[k] for k in ['ccr','name','kv','store','secret','secret_key']} for x in lanes]
receipt['phase']='reader_verification';save();values=verify_readers(kube,lanes,receipt,save)
receipt['phase']='delivery';save();deliver(kube,lanes,values,receipt,save)
receipt['phase']='namespace_verification';save();namespace_checks(kube,lanes,receipt,save)
receipt.update(status='delivered_and_scope_verified_pending_producer_acceptance',completed_at=datetime.now(timezone.utc).isoformat());save();return 0
except Exception as exc:
receipt.update(status='refused',error=str(exc) if isinstance(exc,LaneError) else 'contained_delivery_failed')
if fd is not None:save()
return 1
finally:
if fd is not None:os.close(fd)
if __name__=='__main__':raise SystemExit(main())

View file

@ -0,0 +1,70 @@
"""Delivery refuses custody/projection drift before changing a live receiver."""
import base64
import copy
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
from unittest.mock import patch
sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'scripts'))
import factory_audit_delivery as delivery
class DeliveryContracts(unittest.TestCase):
def setUp(self):
self.lane=delivery.contracts()[0]
self.value='fixture_only_'+('x'*51)
self.raw={'metadata':{'version':1},'data':{'AUDIT_TOKEN':self.value,'CUSTODY_REQUEST':self.lane['ccr']}}
def test_version_one_exact_provenance_is_required(self):
self.assertEqual(delivery.custody_value(self.lane,self.raw),self.value)
for raw in [dict(self.raw,metadata={'version':2}),dict(self.raw,data=dict(self.raw['data'],CUSTODY_REQUEST='different-request')),dict(self.raw,data=dict(self.raw['data'],extra='unexpected'))]:
with self.assertRaisesRegex(delivery.LaneError,'custody_provenance_mismatch'):delivery.custody_value(self.lane,raw)
def test_bad_token_shape_never_appears_in_the_diagnostic(self):
raw=copy.deepcopy(self.raw);raw['data']['AUDIT_TOKEN']='sensitive fixture with spaces'
with self.assertRaisesRegex(delivery.LaneError,'^custody_token_shape$'):delivery.custody_value(self.lane,raw)
def test_projection_must_match_value_and_controller(self):
es={'metadata':{'uid':'expected-controller'}}
secret={'data':{self.lane['secret_key']:base64.b64encode(self.value.encode()).decode()},'metadata':{'ownerReferences':[{'uid':'expected-controller','controller':True}]}}
delivery.projection_matches(secret,es,self.lane,self.value)
with self.assertRaisesRegex(delivery.LaneError,'projection_value_mismatch'):delivery.projection_matches(secret,es,self.lane,'wrong-value')
secret['metadata']['ownerReferences'][0]['controller']=False
with self.assertRaisesRegex(delivery.LaneError,'projection_owner_mismatch'):delivery.projection_matches(secret,es,self.lane,self.value)
def test_manifest_byte_change_refuses_before_apply(self):
path,objects=delivery.guarded_manifest();self.assertEqual(len(objects),4)
with tempfile.TemporaryDirectory() as d:
target=Path(d)/'manifests';target.mkdir();(target/path.name).write_bytes(path.read_bytes()+b'\n')
with patch.object(delivery,'ROOT',Path(d)):
with self.assertRaisesRegex(delivery.LaneError,'reviewed_manifest_changed'):delivery.guarded_manifest()
def test_existing_projection_must_not_be_redirected(self):
_,objects=delivery.guarded_manifest();expected=objects[1];actual=copy.deepcopy(expected)
actual['spec']['secretStoreRef']['name']='unrelated-store'
def result(argv, **kwargs):return subprocess.CompletedProcess(argv,0,json.dumps(actual).encode(),b'')
with patch.object(delivery,'command',side_effect=result) as command:
with self.assertRaisesRegex(delivery.LaneError,'existing_projection_drift'):delivery.preflight_projection(['kubectl'],[expected])
self.assertTrue(all('apply' not in call.args[0] for call in command.call_args_list))
def test_presence_errors_are_not_treated_as_absence(self):
_,objects=delivery.guarded_manifest()
with patch.object(delivery,'command',return_value=subprocess.CompletedProcess([],1,b'',b'Forbidden')):
with self.assertRaisesRegex(delivery.LaneError,'projection_presence_unknown'):delivery.preflight_projection(['kubectl'],objects)
def test_unexpected_reader_grant_is_revoked_and_not_used(self):
lanes=delivery.contracts();lane=lanes[0]
def read(path):
if path.startswith('sys/policies'):return {'policy':lane['hcl']}
return {}
auth={'auth':{'client_token':'test-reader','token_policies':['root'],'lease_duration':900}}
with patch.object(delivery,'read_optional',side_effect=read),patch.object(delivery,'role_matches',return_value=True),patch.object(delivery,'command',return_value=subprocess.CompletedProcess([],0,b'fixture-jwt',b'')),patch.object(delivery,'bao',return_value=subprocess.CompletedProcess([],0,json.dumps(auth).encode(),b'')) as bao,patch.object(delivery,'revoke') as revoke:
with self.assertRaisesRegex(delivery.LaneError,'reader_scope_or_ttl_mismatch'):delivery.verify_readers(['kubectl'],lanes,{'lanes':[{} for x in lanes]},lambda:None)
revoke.assert_called_once_with('test-reader')
self.assertEqual(bao.call_count,1)
if __name__=='__main__':unittest.main()

View file

@ -397,11 +397,11 @@ into this client-identity grant.
```task
id: RPF-WP-0035-T08
status: wait
status: progress
priority: high
assignee: railiance-platform
needs_human: true
intervention_note: "All named CCR-2026-0021/0022 reviews are approved. Compatible receiver and namespaces are ready. Attended OIDC failed before owner-command handoff; Warden could not confirm remote session revocation. Await operator browser/MFA feedback and login/session resolution before custody retry. Do not repeat sender approval."
needs_human: false
intervention_note: null
state_hub_task_id: "0ef52c26-2669-5cd1-8149-b1f6fcdda6cb"
```
@ -498,3 +498,14 @@ resolution. Approvals remain valid; T08 waits for the identity act, not review.
The receiver check now selects component=receiver, so retained attestation-job
pods cannot falsely block a stable receiver. Eighteen custody tests pass.
[Native return](../docs/evidence/2026-09-11-factory-receiver-live.json).
### Attended custody retry succeeded — 2026-09-11
The user requested a retry and completed the attended identity act. Both CCRs
are applied: independent version-1 custody and registry CAS 7→8 succeeded,
preserving other senders/fields. Warden exited 0 with self-revocation confirmed
for this session. This does not retrospectively attest the earlier failed
login's remote session state. The old login wait is cleared; T08 is progress
for ESO delivery, exact native reader/namespace refusals, receiver reload and
producer acceptance. No token is reseeded or rotated on continuation.
[Seed receipt](../docs/evidence/2026-09-11-factory-audit-sender-seed.json).