#!/usr/bin/env python3 """Attended first-provision custody for the two factory audit senders. Plan and receiver-check never read credential values. Seed needs the reviewed CCRs and a compatible receiver; it does not deploy or reload any workload. """ from __future__ import annotations import argparse import copy import hashlib import importlib.util import json import os from pathlib import Path import re import secrets from datetime import datetime, timezone from state_hub_preflight_lane import ROOT, LaneError, assert_cluster, bao, command, data from keycape_approval_custody import read_optional, role_matches REGISTRY = 'platform/data/workloads/audit-core/senders' BOUNDARY = 'sys/policies/acl/agent-high-risk-boundary' LANES = (('CCR-2026-0021', 'approval-engine', 'audit-token'), ('CCR-2026-0022', 'informed-decision', 'token')) CONFIRM = 'PROVISION CCR-2026-0021 CCR-2026-0022' # This probes synthetic identities only; no environment-backed registry is read. RECEIVER_PROBE = '''import json from audit_core.senders import SenderRegistry entry={"name":"compatibility-fixture","tokens":["fixture-only"],"sources":["compatibility-fixture"],"tenants":["tenant:platform"],"may_write":True,"may_read":False,"evidence_kind":"load-bearing","secret_policy":"redact"} r=SenderRegistry.from_env({"AUDIT_CORE_SENDERS":json.dumps([entry])}) s=r.authenticate("Bearer fixture-only") print(json.dumps({"load_bearing":getattr(s,"evidence_kind",None)=="load-bearing","redact":s.secret_policy=="redact","write_only":s.may_write and not s.may_read,"source_exact":s.permits_source("compatibility-fixture") and not s.permits_source("sibling"),"tenant_exact":s.permits_tenant("tenant:platform") and not s.permits_tenant("tenant:sibling")})) ''' def require(condition, code): if not condition: raise LaneError(code) def credential_module(): spec = importlib.util.spec_from_file_location('factory_ccr', ROOT / 'scripts/credential-change.py') module = importlib.util.module_from_spec(spec); spec.loader.exec_module(module) return module def contracts(*, approved=False): module = credential_module() result = [] for ref, name, secret_key in LANES: path = module.resolve_ccr(ref) ccr, errors, _ = module.validate_ccr(path) require(not errors, 'invalid_credential_request') if approved: require(ccr['status'] in {'approved', 'applied', 'verified', 'active'}, 'approved_ccrs_required') for owner in ('platform-operator', 'audit-core-owner', name + '-owner'): require(any(x.get('decision') == 'approved' and owner in x.get('reviewer', '') for x in ccr['review']['comments']), 'named_owner_reviews_required') suffix = name + '-audit'; policy = 'workload-kv-read-' + suffix expected_role = {'bound_service_account_names':['external-secrets'], 'bound_service_account_namespaces':['external-secrets'], 'policies':policy,'ttl':'15m'} expected_path = f'platform/workloads/{name}/audit-sender' require(ccr['openbao']['kv_path'] == expected_path and ccr['openbao']['fields'] == ['AUDIT_TOKEN', 'CUSTODY_REQUEST'] and ccr['openbao']['policy_name'] == policy and ccr['openbao']['metadata_read'] is False and ccr['openbao']['auth']['role'] == 'external-secrets-' + suffix and ccr['openbao']['auth']['mount'] == 'kubernetes' and ccr['openbao']['auth']['bound_claims_confirmed'] is True and module.auth_payload(ccr) == expected_role, 'exact_contract_required') hcl = module.generated_policy_hcl(ccr) require((ROOT / ccr['openbao']['policy_file']).read_text() == hcl, 'policy_source_drift') result.append({'ccr':ref,'name':name,'kv':expected_path.replace('platform/','platform/data/',1), 'metadata':expected_path.replace('platform/','platform/metadata/',1), 'policy':policy,'hcl':hcl,'role':'external-secrets-'+suffix, 'role_payload':expected_role,'store':'openbao-'+suffix,'secret':suffix, 'secret_key':secret_key,'source_sha256':hashlib.sha256(path.read_bytes()).hexdigest()}) return result def receiver_check(kube, expected_image): require(re.fullmatch(r'forgejo\.coulomb\.social/coulomb/audit-core@sha256:[0-9a-f]{64}', expected_image), 'receiver_digest_required') assert_cluster(kube) dep = data(command(kube + ['-n','audit-core','get','deployment','audit-core','-o','json'])) require(dep['spec']['replicas'] == 1 and dep.get('status',{}).get('readyReplicas') == 1 and dep['status'].get('observedGeneration',0) >= dep['metadata']['generation'], 'receiver_not_ready') containers = dep['spec']['template']['spec']['containers'] require(len(containers) == 1 and containers[0]['image'] == expected_image, 'receiver_image_mismatch') pods = data(command(kube + ['-n','audit-core','get','pods','-l','app.kubernetes.io/name=audit-core,app.kubernetes.io/component=receiver','-o','json']))['items'] require(len(pods) == 1 and not pods[0]['metadata'].get('deletionTimestamp'), 'receiver_rollout_in_progress') pod = pods[0] require(pod['spec']['containers'][0]['image'] == expected_image and all(x.get('ready') for x in pod.get('status',{}).get('containerStatuses',[])) and pod.get('status',{}).get('containerStatuses'), 'receiver_pod_not_current') proof = data(command(kube + ['-n','audit-core','exec',pod['metadata']['name'],'-c',containers[0]['name'], '--','python','-c',RECEIVER_PROBE])) require(set(proof) == {'load_bearing','redact','write_only','source_exact','tenant_exact'} and all(v is True for v in proof.values()), 'receiver_lacks_sender_contract') return {'image':expected_image,'deployment_uid':dep['metadata']['uid'], 'deployment_resource_version':dep['metadata']['resourceVersion'],'pod_uid':pod['metadata']['uid'], 'capabilities':proof,'synthetic_probe_only':True,'credential_reads':0} def snapshot(): raw = data(bao(['read','-format=json',REGISTRY]))['data'] require(type(raw['metadata']['version']) is int and raw['metadata']['version'] > 0, 'registry_version_missing') body = raw['data']; require(isinstance(body,dict) and isinstance(body.get('senders.json'),str), 'registry_shape_invalid') try: rows = json.loads(body['senders.json']) except (ValueError, TypeError): raise LaneError('registry_shape_invalid') from None require(isinstance(rows,list) and bool(rows), 'registry_must_preserve_existing_senders') names = set(); tokens = set() for row in rows: require(isinstance(row,dict) and isinstance(row.get('name'),str) and row['name'] and row['name'] not in names, 'registry_identity_invalid_or_duplicate') names.add(row['name']) values = row.get('tokens') or ([row['token']] if row.get('token') else []) require(isinstance(values,list) and bool(values), 'registry_tokens_invalid') for value in values: require(isinstance(value,str) and bool(value) and value not in tokens, 'registry_token_collision') tokens.add(value) return raw['metadata']['version'], body, rows, tokens def desired(lane, token): return {'name':lane['name'],'tokens':[token],'sources':[lane['name']], 'tenants':['tenant:platform'],'may_write':True,'may_read':False, 'evidence_kind':'load-bearing','secret_policy':'redact'} def seed(lanes, receipt, save, *, resume=False): version, body, rows, used_tokens = snapshot() originals = copy.deepcopy(rows) found = {r['name']:r for r in rows} values = {}; pending = [] # Validate every existing custody object before any write. Only this exact # request's version-1 values can be reused after interruption. for lane in lanes: old = read_optional(lane['metadata']) if old is None: require(lane['name'] not in found, 'registry_sender_without_custody') pending.append(lane) else: require(resume and old['current_version'] == 1 and not old['versions']['1'].get('destroyed') and not old['versions']['1'].get('deletion_time'), 'existing_custody_requires_reviewed_resume') existing = data(bao(['read','-format=json',lane['kv']]))['data'] require(existing['metadata']['version'] == 1 and set(existing['data']) == {'AUDIT_TOKEN','CUSTODY_REQUEST'} and existing['data']['CUSTODY_REQUEST'] == lane['ccr'], 'custody_provenance_mismatch') token = existing['data']['AUDIT_TOKEN'] require(isinstance(token,str) and re.fullmatch(r'[A-Za-z0-9_-]{64}',token), 'custody_token_shape') require(token not in values.values(), 'sender_token_collision') if lane['name'] in found: require(found[lane['name']] == desired(lane,token), 'registered_sender_drift') else: require(token not in used_tokens, 'sender_token_collision') values[lane['name']] = token receipt.update(registry_version_before=version,phase='custody_seed') save() for lane in pending: token = secrets.token_urlsafe(48) require(token not in used_tokens and token not in values.values(), 'sender_token_collision') result = data(bao(['write','-format=json',lane['kv'],'-'],payload={ 'options':{'cas':0},'data':{'AUDIT_TOKEN':token,'CUSTODY_REQUEST':lane['ccr']}})) require(result['data']['version'] == 1,'initial_custody_version_mismatch') values[lane['name']] = token receipt.setdefault('seeded',[]).append({'ccr':lane['ccr'],'version':1,'request_id':result.get('request_id')}) save() updated = list(rows) for lane in lanes: if lane['name'] not in found: updated.append(desired(lane,values[lane['name']])) if updated != rows: receipt['phase']='registry_cas'; save() payload = dict(body, **{'senders.json':json.dumps(updated,separators=(',',':'))}) result = data(bao(['write','-format=json',REGISTRY,'-'],payload={'options':{'cas':version},'data':payload})) receipt['registry_write_request_id']=result.get('request_id') # Readback also catches a concurrent removal rather than claiming a success. after_version, after_body, after_rows, _ = snapshot() require(all(x in after_rows for x in originals) and all(desired(lane,values[lane['name']]) in after_rows for lane in lanes) and all(after_body.get(k)==v for k,v in body.items() if k!='senders.json'), 'registry_readback_mismatch') receipt.update(registry_version_after=after_version,unchanged_other_senders=True, unchanged_other_registry_fields=True,credentials_reused=resume, status='custody_seeded_pending_delivery_and_receiver_reload') save() def prepare_metadata(lanes): # Avoid overwriting an unrelated policy or role. Do all drift checks first. for lane in lanes: policy=read_optional('sys/policies/acl/'+lane['policy']) role=read_optional('auth/kubernetes/role/'+lane['role']) require(policy is None or policy['policy']==lane['hcl'],'existing_policy_drift') require(role is None or role_matches(role,lane),'existing_role_drift') boundary=read_optional(BOUNDARY); require(boundary is not None,'coding_agent_boundary_required') current=boundary['policy']; additions='' for lane in lanes: for path in (lane['kv'],lane['metadata']): if '"'+path+'"' in current: require(re.search(r'path\s+"'+re.escape(path)+r'"\s*\{\s*capabilities\s*=\s*\["deny"\]\s*\}',current),'boundary_path_drift') else: additions+=f'path "{path}" {{ capabilities = ["deny"] }}\n' if additions: require(read_optional(BOUNDARY)['policy']==current,'boundary_revision_changed') bao(['write',BOUNDARY,'-'],payload={'policy':current+'\n'+additions}) require(read_optional(BOUNDARY)['policy']==current+'\n'+additions,'boundary_readback_failed') for lane in lanes: bao(['write','sys/policies/acl/'+lane['policy'],'-'],payload={'policy':lane['hcl']}) bao(['write','auth/kubernetes/role/'+lane['role'],'-'],payload=lane['role_payload']) require(read_optional('sys/policies/acl/'+lane['policy'])['policy']==lane['hcl'] and role_matches(read_optional('auth/kubernetes/role/'+lane['role']),lane),'metadata_readback_failed') def main(): parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('action',choices=['plan','receiver-check','seed']) parser.add_argument('--kubeconfig'); parser.add_argument('--server') parser.add_argument('--expected-receiver-image'); parser.add_argument('--confirm') parser.add_argument('--receipt',type=Path); parser.add_argument('--resume',action='store_true') args=parser.parse_args() receipt={'schema':'platform.factory-audit-custody.v1','status':'refused','credential_values_emitted':False, 'started_at':datetime.now(timezone.utc).isoformat()} fd=None try: lanes=contracts(approved=args.action=='seed') receipt['lanes']=[{k:v for k,v in x.items() if k in ('ccr','name','kv','store','secret','secret_key','source_sha256')} for x in lanes] if args.action=='plan': receipt['status']='proposed'; print(json.dumps(receipt,indent=2)); return 0 require(args.kubeconfig and args.expected_receiver_image and args.receipt,'receiver_inputs_required') fd=os.open(args.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',args.kubeconfig,'--request-timeout=20s'] if args.server:kube+=['--server',args.server] receipt['receiver']=receiver_check(kube,args.expected_receiver_image) if args.action=='receiver-check': receipt['status']='receiver_contract_supported';save();return 0 require(args.confirm==CONFIRM,'exact_confirmation_required') require(Path.home().parent.name=='.warden-attended-login' and not os.environ.get('BAO_TOKEN') and not os.environ.get('VAULT_TOKEN'),'attended_warden_envelope_required') identity=data(bao(['token','lookup','-format=json']))['data'] require('platform-admin' in identity['policies'] and 'root' not in identity['policies'],'attended_platform_admin_required') for lane in lanes: command(kube+['get','namespace',lane['name'],'-o','name']) # Refuse malformed registry before changing policy/auth metadata. snapshot() receipt['phase']='metadata';save();prepare_metadata(lanes) seed(lanes,receipt,save,resume=args.resume) return 0 except Exception as exc: receipt['status']='refused' receipt['error']=str(exc) if isinstance(exc,LaneError) else 'contained_operation_failed' if fd is not None: save() print(json.dumps({'status':'refused','error':receipt['error']})) return 1 finally: if fd is not None:os.close(fd) if __name__=='__main__':raise SystemExit(main())