#!/usr/bin/env python3 """Prepare or execute the bounded, attended native factory sender acceptance.""" import argparse import base64 from datetime import datetime, timezone import hashlib import io import json import os from pathlib import Path import secrets import socket import subprocess import time from urllib.error import HTTPError from urllib.request import Request, build_opener, ProxyHandler, HTTPRedirectHandler import zipfile from factory_audit_custody import contracts, desired, snapshot, receiver_check, require from state_hub_preflight_lane import ROOT, LaneError, command, bao, data IMAGE = 'forgejo.coulomb.social/coulomb/approval-engine@sha256:251941a5cb2724b57cc32cff6b693b1ab0be695bee4f56f02d51961189c0fa49' RECEIVER = 'forgejo.coulomb.social/coulomb/audit-core@sha256:c82e0442de0fd181342916ae9cd5d6de41d859e1efda637bd93936c67873afa5' SOURCES = {'approval-engine':('a0a602976eef818f36dde35f76f7f2e589bd051b','approval_engine'), 'informed-decision':('bda9381f070859ae997474cf44b86bf4eea9bb52','informed_decision')} CONFIRM = 'VERIFY CCR-2026-0021 CCR-2026-0022 PRODUCERS' def source_bundle(base): buffer = io.BytesIO(); hashes = {} with zipfile.ZipFile(buffer,'w',compression=zipfile.ZIP_DEFLATED) as out: for repo,(commit,package) in SOURCES.items(): prefix = ['git','-C',str(base/repo)] names = command(prefix+['ls-tree','-r','--name-only',commit,package]).stdout.decode().splitlines() require(names and all(n.startswith(package+'/') and n.endswith('.py') for n in names), 'source_package_shape_changed') for name in names: body = command(prefix+['show',commit+':'+name]).stdout hashes[name] = hashlib.sha256(body).hexdigest() entry = zipfile.ZipInfo(name,date_time=(2026,9,11,0,0,0));entry.compress_type=zipfile.ZIP_DEFLATED out.writestr(entry,body) return buffer.getvalue(), hashes def resources(run_id, sender, bundle, script): suffix = run_id.removeprefix('factory-audit-');name='factory-audit-'+suffix labels={'app.kubernetes.io/name':sender,'railiance.io/factory-audit-probe':run_id} config={'sender':sender,'run_id':run_id,'event_id':run_id+'-'+sender, 'origin':'http://audit-core.audit-core.svc.cluster.local:8080', 'source_zip_sha256':hashlib.sha256(bundle).hexdigest()} key='audit-token' if sender=='approval-engine' else 'token' meta={'name':name,'namespace':sender,'labels':labels} cm={'apiVersion':'v1','kind':'ConfigMap','metadata':meta,'immutable':True, 'data':{'producer.py':script.decode(),'config.json':json.dumps(config,sort_keys=True)}, 'binaryData':{'source.zip':base64.b64encode(bundle).decode()}} policy={'apiVersion':'networking.k8s.io/v1','kind':'NetworkPolicy','metadata':meta, 'spec':{'podSelector':{'matchLabels':{'railiance.io/factory-audit-probe':run_id}}, 'policyTypes':['Ingress','Egress'],'ingress':[], 'egress':[{'to':[{'namespaceSelector':{'matchLabels':{'kubernetes.io/metadata.name':'audit-core'}}, 'podSelector':{'matchLabels':{'app.kubernetes.io/name':'audit-core','app.kubernetes.io/component':'receiver'}}}], 'ports':[{'protocol':'TCP','port':8080}]}, {'to':[{'namespaceSelector':{'matchLabels':{'kubernetes.io/metadata.name':'kube-system'}}, 'podSelector':{'matchLabels':{'k8s-app':'kube-dns'}}}], 'ports':[{'protocol':'UDP','port':53},{'protocol':'TCP','port':53}]}]}} job={'apiVersion':'batch/v1','kind':'Job','metadata':meta, 'spec':{'backoffLimit':0,'activeDeadlineSeconds':150,'ttlSecondsAfterFinished':3600, 'template':{'metadata':{'labels':labels},'spec':{ 'restartPolicy':'Never','automountServiceAccountToken':False, 'securityContext':{'runAsNonRoot':True,'runAsUser':10001,'runAsGroup':10001, 'fsGroup':10001,'seccompProfile':{'type':'RuntimeDefault'}}, 'containers':[{'name':'probe','image':IMAGE,'imagePullPolicy':'IfNotPresent', 'command':['python','-I','/probe/producer.py'], 'securityContext':{'allowPrivilegeEscalation':False,'readOnlyRootFilesystem':True, 'capabilities':{'drop':['ALL']}}, 'resources':{'requests':{'cpu':'25m','memory':'64Mi'},'limits':{'cpu':'500m','memory':'192Mi'}}, 'volumeMounts':[{'name':'probe','mountPath':'/probe','readOnly':True}, {'name':'audit','mountPath':'/credential','readOnly':True}, {'name':'state','mountPath':'/state'}]}], 'volumes':[{'name':'probe','configMap':{'name':name,'defaultMode':0o444}}, {'name':'audit','secret':{'secretName':sender+'-audit','defaultMode':0o440, 'items':[{'key':key,'path':'token'}]}}, {'name':'state','emptyDir':{'sizeLimit':'32Mi'}}]}}}} return [cm,policy,job] def prepare(base, path): bundle,hashes=source_bundle(base) script=(ROOT/'scripts/native_factory_probe/producer.py').read_bytes() run_id='factory-audit-'+datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S')+'-'+secrets.token_hex(3) packet={'schema':'platform.factory-native-acceptance.v1','run_id':run_id,'source_commits':SOURCES, 'source_hashes':hashes,'source_zip_sha256':hashlib.sha256(bundle).hexdigest(), 'probe_sha256':hashlib.sha256(script).hexdigest(),'objects':sum([resources(run_id,s,bundle,script) for s in SOURCES],[])} with path.open('x') as f:json.dump(packet,f,indent=2);f.write('\n') return {'packet':str(path),'sha256':hashlib.sha256(path.read_bytes()).hexdigest(), 'run_id':run_id,'objects':len(packet['objects']),'source_files':len(hashes)} def validate_packet(path, sha256): raw=path.read_bytes();require(hashlib.sha256(raw).hexdigest()==sha256,'packet_digest_changed') packet=json.loads(raw);run_id=packet['run_id'] import re require(re.fullmatch(r'factory-audit-[0-9]{14}-[0-9a-f]{6}',run_id),'invalid_run_id') require(packet['source_commits']=={k:list(v) for k,v in SOURCES.items()},'source_pins_changed') objects=packet['objects'];require(len(objects)==6,'exact_resources_required') bundle=base64.b64decode(objects[0]['binaryData']['source.zip'],validate=True) script=(ROOT/'scripts/native_factory_probe/producer.py').read_bytes() require(hashlib.sha256(bundle).hexdigest()==packet['source_zip_sha256'],'bundle_digest_changed') require(hashlib.sha256(script).hexdigest()==packet['probe_sha256'],'probe_digest_changed') with zipfile.ZipFile(io.BytesIO(bundle)) as z: require(set(z.namelist())==set(packet['source_hashes']),'source_inventory_changed') require(all(hashlib.sha256(z.read(n)).hexdigest()==h for n,h in packet['source_hashes'].items()),'source_hash_changed') require(objects==sum([resources(run_id,s,bundle,script) for s in SOURCES],[]),'resource_scope_changed') return packet class NoRedirect(HTTPRedirectHandler): def redirect_request(self,*args):return None def read_http(origin, token, path): require(path=='/v1/integrity' or path.startswith('/v1/events/factory-audit-'),'reader_path_not_scoped_to_probe') request=Request(origin+path,headers={'Authorization':'Bearer '+token}) try:response=build_opener(ProxyHandler({}),NoRedirect()).open(request,timeout=10) except HTTPError as exc:response=exc with response: raw=response.read(524289);require(len(raw)<=524288,'readback_too_large') require(response.status==200,'operator_readback_failed') return json.loads(raw) def operator_identity(rows): # Select an already existing independent, read-only, full-tenant operator. candidates=[r for r in rows if r.get('may_read') is True and r.get('may_write',True) is False and r.get('tenants')==['*'] and r['name'] not in SOURCES] named=[r for r in candidates if 'operator' in r['name']] if named:candidates=named require(len(candidates)==1,'independent_operator_reader_ambiguous_or_absent') return candidates[0] def jobs(packet, kube, receipt, save): """Consume only the producer's already-delivered credential inside its Job.""" receipt['receiver_before']=receiver_check(kube,RECEIVER) for lane in contracts(approved=True): policies=data(command(kube+['-n',lane['name'],'get','networkpolicy','-o','json'])) require(not policies['items'],'existing_egress_policy_requires_review') es=data(command(kube+['-n',lane['name'],'get','externalsecret',lane['secret'],'-o','json'])) require(any(c['type']=='Ready' and c['status']=='True' for c in es['status']['conditions']),'producer_projection_not_ready') receipt.update(phase='native_jobs',operator_registry_reads=0);save() created=[] try: for obj in packet['objects']: # Create is exclusive; never replace another operation's object. native=data(command(kube+['create','-f','-','-o','json'],payload=obj)) created.append({k:native[k] for k in ['apiVersion','kind','metadata']}) receipt['created_objects']=[{'kind':x['kind'],'name':x['metadata']['name'],'namespace':x['metadata']['namespace'],'uid':x['metadata']['uid']} for x in created];save() receipt['producers']=[] for sender in SOURCES: name=packet['run_id'];pods=[] for _ in range(80): job=data(command(kube+['-n',sender,'get','job',name,'-o','json'])) if job.get('status',{}).get('succeeded')==1:break if job.get('status',{}).get('failed'): raise LaneError('native_probe_job_failed_'+sender) time.sleep(2) else:raise LaneError('native_probe_job_timeout_'+sender) pods=data(command(kube+['-n',sender,'get','pods','-l','job-name='+name,'-o','json']))['items'] require(len(pods)==1 and pods[0]['status']['phase']=='Succeeded','probe_pod_not_succeeded') raw=command(kube+['-n',sender,'logs',pods[0]['metadata']['name'],'-c','probe']).stdout require(len(raw)<=8192,'probe_receipt_too_large') producer=json.loads(raw) require(producer['status']=='passed' and producer['sender']==sender and producer['http_statuses']==[202,200] and producer['source_zip_sha256']==packet['source_zip_sha256'], 'probe_receipt_mismatch') require(any(o['uid']==job['metadata']['uid'] and o.get('controller') is True for o in pods[0]['metadata'].get('ownerReferences',[])), 'probe_pod_owner_mismatch') require(producer['event_id']==packet['run_id']+'-'+sender,'probe_event_id_mismatch') producer.update(pod_uid=pods[0]['metadata']['uid'],image=IMAGE, runtime_image_id=pods[0]['status']['containerStatuses'][0]['imageID']) receipt['producers'].append(producer);save() receipt.update(status='native_sender_checks_passed_pending_independent_readback',phase='sender_checks_complete');save() finally: # Delete only this run's objects with UID preconditions, Jobs first. for obj in reversed(created): meta=obj['metadata'];kind=obj['kind'];plural={'Job':'jobs','ConfigMap':'configmaps','NetworkPolicy':'networkpolicies'}[kind] prefix='/api/v1' if obj['apiVersion']=='v1' else '/apis/'+obj['apiVersion'] uri=prefix+'/namespaces/'+meta['namespace']+'/'+plural+'/'+meta['name'] command(kube+['delete','--raw='+uri,'-f','/dev/stdin'],payload={'apiVersion':'v1','kind':'DeleteOptions','propagationPolicy':'Foreground','preconditions':{'uid':meta['uid']}}) if kind=='Job': # Keep the probe's isolation policy until every dependent pod is gone. command(kube+['-n',meta['namespace'],'wait','--for=delete','job/'+meta['name'],'--timeout=45s']) receipt['cleanup_requested']=True;save() def readback(packet, kube, receipt, save): """Operator-only archive readback; never rerun jobs or expose the registry.""" require(receipt['status']=='native_sender_checks_passed_pending_independent_readback' and receipt['cleanup_requested'] is True and receipt['run_id']==packet['run_id'], 'successful_sender_receipt_required') require({p['sender'] for p in receipt['producers']}==set(SOURCES) and len(receipt['producers'])==2,'exact_producer_receipts_required') for producer in receipt['producers']: require(producer['event_id']==packet['run_id']+'-'+producer['sender'] and producer['source_zip_sha256']==packet['source_zip_sha256'] and producer['status']=='passed' and producer['http_statuses']==[202,200],'producer_receipt_mismatch') receipt['receiver_at_readback']=receiver_check(kube,RECEIVER);save() identity=data(bao(['token','lookup','-format=json']))['data'] require('platform-admin' in identity['policies'] and 'root' not in identity['policies'],'attended_platform_admin_required') version,_,rows,_=snapshot() for lane in contracts(approved=True): registered=next(r for r in rows if r['name']==lane['name']) require(registered==desired(lane,registered['tokens'][0]),'sender_registry_scope_changed') receipt['reader_candidates']=[{k:r.get(k) for k in ['name','may_read','may_write','tenants']} for r in rows if r.get('may_read') is True];save() reader=operator_identity(rows);reader_token=(reader.get('tokens') or [reader['token']])[0] receipt.update(registry_version=version,reader_name=reader['name'],phase='independent_readback');save() with socket.socket() as sock:sock.bind(('127.0.0.1',0));port=sock.getsockname()[1] forward=subprocess.Popen(kube+['-n','audit-core','port-forward','--address=127.0.0.1','service/audit-core',str(port)+':8080'],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL) try: for _ in range(40): if forward.poll() is not None:raise LaneError('port_forward_failed') try: with socket.create_connection(('127.0.0.1',port),timeout=.2):break except OSError:time.sleep(.2) else:raise LaneError('port_forward_not_ready') origin='http://127.0.0.1:'+str(port) for producer in receipt['producers']: record=read_http(origin,reader_token,'/v1/events/'+producer['event_id']) require(record['event_id']==producer['event_id'] and record['source']==producer['sender'] and record['tenant']=='tenant:platform','independent_readback_scope_mismatch') detail=record['details']['data'] require(detail.get('synthetic') is True or detail.get('details',{}).get('synthetic') is True,'probe_not_marked_synthetic') producer['independent_readback']={'event_id':record['event_id'],'accepted_at':record['accepted_at'], 'source':record['source'],'tenant':record['tenant'],'synthetic':True};save() receipt['integrity_after']=read_http(origin,reader_token,'/v1/integrity') require(receipt['integrity_after']['intact'] is True,'chain_not_intact_after_probes') require(receipt['integrity_after']['events']>=2,'two_probe_events_missing') receipt.update(status='native_producer_delivery_verified_pending_bearer_revocation_and_service_admission',phase='complete');save() finally: forward.terminate();forward.wait(timeout=10) def main(): p=argparse.ArgumentParser(description=__doc__);sub=p.add_subparsers(dest='action',required=True) prep=sub.add_parser('prepare');prep.add_argument('--source-root',type=Path,required=True);prep.add_argument('--packet',type=Path,required=True) for name in ['run','jobs','readback']: native=sub.add_parser(name);native.add_argument('--packet',type=Path,required=True);native.add_argument('--packet-sha256',required=True) native.add_argument('--kubeconfig',required=True);native.add_argument('--server',required=True);native.add_argument('--receipt',type=Path,required=True);native.add_argument('--confirm',required=True) if name=='readback':native.add_argument('--producer-receipt',type=Path,required=True) a=p.parse_args() if a.action=='prepare':print(json.dumps(prepare(a.source_root,a.packet)));return 0 receipt={'schema':'platform.factory-native-acceptance-receipt.v1','status':'refused','credential_values_emitted':False,'started_at':datetime.now(timezone.utc).isoformat()};fd=None try: packet=validate_packet(a.packet,a.packet_sha256) require(a.confirm==CONFIRM,'exact_confirmation_required') if a.action!='jobs': require(Path.home().parent.name=='.warden-attended-login' and not os.getenv('BAO_TOKEN') and not os.getenv('VAULT_TOKEN'),'attended_warden_envelope_required') if a.action=='readback': prior=json.loads(a.producer_receipt.read_text()) require(prior['packet_sha256']==a.packet_sha256 and prior['run_id']==packet['run_id'],'producer_packet_mismatch') receipt.update(prior) receipt['readback_started_at']=datetime.now(timezone.utc).isoformat() 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) receipt.update(run_id=packet['run_id'],packet_sha256=a.packet_sha256,source_commits=packet['source_commits']);save() kube=['kubectl','--kubeconfig',a.kubeconfig,'--server',a.server,'--request-timeout=20s'] if a.action in {'run','jobs'}:jobs(packet,kube,receipt,save) if a.action in {'run','readback'}:readback(packet,kube,receipt,save) receipt['completed_at']=datetime.now(timezone.utc).isoformat();save();return 0 except BaseException as exc: receipt.update(status='refused',error=str(exc) if isinstance(exc,LaneError) else 'contained_native_acceptance_failed') if fd is not None:save() return 1 finally: if fd is not None:os.close(fd) if __name__=='__main__':raise SystemExit(main())