docs: preserve reviewed NetKingdom session artifacts
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 3s
Reject generated-only commits / generated-commit-guard (push) Successful in 12s

This commit is contained in:
codex 2026-11-01 23:09:51 +01:00
parent 4bf35dffaa
commit 6cf0012a6e
50 changed files with 8349 additions and 0 deletions

View file

@ -0,0 +1,452 @@
#!/usr/bin/env python3
"""T18 native adapter for an owner-admitted, already restored isolated session.
Called after identity-bundle.restore, never with an unvalidated directory. This
module does not provision TLS, grant credentials, or claim downstream acceptance.
The existing decrypt/tmpfs wrapper remains responsible for archive cleanup.
"""
import base64
import hashlib
import importlib.util
import json
import io
import os
import secrets
import tarfile
import time
import re
import subprocess
from pathlib import Path
HERE = Path(__file__).resolve().parent
IMAGE = 'forgejo.coulomb.social/coulomb/key-cape@sha256:cdec9dd7c515f7e0a6169fc3fa560ee2cc47bf40d899755dec808c362f5ce010'
LABEL = 'net-kingdom.recovery-run'
class Refused(Exception):
pass
def load_bundle():
spec = importlib.util.spec_from_file_location('identity_bundle', HERE / 'identity-bundle.py')
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def private_payload(root, manifest, expected_manifest_sha256):
"""Bind the complete validated archive before selecting KeyCape material.
Private hashes never enter the public receipt. The digest is supplied by the
existing private archive admission, not inferred from an untrusted manifest.
"""
encoded = json.dumps(manifest, sort_keys=True, separators=(',', ':')).encode()
if hashlib.sha256(encoded).hexdigest() != expected_manifest_sha256:
raise Refused()
entries = manifest['files']
if len(entries) != len({e['path'] for e in entries}):
raise Refused()
values = {}
bundle = load_bundle()
for entry in entries:
if not bundle.allowed(entry['path'], manifest['profile']):
raise Refused()
path = root / entry['path']
bundle.private(path)
value = path.read_bytes()
if len(value) != entry['bytes'] or hashlib.sha256(value).hexdigest() != entry['sha256']:
raise Refused()
values[entry['path']] = value
deployment = json.loads(values['resources/sso/deployment/keycape.json'])
main = next(c for c in deployment['spec']['template']['spec']['containers'] if c['name'] == 'keycape')
if main.get('command') or main.get('args') or main.get('envFrom'):
raise Refused()
if main['image'] != IMAGE:
# Mutable-tag/historical mismatches need a separate explicit owner admission.
raise Refused()
secret = json.loads(values['resources/sso/secret/keycape-config.json'])['data']
files = {'/etc/keycape/' + name: base64.b64decode(secret[name], validate=True)
for name in ('config.yaml', 'key.pem')}
for path, value in values.items():
if path.startswith('data/keycape/'):
relative = path.removeprefix('data/keycape/')
files['/var/lib/keycape-policy/' + relative] = value
if '/var/lib/keycape-policy/policy.json' not in files:
raise Refused()
if 'resources/sso/secret/keycape-factor-read.json' in values:
factor=json.loads(values['resources/sso/secret/keycape-factor-read.json'])['data']
files['/etc/keycape-factor/admin-token']=base64.b64decode(factor['admin-token'],validate=True)
# Resolve original delivered environment, including all referenced secrets.
env = {}
for item in main.get('env', []):
if not re.fullmatch(r'KEYCAPE_[A-Z0-9_]+',item['name']):raise Refused()
if item['name'] in env:
raise Refused()
if 'value' in item:
env[item['name']] = item['value']
else:
ref = item['valueFrom']['secretKeyRef']
source = json.loads(values['resources/sso/secret/' + ref['name'] + '.json'])['data']
env[item['name']] = base64.b64decode(source[ref['key']], validate=True).decode()
if env.get('KEYCAPE_CONFIG') != '/etc/keycape/config.yaml' or env.get('KEYCAPE_POLICY_PATH') != '/var/lib/keycape-policy/policy.json':
raise Refused()
return {'files': {p: base64.b64encode(v).decode() for p, v in files.items()}, 'env': env}
ENDPOINT = 'unix:///var/run/docker.sock'
EXECUTION = None
def docker_environment(environment=None):
# Even an admitted daemon exposed by another Unix socket is outside this
# exact session binding. Conflicting ambient routing is refused, not ignored.
for source in (os.environ, environment or {}):
if source.get('DOCKER_HOST') not in (None,'',ENDPOINT):raise Refused()
if source.get('DOCKER_CONTEXT') not in (None,'','default'):raise Refused()
if any(source.get(k) for k in ('DOCKER_TLS_VERIFY','DOCKER_CERT_PATH')):raise Refused()
env=dict(os.environ if environment is None else environment)
for key in ('DOCKER_CONTEXT','DOCKER_TLS_VERIFY','DOCKER_CERT_PATH'):env.pop(key,None)
env['DOCKER_HOST']=ENDPOINT
return env
def process(argv, **kwargs):
marker=None
if EXECUTION is not None and (kwargs.get('start_new_session') or kwargs.get('process_group') not in (None,-1)):raise Refused()
if argv[0]=='docker':
argv=['docker','--host',ENDPOINT,*argv[1:]]
kwargs['env']=docker_environment(kwargs.get('env'))
if EXECUTION is not None:
if (os.getpid()!=EXECUTION['pid'] or os.getsid(0)!=EXECUTION['sid'] or os.getpgrp()!=EXECUTION['pgid']
or time.monotonic()>=EXECUTION['deadline']):raise Refused()
marker=Path(EXECUTION['staging']+'-docker-inflight')
fd=os.open(marker,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
os.close(fd)
try:
result=subprocess.run(argv,**kwargs)
if marker is not None and result.returncode!=0:
fd=os.open(Path(EXECUTION['staging']+'-docker-uncertain'),os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW,0o600);os.close(fd)
return result
except BaseException:
# Parent loss leaves .docker-inflight; caught timeout/transport failures
# retain a sticky uncertainty marker even if later cleanup can run.
if marker is not None:
fd=os.open(Path(EXECUTION['staging']+'-docker-uncertain'),os.O_WRONLY|os.O_CREAT|os.O_NOFOLLOW,0o600);os.close(fd)
raise
finally:
if marker is not None:
try:marker.unlink()
except FileNotFoundError:pass
def command(argv, **kwargs):
maximum = kwargs.pop('max_output', 32768)
result = process(argv, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
timeout=kwargs.pop('timeout', 180), **kwargs)
if result.returncode or len(result.stdout) > maximum:
raise Refused()
return result.stdout
def inspect_session(session, worker_only=False):
"""No credential environment is read by Docker inspect or returned to caller."""
if not re.fullmatch(r'[a-f0-9]{32}', session['run_id']):
raise Refused()
names = session['containers']
if not names or len(names) > 12 or len(set(names)) != len(names):
raise Refused()
for name in names:
if not re.fullmatch(r'nk49-kc-' + session['run_id'] + r'-[a-z0-9-]{1,24}', name):
raise Refused()
if session['worker'] not in names or session['issuer'] not in names:
raise Refused()
import os
contexts=json.loads(command(['docker','context','inspect']))
endpoints=[contexts[0]['Endpoints']['docker']['Host'],os.environ.get('DOCKER_HOST') or ENDPOINT]
if not all(x==ENDPOINT for x in endpoints):raise Refused()
info = json.loads(command(['docker', 'info', '--format', '{"ID":{{json .ID}},"Name":{{json .Name}},"OSType":{{json .OSType}}}']))
if info != {'ID': '74f93783-6cef-43a8-a3c4-f62586cd3bc1', 'Name': 'bnt-lap001', 'OSType': 'linux'}:
raise Refused()
# The worker is the anchor; all dependencies/TLS/issuer share its network-none namespace.
anchor = command(['docker', 'container', 'inspect', session['worker'], '--format', '{{.Id}}']).decode().strip()
for name,spec in session.get('volumes',{}).items():
volume=json.loads(command(['docker','volume','inspect',name]))
expected={'type':'tmpfs','device':'tmpfs','o':'size=33554432,uid=1000,gid=1000,mode=0700,nosuid,nodev,noexec'}
if len(volume)!=1 or volume[0]['Driver']!='local' or volume[0].get('Scope')!='local' or volume[0].get('Options')!=expected or spec['options']!=expected or volume[0].get('Labels',{}).get(LABEL)!=session['run_id']:raise Refused()
for name in names:
obj = json.loads(command(['docker', 'container', 'inspect', name, '--format',
'{"host":{{json .HostConfig}},"mounts":{{json .Mounts}},"labels":{{json .Config.Labels}},"image":{{json .Config.Image}},"user":{{json .Config.User}},"entrypoint":{{json .Config.Entrypoint}},"cmd":{{json .Config.Cmd}}}']))
host = obj['host']
mounts=obj['mounts']
destinations=[m['Destination'] for m in mounts]
if len(destinations)!=len(set(destinations)):raise Refused()
for volume,expected_spec in session.get('volumes',{}).items():
matches=[m for m in mounts if m['Destination']==expected_spec['target']]
if len(matches)!=1 or matches[0]['Type']!='volume' or matches[0]['Name']!=volume or matches[0]['RW']!=(name==session['worker'] or expected_spec['target']=='/var/lib/keycape-policy'):raise Refused()
if (obj['labels'].get(LABEL) != session['run_id'] or not obj['user'] or obj['user'].split(':')[0] in ('0', 'root')
or not host['ReadonlyRootfs'] or host['Privileged'] or host.get('Binds') or host.get('PortBindings')
or host.get('PidMode') or host.get('IpcMode') == 'host'
or host.get('UTSMode') or host.get('UsernsMode') == 'host' or host.get('CgroupnsMode') == 'host'
or host.get('Sysctls',{}) not in ({},{'net.ipv4.ip_unprivileged_port_start':'0'} if name==session['worker'] else {})
or host.get('CapAdd') or host.get('Devices') or host.get('DeviceRequests') or host.get('VolumesFrom')
or host.get('CapDrop') != ['ALL']
or host.get('SecurityOpt') not in (['no-new-privileges'], ['no-new-privileges:true'])
or any(m['Type']!='tmpfs' and not (m['Type']=='volume' and m['Name'] in session.get('volumes',{}) and m['Destination']==session['volumes'][m['Name']]['target']) for m in obj['mounts'])
or host['NetworkMode'] != ('none' if name == session['worker'] else 'container:' + anchor)
or obj['image'] != session['images'][name] or '@sha256:' not in obj['image']):
raise Refused()
if worker_only:return
if session['images'][session['issuer']] != IMAGE:
raise Refused()
# Known admitted archive has no command/args override; image default /keycape
# must be the actual issuer executable, not an alternate same-image command.
launch=json.loads(command(['docker','container','inspect',session['issuer'],'--format',
'{"entrypoint":{{json .Config.Entrypoint}},"cmd":{{json .Config.Cmd}},"path":{{json .Path}},"args":{{json .Args}}}']))
if launch != {'entrypoint':['/keycape'],'cmd':None,'path':'/keycape','args':[]}:
raise Refused()
def container_file(container, path):
# Docker streams one tar member into memory; never materialize private files.
raw = command(['docker', 'cp', container + ':' + path, '-'], max_output=128*1024*1024)
with tarfile.open(fileobj=io.BytesIO(raw), mode='r:') as archive:
members = archive.getmembers()
if len(members) != 1 or not members[0].isfile() or members[0].size > 96*1024*1024:
raise Refused()
return archive.extractfile(members[0]).read()
JWKS_PROBE = r"""
import base64,ipaddress,json,socket,ssl,sys,urllib.request
from urllib.parse import urlsplit
p=json.load(sys.stdin);issuer=p['issuer'];origin=urlsplit(issuer)
def require(condition):
if not condition:raise ValueError('public_jwks_binding_refused')
require(origin.scheme=='https')
resolved=socket.getaddrinfo(origin.hostname,origin.port or 443,type=socket.SOCK_STREAM)
require(bool(resolved) and all(ipaddress.ip_address(x[4][0]).is_loopback for x in resolved))
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self,*args,**kwargs):return None
opener=urllib.request.build_opener(urllib.request.ProxyHandler({}),NoRedirect(),
urllib.request.HTTPSHandler(context=ssl.create_default_context(cadata=p['trust'])))
def get(url):
u=urlsplit(url)
require((u.scheme,u.hostname,u.port or 443)==(origin.scheme,origin.hostname,origin.port or 443))
require(not u.username and not u.password and not u.fragment)
with opener.open(url,timeout=10) as response:
raw=response.read(1024*1024+1);require(len(raw)<=1024*1024)
return json.loads(raw)
d=get(issuer.rstrip('/')+'/.well-known/openid-configuration');require(d['issuer']==issuer)
keys=get(d['jwks_uri'])['keys']
def integer(value):return int.from_bytes(base64.urlsafe_b64decode(value+'='*(-len(value)%4)),'big')
# The exact admitted owner release constructs one key, kid key-1. Reject every
# extra key, including irrelevant keys: a native successful exchange can then
# only have used the original signer through the existing verifier.
require(isinstance(keys,list) and len(keys)==1)
k=keys[0]
require(k.get('kty')=='RSA' and k.get('kid')=='key-1' and k.get('use')=='sig' and k.get('alg')=='RS256')
require(integer(k['n'])==int(p['n']) and integer(k['e'])==int(p['e']))
print('{"original_public_signing_key_in_live_jwks":true}')
"""
# A failed public readiness probe is closed data, not a failing Docker RPC.
# Transport/CLI nonzero remains uncertain; no deliberately failing exec is
# retried as if daemon acceptance had been established.
JWKS_TRANSPORT = ("import contextlib,io\ntry:\n"
" with contextlib.redirect_stdout(io.StringIO()):\n"
" exec(" + repr(JWKS_PROBE) + ")\n"
"except Exception:\n print('{\"original_public_signing_key_in_live_jwks\":false}')\n"
"else:\n print('{\"original_public_signing_key_in_live_jwks\":true}')\n")
def check_jwks(session, admission, public_numbers):
trust=runtime_file(session,admission['trust_path']).decode()
request={'issuer':admission['issuer'],'trust':trust,'n':str(public_numbers.n),'e':str(public_numbers.e)}
result=json.loads(command(['docker','exec','-i',session['worker'],'python3','-c',JWKS_TRANSPORT],input=json.dumps(request).encode()))
if result != {'original_public_signing_key_in_live_jwks': True}:raise Refused()
RUNTIME_FILES = r"""
import base64,json,sys
from pathlib import Path
p=json.load(sys.stdin)
roots=('/etc/keycape/','/var/lib/keycape-policy/','/etc/keycape-factor/','/recovery-tls/')
result={}
paths=[]
if p.get('tree'):
paths=list(Path('/var/lib/keycape-policy').rglob('*'))
else:paths=[Path(x) for x in p['paths']]
for f in paths:
if f.is_dir():continue
if not str(f).startswith(roots) or not f.is_file() or any(x.is_symlink() for x in (f,*f.parents)) or f.stat().st_size>8*1024*1024:raise ValueError()
result[str(f)]=base64.b64encode(f.read_bytes()).decode()
if len(result)>4096:raise ValueError()
print(json.dumps(result))
"""
def runtime_files(session, paths=(), tree=False):
raw=command(['docker','exec','-i',session['worker'],'python3','-c',RUNTIME_FILES],
input=json.dumps({'paths':list(paths),'tree':tree}).encode(),max_output=48*1024*1024)
return {k:base64.b64decode(v,validate=True) for k,v in json.loads(raw).items()}
def runtime_file(session,path):
return runtime_files(session,[path])[path]
def run_verifier(session, admission, payload):
issuer = session['issuer']
if {v['target'] for v in session.get('volumes',{}).values()}!={'/etc/keycape','/var/lib/keycape-policy','/etc/keycape-factor','/recovery-tls'}:raise Refused()
from urllib.parse import urlsplit
import ipaddress
address = urlsplit(admission['issuer'])
if (address.scheme != 'https' or not address.hostname or address.username or address.password
or address.query or address.fragment):
raise Refused()
if hashlib.sha256(container_file(issuer, '/keycape')).hexdigest() != admission['verifier_sha256']:
raise Refused()
if hashlib.sha256(runtime_file(session, admission['trust_path'])).hexdigest() != admission['trust_sha256']:
raise Refused()
# Capture private environment only in owner process memory, never receipt.
environment = json.loads(command(['docker','container','inspect',issuer,'--format','{{json .Config.Env}}']))
env = dict(item.split('=',1) for item in environment)
if len(env)!=len(environment):raise Refused()
if set(env)-set(payload['env'])-{'PATH','HOME','HOSTNAME','SSL_CERT_FILE'}:raise Refused()
if env.get('SSL_CERT_FILE') != admission['trust_path'] or any(k.lower().endswith('_proxy') and v for k,v in env.items()):
raise Refused()
if {k:v for k,v in env.items() if k.startswith('KEYCAPE_')}!={k:v for k,v in payload['env'].items() if k.startswith('KEYCAPE_')}:raise Refused()
for name,value in payload['env'].items():
if env.get(name) != value:
raise Refused()
files = {k:base64.b64decode(v,validate=True) for k,v in payload['files'].items()}
def equal():
if runtime_files(session,files)!=files:raise Refused()
durable=runtime_files(session,tree=True)
if durable!={k:v for k,v in files.items() if k.startswith('/var/lib/keycape-policy/')}:
raise Refused()
equal()
# Reading YAML is data parsing only; no archived executable is evaluated.
import yaml
cfg = yaml.safe_load(files['/etc/keycape/config.yaml'])
if cfg['issuer'] != admission['issuer']:
raise Refused()
consumer = admission['consumer']
client = next(x for x in cfg['clients'] if x['clientId']==consumer['client_id'])
if (client['secretRef']!='env:'+consumer['secret_env'] or client['clientType']!='confidential'
or 'client_credentials' not in client['grantTypes'] or not env.get(consumer['secret_env'])):
raise Refused()
from cryptography.hazmat.primitives import serialization
key=serialization.load_pem_private_key(files['/etc/keycape/key.pem'],password=None)
check_jwks(session,admission,key.public_key().public_numbers())
wrong = secrets.token_urlsafe(48)
if wrong == env[consumer['secret_env']]:raise Refused()
args = ['docker','exec','--env','NK49_UNRELATED_NEGATIVE_SECRET='+wrong,issuer,'/keycape','verify-client',
'-issuer',admission['issuer'],'-previous-secret-env','NK49_UNRELATED_NEGATIVE_SECRET']
flags = {'client_id':'client-id','secret_env':'secret-env','audience':'audience','scope':'scope',
'subject':'expect-subject','tenant':'expect-tenant','roles':'expect-roles','deny_scope':'deny-scope'}
for name,flag in flags.items():args+=['-'+flag,consumer[name]]
# Native command reports on stderr. Do not return or print its raw response.
result = process(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE,timeout=150)
raw = (result.stdout+result.stderr).decode()
required = ['discovery and endpoint origin','exchange and JWKS signature for granted scopes','access-token claim bindings',
'principal_type is service','sub matches the registration','tenant matches the registration','roles present',
'granted scope carries no excess grant','predecessor secret refused']
required += ['excess scope refused: '+scope for scope in consumer['deny_scope'].split()]
if result.returncode or any('PASS '+name+'\n' not in raw for name in required) or 'FAIL ' in raw:
raise Refused()
equal()
from cryptography.hazmat.primitives import serialization
key=serialization.load_pem_private_key(files['/etc/keycape/key.pem'],password=None)
check_jwks(session,admission,key.public_key().public_numbers())
public=key.public_key().public_bytes(serialization.Encoding.DER,serialization.PublicFormat.SubjectPublicKeyInfo)
return {'native_service_registration_verified':True,'wrong_unrelated_secret_explicitly_refused':True,
'denied_scopes_refused':True,'original_config_registry_policy_receipts_equal':True,
'public_signing_spki_sha256':hashlib.sha256(public).hexdigest(),
'original_https_issuer_and_trust_preserved':True,'original_public_signing_key_in_live_jwks':True}
def cleanup(session, receipt=None):
"""Attempt every owned cleanup even when one obligation fails.
Preserve exact safe container names and outcomes so the owner can recover an
uncertain/partial cleanup. An inspect/daemon failure never means absence.
"""
obligations = []
failed = False
for name in reversed(session['containers']):
row = {'container': name, 'status': 'unverified'}
obligations.append(row)
try:
present=command(['docker','container','ls','--all','--filter','name=^/'+name+'$','--format','{{.Names}}']).decode().splitlines()
if not present:
row['status']='absence_verified'
continue
label = command(['docker', 'container', 'inspect', name, '--format',
'{{index .Config.Labels "' + LABEL + '"}}']).decode().strip()
if label != session['run_id']:
row['status'] = 'ownership_mismatch_not_removed'
failed = True
continue
# A failed remove is followed by a successful explicit inventory;
# interrupted remove may actually have completed.
try:
command(['docker', 'rm', '--force', '--volumes', name])
except Exception:
pass
remaining = command(['docker','container','ls','--all','--filter',
'name=^/' + name + '$','--format','{{.Names}}']).decode().splitlines()
if remaining:
row['status'] = 'still_present'
failed = True
else:
row['status'] = 'absence_verified'
except Exception:
failed = True
if receipt is not None:
receipt['container_cleanup_obligations'] = obligations
if failed:
raise Refused()
def native_checks(root, manifest, admission, receipt, cleanup_session=True):
"""Owner wrapper provides a reviewed session after existing restore pipeline.
Admission must bind ciphertext, canonical private manifest digest, executable
and trust bytes, exact image/target list, original issuer and selected consumer.
None of these admission fields grant authority or carry a client secret.
"""
session = admission['session']
# Validate ownership/containment before permitting either exec or cleanup.
inspect_session(session)
receipt.update(status='failed', source_sha256=hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
ciphertext_sha256=admission['ciphertext_sha256'], production_contacted=False,
human_mfa_proven=False, downstream_consumer_reconnect_proven=False,
container_cleanup_verified=False)
try:
for field in ('ciphertext_sha256', 'manifest_sha256', 'verifier_sha256', 'trust_sha256'):
if not re.fullmatch('[a-f0-9]{64}', admission[field]):
raise Refused()
for field in ('client_id','secret_env','audience','scope','subject','tenant','roles','deny_scope'):
if not isinstance(admission['consumer'].get(field), str) or not admission['consumer'][field].strip():
raise Refused()
payload = private_payload(root, manifest, admission['manifest_sha256'])
result = run_verifier(session, admission, payload)
required = {'native_service_registration_verified','wrong_unrelated_secret_explicitly_refused',
'denied_scopes_refused','original_config_registry_policy_receipts_equal',
'original_https_issuer_and_trust_preserved','original_public_signing_key_in_live_jwks'}
if any(result.get(k) is not True for k in required) or not re.fullmatch('[a-f0-9]{64}', result.get('public_signing_spki_sha256','')):
raise Refused()
# Closed receipt allowlist: never copy arbitrary worker output.
receipt.update({k: True for k in required})
receipt['public_signing_spki_sha256'] = result['public_signing_spki_sha256']
receipt['status'] = 'isolated_keycape_native_registration_checked'
finally:
try:
if cleanup_session:
cleanup(session, receipt)
receipt['container_cleanup_verified'] = True
except Exception:
receipt['status'] = 'failed'
raise

View file

@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Candidate owner launcher. Review and native fixture proof required before use.
set +x
set -euo pipefail
ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)"
cd -- "$ROOT"
umask 077
[[ $# == 6 ]] || { printf '%s\n' 'Usage: run-isolated-keycape.sh VERIFIED_TRANSFER_RECEIPT ADMISSION ADMISSION_SHA256 TLS_CERT TLS_KEY TRUST'; exit 1; }
[[ "$3" =~ ^[a-f0-9]{64}$ ]] || { printf '%s\n' 'ERROR: exact reviewed admission digest required.'; exit 1; }
for index in 1 2 4 5 6; do
[[ -f "${!index}" && ! -L "${!index}" ]] || { printf '%s\n' 'ERROR: provide regular owner-controlled input files.'; exit 1; }
done
[[ -t 0 && -t 1 && -t 2 ]] || { printf '%s\n' 'ERROR: run in your own interactive terminal.'; exit 1; }
for tool in python3 age age-keygen docker; do
command -v "$tool" >/dev/null || { printf 'ERROR: required tool unavailable: %s\n' "$tool"; exit 1; }
done
python3 - <<'PY'
import importlib.util
from pathlib import Path
p=Path('tools/bootstrap-recovery/isolated-keycape-session.py')
spec=importlib.util.spec_from_file_location('session',p)
m=importlib.util.module_from_spec(spec);spec.loader.exec_module(m)
try:
m.local.preflight()
m.run(['docker','image','inspect',m.adapter.IMAGE])
except Exception:
raise SystemExit('ERROR: exact local Docker/images/tmpfs preflight failed; no key requested.')
print('OK: fixed local target and cached pinned images available.')
PY
NK49_KC_RUN="$(mktemp -d /tmp/nk49-isolated-keycape-XXXXXXXX)"
printf 'Safe evidence directory: %s\n' "$NK49_KC_RUN"
printf '%s\n' 'Owner-reviewed candidate: isolated original issuer/trust and current registered service-client checks only.'
printf '%s\n' 'No browser, downstream consumer or retained-factor MFA acceptance is claimed.'
printf '%s\n' 'Enter only the independently held backup age key at the hidden prompt.'
bash sso-mfa/bootstrap/sops-custody-unlock.sh \
--expected-recipient age1zvryunvjhvpkmasskauga2heeg0ztnte9ymgppvjge36ekumk50syr3tsz \
--prompt -- python3 tools/bootstrap-recovery/isolated-keycape-session.py \
--transfer-receipt "$1" --admission "$2" --admission-sha256 "$3" \
--tls-cert "$4" --tls-key "$5" --trust "$6" --receipt "$NK49_KC_RUN/recovery.json"
printf 'Safe receipt: %s/recovery.json\n' "$NK49_KC_RUN"

View file

@ -0,0 +1,119 @@
# Discovery parity repair — integrated and verified
Canonical source and normal publication are complete:
| Owner | Implementation | Source work record |
| --- | --- | --- |
| Reuse Surface | `dd00b27` | `01af189` |
| NetKingdom | `49ddde4` | `3223039` normal identifier assignment |
| Railiance Bootstrap | `6927f5e` | `8d23103` |
The native validator now refuses stale owner/index projections, duplicate IDs or
paths and malformed index shapes without tracebacks. There are **23 new CLI
regression cases**, including null/list availability, malformed YAML/frontmatter
and unvalidated index rows before Counter. Root independently passed all **30**
parity/native-registry checks across 27 initial successes and three required
filesystem-approved reruns. The sandbox write failures and initial isolated
238-test history below remain historical evidence.
Current local cohort validation is **0 errors, 0 warnings**. Root forced fresh
hosted composition at **2026-10-16 19:25:11 UTC**: all **70 reviewed IDs exactly
once**, correct owners, no warning header, and both corrected owners' summary/tags
match their authoritative entries. See `coordinator-hosted-refresh.json` and
`integration-r2.json` for exact revisions and receipts. No further hosted readback
is needed for this bounded correction. Source publication does not establish
mandatory consumer fleet adoption or close every TSA-11 requirement.
Normal Reuse publication/consistency passed with existing warnings. Bootstrap
source and record publication succeeded, while its consistency result retains
11 historical failures and 36 warnings (zero automation errors). The unchanged
generated-only hook refused automatic brief-only commits; no bypass was used.
Public actions API requests returned HTTP404, so CI outcomes remain **unverified**.
The final combined NetKingdom projection sync against browser publication
`015694ee414c802dc2ef1fa0120dfed610086e69` completed exit 0, zero failures or
automation errors, 29 existing/pre-repair warnings and five informational items.
It indexed browser T10/T11/T12 and retained their source IDs through the normal
tool; exact final receipt is `netkingdom-parity-consistency-final.json`.
NetKingdom's original brief staged blob/mode and worktree were preserved with
explicit staging isolation and race-checked restoration. Its current
WORK-RECORDS was preserved amid concurrent recovery/browser owner work.
TSA-12 still requires the selected rein/profile acceptance, actual schedule
activation and the first unassisted scheduled fire. No production action, model
spend, credential access or external message was performed.
## Historical preparation and counterexamples
Prepared source candidate, 16 October 2026. No owner checkout or runtime changed.
The existing native `reuse-surface validate --fail-on-warnings` accepts index rows
whose names, summaries, owner, status, domain, tags, consumption modes or vector
no longer project their owner entry. It also accepts duplicate IDs/paths because
it converts paths to sets. This is a gap in the current tool, not a new observer
service. `.forgejo/workflows/ci.yml:37` already runs this validator as a mandatory
job in Reuse Surface. Consumer fleet adoption is not claimed.
`reuse-native-validator.patch` extends that native command with parity and
uniqueness warnings. Strict mode rejects them; normal mode preserves the existing
warning policy. Equivalent vector whitespace remains accepted. It does not edit
owner entries, maturity assessments, credentials or hosted registrations.
Actual current source has **3 differing fields across 2 capabilities**:
- NetKingdom bootstrap-recovery: summary.
- Railiance Bootstrap bootstrap-sequencing: summary and tags.
`observed-drift.json` retains exact entry/index values. Native CLI evidence shows
old exit 0, candidate exit 1, then exit 0 against corrected temporary indexes.
The two `*-index-parity.patch` files correct only those index fields to their
existing owner entries. No existing general index regeneration command was found;
these are constrained source projections, not newly authored capability claims.
Do not integrate the validator while silently ignoring its known cohort findings.
Review both owner patches in the same source integration wave. No generated-only
commit exception is implied; use the owners' normal publication workflow.
A separate **synthetic** duplicated row proves old exit 0 versus candidate exit 1.
There is no observed duplicate in the actual reviewed cohort: 70/70 reviewed IDs
appear exactly once across the current 60 local providers; those indexes contain
71 total entries including an additional out-of-denominator owner entry.
Tests: 11 new native CLI cases; the full isolated suite had 237 pass and one
missing-sibling-path failure. After resolving only the temporary federation
manifest's relative local paths against `/home/worsch/reuse-surface`, the single
failed composition test passed. All 238 tests have passed across those runs.
The failed first run remains in `reuse-source.json`. No manifest edit belongs to
the patch. Temporary reconstruction: `/tmp/netkingdom-improvement-loop-next-reuse`;
corrected owner registry copies: `/tmp/netkingdom-improvement-loop-next-owners`.
After review/integration, verification uses existing commands:
```sh
python3 -m pytest -q tests/test_index_parity.py tests/test_registry.py
reuse-surface validate --root /home/worsch/net-kingdom --fail-on-warnings
reuse-surface validate --root /home/worsch/railiance-bootstrap --fail-on-warnings
```
The existing Activity schedule remains disabled. ACTIVITY-WP-0047-T02 specifically
requires claiming-rein workstation checkout/hub acceptance, then owner release,
admin-sync and manual trigger; T03 requires the first unassisted scheduled fire.
The Oct15 rein candidate is rebuilt with startup proof but remains uninstalled
and unadmitted in its owner record, with real-model acceptance open. This is its
selected rein/profile dependency, not an invented dependency on completing the
unrelated task-scoped protected operation. Exact source hashes are retained in
`schedule-dependency-evidence.json`.
Kings Guard's cadence observer and Maturity Engine remain their existing owners;
a deterministic registry projection check creates neither posture nor a second
maturity assessor. The adopted coordination frame is 3600 seconds; source
configuration does not establish source/receiver runtime admission. The NetKingdom
knowledge pilot returned package 0.2.6, digest
`8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02`,
withheld/orientation-only because sources changed. Current owner instructions,
companion and source artifacts were reviewed directly for this bounded change.
Next authority gates: coordinator and affected-owner source review, normal owner
publication and CI, then native hosted source refresh/readback if publication is
admitted. TSA-11 remains open for full hosted accuracy/version/link requirements;
TSA-12 remains open for accepted runtime activation, unassisted firing, checkpoint
and changed-state delivery, owner remediation and native remeasurement. No
production action, model spend, credential access or external message occurred.

View file

@ -0,0 +1,490 @@
{
"repo_slug": "railiance-bootstrap",
"repo_path": "/home/worsch/railiance-bootstrap",
"source_revision": "8d23103af2d5ff0da4eb5f612ea2bf09d8ff7344",
"source_revision_reason": null,
"issues": [
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager warning: .repo-classification.yaml: category is project but slug is not prj-*; not every category:project is a prj- repo",
"file_path": ".repo-classification.yaml",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-1: task id 'T01' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-1",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-2: task id 'T02' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-2",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-3: task id 'T03' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-3",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-4: task id 'T04' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-4",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-5: task id 'T05' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-5",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-1: task id 'T01' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-1",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-2: task id 'T02' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-2",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-3: task id 'T03' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-3",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-4: task id 'T04' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-4",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-1: task id 'T01' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-1",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-2: task id 'T02' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-2",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-3: task id 'T03' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-3",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-4: task id 'T04' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-4",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-5: task id 'T05' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-5",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-6: task id 'T06' is not accepted by the canon kind registry",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-6",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: intakes/intakes.md#record-block-1: intake id 'RAIL-BS-IN-0001' is not accepted by the canon kind registry",
"file_path": "intakes/intakes.md#record-block-1",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-1: task 'T01' has conflicting UUID assignments at ['workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-1', 'workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-1', 'workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-1']",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-1",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-2: task 'T02' has conflicting UUID assignments at ['workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-2', 'workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-2', 'workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-2']",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-2",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-3: task 'T03' has conflicting UUID assignments at ['workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-3', 'workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-3', 'workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-3']",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-3",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-4: task 'T04' has conflicting UUID assignments at ['workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-4', 'workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md#task-block-4', 'workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-4']",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-4",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager contradictory: workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-5: task 'T05' has conflicting UUID assignments at ['workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-5', 'workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md#task-block-5']",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md#task-block-5",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-31",
"message": "[intakes/intakes.md] id 'RAIL-BS-IN-0001' matches no kind in the canon work-record type registry \u2014 unregistered species become sidetracks needing manual reintegration (work-record-types_v0.1)",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id 7cbbe0d6\u2026 not found in DB (stale reference)",
"file_path": "workplans/RAIL-BS-WP-0008-activity-core-wp0016-triage-output-deploy.md",
"db_id": "7cbbe0d6-fea9-41c6-840c-46d0d8e8edde",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id 2c9e8e96\u2026 not found in DB (stale reference)",
"file_path": "workplans/RAIL-BS-WP-0009-activity-core-admin-sync-smoke.md",
"db_id": "2c9e8e96-ec6a-433c-9e6d-0efbcd18679e",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id 59155efb\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0001-dependency-management.md",
"db_id": "59155efb-b461-4caa-ad7b-b3fce348db84",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id 4c63dfc6\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md",
"db_id": "4c63dfc6-9eac-4e79-9f77-8f644ad7147d",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id 7ee9ee22\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md",
"db_id": "7ee9ee22-1fae-4567-9194-8d70a9e0f45b",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id 7e8b0c20\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md",
"db_id": "7e8b0c20-51eb-40c9-9e3b-85dd380d7625",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id b236de41\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0005-kubeconfig-delivery.md",
"db_id": "b236de41-2f33-4ebc-bb84-5fcedb2982f8",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id 6434f7cb\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260622-RAILIANCE-WP-0012-activity-core-cluster-owned-deploy-verify.md",
"db_id": "6434f7cb-e13c-4c05-839b-197bb239d5cd",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id a3abb83a\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260622-RAILIANCE-WP-0013-activity-core-verifier-evidence-hardening.md",
"db_id": "a3abb83a-2d42-40f9-a5f6-1dbc36903436",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id cb72d3ba\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260627-RAIL-BS-WP-0006-staged-promotion-lifecycle.md",
"db_id": "cb72d3ba-1863-43c2-a2a5-49ac75fc2603",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "FAIL",
"check_id": "C-03",
"message": "state_hub_workstream_id a152ddda\u2026 not found in DB (stale reference)",
"file_path": "workplans/archived/260701-RAILIANCE-WP-0014-activity-core-llm-connect-live-reconcile.md",
"db_id": "a152ddda-d60a-4a65-9b9c-59e2db9ff2b7",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-28",
"message": "9 unread inbox message(s) older than 3 day(s): prj-forgejo-org-refactor:37fe499a, prj-forgejo-org-refactor:5636187f, prj-forgejo-org-refactor:04d07b60, the-custodian:23172cb2, the-custodian:51e9eace, ...",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-28",
"message": "9 unread message(s) lack thread_id for supersession tracking: prj-forgejo-org-refactor:37fe499a, prj-forgejo-org-refactor:5636187f, prj-forgejo-org-refactor:04d07b60, the-custodian:23172cb2, the-custodian:51e9eace",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'RAILIANCE-WP-0012' uses prefix 'RAILIANCE-WP' but repo canonical prefix is 'RAIL-BS-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260622-RAILIANCE-WP-0012-activity-core-cluster-owned-deploy-verify.md",
"db_id": "",
"file_value": "RAILIANCE-WP-0012",
"db_value": "RAIL-BS-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'RAILIANCE-WP-0013' uses prefix 'RAILIANCE-WP' but repo canonical prefix is 'RAIL-BS-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260622-RAILIANCE-WP-0013-activity-core-verifier-evidence-hardening.md",
"db_id": "",
"file_value": "RAILIANCE-WP-0013",
"db_value": "RAIL-BS-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'RAILIANCE-WP-0014' uses prefix 'RAILIANCE-WP' but repo canonical prefix is 'RAIL-BS-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260701-RAILIANCE-WP-0014-activity-core-llm-connect-live-reconcile.md",
"db_id": "",
"file_value": "RAILIANCE-WP-0014",
"db_value": "RAIL-BS-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-27",
"message": "Workplan id 'RAIL-BS-WP-0008' collides across repos: railiance-bootstrap/RAIL-BS-WP-0008-activity-core-wp0016-triage-output-deploy.md, railiance-cluster/RAIL-BS-WP-0008-activity-core-wp0016-triage-output-deploy.md",
"file_path": "workplans/RAIL-BS-WP-0008-activity-core-wp0016-triage-output-deploy.md",
"db_id": "",
"file_value": "RAIL-BS-WP-0008",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-27",
"message": "Workplan id 'RAIL-BS-WP-0009' collides across repos: railiance-bootstrap/RAIL-BS-WP-0009-activity-core-admin-sync-smoke.md, railiance-cluster/RAIL-BS-WP-0009-activity-core-admin-sync-smoke.md",
"file_path": "workplans/RAIL-BS-WP-0009-activity-core-admin-sync-smoke.md",
"db_id": "",
"file_value": "RAIL-BS-WP-0009",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-27",
"message": "Workplan id 'RAIL-BS-WP-0001' collides across repos: railiance-bootstrap/260622-RAIL-BS-WP-0001-dependency-management.md, railiance-cluster/260622-RAIL-BS-WP-0001-dependency-management.md",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0001-dependency-management.md",
"db_id": "",
"file_value": "RAIL-BS-WP-0001",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-27",
"message": "Workplan id 'RAIL-BS-WP-0002' collides across repos: railiance-bootstrap/260622-RAIL-BS-WP-0002-k3s-baseline.md, railiance-cluster/260622-RAIL-BS-WP-0002-k3s-baseline.md",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0002-k3s-baseline.md",
"db_id": "",
"file_value": "RAIL-BS-WP-0002",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-27",
"message": "Workplan id 'RAIL-BS-WP-0003' collides across repos: railiance-bootstrap/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md, railiance-cluster/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0003-pgpool-ha-failover-fix.md",
"db_id": "",
"file_value": "RAIL-BS-WP-0003",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-27",
"message": "Workplan id 'RAIL-BS-WP-0004' collides across repos: railiance-bootstrap/260622-RAIL-BS-WP-0004-safety-net.md, railiance-cluster/260622-RAIL-BS-WP-0004-safety-net.md",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0004-safety-net.md",
"db_id": "",
"file_value": "RAIL-BS-WP-0004",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-27",
"message": "Workplan id 'RAIL-BS-WP-0005' collides across repos: railiance-bootstrap/260622-RAIL-BS-WP-0005-kubeconfig-delivery.md, railiance-cluster/260622-RAIL-BS-WP-0005-kubeconfig-delivery.md",
"file_path": "workplans/archived/260622-RAIL-BS-WP-0005-kubeconfig-delivery.md",
"db_id": "",
"file_value": "RAIL-BS-WP-0005",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-27",
"message": "Workplan id 'RAIL-BS-WP-0006' collides across repos: railiance-bootstrap/260627-RAIL-BS-WP-0006-staged-promotion-lifecycle.md, railiance-cluster/260627-RAIL-BS-WP-0006-staged-promotion-lifecycle.md",
"file_path": "workplans/archived/260627-RAIL-BS-WP-0006-staged-promotion-lifecycle.md",
"db_id": "",
"file_value": "RAIL-BS-WP-0006",
"db_value": "",
"fixable": false
}
],
"fixes_applied": [
"bindings: synced 12/12",
"C-17 cleared: pushed 1 backlogged commit(s)",
"push: pushed"
],
"summary": {
"fail": 11,
"automation_error": 0,
"warn": 36,
"info": 0
},
"result": "fail"
}

View file

@ -0,0 +1,131 @@
{
"actual_owner_before_after": {
"net-kingdom": {
"old_validator": {
"command": [
"python3",
"-m",
"reuse_surface.cli",
"validate",
"--root",
"/home/worsch/net-kingdom",
"--fail-on-warnings"
],
"cwd": "/home/worsch/reuse-surface",
"exit_code": 0,
"stdout": "ok: validated 6 capability entries\n",
"stderr": ""
},
"candidate_validator": {
"command": [
"python3",
"-m",
"reuse_surface.cli",
"validate",
"--root",
"/home/worsch/net-kingdom",
"--fail-on-warnings"
],
"cwd": "/tmp/netkingdom-improvement-loop-next-reuse",
"exit_code": 1,
"stdout": "",
"stderr": "warning: index drift: registry/capabilities/capability.security.bootstrap-recovery.md: summary differs from entry\n"
},
"corrected_temporary_index": {
"command": [
"python3",
"-m",
"reuse_surface.cli",
"validate",
"--root",
"/tmp/netkingdom-improvement-loop-next-owners/net-kingdom",
"--fail-on-warnings"
],
"cwd": "/tmp/netkingdom-improvement-loop-next-reuse",
"exit_code": 0,
"stdout": "ok: validated 6 capability entries\n",
"stderr": ""
}
},
"railiance-bootstrap": {
"old_validator": {
"command": [
"python3",
"-m",
"reuse_surface.cli",
"validate",
"--root",
"/home/worsch/railiance-bootstrap",
"--fail-on-warnings"
],
"cwd": "/home/worsch/reuse-surface",
"exit_code": 0,
"stdout": "ok: validated 1 capability entry\n",
"stderr": ""
},
"candidate_validator": {
"command": [
"python3",
"-m",
"reuse_surface.cli",
"validate",
"--root",
"/home/worsch/railiance-bootstrap",
"--fail-on-warnings"
],
"cwd": "/tmp/netkingdom-improvement-loop-next-reuse",
"exit_code": 1,
"stdout": "",
"stderr": "warning: index drift: registry/capabilities/capability.railiance.bootstrap-sequencing.md: summary differs from entry\nwarning: index drift: registry/capabilities/capability.railiance.bootstrap-sequencing.md: tags differs from entry\n"
},
"corrected_temporary_index": {
"command": [
"python3",
"-m",
"reuse_surface.cli",
"validate",
"--root",
"/tmp/netkingdom-improvement-loop-next-owners/railiance-bootstrap",
"--fail-on-warnings"
],
"cwd": "/tmp/netkingdom-improvement-loop-next-reuse",
"exit_code": 0,
"stdout": "ok: validated 1 capability entry\n",
"stderr": ""
}
}
},
"synthetic_duplicate": {
"old_validator": {
"command": [
"python3",
"-m",
"reuse_surface.cli",
"validate",
"--root",
"/tmp/netkingdom-improvement-loop-next-duplicate",
"--fail-on-warnings"
],
"cwd": "/home/worsch/reuse-surface",
"exit_code": 0,
"stdout": "ok: validated 1 capability entry\n",
"stderr": ""
},
"candidate_validator": {
"command": [
"python3",
"-m",
"reuse_surface.cli",
"validate",
"--root",
"/tmp/netkingdom-improvement-loop-next-duplicate",
"--fail-on-warnings"
],
"cwd": "/tmp/netkingdom-improvement-loop-next-reuse",
"exit_code": 1,
"stdout": "",
"stderr": "warning: index drift: duplicate id: capability.infotech.headless-browser (2 rows)\nwarning: index drift: duplicate path: registry/capabilities/capability.infotech.headless-browser.md (2 rows)\n"
}
},
"proof_boundary": "Actual local source CLI behavior and corrected temporary owner indexes only. No owner checkout, hosted registration, schedule, model spend, credential or native acceptance change."
}

View file

@ -0,0 +1,13 @@
--- a/registry/indexes/capabilities.yaml
+++ b/registry/indexes/capabilities.yaml
@@ -67,8 +67,8 @@
- source
- id: capability.security.bootstrap-recovery
name: Guarded bootstrap and recovery preparation
- summary: Encrypted identity backup, independent retrieval and isolated recovery
- verification with bootstrap handoff.
+ summary: Guarded bootstrap custody, encrypted identity backup capture, independent
+ retrieval and isolated recovery verification.
domain: infotech
status: draft
owner: net-kingdom

View file

@ -0,0 +1,5 @@
{
"commit": "0e298813c7d45ada98195bb1899f334b14612b06",
"status": "MM .custodian-brief.md\n M WORK-RECORDS.md\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json\n?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md\n",
"index_sha256": "065f08f68df57cec83b1025c56cdfebfbe091a8593bb3b33e355dea92a6873a3"
}

View file

@ -0,0 +1,364 @@
{
"repo_slug": "net-kingdom",
"repo_path": "/home/worsch/net-kingdom",
"source_revision": "015694ee414c802dc2ef1fa0120dfed610086e69",
"source_revision_reason": null,
"issues": [
{
"severity": "WARN",
"check_id": "C-24",
"message": "Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-35",
"message": "Repo Manager warning: workplans/: repository uses more than one workplan prefix: ['NET-WP', 'NK-WP']",
"file_path": "workplans/",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-33",
"message": "WORK-RECORDS.md is missing or stale \u2014 regenerate via fix-consistency (CUST-WP-0061-T04 generated work-record index)",
"file_path": "WORK-RECORDS.md",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": true
},
{
"severity": "WARN",
"check_id": "C-37",
"message": "Residual workplan 'nk-wp-0011' has no origin/origin_ref provenance (not invented)",
"file_path": "workplans/NK-WP-0011-enterprise-federation-saml.md",
"db_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub",
"file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md",
"db_id": "",
"file_value": "USER-WP-0020",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub",
"file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md",
"db_id": "",
"file_value": "KEY-WP-0004",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub",
"file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md",
"db_id": "",
"file_value": "USER-WP-0020",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub",
"file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md",
"db_id": "",
"file_value": "KEY-WP-0004",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'USER-WP-0021' is not linked to State Hub",
"file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md",
"db_id": "",
"file_value": "USER-WP-0021",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'FLEX-WP-0009' is not linked to State Hub",
"file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md",
"db_id": "",
"file_value": "FLEX-WP-0009",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'AUDIT-WP-0003' is not linked to State Hub",
"file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md",
"db_id": "",
"file_value": "AUDIT-WP-0003",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'EMAIL-WP-0004' is not linked to State Hub",
"file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md",
"db_id": "",
"file_value": "EMAIL-WP-0004",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'USER-WP-0022' is not linked to State Hub",
"file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md",
"db_id": "",
"file_value": "USER-WP-0022",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'KEY-WP-0008' is not linked to State Hub",
"file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md",
"db_id": "",
"file_value": "KEY-WP-0008",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'CSOC-WP-0003' is not linked to State Hub",
"file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md",
"db_id": "",
"file_value": "CSOC-WP-0003",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-37",
"message": "Residual workplan 'nk-wp-0027' has no origin/origin_ref provenance (not invented)",
"file_path": "workplans/NK-WP-0027-reef-placement-reconciliation.md",
"db_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-37",
"message": "Residual workplan 'nk-wp-0031' has no origin/origin_ref provenance (not invented)",
"file_path": "workplans/NK-WP-0031-deterministic-posture-feedback.md",
"db_id": "9d7b04f9-3803-5613-b7a5-8bd606c77f5a",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-37",
"message": "Residual workplan 'nk-wp-0040' has no origin/origin_ref provenance (not invented)",
"file_path": "workplans/NK-WP-0040-execution-attribution-receipt.md",
"db_id": "e2533f3a-aa43-59b3-bff3-8e64b6149487",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Task dependency target 'NK-WP-0048-T12' of task 'NK-WP-0048-T06' is not linked to State Hub",
"file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06",
"db_id": "",
"file_value": "NK-WP-0048-T12",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-11",
"message": "Task 'NK-WP-0048-T10' has no state_hub_task_id",
"file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T10",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": true
},
{
"severity": "WARN",
"check_id": "C-11",
"message": "Task 'NK-WP-0048-T11' has no state_hub_task_id",
"file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T11",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": true
},
{
"severity": "WARN",
"check_id": "C-11",
"message": "Task 'NK-WP-0048-T12' has no state_hub_task_id",
"file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T12",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": true
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'RAIL-PL-WP-0002' is not linked to State Hub",
"file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md",
"db_id": "",
"file_value": "RAIL-PL-WP-0002",
"db_value": "",
"fixable": false
},
{
"severity": "INFO",
"check_id": "C-08",
"message": "Retired DB workstream 'nk-wp-0035@retired-20260908' (id=064c5e8b\u2026, status=archived) has no backing workplan file \u2014 projection tombstone, not live work",
"file_path": "",
"db_id": "064c5e8b-7852-4d7f-a5fa-e26e406ab8e0",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "INFO",
"check_id": "C-08",
"message": "Retired DB workstream 'adhoc-2026-08-14@retired-20260827' (id=a5097121\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
"file_path": "",
"db_id": "a5097121-1461-59f8-81fa-e6ecef527337",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "INFO",
"check_id": "C-08",
"message": "Retired DB workstream 'net-kingdom-adhoc-2026-06-14@retired-20260827' (id=c6f3d6bf\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
"file_path": "",
"db_id": "c6f3d6bf-4916-490d-96ce-092d2776d7e7",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "INFO",
"check_id": "C-08",
"message": "Retired DB workstream 'net-kingdom-adhoc-2026-07-02@retired-20260827' (id=67c1c7ac\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
"file_path": "",
"db_id": "67c1c7ac-d8b1-41dc-a81a-4e43f1afd068",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "INFO",
"check_id": "C-08",
"message": "Retired DB workstream 'adhoc-2026-08-23@retired-20260827' (id=e25ba5fa\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
"file_path": "",
"db_id": "e25ba5fa-eaae-5039-8d93-a69c6d837fc4",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'NET-WP-0020' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
"file_path": "workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md",
"db_id": "",
"file_value": "NET-WP-0020",
"db_value": "NK-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'NET-WP-0015' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260603-NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap.md",
"db_id": "",
"file_value": "NET-WP-0015",
"db_value": "NK-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'NET-WP-0016' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260603-NET-WP-0016-guided-security-bootstrap-experience.md",
"db_id": "",
"file_value": "NET-WP-0016",
"db_value": "NK-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'NET-WP-0018' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260604-NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md",
"db_id": "",
"file_value": "NET-WP-0018",
"db_value": "NK-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'NET-WP-0017' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md",
"db_id": "",
"file_value": "NET-WP-0017",
"db_value": "NK-WP",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-26",
"message": "Workplan id 'NET-WP-0019' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
"file_path": "workplans/archived/260702-NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md",
"db_id": "",
"file_value": "NET-WP-0019",
"db_value": "NK-WP",
"fixable": false
}
],
"fixes_applied": [
"bindings: synced 64/64",
"C-33 fixed: regenerated WORK-RECORDS.md",
"C-11 fixed: task 'NK-WP-0048-T10' \u2192 4b06ca97\u2026",
"C-11 fixed: task 'NK-WP-0048-T11' \u2192 ef2a0119\u2026",
"C-11 fixed: task 'NK-WP-0048-T12' \u2192 7ce4eee3\u2026",
"brief: .custodian-brief.md updated",
"push: pushed"
],
"summary": {
"fail": 0,
"automation_error": 0,
"warn": 29,
"info": 5
},
"result": "warn"
}

View file

@ -0,0 +1 @@
WARN: git commit failed for writeback: Generated-only commits rejected: staged

View file

@ -0,0 +1,40 @@
[
{
"owner": "net-kingdom",
"capability_id": "capability.security.bootstrap-recovery",
"field": "summary",
"index": "Encrypted identity backup, independent retrieval and isolated recovery verification with bootstrap handoff.",
"entry": "Guarded bootstrap custody, encrypted identity backup capture, independent retrieval and isolated recovery verification.",
"entry_path": "registry/capabilities/capability.security.bootstrap-recovery.md",
"entry_sha256": "3dc7a15170af69f02ade2df7723aae8cf9164c20c47aff1ffa5abc82a480cdf6"
},
{
"owner": "railiance-bootstrap",
"capability_id": "capability.railiance.bootstrap-sequencing",
"field": "summary",
"index": "Bare-VM sequencing with NetKingdom identity backup recovery handoff.",
"entry": "Bare-VM to estate sequence and handoff contract.",
"entry_path": "registry/capabilities/capability.railiance.bootstrap-sequencing.md",
"entry_sha256": "f0066cc31c3a39388827684ec016a94b007c22df18d03553b4dd118c9c7cf5a7"
},
{
"owner": "railiance-bootstrap",
"capability_id": "capability.railiance.bootstrap-sequencing",
"field": "tags",
"index": [
"security",
"netkingdom",
"framework-reuse",
"backup",
"restore",
"disaster-recovery"
],
"entry": [
"security",
"netkingdom",
"framework-reuse"
],
"entry_path": "registry/capabilities/capability.railiance.bootstrap-sequencing.md",
"entry_sha256": "f0066cc31c3a39388827684ec016a94b007c22df18d03553b4dd118c9c7cf5a7"
}
]

View file

@ -0,0 +1,17 @@
{
"reuse-surface": {
"url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/reuse-surface/actions/runs?limit=10",
"status": 404,
"ci_acceptance": "unverified"
},
"net-kingdom": {
"url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/net-kingdom/actions/runs?limit=10",
"status": 404,
"ci_acceptance": "unverified"
},
"railiance-bootstrap": {
"url": "https://forgejo.coulomb.social/api/v1/repos/coulomb/railiance-bootstrap/actions/runs?limit=10",
"status": 404,
"ci_acceptance": "unverified"
}
}

View file

@ -0,0 +1,21 @@
--- a/registry/indexes/capabilities.yaml
+++ b/registry/indexes/capabilities.yaml
@@ -4,7 +4,7 @@
capabilities:
- id: capability.railiance.bootstrap-sequencing
name: Bootstrap sequencing contracts
- summary: Bare-VM sequencing with NetKingdom identity backup recovery handoff.
+ summary: Bare-VM to estate sequence and handoff contract.
domain: infotech
status: draft
owner: railiance-bootstrap
@@ -12,9 +12,6 @@
- security
- netkingdom
- framework-reuse
- - backup
- - restore
- - disaster-recovery
vector: D3 / A0 / C1 / R0
path: registry/capabilities/capability.railiance.bootstrap-sequencing.md
consumption_modes:

View file

@ -0,0 +1,5 @@
{
"commit": "d1dcfde527afde15a6fbe7ae65a2d94b70c3c263",
"status": "",
"index_sha256": "abb5f57df8f01e4184dbcd0ed5f75d271e2eaef7e95a3753d3008d01009947c6"
}

View file

@ -0,0 +1,191 @@
diff --git a/reuse_surface/cli.py b/reuse_surface/cli.py
index 39ba2de..5c47322 100644
--- a/reuse_surface/cli.py
+++ b/reuse_surface/cli.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import argparse
+from collections import Counter
import json
import sys
from pathlib import Path
@@ -87,6 +88,7 @@ from reuse_surface.registry import (
parse_vector,
registry_paths,
resolve_repo_root,
+ vectors_match,
)
@@ -114,12 +116,48 @@ def _check_index_drift(
repo_root: Path,
) -> list[str]:
warnings: list[str] = []
- indexed_paths = {item["path"] for item in index.get("capabilities", [])}
+ if not isinstance(index, dict) or not isinstance(index.get("capabilities"), list):
+ return ["index drift: index must contain a capabilities list"]
+ rows = []
+ for position, row in enumerate(index["capabilities"]):
+ if not isinstance(row, dict) or any(
+ not isinstance(row.get(field), str) or not row[field]
+ for field in ("id", "path")
+ ):
+ warnings.append(f"index drift: row {position} requires nonempty string id and path")
+ continue
+ rows.append(row)
+ indexed_paths = {item["path"] for item in rows}
file_paths = {str(path.relative_to(repo_root)) for path in entry_paths}
for path in sorted(file_paths - indexed_paths):
warnings.append(f"index drift: entry file not indexed: {path}")
for path in sorted(indexed_paths - file_paths):
warnings.append(f"index drift: index references missing file: {path}")
+ for field in ("id", "path"):
+ for value, count in sorted(Counter(row[field] for row in rows).items()):
+ if count > 1:
+ warnings.append(f"index drift: duplicate {field}: {value} ({count} rows)")
+ for row in rows:
+ path = repo_root / row["path"]
+ if not path.is_file():
+ continue
+ try:
+ entry = parse_front_matter(path)
+ except (ValueError, yaml.YAMLError):
+ continue # The entry validation pass reports malformed source.
+ # The index projects the owner entry; index-only changes cannot replace it.
+ for field in ("id", "name", "summary", "domain", "status", "owner", "tags"):
+ if row.get(field) != entry.get(field):
+ warnings.append(f"index drift: {row['path']}: {field} differs from entry")
+ availability = entry.get("availability")
+ modes = availability.get("consumption_modes") if isinstance(availability, dict) else None
+ if row.get("consumption_modes") != modes:
+ warnings.append(f"index drift: {row['path']}: consumption_modes differs from entry")
+ try:
+ if not vectors_match(row["vector"], entry):
+ warnings.append(f"index drift: {row['path']}: vector differs from entry")
+ except (KeyError, TypeError, AttributeError):
+ warnings.append(f"index drift: {row['path']}: vector cannot be compared to entry")
return warnings
@@ -145,7 +183,7 @@ def _run_validate(
for path in paths:
try:
data = parse_front_matter(path)
- except ValueError as exc:
+ except (ValueError, yaml.YAMLError) as exc:
errors.append(str(exc))
continue
for error in sorted(validator.iter_errors(data), key=lambda e: e.path):
@@ -155,8 +193,12 @@ def _run_validate(
if not target:
index_path = registry_paths(repo_root)["index"]
if index_path.exists():
- index = load_index_at(index_path)
- warnings.extend(_check_index_drift(paths, index, repo_root))
+ try:
+ index = load_index_at(index_path)
+ except (ValueError, yaml.YAMLError) as exc:
+ errors.append(f"{index_path}: {exc}")
+ else:
+ warnings.extend(_check_index_drift(paths, index, repo_root))
if relations and repo_root == ROOT:
warnings.extend(check_relations())
return errors, warnings, paths
diff --git a/tests/test_index_parity.py b/tests/test_index_parity.py
new file mode 100644
index 0000000..3a2774b
--- /dev/null
+++ b/tests/test_index_parity.py
@@ -0,0 +1,91 @@
+"""Exercise the same strict native validator used by the existing Forgejo job."""
+from __future__ import annotations
+
+import copy
+import subprocess
+import sys
+from pathlib import Path
+
+import pytest
+import yaml
+
+ROOT = Path(__file__).resolve().parent.parent
+
+
+def fixture_registry(tmp_path):
+ index = yaml.safe_load((ROOT / "registry/indexes/capabilities.yaml").read_text())
+ row = copy.deepcopy(index["capabilities"][0])
+ source = ROOT / row["path"]
+ target = tmp_path / row["path"]
+ target.parent.mkdir(parents=True)
+ target.write_bytes(source.read_bytes())
+ index["capabilities"] = [row]
+ index_path = tmp_path / "registry/indexes/capabilities.yaml"
+ index_path.parent.mkdir(parents=True)
+ return index_path, index
+
+
+def validate(root):
+ return subprocess.run(
+ [sys.executable, "-m", "reuse_surface.cli", "validate", "--root", str(root),
+ "--fail-on-warnings"], cwd=ROOT, capture_output=True, text=True,
+ )
+
+
+@pytest.mark.parametrize("field,value", [
+ ("id", "capability.synthetic.stale"), ("name", "stale name"),
+ ("summary", "stale summary"), ("domain", "stale-domain"),
+ ("status", "retired"), ("owner", "wrong-owner"),
+ ("tags", ["stale"]), ("consumption_modes", ["stale"]),
+ ("vector", "D0 / A0 / C0 / R0"),
+])
+def test_existing_strict_validator_refuses_stale_projection(tmp_path, field, value):
+ path, index = fixture_registry(tmp_path)
+ path.write_text(yaml.safe_dump(index))
+ assert validate(tmp_path).returncode == 0
+ original_entry = (tmp_path / index["capabilities"][0]["path"]).read_bytes()
+ index["capabilities"][0][field] = value
+ path.write_text(yaml.safe_dump(index))
+ result = validate(tmp_path)
+ assert result.returncode == 1, result.stdout + result.stderr
+ assert f"{field} differs from entry" in result.stderr
+ assert (tmp_path / index["capabilities"][0]["path"]).read_bytes() == original_entry
+
+
+def test_duplicate_id_and_path_cannot_satisfy_exactly_once(tmp_path):
+ path, index = fixture_registry(tmp_path)
+ index["capabilities"].append(copy.deepcopy(index["capabilities"][0]))
+ path.write_text(yaml.safe_dump(index))
+ result = validate(tmp_path)
+ assert result.returncode == 1
+ assert "duplicate id:" in result.stderr
+ assert "duplicate path:" in result.stderr
+
+
+def test_equivalent_vector_whitespace_is_allowed(tmp_path):
+ path, index = fixture_registry(tmp_path)
+ index["capabilities"][0]["vector"] = index["capabilities"][0]["vector"].replace(" ", "")
+ path.write_text(yaml.safe_dump(index))
+ assert validate(tmp_path).returncode == 0
+
+
+@pytest.mark.parametrize("source", ["null", "[]", "availability: null", "availability: []", "availability: ["])
+def test_malformed_frontmatter_returns_failure_without_traceback(tmp_path, source):
+ path, index = fixture_registry(tmp_path)
+ path.write_text(yaml.safe_dump(index))
+ (tmp_path / index["capabilities"][0]["path"]).write_text("---\n" + source + "\n---\n")
+ result = validate(tmp_path)
+ assert result.returncode == 1
+ assert "Traceback" not in result.stderr
+ assert "error:" in result.stderr
+
+
+@pytest.mark.parametrize("rows", [None, {}, [None], [[]], [{}], [{"id": [], "path": "x"}], [{"id": "x", "path": None}]])
+def test_unvalidated_index_shape_returns_failure_without_traceback(tmp_path, rows):
+ path, index = fixture_registry(tmp_path)
+ index["capabilities"] = rows
+ path.write_text(yaml.safe_dump(index))
+ result = validate(tmp_path)
+ assert result.returncode == 1
+ assert "Traceback" not in result.stderr
+ assert "index drift:" in result.stderr

View file

@ -0,0 +1,122 @@
--- a/reuse_surface/cli.py
+++ b/reuse_surface/cli.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import argparse
+from collections import Counter
import json
import sys
from pathlib import Path
@@ -87,6 +88,7 @@
parse_vector,
registry_paths,
resolve_repo_root,
+ vectors_match,
)
@@ -120,6 +122,31 @@
warnings.append(f"index drift: entry file not indexed: {path}")
for path in sorted(indexed_paths - file_paths):
warnings.append(f"index drift: index references missing file: {path}")
+ rows = index.get("capabilities", [])
+ for field in ("id", "path"):
+ for value, count in sorted(Counter(row[field] for row in rows).items()):
+ if count > 1:
+ warnings.append(f"index drift: duplicate {field}: {value} ({count} rows)")
+ for row in rows:
+ path = repo_root / row["path"]
+ if not path.is_file():
+ continue
+ try:
+ entry = parse_front_matter(path)
+ except (ValueError, yaml.YAMLError):
+ continue # The entry validation pass reports malformed source.
+ # The index projects the owner entry; index-only changes cannot replace it.
+ for field in ("id", "name", "summary", "domain", "status", "owner", "tags"):
+ if row.get(field) != entry.get(field):
+ warnings.append(f"index drift: {row['path']}: {field} differs from entry")
+ modes = entry.get("availability", {}).get("consumption_modes")
+ if row.get("consumption_modes") != modes:
+ warnings.append(f"index drift: {row['path']}: consumption_modes differs from entry")
+ try:
+ if not vectors_match(row["vector"], entry):
+ warnings.append(f"index drift: {row['path']}: vector differs from entry")
+ except (KeyError, TypeError, AttributeError):
+ warnings.append(f"index drift: {row['path']}: vector cannot be compared to entry")
return warnings
--- /dev/null
+++ b/tests/test_index_parity.py
@@ -0,0 +1,69 @@
+"""Exercise the same strict native validator used by the existing Forgejo job."""
+from __future__ import annotations
+
+import copy
+import subprocess
+import sys
+from pathlib import Path
+
+import pytest
+import yaml
+
+ROOT = Path(__file__).resolve().parent.parent
+
+
+def fixture_registry(tmp_path):
+ index = yaml.safe_load((ROOT / "registry/indexes/capabilities.yaml").read_text())
+ row = copy.deepcopy(index["capabilities"][0])
+ source = ROOT / row["path"]
+ target = tmp_path / row["path"]
+ target.parent.mkdir(parents=True)
+ target.write_bytes(source.read_bytes())
+ index["capabilities"] = [row]
+ index_path = tmp_path / "registry/indexes/capabilities.yaml"
+ index_path.parent.mkdir(parents=True)
+ return index_path, index
+
+
+def validate(root):
+ return subprocess.run(
+ [sys.executable, "-m", "reuse_surface.cli", "validate", "--root", str(root),
+ "--fail-on-warnings"], cwd=ROOT, capture_output=True, text=True,
+ )
+
+
+@pytest.mark.parametrize("field,value", [
+ ("id", "capability.synthetic.stale"), ("name", "stale name"),
+ ("summary", "stale summary"), ("domain", "stale-domain"),
+ ("status", "retired"), ("owner", "wrong-owner"),
+ ("tags", ["stale"]), ("consumption_modes", ["stale"]),
+ ("vector", "D0 / A0 / C0 / R0"),
+])
+def test_existing_strict_validator_refuses_stale_projection(tmp_path, field, value):
+ path, index = fixture_registry(tmp_path)
+ path.write_text(yaml.safe_dump(index))
+ assert validate(tmp_path).returncode == 0
+ original_entry = (tmp_path / index["capabilities"][0]["path"]).read_bytes()
+ index["capabilities"][0][field] = value
+ path.write_text(yaml.safe_dump(index))
+ result = validate(tmp_path)
+ assert result.returncode == 1, result.stdout + result.stderr
+ assert f"{field} differs from entry" in result.stderr
+ assert (tmp_path / index["capabilities"][0]["path"]).read_bytes() == original_entry
+
+
+def test_duplicate_id_and_path_cannot_satisfy_exactly_once(tmp_path):
+ path, index = fixture_registry(tmp_path)
+ index["capabilities"].append(copy.deepcopy(index["capabilities"][0]))
+ path.write_text(yaml.safe_dump(index))
+ result = validate(tmp_path)
+ assert result.returncode == 1
+ assert "duplicate id:" in result.stderr
+ assert "duplicate path:" in result.stderr
+
+
+def test_equivalent_vector_whitespace_is_allowed(tmp_path):
+ path, index = fixture_registry(tmp_path)
+ index["capabilities"][0]["vector"] = index["capabilities"][0]["vector"].replace(" ", "")
+ path.write_text(yaml.safe_dump(index))
+ assert validate(tmp_path).returncode == 0

View file

@ -0,0 +1,91 @@
{
"repo_slug": "reuse-surface",
"repo_path": "/home/worsch/reuse-surface",
"source_revision": "01af18964b170eaa87f8560ce9285518e8dc6d7e",
"source_revision_reason": null,
"issues": [
{
"severity": "WARN",
"check_id": "C-24",
"message": "Repo classification advisory: capability_tag 'capability-registry' is not a recommended family tag (allowed, check for synonym)",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-24",
"message": "Repo classification advisory: capability_tag 'discovery' is not a recommended family tag (allowed, check for synonym)",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-24",
"message": "Repo classification advisory: capability_tag 'reuse' is not a recommended family tag (allowed, check for synonym)",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-24",
"message": "Repo classification advisory: capability_tag 'maturity' is not a recommended family tag (allowed, check for synonym)",
"file_path": "",
"db_id": "",
"file_value": "",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-22",
"message": "Task description drift 'REUSE-WP-ADHOC-2026-10-16-T01': file content differs from DB (file wins)",
"file_path": "workplans/ADHOC-2026-10-16.md#REUSE-WP-ADHOC-2026-10-16-T01",
"db_id": "63c0e025-f7aa-5849-ac2f-61128ceb7a0e",
"file_value": "The existing strict validator accepted stale index metadata and duplicate IDs/paths. Extend its existing parity warnings",
"db_value": "The existing strict validator accepted stale index metadata and duplicate IDs/paths. Extend its existing parity warnings",
"fixable": true
},
{
"severity": "WARN",
"check_id": "C-20",
"message": "Workplan dependency target 'CUST-WP-0081' is not linked to State Hub",
"file_path": "workplans/REUSE-WP-0025-private-source-reader-boundary.md",
"db_id": "",
"file_value": "CUST-WP-0081",
"db_value": "",
"fixable": false
},
{
"severity": "WARN",
"check_id": "C-39",
"message": "Wait task 'REUSE-WP-0025-T03' in workplan 'REUSE-WP-0025' has neither depends_on, needs_human: true, blocked_on: message-from:\u2026 nor wait_until \u2014 unqualified wait",
"file_path": "workplans/REUSE-WP-0025-private-source-reader-boundary.md#REUSE-WP-0025-T03",
"db_id": "",
"file_value": "wait",
"db_value": "",
"fixable": false
}
],
"fixes_applied": [
"bindings: synced 27/27",
"C-17 cleared: pushed 1 backlogged commit(s)",
"C-22 fixed: task 63c0e025\u2026 description updated",
"push: pushed"
],
"summary": {
"fail": 0,
"automation_error": 0,
"warn": 7,
"info": 0
},
"result": "warn"
}

View file

@ -0,0 +1,29 @@
{
"commit": "662782e65ade239e5194e16f4c4a902c792d8d44",
"status": "",
"original_cli_sha256": "da968735cb45dc9944c3a722234641e17cd49a6bef9aa91f40e78ddde3fb9c0e",
"candidate_cli_sha256": "753abc928d63665df34b346e26386dcdc3617570d7eb979cb65fbdde2bd13efa",
"adopted_consumer": ".forgejo/workflows/ci.yml:37 (mandatory validate --relations --fail-on-warnings)",
"new_tests": 11,
"full_suite_initial": {
"passed": 237,
"failed": 1,
"reason": "test_federation_compose requires sibling indexes; isolation moved relative sibling root from /home/worsch to /tmp"
},
"full_suite_followup": {
"command": "python3 -m pytest -q tests/test_registry.py::test_federation_compose",
"passed": 1,
"failed": 0,
"temporary_fixture_adaptation": "Only temporary registry/federation/sources.yaml index paths resolved against original owner checkout; source patch has no manifest change. This fixes missing siblings after staging in /tmp. All 238 tests have passed across the full run and this targeted rerun."
},
"historical_preparation_only": true,
"current_integration": {
"receipt": "integration-r2.json",
"canonical_source": "dd00b27b4ce77180b01c5756c2a3841819fcac76",
"new_cli_tests": 23,
"root_independent_checks_passed": 30,
"publication": "normal consistency workflow completed",
"ci": "unverified; public actions API404",
"hosted": "root fresh readback19:25:11 matches both corrected owners"
}
}

View file

@ -0,0 +1,802 @@
{
"repositories": 60,
"reviewed_ids": 70,
"reviewed_exactly_once": 70,
"all_provider_index_rows": 71,
"errors": 0,
"warnings": 3,
"checks": [
{
"owner": "access-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "activity-core",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "agentic-resources",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "approval-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "audit-core",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "coordination-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "emergent-security",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "fin-hub",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "gate-house",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "glas-harness",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "info-tech-canon",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "informed-decision",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "issue-core",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "key-cape",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "kings-guard",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "maturity-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "net-kingdom",
"entry_count": 6,
"errors": [],
"warnings": [
"index drift: registry/capabilities/capability.security.bootstrap-recovery.md: summary differs from entry"
]
},
{
"owner": "ops-bridge",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "ops-mason",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "ops-warden",
"entry_count": 3,
"errors": [],
"warnings": []
},
{
"owner": "policy-nexus",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rail-knative",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rail-kubernetes",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "railiance-apps",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "railiance-bootstrap",
"entry_count": 1,
"errors": [],
"warnings": [
"index drift: registry/capabilities/capability.railiance.bootstrap-sequencing.md: summary differs from entry",
"index drift: registry/capabilities/capability.railiance.bootstrap-sequencing.md: tags differs from entry"
]
},
{
"owner": "railiance-cluster",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "railiance-enablement",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "railiance-fabric",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "railiance-forge",
"entry_count": 2,
"errors": [],
"warnings": []
},
{
"owner": "railiance-infra",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "railiance-master",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "railiance-platform",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "railiance-telemetry",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rapp-openbao",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rapp-playwright",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rapp-policy-nexus",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rapp-postgres",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rapp-sbom-nexus",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rapp-telemetry",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rapp-tenant-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rapp-user-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "reef-railiance",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rein-aharness",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "rein-openweights",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "repo-manager",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "resource-control",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "reuse-surface",
"entry_count": 3,
"errors": [],
"warnings": []
},
{
"owner": "risk-nexus",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "role-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "runbook-tutorials",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "sand-boxer",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "sbom-nexus",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "secrets-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "security-canon",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "state-hub",
"entry_count": 2,
"errors": [],
"warnings": []
},
{
"owner": "tenant-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "the-custodian",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "user-engine",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "whitehat-security",
"entry_count": 1,
"errors": [],
"warnings": []
},
{
"owner": "zone-engine",
"entry_count": 1,
"errors": [],
"warnings": []
}
],
"proof_boundary": "Current local registry source only; warnings retain 3 field mismatches across 2 capabilities. Additional owner entries are outside the reviewed denominator. No hosted check or runtime acceptance.",
"corrected_temporary_owner_projection": {
"errors": 0,
"warnings": 0,
"checks": [
{
"owner": "access-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/access-engine"
},
{
"owner": "activity-core",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/activity-core"
},
{
"owner": "agentic-resources",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/agentic-resources"
},
{
"owner": "approval-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/approval-engine"
},
{
"owner": "audit-core",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/audit-core"
},
{
"owner": "coordination-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/coordination-engine"
},
{
"owner": "emergent-security",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/emergent-security"
},
{
"owner": "fin-hub",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/fin-hub"
},
{
"owner": "gate-house",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/gate-house"
},
{
"owner": "glas-harness",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/glas-harness"
},
{
"owner": "info-tech-canon",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/info-tech-canon"
},
{
"owner": "informed-decision",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/informed-decision"
},
{
"owner": "issue-core",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/issue-core"
},
{
"owner": "key-cape",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/key-cape"
},
{
"owner": "kings-guard",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/kings-guard"
},
{
"owner": "maturity-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/maturity-engine"
},
{
"owner": "net-kingdom",
"entry_count": 6,
"errors": [],
"warnings": [],
"root": "/tmp/netkingdom-improvement-loop-next-owners/net-kingdom"
},
{
"owner": "ops-bridge",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/ops-bridge"
},
{
"owner": "ops-mason",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/ops-mason"
},
{
"owner": "ops-warden",
"entry_count": 3,
"errors": [],
"warnings": [],
"root": "/home/worsch/ops-warden"
},
{
"owner": "policy-nexus",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/policy-nexus"
},
{
"owner": "rail-knative",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rail-knative"
},
{
"owner": "rail-kubernetes",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rail-kubernetes"
},
{
"owner": "railiance-apps",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-apps"
},
{
"owner": "railiance-bootstrap",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/tmp/netkingdom-improvement-loop-next-owners/railiance-bootstrap"
},
{
"owner": "railiance-cluster",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-cluster"
},
{
"owner": "railiance-enablement",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-enablement"
},
{
"owner": "railiance-fabric",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-fabric"
},
{
"owner": "railiance-forge",
"entry_count": 2,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-forge"
},
{
"owner": "railiance-infra",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-infra"
},
{
"owner": "railiance-master",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-master"
},
{
"owner": "railiance-platform",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-platform"
},
{
"owner": "railiance-telemetry",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/railiance-telemetry"
},
{
"owner": "rapp-openbao",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rapp-openbao"
},
{
"owner": "rapp-playwright",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rapp-playwright"
},
{
"owner": "rapp-policy-nexus",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rapp-policy-nexus"
},
{
"owner": "rapp-postgres",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rapp-postgres"
},
{
"owner": "rapp-sbom-nexus",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rapp-sbom-nexus"
},
{
"owner": "rapp-telemetry",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rapp-telemetry"
},
{
"owner": "rapp-tenant-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rapp-tenant-engine"
},
{
"owner": "rapp-user-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rapp-user-engine"
},
{
"owner": "reef-railiance",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/reef-railiance"
},
{
"owner": "rein-aharness",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rein-aharness"
},
{
"owner": "rein-openweights",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/rein-openweights"
},
{
"owner": "repo-manager",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/repo-manager"
},
{
"owner": "resource-control",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/resource-control"
},
{
"owner": "reuse-surface",
"entry_count": 3,
"errors": [],
"warnings": [],
"root": "/home/worsch/reuse-surface"
},
{
"owner": "risk-nexus",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/risk-nexus"
},
{
"owner": "role-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/role-engine"
},
{
"owner": "runbook-tutorials",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/runbook-tutorials"
},
{
"owner": "sand-boxer",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/sand-boxer"
},
{
"owner": "sbom-nexus",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/sbom-nexus"
},
{
"owner": "secrets-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/secrets-engine"
},
{
"owner": "security-canon",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/security-canon"
},
{
"owner": "state-hub",
"entry_count": 2,
"errors": [],
"warnings": [],
"root": "/home/worsch/state-hub"
},
{
"owner": "tenant-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/tenant-engine"
},
{
"owner": "the-custodian",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/the-custodian"
},
{
"owner": "user-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/user-engine"
},
{
"owner": "whitehat-security",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/whitehat-security"
},
{
"owner": "zone-engine",
"entry_count": 1,
"errors": [],
"warnings": [],
"root": "/home/worsch/zone-engine"
}
]
}
}

View file

@ -0,0 +1,40 @@
{
"scope": "Source-only observation, 2026-10-16",
"activation": {
"definition_enabled": false,
"activity_task": "ACTIVITY-WP-0047-T02",
"blocking_seam": "Claiming rein workstation checkout and hub acceptance, then Activity owner release/admin-sync/manual trigger. No such acceptance in owner record.",
"rein_record": "REINAH-WP-0008 says candidate818b1b7c not installed/admitted; startup receipt has model_run_proven:false. Existing owner memo/native execution gates remain."
},
"limits": "No requirement is invented that the unrelated task-scoped protected operation must complete first. The actual accepted schedule depends on its selected rein/profile, not a newly substituted scheduler.",
"sources": {
"activity-core/activity-definitions/daily-security-flow.md": {
"sha256": "0281f0cd818b02d1cbd3a732dfb72145e8cb5e9881c4d80c6f6eb3d664da9375",
"owner_head": "f9f8df039258a0d8b6fc29dc1f7c4bcc75fdc8c0"
},
"activity-core/workplans/ACTIVITY-WP-0047-daily-security-flow-adoption.md": {
"sha256": "e0b83b570da688abb405bed165d81db7cf49bc7ff11ef9a8ad86cbee2dc651db",
"owner_head": "f9f8df039258a0d8b6fc29dc1f7c4bcc75fdc8c0"
},
"rein-aharness/workplans/REINAH-WP-0008-simpler-metered-retry.md": {
"sha256": "3f46cf1f278e35be994772cb2d7e6d95f570fb5bfba28c6b55dc89b663d25f89",
"owner_head": "be5ef400861a5e31a31f29d173990c7a143fb189"
},
"rein-aharness/docs/evidence/2026-10-15-rebuilt-owner-runtime.json": {
"sha256": "2c9001de6eda33a0e99aa7321c7401b9997e583eeaa557bf656b5252e68e3418",
"owner_head": "be5ef400861a5e31a31f29d173990c7a143fb189"
},
"kings-guard/docs/PostureMaturityBoundary.md": {
"sha256": "6bfb15b4d50297fd0c1fa91eda94143b85df537d2d777f8ae86774bd30bc412a",
"owner_head": "09ada1d98100d4f3c6aa6e94e8ec5f41b7465c3b"
},
"maturity-engine/workplans/MAT-WP-0001-netkingdom-security-layer-engine.md": {
"sha256": "bdb6b10162793cb80e45fa228b1d5e6f441eddbcc1a8d45680af4ac08f3b7a81",
"owner_head": "2f1de542cc8f24a79452f906d99f95be39d8b6a9"
},
"coordination-engine/config/coordination-frame.json": {
"sha256": "d15e092387f128ffdfd00b9815707a05ff77c5bb0ba4d9a428fbc37b8c440b67",
"owner_head": "f658556690f3f6b7efe9c95a445222019182e402"
}
}
}

View file

@ -0,0 +1,24 @@
from pathlib import Path
import subprocess,hashlib,json,datetime
ROOT=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/protected-execution-trusted-successor')
REPO=Path('/home/worsch/railiance-platform')
manifest=json.loads((ROOT/'budget-source-manifest-r1.json').read_text())
patch=ROOT/'budget-source-slice-r1.patch'
assert hashlib.sha256(patch.read_bytes()).hexdigest()==manifest['patch_sha256']
base=subprocess.check_output(['git','rev-parse','HEAD'],cwd=REPO,text=True).strip()
assert base==manifest['canonical_base_full'], 'canonical HEAD changed; re-review before mutation'
for name,expected in manifest['files'].items():
path=REPO/name
actual=hashlib.sha256(path.read_bytes()).hexdigest() if path.exists() else None
assert actual==expected['before_sha256'], name
subprocess.run(['git','apply','--check',str(patch)],cwd=REPO,check=True)
subprocess.run(['git','apply',str(patch)],cwd=REPO,check=True)
files={}
for name,expected in manifest['files'].items():
digest=hashlib.sha256((REPO/name).read_bytes()).hexdigest()
assert digest==expected['after_sha256'], name
files[name]='sha256:'+digest
subprocess.run(['git','diff','--check','--',*files],cwd=REPO,check=True)
receipt={'repo':'railiance-platform','base_commit':base,'patch_sha256':'sha256:'+manifest['patch_sha256'],'integrated_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'files_sha256':files,'source_integrated':True,'committed':False,'published':False,'native_accepted':False,'review_scope':'fixed Unix native fixture budget and exact v2 source byte guard; full provider/native proof pending'}
(ROOT/'canonical-integration.json').write_text(json.dumps(receipt,indent=2)+'\n')
print('Integrated five exact source files; unrelated WORK-RECORDS preserved. Commit/publication pending.')

View file

@ -0,0 +1,11 @@
"""Integrate exact reviewed recovery source; no native custody or grants."""
import integrate_source_wave5 as integration
integration.TARGETS = (
('railiance-platform', 'protected-execution-recovery', 'implementation-r2.patch',
'61f516c876fd612b83300e9913f0e80aad836a50014310797913c7c4505e3114',
'd870596cc72f74a5762cce2400802e2b351e903e', 'coordinator-files-r2.json', 'files'),
)
if __name__ == '__main__':
integration.main()

View file

@ -0,0 +1,52 @@
"""Apply only the two reviewed source patches; no commit, sync or live action."""
from pathlib import Path
import hashlib
import json
import subprocess
from datetime import datetime, timezone
ROOT = Path(__file__).resolve().parent
TARGETS = (
("railiance-platform", "protected-execution", "platform-cleanup.patch",
"be8de4975980e4b88b37ed98d3fdd509d513f85834efc3a5cb744410c4e95392"),
("net-kingdom", "browser-case", "implementation.patch",
"d32a7f51cbf3012bee3e4bb329f4259dea867eff1c59a2b01a65d72774488d05"),
)
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def main():
prepared = []
for repo, part, patch_name, expected_patch in TARGETS:
checkout = Path("/home/worsch") / repo
patch = ROOT / part / patch_name
manifest = json.loads((patch.parent / "manifest.json").read_text())
if digest(patch) != expected_patch:
raise SystemExit(f"reviewed patch changed: {repo}")
head = subprocess.check_output(["git", "-C", str(checkout), "rev-parse", "HEAD"], text=True).strip()
if head != manifest["base_commit"]:
raise SystemExit(f"owner revision changed: {repo}")
files = manifest.get("files_sha256", manifest.get("files"))
dirty = subprocess.check_output(["git", "-C", str(checkout), "diff", "HEAD", "--", *files])
if dirty:
raise SystemExit(f"targeted owner files contain independent changes: {repo}")
subprocess.run(["git", "-C", str(checkout), "apply", "--check", str(patch)], check=True)
prepared.append((repo, checkout, patch, files, expected_patch, head))
for repo, checkout, patch, files, patch_hash, head in prepared:
subprocess.run(["git", "-C", str(checkout), "apply", str(patch)], check=True)
for name, expected in files.items():
if digest(checkout / name) != expected.removeprefix("sha256:"):
raise SystemExit(f"integrated file differs: {repo}/{name}")
receipt = {"repo": repo, "base_commit": head, "patch_sha256": patch_hash,
"integrated_at": datetime.now(timezone.utc).isoformat(),
"files_sha256": files, "source_integrated": True,
"committed": False, "published": False, "native_accepted": False}
(patch.parent / "canonical-integration.json").write_text(json.dumps(receipt, indent=2) + "\n")
print(f"{repo}: exact reviewed source integrated ({len(files)} files)")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,56 @@
"""Integrate three exact reviewed source patches. No commit, deploy or admission."""
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
ROOT = Path(__file__).resolve().parent
TARGETS = (
("glas-harness", "agent-isolation", "glas-harness.patch",
"83eeeb0017603aabae0ff49a23f96248f677f6c9f3e2c01b22f6d0416fca69c7",
"dc88f302044c7c2b41795958527e64be8a83e3ab"),
("railiance-platform", "protected-execution-wave2", "platform-native-fixture.patch",
"c419ac0f6dba3e36f15dc51a6a36fbd87fe7d255cce40c33c7f4589d80355580",
"69a792c070238273acd9a989e2707072a5ef55ed"),
("secrets-engine", "decision-auth", "implementation-r2.patch",
"98c4300c42fe93759a55297fe6629f1d4aeed249c3625f7fd37c8ab523a08ca9",
"6bd7129eac94131b4cf689e47cbdc9899418beb3"),
)
def main():
prepared = []
for repo, part, patch_name, expected_patch, expected_head in TARGETS:
owner, folder = Path("/home/worsch") / repo, ROOT / part
patch = folder / patch_name
assert hashlib.sha256(patch.read_bytes()).hexdigest() == expected_patch, repo
head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=owner, text=True).strip()
assert head == expected_head, f"owner revision changed: {repo}"
if repo == "glas-harness":
manifest = json.loads((folder / "source-manifest.json").read_text())
files = {item["path"]: item["candidate_sha256"] for item in manifest["changes"]}
elif repo == "railiance-platform":
files = json.loads((folder / "manifest.json").read_text())["files_sha256"]
else:
files = json.loads((folder / "reviewed-r2-file-hashes.json").read_text())
assert not subprocess.check_output(["git", "diff", "HEAD", "--", *files], cwd=owner), f"target files dirty: {repo}"
for name in files:
tracked = subprocess.run(["git", "ls-files", "--error-unmatch", name], cwd=owner, capture_output=True).returncode == 0
assert tracked or not (owner / name).exists(), f"untracked target collision: {repo}/{name}"
subprocess.run(["git", "apply", "--check", str(patch)], cwd=owner, check=True)
prepared.append((repo, owner, folder, patch, expected_patch, head, files))
for repo, owner, folder, patch, patch_hash, head, files in prepared:
subprocess.run(["git", "apply", str(patch)], cwd=owner, check=True)
for name, expected in files.items():
assert hashlib.sha256((owner / name).read_bytes()).hexdigest() == expected, f"integrated file differs: {repo}/{name}"
receipt = {"repo": repo, "base_commit": head, "patch_sha256": patch_hash,
"integrated_at": datetime.now(timezone.utc).isoformat(), "files_sha256": files,
"source_integrated": True, "committed": False, "published": False,
"native_accepted": False}
(folder / "canonical-integration.json").write_text(json.dumps(receipt, indent=2) + "\n")
print(f"{repo}: {len(files)} exact reviewed source files integrated")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,68 @@
"""Integrate reviewed source only; preserve all unrelated owner edits."""
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
ROOT = Path(__file__).resolve().parent
TARGETS = (
("approval-engine", "approval-reference", "implementation.patch",
"a6bf77fa8b6916f423db7832f475e38747b828dd632a88f9631131e28e3230ee",
"9491d1d3a7b6554fa04f8dc06f65d50e3d6ee4e1", "files"),
("ops-mason", "mason-browser", "implementation.patch",
"cfe7648ed119d83faca228a2f21611c9388de1ba166598cbeb64f40e81620067",
"e4761b843cd881a672cbb33a5b451de31a61d730", "changed_source"),
("railiance-platform", "protected-execution-wave3", "platform-grant-projection.patch",
"d91fdbd68b328d430e0e2b0b7f44d552095b8688ae4942d398d2e126b3648ad5",
"4021ca5e79d3f85197b26cc20d29c55771462cf1", "files_sha256"),
("access-engine", "access-verifier-review", "producer-signing-r2.patch",
"25e05b04f96cfe6f7d1abb11ab5d533b14a830c437136fc78aa381198ff8db37",
"62ac2b4e99dec2005c7c407fe633612578a7907d", "source_files"),
)
def main():
prepared = []
for repo, part, filename, expected, base, files_key in TARGETS:
owner, folder = Path("/home/worsch") / repo, ROOT / part
patch = folder / filename
assert hashlib.sha256(patch.read_bytes()).hexdigest() == expected, repo
head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=owner, text=True).strip()
assert head == base, f"owner revision changed: {repo}"
manifest = "manifest-r2.json" if repo == "access-engine" else "manifest.json"
files = json.loads((folder / manifest).read_text())[files_key]
assert not subprocess.check_output(["git", "diff", "HEAD", "--", *files], cwd=owner), f"target files dirty: {repo}"
for name in files:
tracked = subprocess.run(["git", "ls-files", "--error-unmatch", name], cwd=owner, capture_output=True).returncode == 0
assert tracked or not (owner / name).exists(), f"untracked target collision: {repo}/{name}"
subprocess.run(["git", "apply", "--check", str(patch)], cwd=owner, check=True)
prepared.append((repo, owner, folder, patch, expected, head, files))
# Secrets R2 is deliberately still uncommitted after two public-ID scanner
# false positives. Apply only the reviewed naming delta to those exact bytes.
owner, folder = Path("/home/worsch/secrets-engine"), ROOT / "decision-auth"
manifest = json.loads((folder / "integration-manifest-r3.json").read_text())
patch = folder / "implementation-r3-delta.patch"
expected = "b6c944fee615298a29dbd50ba0ad437ff76e6eab190f6168427e4ae5dbbfc59c"
assert hashlib.sha256(patch.read_bytes()).hexdigest() == expected
for name, hashes in manifest["files"].items():
assert hashlib.sha256((owner / name).read_bytes()).hexdigest() == hashes["r2_sha256"], name
subprocess.run(["git", "apply", "--check", str(patch)], cwd=owner, check=True)
head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=owner, text=True).strip()
prepared.append(("secrets-engine", owner, folder, patch, expected, head,
{name: hashes["r3_sha256"] for name, hashes in manifest["files"].items()}))
for repo, owner, folder, patch, patch_hash, head, files in prepared:
subprocess.run(["git", "apply", str(patch)], cwd=owner, check=True)
for name, expected in files.items():
assert hashlib.sha256((owner / name).read_bytes()).hexdigest() == expected.removeprefix("sha256:"), name
receipt = {"repo": repo, "base_commit": head, "patch_sha256": patch_hash,
"integrated_at": datetime.now(timezone.utc).isoformat(), "files_sha256": files,
"source_integrated": True, "committed": False, "published": False,
"native_accepted": False}
name = "canonical-integration-r3.json" if repo == "secrets-engine" else "canonical-integration.json"
(folder / name).write_text(json.dumps(receipt, indent=2) + "\n")
print(f"{repo}: {len(files)} exact reviewed source files integrated")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,54 @@
"""Apply four reviewed source packages; no install, credentials or live effects."""
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
ROOT = Path(__file__).resolve().parent
TARGETS = (
('secrets-engine', 'decision-package', 'implementation-r1.patch',
'86179e607509d782f8a00012174c549997e0997ce4f84fc1078d245e4670a4ae',
'c80a944105a00220cd3c1a007709e72d830dd08d', 'coordinator-file-hashes.json', None),
('secrets-engine', 'decision-handoff', 'implementation-r2.patch',
'1ef8b67e2352415ddbdadc86cd151a8b6caf76628350657ed3e674aefcf49945',
'c80a944105a00220cd3c1a007709e72d830dd08d', 'integration-manifest-r2.json', 'files'),
('railiance-platform', 'protected-execution-wave4', 'platform-confirmation-seam.patch',
'f26a5e1f63f68c487930d3f076502f1bae73d16107d3be2537aeeda56e73628e',
'd042d468df6d4085bb93a73ec26f4c4103554575', 'manifest.json', 'files_sha256'),
('approval-engine', 'approval-confirmation', 'implementation.patch',
'a2dfa6585a8b97fc6facec7b0815ffc3374869b8de51cc696d534c69643e3621',
'8624fa20bbfb0c0329e5327b4dd8d8c05a312d7a', 'manifest.json', 'files'),
)
def main():
prepared = []
for repo, part, filename, expected, base, manifest_name, files_key in TARGETS:
owner, folder = Path('/home/worsch') / repo, ROOT / part
patch = folder / filename
assert hashlib.sha256(patch.read_bytes()).hexdigest() == expected, repo
head = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=owner, text=True).strip()
assert head == base, f'owner HEAD changed: {repo}'
manifest = json.loads((folder / manifest_name).read_text())
files = manifest[files_key] if files_key else manifest
assert not subprocess.check_output(['git', 'diff', 'HEAD', '--', *files], cwd=owner), f'target files dirty: {repo}'
for name in files:
tracked = subprocess.run(['git', 'ls-files', '--error-unmatch', name], cwd=owner, capture_output=True).returncode == 0
assert tracked or not (owner / name).exists(), f'untracked target collision: {repo}/{name}'
subprocess.run(['git', 'apply', '--check', str(patch)], cwd=owner, check=True)
prepared.append((repo, owner, folder, patch, expected, head, files))
for repo, owner, folder, patch, patch_hash, head, files in prepared:
subprocess.run(['git', 'apply', str(patch)], cwd=owner, check=True)
for name, expected in files.items():
assert hashlib.sha256((owner / name).read_bytes()).hexdigest() == expected, name
receipt = {'repo': repo, 'base_commit': head, 'patch_sha256': patch_hash,
'integrated_at': datetime.now(timezone.utc).isoformat(), 'files_sha256': files,
'source_integrated': True, 'committed': False, 'published': False,
'native_accepted': False}
(folder / 'canonical-integration.json').write_text(json.dumps(receipt, indent=2) + '\n')
print(f'{repo}/{folder.name}: {len(files)} reviewed files integrated')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,64 @@
"""Integrate three reviewed source packages; never deploy or admit credentials."""
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
ROOT = Path(__file__).resolve().parent
TARGETS = (
('railiance-platform', 'protected-execution-wave5', 'platform-wave5.patch',
'b4c44a452a1b62d0a2c114e2de60537666a139a387c478c663236b3aacdaadec',
'5f30ddd90c33e7eb1b9efa1093c7324d83210cf2', 'manifest.json', 'files'),
('access-engine', 'access-native-source', 'source-preparation-r2.patch',
'cee7af8e1e8ee8b63f9d5bc5927a6d89ae6c1169b3ec23f59e0958664fc91dca',
'ec21518e25df2a58444f19ff3413ce0b83383d3c', 'manifest-r2.json', 'source_files_sha256'),
('secrets-engine', 'decision-native-preparation', 'implementation-r1.patch',
'316daaba4afa309b2f13acf995f781d9fae1ea84e12837a8b494e31b8489dd46',
'ff1a13dabd44ba02ddd6bc26ad75568f6d2e6225', 'integration-manifest-r1.json', 'files'),
)
def digest(path):
return hashlib.sha256(path.read_bytes()).hexdigest() if path.exists() else None
def main():
prepared = []
for repo, part, filename, patch_hash, head, manifest_name, key in TARGETS:
owner, folder = Path('/home/worsch') / repo, ROOT / part
assert not (folder / 'canonical-integration.json').exists(), 'already integrated'
patch = folder / filename
assert digest(patch) == patch_hash, repo
assert subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=owner, text=True).strip() == head, repo
files = json.loads((folder / manifest_name).read_text())[key]
assert not subprocess.check_output(['git', 'diff', 'HEAD', '--', *files], cwd=owner), repo
after = {}
before = {}
for name, expected in files.items():
assert not Path(name).is_absolute() and '..' not in Path(name).parts
tracked = subprocess.run(['git', 'ls-files', '--error-unmatch', name], cwd=owner, capture_output=True).returncode == 0
assert tracked or not (owner / name).exists(), name
before[name] = digest(owner / name)
if isinstance(expected, dict):
assert before[name] == expected['before_sha256'], name
expected = expected['after_sha256']
after[name] = expected
subprocess.run(['git', 'apply', '--check', str(patch)], cwd=owner, check=True)
prepared.append((repo, owner, folder, patch, patch_hash, head, before, after))
for repo, owner, folder, patch, patch_hash, head, before, after in prepared:
assert subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=owner, text=True).strip() == head
assert all(digest(owner / name) == value for name, value in before.items())
subprocess.run(['git', 'apply', str(patch)], cwd=owner, check=True)
assert all(digest(owner / name) == value for name, value in after.items())
receipt = {'repo': repo, 'base_commit': head, 'patch_sha256': 'sha256:' + patch_hash,
'integrated_at': datetime.now(timezone.utc).isoformat(),
'files_sha256': {name: 'sha256:' + value for name, value in after.items()},
'source_integrated': True, 'committed': False, 'published': False,
'native_accepted': False}
(folder / 'canonical-integration.json').write_text(json.dumps(receipt, indent=2) + '\n')
print(f'{repo}: {len(after)} reviewed source files integrated')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,72 @@
"""Apply independently reviewed Approval adapter and Access B1 R2 source."""
import integrate_source_wave5 as integration
import hashlib
import json
from pathlib import Path
import subprocess
from datetime import datetime, timezone
integration.TARGETS = (
('approval-engine', 'approval-go-fact', 'implementation.patch',
'096de94db8afff6b429215d951a98b94cd501cc6a7e1c374420e546169df2179',
'eece796b8bbee19551f6aa07ad389cbf83cabcdb', 'manifest.json', 'files'),
('access-engine', 'access-native-source/b1', 'b1-r2-over-native-prep-r2.patch',
'8880eb0f693f6793cfa6b3ea6f2466dbe67313e521a7f79c30180ca4880609b9',
'919ce25a377300ef9749c85e41b608467bed02ad', 'manifest-r2.json', 'source_files_sha256'),
)
def main():
prepared = []
wp = 'workplans/FLEX-WP-0024-decision-envelope-authenticity.md'
managed = {'FLEX-WP-0024-T05': 'cfe4d275-9f02-5090-8391-06eea59d45c5',
'FLEX-WP-0024-T06': '7f785b52-bf62-5884-8d34-670d70bc9023'}
for repo, part, filename, patch_hash, head, manifest_name, key in integration.TARGETS:
owner, folder = Path('/home/worsch') / repo, integration.ROOT / part
patch = folder / filename
assert not (folder / 'canonical-integration.json').exists()
assert integration.digest(patch) == patch_hash
assert subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=owner, text=True).strip() == head
files = json.loads((folder / manifest_name).read_text())[key]
before = {name: integration.digest(owner / name) for name in files}
for name in files:
tracked = subprocess.run(['git', 'ls-files', '--error-unmatch', name], cwd=owner, capture_output=True).returncode == 0
assert tracked or not (owner / name).exists(), name
if tracked:
original = subprocess.check_output(['git', 'show', 'HEAD:' + name], cwd=owner)
current = (owner / name).read_bytes()
if repo == 'access-engine' and name == wp:
without_ids = current
for value in managed.values():
line = f'state_hub_task_id: "{value}"\n'.encode()
assert without_ids.count(line) == 1
without_ids = without_ids.replace(line, b'')
assert without_ids == original, 'only verified registrar IDs may differ'
source = Path('/tmp/netkingdom-target-root-review/access-b1') / name
reviewed = source.read_bytes()
assert hashlib.sha256(reviewed).hexdigest() == files[name]
for task, value in managed.items():
anchor = ('id: ' + task + '\nstatus: ' + ('done' if task.endswith('T05') else 'progress') + '\npriority: high\n').encode()
assert reviewed.count(anchor) == 1
reviewed = reviewed.replace(anchor, anchor + f'state_hub_task_id: "{value}"\n'.encode())
files[name] = hashlib.sha256(reviewed).hexdigest()
else:
assert current == original, name
subprocess.run(['git', 'apply', '--check', str(patch)], cwd=owner, check=True)
prepared.append((repo, owner, folder, patch, patch_hash, head, before, files))
for repo, owner, folder, patch, patch_hash, head, before, files in prepared:
assert subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=owner, text=True).strip() == head
assert all(integration.digest(owner / name) == value for name, value in before.items())
subprocess.run(['git', 'apply', str(patch)], cwd=owner, check=True)
assert all(integration.digest(owner / name) == value for name, value in files.items())
receipt = {'repo': repo, 'base_commit': head, 'patch_sha256': 'sha256:' + patch_hash,
'integrated_at': datetime.now(timezone.utc).isoformat(),
'files_sha256': {name: 'sha256:' + value for name, value in files.items()},
'source_integrated': True, 'committed': False, 'published': False,
'native_accepted': False,
'preserved_registrar_ids': managed if repo == 'access-engine' else {}}
(folder / 'canonical-integration.json').write_text(json.dumps(receipt, indent=2) + '\n')
print(repo, 'reviewed source integrated; native acceptance unchanged')
if __name__ == '__main__':
main()

View file

@ -0,0 +1,61 @@
"""Apply exactly reviewed source; never deploy or confer runtime authority."""
from pathlib import Path
import argparse
import datetime
import hashlib
import json
import shutil
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent
PLAN = json.loads((ROOT / 'source-wave7-integration-plan.json').read_text())
def git(repo, *args):
return subprocess.check_output(['git', *args], cwd=repo, text=True).strip()
def digest(path):
return 'sha256:' + hashlib.sha256(path.read_bytes()).hexdigest()
def apply(repo, step):
for patch in step['patches']:
path = ROOT / patch['path']
assert digest(path) == 'sha256:' + patch['sha256']
subprocess.run(['git', 'apply', '--check', str(path)], cwd=repo, check=True)
subprocess.run(['git', 'apply', str(path)], cwd=repo, check=True)
for name, expected in step['files_sha256'].items():
assert digest(repo / name) == expected, name
parser = argparse.ArgumentParser()
parser.add_argument('--apply', action='store_true')
args = parser.parse_args()
# Check every base and target before any owner mutation. Reconstruct exact final
# source independently in disposable directories, including dependent patches.
for step in PLAN:
repo = Path('/home/worsch') / step['repo']
assert git(repo, 'rev-parse', 'HEAD') == step['base'], step['repo']
assert not git(repo, 'status', '--porcelain', '--', *step['files_sha256']), step['repo']
with tempfile.TemporaryDirectory(prefix='netkingdom-wave7-preflight-') as temp:
target = Path(temp)
for name in step['files_sha256']:
source = repo / name
if source.exists():
assert source.is_file() and not source.is_symlink(), name
(target / name).parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target / name)
apply(target, step)
print('Preflight passed:', step['repo'], flush=True)
if args.apply:
for step in PLAN:
repo = Path('/home/worsch') / step['repo']
assert git(repo, 'rev-parse', 'HEAD') == step['base']
assert not git(repo, 'status', '--porcelain', '--', *step['files_sha256'])
apply(repo, step)
subprocess.run(['git', 'diff', '--check', '--', *step['files_sha256']], cwd=repo, check=True)
receipt = dict(step, integrated_at=datetime.datetime.now(datetime.timezone.utc).isoformat(),
source_integrated=True, committed=False, published=False,
native_accepted=False, scope='Reviewed source only; native custody and admission remain open')
for patch in receipt['patches']:
patch['sha256'] = 'sha256:' + patch['sha256']
(ROOT / step['receipt']).write_text(json.dumps(receipt, indent=2) + '\n')
print('Integrated exact source:', step['repo'], flush=True)

View file

@ -0,0 +1,61 @@
"""Apply exactly reviewed source; never deploy or confer runtime authority."""
from pathlib import Path
import argparse
import datetime
import hashlib
import json
import shutil
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parent
PLAN = json.loads((ROOT / 'source-wave8-integration-plan.json').read_text())
def git(repo, *args):
return subprocess.check_output(['git', *args], cwd=repo, text=True).strip()
def digest(path):
return 'sha256:' + hashlib.sha256(path.read_bytes()).hexdigest()
def apply(repo, step):
for patch in step['patches']:
path = ROOT / patch['path']
assert digest(path) == 'sha256:' + patch['sha256']
subprocess.run(['git', 'apply', '--check', str(path)], cwd=repo, check=True)
subprocess.run(['git', 'apply', str(path)], cwd=repo, check=True)
for name, expected in step['files_sha256'].items():
assert digest(repo / name) == expected, name
parser = argparse.ArgumentParser()
parser.add_argument('--apply', action='store_true')
args = parser.parse_args()
# Check every base and target before any owner mutation. Reconstruct exact final
# source independently in disposable directories, including dependent patches.
for step in PLAN:
repo = Path('/home/worsch') / step['repo']
assert git(repo, 'rev-parse', 'HEAD') == step['base'], step['repo']
assert all((digest(repo / f) if (repo / f).exists() else None) == h for f, h in step['before_sha256'].items()), step['repo']
with tempfile.TemporaryDirectory(prefix='netkingdom-wave8-preflight-') as temp:
target = Path(temp)
for name in step['files_sha256']:
source = repo / name
if source.exists():
assert source.is_file() and not source.is_symlink(), name
(target / name).parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(source, target / name)
apply(target, step)
print('Preflight passed:', step['repo'], flush=True)
if args.apply:
for step in PLAN:
repo = Path('/home/worsch') / step['repo']
assert git(repo, 'rev-parse', 'HEAD') == step['base']
assert all((digest(repo / f) if (repo / f).exists() else None) == h for f, h in step['before_sha256'].items()), step['repo']
apply(repo, step)
subprocess.run(['git', 'diff', '--check', '--', *step['files_sha256']], cwd=repo, check=True)
receipt = dict(step, integrated_at=datetime.datetime.now(datetime.timezone.utc).isoformat(),
source_integrated=True, committed=False, published=False,
native_accepted=False, scope='Reviewed source only; native custody and admission remain open')
for patch in receipt['patches']:
patch['sha256'] = 'sha256:' + patch['sha256']
(ROOT / step['receipt']).write_text(json.dumps(receipt, indent=2) + '\n')
print('Integrated exact source:', step['repo'], flush=True)

View file

@ -0,0 +1,37 @@
"""Integrate the reviewed Warden source revision; no live or trust changes."""
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import subprocess
ROOT = Path(__file__).resolve().parent / "warden-browser"
OWNER = Path("/home/worsch/ops-warden")
EXPECTED = "1fb05b513c4db784f8691b5bc2790f5ca396071d4354615d2da7fc505c45684b"
def main():
manifest = json.loads((ROOT / "manifest.json").read_text())
patch = ROOT / "implementation.patch"
assert hashlib.sha256(patch.read_bytes()).hexdigest() == EXPECTED
head = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=OWNER, text=True).strip()
assert head == manifest["base_commit"], "owner HEAD changed"
files = manifest["files"]
assert not subprocess.check_output(["git", "diff", "HEAD", "--", *files], cwd=OWNER), "target files changed"
for name in files:
tracked = subprocess.run(["git", "ls-files", "--error-unmatch", name], cwd=OWNER, capture_output=True).returncode == 0
assert tracked or not (OWNER / name).exists(), "untracked target collision"
subprocess.run(["git", "apply", "--check", str(patch)], cwd=OWNER, check=True)
subprocess.run(["git", "apply", str(patch)], cwd=OWNER, check=True)
for name, expected in files.items():
assert hashlib.sha256((OWNER / name).read_bytes()).hexdigest() == expected.removeprefix("sha256:"), name
receipt = {"repo": "ops-warden", "base_commit": head, "patch_sha256": EXPECTED,
"integrated_at": datetime.now(timezone.utc).isoformat(), "files_sha256": files,
"source_integrated": True, "committed": False, "published": False,
"native_accepted": False, "independent_tests": "102 passed in 12.91s"}
(ROOT / "canonical-integration.json").write_text(json.dumps(receipt, indent=2) + "\n")
print("ops-warden: five exact reviewed source files integrated")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,46 @@
Detected: key-cape (/home/worsch/key-cape)
ADR-001 Consistency Report
Repo: key-cape
Path: /home/worsch/key-cape
==================================================================
WARNS (9):
C-24
Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)
C-11 [workplans/ADHOC-2026-09-27.md#ADHOC-2026-09-27-T02] [fixable]
Task 'ADHOC-2026-09-27-T02' has no state_hub_task_id
C-20 [workplans/KEY-WP-0007-user-engine-portal-oidc-client.md]
Workplan dependency target 'USER-WP-0020' is not linked to State Hub
file='USER-WP-0020' db=''
C-20 [workplans/KEY-WP-0008-registration-handoff-and-client-mfa-policy.md]
Workplan dependency target 'NK-WP-0025' is not linked to State Hub
file='NK-WP-0025' db=''
C-20 [workplans/KEY-WP-0009-provider-capabilities-and-service-identities.md]
Workplan dependency target 'NK-WP-0030' is not linked to State Hub
file='NK-WP-0030' db=''
C-20 [workplans/KEY-WP-0010-openbao-operator-loopback-callback.md]
Workplan dependency target 'RAILIANCE-WP-0027' is not linked to State Hub
file='RAILIANCE-WP-0027' db=''
C-22 [workplans/KEY-WP-0038-forgejo-oidc-client.md#KEY-WP-0038-T02] [fixable]
Task description drift 'KEY-WP-0038-T02': file content differs from DB (file wins)
file='**Changed 2026-10-08 (founder direction):** no case-specific helper script.\nRegistration is the first case of the Securi' db='**Changed 2026-10-08 (founder direction):** no case-specific helper script.\nRegistration is the first case of the Securi'
C-28
2 unread inbox message(s) older than 3 day(s): the-custodian:23172cb2, the-custodian:51e9eace
C-28
2 unread message(s) lack thread_id for supersession tracking: the-custodian:23172cb2, the-custodian:51e9eace
INFOS (1):
C-08
Retired DB workstream 'keycape-implementation@retired-20260826' (id=2c9caad8…, status=archived) has no backing workplan file — projection tombstone, not live work
FIXES APPLIED (4):
bindings: synced 44/44
C-11 skipped: task 'ADHOC-2026-09-27-T02' in finished workstream — not created
C-22 fixed: task ddcfb482… description updated
push: pushed
==================================================================
0 automation-error | 0 assessment-fail | 9 warn | 1 info
RESULT: ✓ PASS (with warnings)
==================================================================

View file

@ -0,0 +1,23 @@
{
"schema": "keycape.offline-checker-owner-review-packet.v1",
"owner_commit": "834b0d06a13bb2ece9cc98392cb39ba0e2167ecb",
"files": {
"README.md": "sha256:75ed6866b29cbb9f2c5c2a395da2afa25d021f248587c7055bd721e2c55b8685",
"consistency-sync.txt": "sha256:6bd932221a782cf819827d791df8ff2dc43731947b7a8f2814702a1282ce2b5c",
"owner-checks.json": "sha256:4669c8ac2fcee76926b1419a9fe6c5924abf354c7ebc1fd2ee1b697c6e4f5419",
"owner-review.json": "sha256:aa9083e77ea08d166e8cec7c5d73362c19058567c5e80a576766af3745958dc0",
"postimages/docs/offline-checker-owner-review-2026-10-16.json": "sha256:aa9083e77ea08d166e8cec7c5d73362c19058567c5e80a576766af3745958dc0",
"postimages/docs/offline-checker-owner-review-2026-10-16.md": "sha256:75ed6866b29cbb9f2c5c2a395da2afa25d021f248587c7055bd721e2c55b8685",
"postimages/workplans/KEY-WP-0038-forgejo-oidc-client.md": "sha256:dc8d88d462799e7e6e44da759a973d52e44102a271969a5daaa9d30ec015b064",
"pre-state.json": "sha256:bd225b77370da39379eea5abc17883aa4e09d30f8093cfdb8caa79a063121873",
"preimages/workplans/KEY-WP-0038-forgejo-oidc-client.md": "sha256:9c265970cc230bcbab0d5e07ff94c27c81b9e0c513eb015560e7c756ddcbf7e0",
"progress-receipt.json": "sha256:c002472c56ba04866d10c981eb947f0efd4990e5747346b855e2159eddcc18e1",
"publication.json": "sha256:b6011ec219e457a16847d37851bb0ed80466a55b0543c8a4b6ff511e879a4ac2",
"publish_review.py": "sha256:c4814bc80c55f1b883a9eb1e198e4b42f84ba08c12b2774016dae1fafaac91b1",
"review_checks.py": "sha256:a7d5302740797dde2e0e955b78f0aaa98947d048c58f357228deec6f40892d1d",
"source-tests-first-attempt.txt": "sha256:9ecee48013519c9e9a0d8e9631574ca11dbf2514606f5914cd390b78c5a2e8ff",
"source-tests.json": "sha256:e606adb20b943072d1052f8f9dffd89b78f73c4dd10bd761f29b6cec72fc21a0",
"source-tests.txt": "sha256:0d08ecb4bee66fc9d3ca915a9fb508a4b3ac9b553ac013cf9030d8e2d2ff2051"
},
"backup_files_included": false
}

View file

@ -0,0 +1,34 @@
{
"reviewer": "key-cape-owner (agent key-cape, codex)",
"decision": "approved",
"scope": "exact source/artifact offline configuration validation behavior only",
"comment": "INTENT.md Tooling declaration, Purpose/Primary Utility and Strategic Boundaries; deterministic constrained IAM config validation and delivered-reference boundary. KEY-WP-0038-T02 remains wait. See bound narrative for explicit limits; this is an owner review, not human approval or admission.",
"work_item": "KEY-WP-0038-T02",
"source_revision": "3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987",
"source_tree": "f0010b9cab17dbc3a787628a29657d98b900320f",
"artifact_sha256": "sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5",
"artifact_bytes": 13633409,
"closure_sha256": "sha256:cb2f8ce0a3ee1efb7099ce00d139ebe00a23a8b356fba160ab8c4b067f5818f9",
"review_narrative_sha256": "sha256:75ed6866b29cbb9f2c5c2a395da2afa25d021f248587c7055bd721e2c55b8685",
"owner_checks_sha256": "sha256:4669c8ac2fcee76926b1419a9fe6c5924abf354c7ebc1fd2ee1b697c6e4f5419",
"source_tests_receipt_sha256": "sha256:e606adb20b943072d1052f8f9dffd89b78f73c4dd10bd761f29b6cec72fc21a0",
"candidate_inputs": {
"README.md": "sha256:0df0fd7f263481ef706433c320af0d908f1b763557c3b0f5e4beca57408d7d5a",
"build_checker.py": "sha256:ea58117252e7cc5328307d3551580dc251780db12e9e8c1600de88633ea24179",
"build-receipt.json": "sha256:cc227c17375cd89edb9a1b0304e2cf277a8c8d46370cd71f1c071da57931d3de",
"source-toolchain-module-closure.json": "sha256:cb2f8ce0a3ee1efb7099ce00d139ebe00a23a8b356fba160ab8c4b067f5818f9",
"artifact-build-info.txt": "sha256:025391cc0fc013352783eedadd34a9bdaf6dc6cf10d5d2cebec7099598a8300a",
"verification.json": "sha256:1655501a3124cbb3ebd88cd440d96592ccfd1138e01724b79eaafff69031e464"
},
"limits": [
"two non-secret URL environment overrides remain inputs",
"omitted/empty env-names does not check provision",
"policy-store flag is caller assertion",
"permissive server YAML semantics",
"no executable custody, launch descriptors or native/type admission",
"no actual identity, signing, upstream or group/consumer proof"
],
"native_admission": false,
"production_authorization": false,
"other_owner_roles_reviewed": []
}

View file

@ -0,0 +1,94 @@
# KeyCape owner review: exact offline configuration checker
Reviewer: `key-cape-owner (agent key-cape, codex)`
Decision: `approved` for the bounded source/artifact behavior below
Work item: `KEY-WP-0038-T02` (remains `wait`)
Date: 2026-10-16
KeyCape endorses the offline configuration validation behavior of published
source `3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, `src` tree
`f0010b9cab17dbc3a787628a29657d98b900320f`, and the Linux/amd64 Go 1.23.0
artifact SHA256 `be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`
(13,633,409 bytes). This review is an owner judgement, not human approval,
production authorization or native admission.
The INTENT.md basis is KeyCape's Tooling declaration, Purpose/Primary Utility and
Strategic Boundaries: deterministic profile-constrained authentication tooling,
explicit configuration checks, delivered secret references, and separation from
authorization decisions. The review stays within these responsibilities.
## Source and build evidence
The candidate's source archive matches the exact published revision and src tree.
Independent owner checks matched all 108 archived source files, 9876 cached
Go toolchain files and every file in the ten selected module source trees against
the closure. The closure JSON SHA256 is
`cb2f8ce0a3ee1efb7099ce00d139ebe00a23a8b356fba160ab8c4b067f5818f9`;
archive SHA256 is `770a15933e6c45d4b94beb37d4322fd4ae1436d8193dfb7f5655bbc851f3fc73`.
The actual executable matches the artifact above. Its command is the existing
`./cmd/keycape` target, also built by the owner Makefile/Dockerfile. The retained
build uses explicit environment, cached Go 1.23.0, readonly module resolution,
CGO disabled, fixed Linux/amd64/v1, trimpath and buildvcs=false, GOPROXY=off and
GOSUMDB=off. These are deterministic local input checks, not independent
attestation of upstream toolchain/module provenance or host custody.
`main.go` dispatches `check-config` first, before server config processing,
private-key loading, adapter construction, policy-store opening or listener
startup. `checkconfig.go` calls the same `config.Load`, `ValidateConfig`, global
lifetime parse, and registry conversion used by startup, with a constant
placeholder in place of client-secret lookup. `policy.CheckClients` checks
registry shape without opening the policy store. Source inspection found no
application init path that reads credentials or contacts an upstream for this
command. No KeyCape private-key read, client-secret lookup, upstream request,
listener, real authentication or signing occurs in this branch.
Fourteen direct executable checks passed: valid confidential/public registrations
without any actual secret/key; bad port/lifetime/query redirect/browser roles;
missing declared env name; omitted and empty env-name semantics; required policy
client presence with and without policy assertion; non-secret URL environment
overlay; synthetic secret non-emission; permissive unknown YAML field handling;
and success with an unopened signing-key FIFO plus unused policy path. The three
relevant package suites (`cmd/keycape`, `internal/config`, `internal/server/policy`)
passed. The first package-suite attempt failed because the candidate src-only
archive omitted two committed external config fixtures. It is retained. Rerun
added only those fixture paths archived from the exact reviewed revision.
The coordinator separately reports reproducing the same artifact and eight
adapter fixture checks; that is supporting coordinator evidence, not an owner
claim of native or caller integration acceptance.
## Contract and limitations
- Always pass the explicit synthetic/config path. This command loads the supplied
YAML file; it cannot guarantee the caller supplied only non-secret contents.
- `config.Load` reads the non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and
`KEYCAPE_BROWSER_LOGOUT_URL` environment overrides. Validation therefore is not
independent of the caller environment. The caller must intentionally supply
the desired URL configuration in a minimal environment. This endorsement does
not certify the current NetKingdom subprocess environment or its correction.
- `--env-names` checks names only when its value is nonempty. Omitted/empty means
list required names without proving their provision. Even nonempty assertions
prove neither Secret existence, values, mount/ESO delivery nor readable custody.
- `--policy-store` asserts the target enables that store and checks its required
clients. It proves no path permissions, lock availability or stored state.
- The loader follows existing server YAML semantics, including acceptance of
unknown fields. Validation is the implemented startup subset, not a complete
OIDC/profile policy or schema audit. It does not strengthen every currently
accepted redirect/grant/client field.
- No key existence/parsing, policy-file access, port binding, upstream readiness,
live config equivalence, browser flow, token claims, group enforcement, secret
minting/delivery, consumer login or actual human/agent/service identity is proved.
- Exact byte identity does not establish immutable executable storage, safe file
descriptors, launch custody, type/version acknowledgement, descriptor step-set
composition, Warden admission or parent-session revocation. Those remain with
the respective owners. No other owner role is endorsed here.
No blocking defect was found within this exact offline checker scope. The broader
browser registration and identity tasks remain open. No production configuration,
credentials, cluster mutations, external messages or paid calls were used.
Reproduction and detailed local receipts are in
`the-custodian/docs/changes/netkingdom-target-2026-10-16/keycape-checker-owner-review/`:
`review_checks.py`, `owner-checks.json`, `source-tests.json`, `source-tests.txt`
and `source-tests-first-attempt.txt`. Original candidate build/closure receipts
remain in the adjacent `browser-checker-artifact-next/` packet, including failed
closure collection attempts.

View file

@ -0,0 +1,212 @@
---
id: KEY-WP-0038
type: workplan
title: "Forgejo as a NetKingdom OIDC client, so the forge can close"
domain: infotech
repo: key-cape
status: active
flavor: implementation
owner: claude-code
topic_slug: railiance
created: "2026-10-08"
updated: "2026-10-16"
depends_on: []
related_repos:
- prj-forgejo-org-refactor
- railiance-apps
- railiance-platform
state_hub_workstream_id: "098e3e2c-a138-560e-b2d3-3a15eb49d8a9"
---
# KEY-WP-0038 - Forgejo as a NetKingdom OIDC client, so the forge can close
## Why
`forgejo.coulomb.social` is closing: private by default, no anonymous access
(prj-forgejo-org-refactor ORGREF-WP-0002, decision
`decisions/forge-visibility-policy.md`). The founder's condition is that Forgejo
sign-in runs through NetKingdom **before** anything closes, so that every human,
agent and service reader has a governed identity rather than an ad-hoc local
account. This plan is ORGREF-WP-0002-T04. Its Forgejo-side counterpart is
railiance-apps `RAPPS-WP-0016` (ORGREF-WP-0002-T05), which owns the Forgejo
Helm release.
## Boundaries
- Registration follows the existing human browser-client pattern
(`docs/approval-engine-provisioning-request.yaml`, `human_registration`) and the
client fields validated in KEY-WP-0028. Redirect URIs are exact, never
wildcards.
- The client secret goes to OpenBao through railiance-platform custody. No secret
value in git, State Hub, logs or chat.
- Founder guardrail for the whole closure: tighten gradually, never lock anyone
out, never risk assets. Nothing in this plan changes who can read the forge
today. It only makes the NetKingdom path available.
## T01 — Define what Forgejo receives
```task
id: KEY-WP-0038-T01
status: done
priority: high
state_hub_task_id: "5f019d13-1fa3-5b88-a27a-75a3339ca96e"
```
Write the claim contract Forgejo consumes, as a non-secret request packet under
`docs/`:
- issuer `https://kc.coulomb.social`; confidential client `forgejo`; authorization
code with PKCE; scopes `openid profile email groups`
- redirect URI
`https://forgejo.coulomb.social/user/oauth2/netkingdom/callback`. The
authentication-source name `netkingdom` must match railiance-apps'
configuration exactly.
- subject and username: canonical directory subject, plus a stable
`preferred_username` that Forgejo can match to the existing local accounts
(founder and agent accounts) for linking
- `groups` naming the Forgejo organisations and teams to map to (for example
`forge:coulomb:owners`, `forge:coulomb:readers`), and `principal_type` so that
human and service principals stay distinguishable
- MFA: forge administrators and owners require assurance with a fresh second
factor; plain readers follow the client-aware MFA policy (KEY-WP-0008)
Acceptance: the founder accepts the packet. The group names are agreed with
railiance-apps.
2026-10-08: packet drafted at `docs/forgejo-oidc-client-request.yaml`. It covers a
confidential client `forgejo` with PKCE and `mfaRequired: true`, the exact
callback, scopes, a custody path, the claims as emitted at this revision, new
LLDAP groups `forge-admins`, `forge-coulomb-owners` and `forge-coulomb-readers`
with a required-claim gate, no automatic account creation, and linking of local
accounts. It also proposes that agents and services use scoped tokens bound to
service identities, not browser sign-in.
2026-10-08: accepted by the founder (in session). railiance-apps' RAPPS-WP-0016
uses the same authentication-source name `netkingdom` and the same group names,
so the agreement condition holds. Before T02, the LLDAP groups `forge-admins`,
`forge-coulomb-owners` and `forge-coulomb-readers` must exist, with the founder
in the first two.
## T02 — Register the client and hand over its secret
```task
id: KEY-WP-0038-T02
status: wait
depends_on: [KEY-WP-0038-T01, NK-WP-0048-T06]
blocking_reason: "Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout."
priority: high
state_hub_task_id: "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30"
```
**Changed 2026-10-08 (founder direction):** no case-specific helper script.
Registration is the first case of the SecurityCaseType `oidc-browser-relying-party`
(net-kingdom NK-WP-0048, canon `security-case-types_v0.1`). The parameters
accepted in T01 become the case; ops-mason materializes it (MASON-WP-0010) and
ops-warden follows through (WARDEN-WP-0044). The registration capability is
net-kingdom's (`net-kingdom.keycape-client-registration`); key-cape's part is its
field rules and reviewing the type's composition.
The original intent below still describes what must result.
Register `forgejo` from the packet. Mint the client secret straight into custody
(OpenBao path `platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, derived
by the case type since net-kingdom 074b3a0; custody owner railiance-platform,
consumers KeyCape via ESO and Forgejo). Record a receipt with no
secret value.
Acceptance: discovery metadata lists the client's redirect URI. The secret is
readable by Forgejo's custody path only.
2026-10-10: key-cape reviewed the type's composition
(`docs/oidc-browser-relying-party-type-review.md`). net-kingdom fixed the findings
in `61ab6bc`, a 1.0 patch: redirects with `?` or `#` refused, the groups scope
required, and descriptions corrected. A new 1.1 (`e2174ce`, founder go) adds the
`consumer-enforces-groups` verification. For Forgejo, that is the required-claim
setting railiance-apps configures. `security-cases/forgejo-netkingdom-oidc.yaml`
is re-pinned to @1.1, with parameters unchanged. KeyCape added `keycape
check-config` (`c528daa`), which register-client runs before the restart. T02 still
waits on NK-WP-0048-T06, which needs both executors' light paths (MASON-WP-0010-T03,
WARDEN-WP-0044-T03).
### Exact offline checker owner review — 2026-10-16
KeyCape owner review under KEY-WP-0038-T02 endorses only the offline config
validation behavior of source `3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`,
`src` tree `f0010b9cab17dbc3a787628a29657d98b900320f`, compiled artifact
`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`.
The exact source/archive, 108 source files, 9876 cached Go 1.23.0 toolchain
files and all ten selected module trees matched the candidate closure. Fourteen
actual artifact checks and the cmd/config/policy Go tests passed using only
synthetic inputs. The check dispatches before private-key loading, upstream
adapter construction or server startup and uses placeholder client secrets.
[Owner review](../docs/offline-checker-owner-review-2026-10-16.md) records the
exact evidence and limits: non-secret portal/logout URL environment overlays,
optional/nonempty env-name assertion and policy-store assertion, permissive YAML
loading, and no key/custody/readiness or authentication proof. It does not endorse
the caller's inherited environment, executable storage/custody, the NetKingdom
descriptor/type, Warden admission or any browser/native identity. Parent review
identified caller environment inheritance; NetKingdom owns its correction and
regression checks. T02 remains `wait`; T03 and all live acceptance gates remain
unchanged. No live config, credentials, cluster objects or paid calls were used.
## T03 — Prove the claims for each kind of identity
```task
id: KEY-WP-0038-T03
status: wait
depends_on: [KEY-WP-0038-T02]
blocking_reason: "Claims are verified against the registered client."
priority: high
state_hub_task_id: "40ddaa29-7245-508c-ad48-ddc063e25115"
```
Before Forgejo uses the client, verify the issued ID token and UserInfo for one
human (the founder, with MFA), one agent identity and one service identity:
subject, `preferred_username`, `groups`, `principal_type`, assurance. Record the
claims, minus tokens, as evidence.
Decide with railiance-apps whether agents and services sign in to Forgejo at
all, or receive scoped Forgejo tokens bound to a NetKingdom service identity
(ORGREF-WP-0002-T06). Record the decision.
Acceptance: evidence for all three principals; the agent and service decision is
recorded. Then hand over to RAPPS-WP-0016.
## T04 — Implement the `keycape-client` entry point
```task
id: KEY-WP-0038-T04
status: cancel
priority: high
state_hub_task_id: "583cf1f1-16dc-515f-83bc-1efc45024a89"
```
**Cancelled 2026-10-08: moved to net-kingdom NK-WP-0048-T09 (done).** The live
registration machinery (contained kubectl, cluster and issuer pins) already lives
in net-kingdom `sso-mfa/k8s/keycape/`, where KeyCape is deployed; a copy here
would drift. The entry point is `net-kingdom/sso-mfa/k8s/keycape/keycape-client.py`
and the declaration is `net-kingdom.keycape-client-registration`; key-cape's draft
declaration is removed. key-cape still owns the client field rules (canonical
model) and their enforcement at authorization time.
`capabilities/playbooks/key-cape.oidc-client-registration.yaml` (contract PASS,
`maturity: planned`) declares client registration as a playbook capability, so
that the SecurityCaseType `oidc-browser-relying-party` (net-kingdom NK-WP-0048)
can compose it. Implement the entry point it names, `tools/keycape-client/`:
- `keycape-client register --client-file <rendered-client.yaml>` with modes
`inspect`, `dry-run`, `apply` and `remove`; adds, updates or removes exactly one
client in the live registry; idempotent; never prints or writes secret values;
a confidential client's secret is read from its custody reference
- one implementation replacing the per-client helpers in net-kingdom
`sso-mfa/k8s/keycape/` (register-coulomb-social, portal-client-rollout,
vergabe-client-rollout, secrets-engine-openbao-client-rollout), keeping their
safeguards (byte-preserving merge, compare-and-swap, cluster pin, safe receipts)
and their tests as regression cases
- client field validation by the KEY-WP-0028 rules
- then set the declaration's `maturity` to `implemented`
Longer term, loading clients from a non-secret registry with secrets referenced
from custody would remove the type's only attended step (see the type README,
"Attended steps").

View file

@ -0,0 +1,12 @@
{
"head": "3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987",
"status": "M .custodian-brief.md\n M WORK-RECORDS.md\n",
"staged_patch_sha256": "41634d8875c0dd90aa489f77831808d517f7fa23be34d9231b099c6157dbd6a3",
"staged_entries": "100644 679d1c58d782fa5c3b61d40ab1126cabd86a8ad9 0\t.custodian-brief.md\n100644 d775a2c088659e6d4f483c01f3ed88a25495f6f8 0\tWORK-RECORDS.md\n",
"files": {
".custodian-brief.md": "0e1be7c07f8ce9ca7f7d92dd16b6b2c2962805f34c2a083aaf0ad72722da4c91",
"WORK-RECORDS.md": "02f21a83afddc6de59a7432ecaee6f03c7a800d3f9b321d837d5a5782ccbc692",
"workplans/KEY-WP-0038-forgejo-oidc-client.md": "9c265970cc230bcbab0d5e07ff94c27c81b9e0c513eb015560e7c756ddcbf7e0"
},
"workplan_sha256": "9c265970cc230bcbab0d5e07ff94c27c81b9e0c513eb015560e7c756ddcbf7e0"
}

View file

@ -0,0 +1,190 @@
---
id: KEY-WP-0038
type: workplan
title: "Forgejo as a NetKingdom OIDC client, so the forge can close"
domain: infotech
repo: key-cape
status: active
flavor: implementation
owner: claude-code
topic_slug: railiance
created: "2026-10-08"
updated: "2026-10-14"
depends_on: []
related_repos:
- prj-forgejo-org-refactor
- railiance-apps
- railiance-platform
state_hub_workstream_id: "098e3e2c-a138-560e-b2d3-3a15eb49d8a9"
---
# KEY-WP-0038 - Forgejo as a NetKingdom OIDC client, so the forge can close
## Why
`forgejo.coulomb.social` is closing: private by default, no anonymous access
(prj-forgejo-org-refactor ORGREF-WP-0002, decision
`decisions/forge-visibility-policy.md`). The founder's condition is that Forgejo
sign-in runs through NetKingdom **before** anything closes, so that every human,
agent and service reader has a governed identity rather than an ad-hoc local
account. This plan is ORGREF-WP-0002-T04. Its Forgejo-side counterpart is
railiance-apps `RAPPS-WP-0016` (ORGREF-WP-0002-T05), which owns the Forgejo
Helm release.
## Boundaries
- Registration follows the existing human browser-client pattern
(`docs/approval-engine-provisioning-request.yaml`, `human_registration`) and the
client fields validated in KEY-WP-0028. Redirect URIs are exact, never
wildcards.
- The client secret goes to OpenBao through railiance-platform custody. No secret
value in git, State Hub, logs or chat.
- Founder guardrail for the whole closure: tighten gradually, never lock anyone
out, never risk assets. Nothing in this plan changes who can read the forge
today. It only makes the NetKingdom path available.
## T01 — Define what Forgejo receives
```task
id: KEY-WP-0038-T01
status: done
priority: high
state_hub_task_id: "5f019d13-1fa3-5b88-a27a-75a3339ca96e"
```
Write the claim contract Forgejo consumes, as a non-secret request packet under
`docs/`:
- issuer `https://kc.coulomb.social`; confidential client `forgejo`; authorization
code with PKCE; scopes `openid profile email groups`
- redirect URI
`https://forgejo.coulomb.social/user/oauth2/netkingdom/callback`. The
authentication-source name `netkingdom` must match railiance-apps'
configuration exactly.
- subject and username: canonical directory subject, plus a stable
`preferred_username` that Forgejo can match to the existing local accounts
(founder and agent accounts) for linking
- `groups` naming the Forgejo organisations and teams to map to (for example
`forge:coulomb:owners`, `forge:coulomb:readers`), and `principal_type` so that
human and service principals stay distinguishable
- MFA: forge administrators and owners require assurance with a fresh second
factor; plain readers follow the client-aware MFA policy (KEY-WP-0008)
Acceptance: the founder accepts the packet. The group names are agreed with
railiance-apps.
2026-10-08: packet drafted at `docs/forgejo-oidc-client-request.yaml`. It covers a
confidential client `forgejo` with PKCE and `mfaRequired: true`, the exact
callback, scopes, a custody path, the claims as emitted at this revision, new
LLDAP groups `forge-admins`, `forge-coulomb-owners` and `forge-coulomb-readers`
with a required-claim gate, no automatic account creation, and linking of local
accounts. It also proposes that agents and services use scoped tokens bound to
service identities, not browser sign-in.
2026-10-08: accepted by the founder (in session). railiance-apps' RAPPS-WP-0016
uses the same authentication-source name `netkingdom` and the same group names,
so the agreement condition holds. Before T02, the LLDAP groups `forge-admins`,
`forge-coulomb-owners` and `forge-coulomb-readers` must exist, with the founder
in the first two.
## T02 — Register the client and hand over its secret
```task
id: KEY-WP-0038-T02
status: wait
depends_on: [KEY-WP-0038-T01, NK-WP-0048-T06]
blocking_reason: "Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout."
priority: high
state_hub_task_id: "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30"
```
**Changed 2026-10-08 (founder direction):** no case-specific helper script.
Registration is the first case of the SecurityCaseType `oidc-browser-relying-party`
(net-kingdom NK-WP-0048, canon `security-case-types_v0.1`). The parameters
accepted in T01 become the case; ops-mason materializes it (MASON-WP-0010) and
ops-warden follows through (WARDEN-WP-0044). The registration capability is
net-kingdom's (`net-kingdom.keycape-client-registration`); key-cape's part is its
field rules and reviewing the type's composition.
The original intent below still describes what must result.
Register `forgejo` from the packet. Mint the client secret straight into custody
(OpenBao path `platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, derived
by the case type since net-kingdom 074b3a0; custody owner railiance-platform,
consumers KeyCape via ESO and Forgejo). Record a receipt with no
secret value.
Acceptance: discovery metadata lists the client's redirect URI. The secret is
readable by Forgejo's custody path only.
2026-10-10: key-cape reviewed the type's composition
(`docs/oidc-browser-relying-party-type-review.md`). net-kingdom fixed the findings
in `61ab6bc`, a 1.0 patch: redirects with `?` or `#` refused, the groups scope
required, and descriptions corrected. A new 1.1 (`e2174ce`, founder go) adds the
`consumer-enforces-groups` verification. For Forgejo, that is the required-claim
setting railiance-apps configures. `security-cases/forgejo-netkingdom-oidc.yaml`
is re-pinned to @1.1, with parameters unchanged. KeyCape added `keycape
check-config` (`c528daa`), which register-client runs before the restart. T02 still
waits on NK-WP-0048-T06, which needs both executors' light paths (MASON-WP-0010-T03,
WARDEN-WP-0044-T03).
## T03 — Prove the claims for each kind of identity
```task
id: KEY-WP-0038-T03
status: wait
depends_on: [KEY-WP-0038-T02]
blocking_reason: "Claims are verified against the registered client."
priority: high
state_hub_task_id: "40ddaa29-7245-508c-ad48-ddc063e25115"
```
Before Forgejo uses the client, verify the issued ID token and UserInfo for one
human (the founder, with MFA), one agent identity and one service identity:
subject, `preferred_username`, `groups`, `principal_type`, assurance. Record the
claims, minus tokens, as evidence.
Decide with railiance-apps whether agents and services sign in to Forgejo at
all, or receive scoped Forgejo tokens bound to a NetKingdom service identity
(ORGREF-WP-0002-T06). Record the decision.
Acceptance: evidence for all three principals; the agent and service decision is
recorded. Then hand over to RAPPS-WP-0016.
## T04 — Implement the `keycape-client` entry point
```task
id: KEY-WP-0038-T04
status: cancel
priority: high
state_hub_task_id: "583cf1f1-16dc-515f-83bc-1efc45024a89"
```
**Cancelled 2026-10-08: moved to net-kingdom NK-WP-0048-T09 (done).** The live
registration machinery (contained kubectl, cluster and issuer pins) already lives
in net-kingdom `sso-mfa/k8s/keycape/`, where KeyCape is deployed; a copy here
would drift. The entry point is `net-kingdom/sso-mfa/k8s/keycape/keycape-client.py`
and the declaration is `net-kingdom.keycape-client-registration`; key-cape's draft
declaration is removed. key-cape still owns the client field rules (canonical
model) and their enforcement at authorization time.
`capabilities/playbooks/key-cape.oidc-client-registration.yaml` (contract PASS,
`maturity: planned`) declares client registration as a playbook capability, so
that the SecurityCaseType `oidc-browser-relying-party` (net-kingdom NK-WP-0048)
can compose it. Implement the entry point it names, `tools/keycape-client/`:
- `keycape-client register --client-file <rendered-client.yaml>` with modes
`inspect`, `dry-run`, `apply` and `remove`; adds, updates or removes exactly one
client in the live registry; idempotent; never prints or writes secret values;
a confidential client's secret is read from its custody reference
- one implementation replacing the per-client helpers in net-kingdom
`sso-mfa/k8s/keycape/` (register-coulomb-social, portal-client-rollout,
vergabe-client-rollout, secrets-engine-openbao-client-rollout), keeping their
safeguards (byte-preserving merge, compare-and-swap, cluster pin, safe receipts)
and their tests as regression cases
- client field validation by the KEY-WP-0028 rules
- then set the declaration's `maturity` to `implemented`
Longer term, loading clients from a non-secret registry with secrets referenced
from custody would remove the type's only attended step (see the type README,
"Attended steps").

View file

@ -0,0 +1,6 @@
{
"id": "3ca7c13f-bf66-47cd-9915-98f6c84f94f6",
"created_at": "2026-10-16T20:08:56.912300Z",
"workplan_id": "098e3e2c-a138-560e-b2d3-3a15eb49d8a9",
"task_id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30"
}

View file

@ -0,0 +1,46 @@
"""Race-checked owner docs publication; preserve prior staging and local files."""
import hashlib, json, pathlib, subprocess, tempfile
P = pathlib.Path(__file__).resolve().parent
R = pathlib.Path('/home/worsch/key-cape')
before = json.loads((P / 'pre-state.json').read_text())
git = lambda *args: subprocess.check_output(['git', '-C', str(R), *args])
digest = lambda b: hashlib.sha256(b).hexdigest()
assert git('rev-parse', 'HEAD').decode().strip() == before['head'], 'HEAD raced'
assert git('status', '--porcelain=v1').decode() == before['status'], 'status raced'
assert digest(git('diff', '--cached', '--binary')) == before['staged_patch_sha256'], 'staging raced'
for name, sha in before['files'].items():
assert digest((R / name).read_bytes()) == sha, 'file raced: ' + name
paths = ['docs/offline-checker-owner-review-2026-10-16.md', 'docs/offline-checker-owner-review-2026-10-16.json', 'workplans/KEY-WP-0038-forgejo-oidc-client.md']
assert not any((R / n).exists() for n in paths[:2]), 'review destination exists'
# Separate /tmp backup of user-owned files; never include backups in packet.
backup = pathlib.Path(tempfile.mkdtemp(prefix='keycape-review-preserve-'))
preserve = {n: (R / n).read_bytes() for n in ['.custodian-brief.md', 'WORK-RECORDS.md']}
for n, data in preserve.items():
(backup / n).write_bytes(data)
for name in paths:
(R / name).write_bytes((P / 'postimages' / name).read_bytes())
subprocess.run(['git', '-C', str(R), 'diff', '--check'], check=True)
subprocess.run(['git', '-C', str(R), 'add', '--', *paths], check=True)
subprocess.run(['git', '-C', str(R), 'commit', '--only', '-m', 'Review exact offline checker behavior for browser registration', '--', *paths], check=True)
commit = git('rev-parse', 'HEAD').decode().strip()
assert digest(git('diff', '--cached', '--binary')) == before['staged_patch_sha256'], 'original staged diff changed'
assert git('ls-files', '--stage', '.custodian-brief.md', 'WORK-RECORDS.md').decode() == before['staged_entries'], 'original index entries changed'
for n, data in preserve.items():
assert (R / n).read_bytes() == data, 'original worktree changed'
subprocess.run(['git', '-C', str(R), 'push', 'origin', 'HEAD:main'], check=True)
# Required owner file -> hub sync; keep its generated output off the user index.
sync = subprocess.run(['statehub', 'fix-consistency'], cwd=R, capture_output=True, text=True)
(P / 'consistency-sync.txt').write_text(sync.stdout + sync.stderr)
assert git('ls-files', '--stage', '.custodian-brief.md', 'WORK-RECORDS.md').decode() == before['staged_entries'], 'sync changed original index entries'
for n, data in preserve.items():
# These are the sync's known generated outputs, restored to exact pre-state.
if (R / n).read_bytes() != data:
(R / n).write_bytes(data)
assert digest(git('diff', '--cached', '--binary')) == before['staged_patch_sha256']
assert git('status', '--porcelain=v1').decode() == before['status']
assert all((R / n).read_bytes() == data for n, data in preserve.items())
receipt = {'commit': commit, 'published_origin': git('rev-parse', 'origin/main').decode().strip(), 'paths': paths, 'original_staged_patch_preserved': True, 'original_generated_worktree_preserved': True, 'post_status': git('status', '--porcelain=v1').decode(), 'consistency_exit_code': sync.returncode, 'hooks_bypassed': False, 'native_admission': False}
(P / 'publication.json').write_text(json.dumps(receipt, indent=2) + '\n')
print(json.dumps(receipt, indent=2))
raise SystemExit(sync.returncode)

View file

@ -0,0 +1,74 @@
"""Owner offline checks against exact candidate; synthetic inputs only."""
import hashlib, json, os, pathlib, subprocess, tempfile
HERE = pathlib.Path(__file__).resolve().parent
CANDIDATE = HERE.parent / 'browser-checker-artifact-next'
OWNER = pathlib.Path('/home/worsch/key-cape')
BUILD = pathlib.Path('/tmp/nk-checker-artifact-next-r3')
sha = lambda b: 'sha256:' + hashlib.sha256(b).hexdigest()
receipt = json.loads((CANDIDATE / 'build-receipt.json').read_text())
closure_bytes = (CANDIDATE / 'source-toolchain-module-closure.json').read_bytes()
assert sha(closure_bytes) == receipt['closure_sha256']
closure = json.loads(closure_bytes)
verified = {}
for label, root, files in [
('source', BUILD / 'src', closure['source']),
('toolchain', pathlib.Path(receipt['go_binary']).parent.parent, closure['toolchain']),
] + [(m['path'], pathlib.Path(receipt['environment']['GOMODCACHE']) / (m['path'].replace('Azure', '!azure') + '@' + m['version']), m['files']) for m in closure['modules']]:
actual = {str(p.relative_to(root)): sha(p.read_bytes()) for p in root.rglob('*') if p.is_file()}
assert actual == files, label
verified[label] = len(files)
archive = subprocess.check_output(['git', '-C', str(OWNER), 'archive', '--format=tar', receipt['keycape_revision'], 'src'])
assert sha(archive) == receipt['archive_sha256']
assert sha((BUILD / 'keycape').read_bytes()) == receipt['artifact_sha256']
assert subprocess.check_output(['git', '-C', str(OWNER), 'rev-parse', receipt['keycape_revision'] + ':src'], text=True).strip() == receipt['keycape_source_tree']
base = '''issuer: https://synthetic.example.invalid
port: 8080
privateKeyPem: /nonexistent/synthetic-key.pem
clients:
- clientId: user-engine-portal
displayName: Portal
redirectUris: ["https://portal.example.invalid/cb"]
allowedScopes: ["openid"]
grantTypes: ["authorization_code"]
clientType: public
- clientId: vergabe-demo-company
displayName: Vergabe
redirectUris: ["https://vergabe.example.invalid/cb"]
allowedScopes: ["openid"]
grantTypes: ["authorization_code"]
clientType: confidential
secretRef: env:SYNTHETIC_CLIENT_SECRET
'''
cases = [
('valid_without_secret_or_key', base, ['--env-names', 'SYNTHETIC_CLIENT_SECRET', '--policy-store'], {}, True),
('bad_port', base.replace('port: 8080', 'port: 0'), [], {}, False),
('bad_global_lifetime', base + 'tokenLifetime: bogus\n', [], {}, False),
('bad_redirect_query', base.replace('/cb', '/cb?x=1', 1), [], {}, False),
('ignored_browser_roles', base.replace(' clientType: public', ' roles: [admin]\n clientType: public', 1), [], {}, False),
('missing_declared_env', base, ['--env-names', 'OTHER'], {}, False),
('omitted_env_names_only_lists', base, [], {}, True),
('empty_env_names_only_lists', base, ['--env-names', ''], {}, True),
('missing_policy_client', base.replace('clientId: user-engine-portal', 'clientId: renamed'), ['--policy-store'], {}, False),
('policy_client_not_asserted', base.replace('clientId: user-engine-portal', 'clientId: renamed'), [], {}, True),
('nonsecret_environment_overlay', base, [], {'KEYCAPE_ACCOUNT_PORTAL_URL': 'http://invalid.example.invalid'}, False),
('synthetic_secret_not_emitted', base, ['--env-names', 'SYNTHETIC_CLIENT_SECRET'], {'SYNTHETIC_CLIENT_SECRET': 'synthetic-value-never-emit'}, True),
('unknown_yaml_field_same_loader', base + 'unknownReviewField: true\n', [], {}, True),
]
results = []
with tempfile.TemporaryDirectory(prefix='keycape-owner-offline-') as tmp:
config = pathlib.Path(tmp) / 'config.yaml'
fifo = pathlib.Path(tmp) / 'unreadable-key-fifo'
os.mkfifo(fifo)
cases.append(('key_fifo_and_policy_path_not_read', base.replace('/nonexistent/synthetic-key.pem', str(fifo)), ['--policy-store'], {'KEYCAPE_POLICY_PATH': str(fifo)}, True))
for name, content, args, extra, valid in cases:
config.write_text(content)
result = subprocess.run([str(BUILD / 'keycape'), 'check-config', '--config', str(config), *args], env={'PATH': '/usr/bin:/bin', **extra}, capture_output=True, text=True, timeout=5)
report = json.loads(result.stdout)
assert report['valid'] is valid and result.returncode == (0 if valid else 1), name
assert 'synthetic-value-never-emit' not in result.stdout + result.stderr
results.append({'name': name, 'exit_code': result.returncode, 'report': report})
output = {'schema': 'keycape.offline-checker-owner-checks.v1', 'source_revision': receipt['keycape_revision'], 'source_tree': receipt['keycape_source_tree'], 'artifact_sha256': receipt['artifact_sha256'], 'closure_sha256': receipt['closure_sha256'], 'closure_verified_files': verified, 'checks': results, 'passed': len(results), 'native_admission': False}
(HERE / 'owner-checks.json').write_text(json.dumps(output, indent=2) + '\n')
print(json.dumps({'passed': len(results), 'closure_verified_files': verified}))

View file

@ -0,0 +1,18 @@
--- FAIL: TestServiceRegistrationAudienceAndScopeIsolation (0.00s)
clients_test.go:15: config: read "../../../config/service-clients.example.yaml": open ../../../config/service-clients.example.yaml: no such file or directory
--- FAIL: TestServiceRegistrationTenantsAreExactPerDecision (0.00s)
clients_test.go:55: config: read "../../../config/service-clients.example.yaml": open ../../../config/service-clients.example.yaml: no such file or directory
--- FAIL: TestApproverRegistrationShapeIsExact (0.00s)
clients_test.go:95: config: read "../../../config/service-clients.example.yaml": open ../../../config/service-clients.example.yaml: no such file or directory
--- FAIL: TestSittingRequesterRegistrationShapeIsExact (0.00s)
clients_test.go:164: config: read "../../../config/service-clients.example.yaml": open ../../../config/service-clients.example.yaml: no such file or directory
FAIL
FAIL keycape/cmd/keycape 0.028s
--- FAIL: TestServiceClientExampleContracts (0.00s)
config_test.go:381: load service client examples: config: read "../../../config/service-clients.example.yaml": open ../../../config/service-clients.example.yaml: no such file or directory
--- FAIL: TestDevConfigOpenBaoAdminAdmitsOperatorTunnelCallback (0.00s)
config_test.go:416: load dev config: config: read "../../../config/dev-config.yaml": open ../../../config/dev-config.yaml: no such file or directory
FAIL
FAIL keycape/internal/config 0.102s
ok keycape/internal/server/policy 0.184s
FAIL

View file

@ -0,0 +1,3 @@
ok keycape/cmd/keycape 0.025s
ok keycape/internal/config 0.076s
ok keycape/internal/server/policy (cached)

View file

@ -0,0 +1,61 @@
# Mason browser-case source compatibility
Owner work: **MASON-WP-0010-T03**, first consumer **NK-WP-0048-T06 / TSA-04**.
Isolated checkout `/tmp/netkingdom-mason-browser-20261016`, source base
`e4761b843cd881a672cbb33a5b451de31a61d730`; canonical owner dirt preserved.
The actual Python APIs already accept an explicit `types_root` and can generate
the sixth consumer role. The shipped CLI did not expose that argument. The patch
passes `--types-root` through `plan`, `build` and `structure-request`, retaining
NetKingdom's exact-version loader, default 1.1 selection and current admission.
No new descriptor schema or structure protocol was introduced.
`forgejo-1.2-draft-plan.md` and `forgejo-1.2-source-render.json` are actual source
projections of the acknowledged 1.2 type and its proposed Forgejo example. They
have six objects and the dedicated consumer ESO role in namespace `forgejo`.
They are not a reviewed plan, build bundle, structural Request or native receipt.
The actual 1.2 case has no acceptance; this worker did not fabricate one to make
build/request preparation succeed. The legacy 1.1 bundle remains historical,
including its known incorrect consumer workload target. The current generator
already fixes that target; reproducing the historical file as new evidence would
be incorrect.
Validation: **50 passed** (six new actual-source checks plus 44 existing plan,
build and structure tests), `git diff --check` passes. Ruff was unavailable.
New checks cover actual six-object draft rendering; default-root version refusal;
ordinary unimplemented refusal; proposed-case build refusal before any output;
historical bundle refusal under 1.2; and malformed consumer namespace refusal by
the actual NetKingdom validator. No live calls, credentials or effects occurred.
`implementation.patch` changes only the Mason CLI, tests, existing owner document
and existing workplan. Apply after review to ops-mason, rerun the scoped command
in `validation.json`, then run owner State Hub consistency through the coordinator.
No task status or acceptance promotion is included. No duplicate workplan exists.
## Contracts still required from the existing owners
- NetKingdom and the actual case acceptors: the exact accepted 1.2 case and reviewed
current plan before Mason builds a six-object bundle/Request; do not carry
historical 1.1 approval forward or label an offline plan reviewed.
- Platform + Mason: authenticate `ops-mason.case-structure-request.v1` observations
for each exact policy, role and fixed layout; distinguish created from matched
preexisting objects; reconcile CCR41/CCR42 source bindings and owner admission.
`StatusBinding` needs independent owner authentication binding the accepted and
materialized raw case hashes. The injected test verifier supplies no authority.
- KeyCape + NetKingdom: accepted exact adapter source/descriptor for groups,
client registration and environment/registry checks, with current case/type
pins, native UID/resource-version preconditions, attributed observations and
created-only undo. No descriptor or owner assent is authored here.
- Platform: broker mint descriptor with exact actor/lane/source/catalog/attempt
pins and separate broker-grant/parent-session revocation evidence; metadata-only
delivery/status/undo adapter with created-object ownership and token revocation
or independently proved expiry. Already-present outcomes require reconciliation.
- Forge/consumer owner (railiance-apps): admitted consumer ESO binding/deployment
and Forgejo configuration adapter or authenticated receipt-only step; independent
successful sign-in, denied-group, revocation and rollback observations.
- NetKingdom + Warden: actual owner-accepted descriptors in a reviewed type
revision and authenticated admission of original receipts. A source checksum,
unsigned journal row or generated inventory is not native acceptance.
The first case remains unmaterialized/unaccepted for native completion. Remaining
work stays in MASON-WP-0010-T03, NK-WP-0048-T06 and their existing owner records.

View file

@ -0,0 +1,151 @@
---
id: case-forgejo-netkingdom-oidc
demand_source: SecurityCase forgejo-netkingdom-oidc (owner railiance-apps)
consumer_repo: railiance-apps
credential_type: security-case
derived_from_type: oidc-browser-relying-party@1.2
render_digest: sha256:c0afd5576cdae0cbc807eccb2b9fa87ac668f15e32ec7cf79d295bdb23c75b35
status: draft
case_acceptance: null
reference_case: true
approved_by: null
approved_at: null
---
# Construction plan (derived-from-type): forgejo-netkingdom-oidc
Rendered from `oidc-browser-relying-party@1.2` (Browser OIDC relying party for an internal service). Do not edit by hand:
re-run `ops-mason plan --case` instead. Render digest `sha256:c0afd5576cdae0cbc807eccb2b9fa87ac668f15e32ec7cf79d295bdb23c75b35`.
**Reference case:** the type is `acknowledged`, not yet `implemented`. This case
is the one whose live materialization takes the type to `implemented`.
## 1. Demand
Let an internal web service sign its human users in through NetKingdom (KeyCape): an exact-redirect OIDC client, optional MFA pin, the LLDAP groups the service maps to its own roles, and, for confidential clients, a client secret minted straight into OpenBao custody. Replaces the per-client rollout scripts in sso-mfa/k8s/keycape. First case: Forgejo (KEY-WP-0038).
## 2. Existing-structure survey
Survey source: none (metadata only).
No collision with existing policies, roles, KV paths or catalog entries.
## 3. Objects (from the type's materialization)
| Item | Kind | Name |
|---|---|---|
| client-secret-path | kv_path | `platform/workloads/keycape/forgejo` |
| keycape-reader-policy | openbao_policy | `workload-kv-read-keycape-forgejo-eso` |
| consumer-reader-policy | openbao_policy | `workload-kv-read-keycape-forgejo-forgejo` |
| eso-role | openbao_role | `external-secrets-keycape-forgejo` |
| consumer-eso-role | openbao_role | `external-secrets-keycape-forgejo-forgejo` |
| catalog-entry | catalog_entry | `forgejo-keycape-client` |
### client-secret-path
```
field CLIENT_SECRET; minted by the credential broker, never written by hand
```
### keycape-reader-policy
```
path "platform/data/workloads/keycape/forgejo" {
capabilities = ["read"]
}
```
### consumer-reader-policy
```
path "platform/data/workloads/keycape/forgejo" {
capabilities = ["read"]
}
```
### eso-role
```
auth: kubernetes
bound_service_account_names: [external-secrets]
bound_service_account_namespaces: [external-secrets]
token_policies: [workload-kv-read-keycape-forgejo-eso]
token_no_default_policy: true
token_ttl: 15m
token_max_ttl: 15m
```
### consumer-eso-role
```
auth: kubernetes
bound_service_account_names: [forgejo-keycape-forgejo-eso]
bound_service_account_namespaces: [forgejo]
audience: openbao
token_policies: [workload-kv-read-keycape-forgejo-forgejo]
token_no_default_policy: true
token_ttl: 15m
token_max_ttl: 15m
```
### catalog-entry
```
id: forgejo-keycape-client
owner_repo: key-cape
warden_executes: false
risk: high
status: active once verification passes
workload_ref: applicable, name forgejo, declaration_ref of the consumer's published declaration or the explicit unknown form, never inferred
path_template: platform/workloads/keycape/forgejo
field: CLIENT_SECRET
delegation: native, intended_owner key-cape
rotation: mint-secret then verification; automatable false until a run has been proven
```
## 4. Review notes
Shape reviewed once, when the type was acknowledged; this review checks only
this case's names against existing structure (section 2).
Follow-through (ops-warden):
- ensure-groups — net-kingdom (attended): identity-provisioner ensure_service_groups, run inside the provisioner pod through the session's contained cluster access (groups forge-admins,forge-coulomb-owners,forge-coulomb-readers; members exactly forge-admins=tegwick;forge-coulomb-owners=tegwick as recorded in the accepted case, nothing else)
- mint-secret — railiance-platform: railiance-platform credential broker: generate a client secret inside custody at bao:platform/workloads/keycape/forgejo
- deliver-secret — railiance-platform: ExternalSecret sso/keycape-forgejo-client (key client-secret) from bao:platform/workloads/keycape/forgejo
- bind-env — net-kingdom (attended): patch Deployment sso/keycape: env KEYCAPE_FORGEJO_CLIENT_SECRET from sso/keycape-forgejo-client
- register-client — net-kingdom (attended): sso-mfa/k8s/keycape/keycape-client.py apply --client-file <client-from-case> --case-acceptance <record> --check-config <keycape binary> (net-kingdom.keycape-client-registration)
- deliver-consumer-secret — railiance-apps: Owner applies dedicated ServiceAccount forgejo/forgejo-keycape-forgejo-eso (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-forgejo-consumer with serviceAccountRef and audiences [openbao], and ExternalSecret forgejo-netkingdom-oidc-client with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/forgejo. Admission validates actual installed ESO TokenRequest support and common controller RBAC before any owner-approved apply.
- hand-over — railiance-apps: configure forgejo with issuer https://kc.coulomb.social, client forgejo and custody bao:platform/workloads/keycape/forgejo, and make it require one of forge-admins,forge-coulomb-owners,forge-coulomb-readers in the groups claim before sign-in completes
Verification:
- authorize-accepts-redirects (present): GET https://kc.coulomb.social/authorize with client forgejo and each of https://forgejo.coulomb.social/user/oauth2/netkingdom/callback returns the login page
- authorize-refuses-other-redirect (deny): GET https://kc.coulomb.social/authorize with client forgejo and an unregistered redirect returns an OAuth error
- custody-denied-to-default (deny): a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/forgejo
- custody-allowed-to-consumer (allow): policy workload-kv-read-keycape-forgejo-forgejo grants read on bao:platform/workloads/keycape/forgejo (capabilities check only)
- groups-present (present): LLDAP groups forge-admins,forge-coulomb-owners,forge-coulomb-readers exist, with exactly the memberships of the accepted case added
- consumer-enforces-groups (present): forgejo's OIDC source for forgejo (owner railiance-apps) requires one of forge-admins,forge-coulomb-owners,forge-coulomb-readers in the groups claim (configuration read, no secret value)
- secret-delivered (present): the delivery for sso/keycape-forgejo-client reports Ready=True (status only)
- mfa-pinned (present): keycape-client.py inspect for forgejo shows the MFA pin on the registered client (non-secret client view)
- consumer-secret-delivered (present): forgejo SecretStore openbao-keycape-forgejo-consumer and ExternalSecret forgejo-netkingdom-oidc-client Ready=True, exact dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value read
Rollback, in order:
- remove the OIDC source for forgejo from forgejo (owner railiance-apps)
- After reverting consumer secret reference, delete only authenticated created ExternalSecret/owned target by UID and ownerReference, then SecretStore and dedicated SA; preserve preexisting-matched resources and never delete a shared target. Retain audit and ownership receipts; removing SecretStore/SA alone is not immediate OpenBao token revocation.
- keycape-client.py apply --operation remove for forgejo
- remove env KEYCAPE_FORGEJO_CLIENT_SECRET from Deployment sso/keycape, after register-client is reversed
- delete ExternalSecret sso/keycape-forgejo-client and its target
- covered by client-secret-path: one metadata delete, after deliver-secret is reversed
- identity-provisioner revoke_service_groups inside the provisioner pod (attended, as ensure-groups) with the case's memberships; delete_if_empty only if no other case uses forge-admins,forge-coulomb-owners,forge-coulomb-readers
- delete metadata and all versions at platform/workloads/keycape/forgejo, after the ExternalSecret is gone (this also reverses mint-secret)
- delete Kubernetes-auth role external-secrets-keycape-forgejo
- delete policy workload-kv-read-keycape-forgejo-eso, after eso-role
- Delete only this case-created role external-secrets-keycape-forgejo-forgejo, before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or prove expiration before declaring deactivation.
- delete policy workload-kv-read-keycape-forgejo-forgejo
- remove catalog entry forgejo-keycape-client
## 5. Case acceptance (replaces the executive summary)
Not accepted yet. The case must carry its acceptance record before a build.

View file

@ -0,0 +1,385 @@
{
"apiVersion": "netkingdom.io/security-case-render/v0.1",
"kind": "SecurityCaseRender",
"case": "forgejo-netkingdom-oidc",
"type": "oidc-browser-relying-party@1.2",
"composition": {
"apiVersion": "netkingdom.io/security-scenario-composition/v0.1",
"kind": "SecurityScenarioComposition",
"scenario": "scenario:case:forgejo-netkingdom-oidc",
"authority": "netkingdom",
"composition_digest": "sha256:f51e45eab2f2c15c93e2f7b8ef7d8fcc1daf1059da5749429ff5678b45c5214f",
"requested_capabilities": [
"c1.lightweight-sso"
],
"capability_providers": {
"c1.lightweight-sso": "net-kingdom.keycape-client-registration"
},
"initial_trust": [
"runtime_identity_trust",
"runtime_secret_trust"
],
"planned_final_trust": [
"runtime_identity_trust",
"runtime_secret_trust",
"tenant_onboarding_trust"
],
"execution": {
"mode": "plan-only",
"permitted": false,
"reason": "Composition does not authorize or perform provider execution."
},
"execution_steps": [
{
"order": 1,
"declaration_id": "net-kingdom.keycape-client-registration",
"executor_owner": "net-kingdom",
"repo": "net-kingdom",
"capabilities": [
"c1.lightweight-sso"
],
"entry_point": {
"path": "sso-mfa/k8s/keycape/keycape-client.py",
"type": "cli-sequence",
"invocation": "keycape-client.py apply --client-file <client.yaml> --case-acceptance <record> --expected-uid <uid> --expected-resource-version <rv>",
"description": "Add or remove exactly one client in the live KeyCape client registry without exporting secret values: contained kubectl, cluster and issuer pins, byte-preserving merge, compare-and-swap, read-back. Replaces the per-client helpers beside it. Field rules follow key-cape's canonical model (KEY-WP-0028). Modes: inspect, dry-run, apply; operations add and remove."
},
"parameters": {
"allowed_scopes": {
"value": "openid,profile,email,groups",
"source": "override",
"sensitivity": "security_sensitive",
"tuning_authority": "netkingdom_tunable"
},
"client_id": {
"value": "forgejo",
"source": "override",
"sensitivity": "operational",
"tuning_authority": "netkingdom_tunable"
},
"client_type": {
"value": "confidential",
"source": "override",
"sensitivity": "security_sensitive",
"tuning_authority": "netkingdom_tunable"
},
"display_name": {
"value": "Forgejo source forge",
"source": "override",
"sensitivity": "public",
"tuning_authority": "netkingdom_tunable"
},
"mfa_required": {
"value": "true",
"source": "override",
"sensitivity": "security_sensitive",
"tuning_authority": "netkingdom_tunable"
},
"redirect_uris": {
"value": "https://forgejo.coulomb.social/user/oauth2/netkingdom/callback",
"source": "override",
"sensitivity": "security_sensitive",
"tuning_authority": "netkingdom_tunable"
}
},
"requires_trust": [
{
"state": "runtime_secret_trust",
"readiness_checks": []
},
{
"state": "runtime_identity_trust",
"readiness_checks": []
}
],
"satisfies_trust": [
{
"state": "tenant_onboarding_trust",
"readiness_checks": [
{
"id": "client-authorize-accepts-registered-redirect",
"description": "The authorization endpoint accepts the client with each registered redirect URI.",
"evidence": "GET /authorize with client_id and each redirect_uri returns the login page, not an error."
},
{
"id": "client-authorize-refuses-other-redirect",
"description": "The authorization endpoint refuses an unregistered redirect URI for the client.",
"evidence": "GET /authorize with an unregistered redirect_uri returns an OAuth error."
}
]
}
],
"readiness_obligations": [
{
"state": "tenant_onboarding_trust",
"checks": [
{
"id": "client-authorize-accepts-registered-redirect",
"description": "The authorization endpoint accepts the client with each registered redirect URI.",
"evidence": "GET /authorize with client_id and each redirect_uri returns the login page, not an error."
},
{
"id": "client-authorize-refuses-other-redirect",
"description": "The authorization endpoint refuses an unregistered redirect URI for the client.",
"evidence": "GET /authorize with an unregistered redirect_uri returns an OAuth error."
}
]
}
]
}
],
"responsibility_map": [
{
"declaration_id": "net-kingdom.keycape-client-registration",
"resource_kind": "identities",
"owner": "net-kingdom",
"resources": [
"oidc:static-clients"
],
"repo_owns": "Live registry change for exactly this client, its idempotency and removal; field rules are key-cape's canonical model.",
"netkingdom_orchestrates": "Supply the case's validated, non-secret client fields."
},
{
"declaration_id": "net-kingdom.keycape-client-registration",
"resource_kind": "roles_scopes_policies",
"owner": "key-cape",
"resources": [
"iam-profile:client-scope-allowlists"
],
"repo_owns": "Enforce exact redirect URIs, allowed scopes and the MFA pin at authorization time.",
"netkingdom_orchestrates": "Choose scopes and MFA posture per SecurityCaseType."
}
]
},
"materialization": [
{
"id": "client-secret-path",
"kind": "kv_path",
"template": "platform/workloads/keycape/forgejo",
"body": "field CLIENT_SECRET; minted by the credential broker, never written by hand"
},
{
"id": "keycape-reader-policy",
"kind": "openbao_policy",
"template": "workload-kv-read-keycape-forgejo-eso",
"body": "path \"platform/data/workloads/keycape/forgejo\" {\n capabilities = [\"read\"]\n}\n",
"description": "KeyCape reads its client secret only through ESO. No metadata, list, parent or sibling access."
},
{
"id": "consumer-reader-policy",
"kind": "openbao_policy",
"template": "workload-kv-read-keycape-forgejo-forgejo",
"body": "path \"platform/data/workloads/keycape/forgejo\" {\n capabilities = [\"read\"]\n}\n",
"description": "The consuming service reads the same secret. No metadata, list, parent or sibling access."
},
{
"id": "eso-role",
"kind": "openbao_role",
"template": "external-secrets-keycape-forgejo",
"body": "auth: kubernetes\nbound_service_account_names: [external-secrets]\nbound_service_account_namespaces: [external-secrets]\ntoken_policies: [workload-kv-read-keycape-forgejo-eso]\ntoken_no_default_policy: true\ntoken_ttl: 15m\ntoken_max_ttl: 15m\n",
"description": "The Kubernetes-auth role deliver-secret uses; bound to the ESO service account only."
},
{
"id": "consumer-eso-role",
"kind": "openbao_role",
"template": "external-secrets-keycape-forgejo-forgejo",
"body": "auth: kubernetes\nbound_service_account_names: [forgejo-keycape-forgejo-eso]\nbound_service_account_namespaces: [forgejo]\naudience: openbao\ntoken_policies: [workload-kv-read-keycape-forgejo-forgejo]\ntoken_no_default_policy: true\ntoken_ttl: 15m\ntoken_max_ttl: 15m\n",
"description": "Consumer ESO requests a token for this separate reference identity. Common ESO controller remains trusted and can request tokens; no cryptographic controller isolation is claimed."
},
{
"id": "catalog-entry",
"kind": "catalog_entry",
"template": "forgejo-keycape-client",
"body": "id: forgejo-keycape-client\nowner_repo: key-cape\nwarden_executes: false\nrisk: high\nstatus: active once verification passes\nworkload_ref: applicable, name forgejo, declaration_ref of the consumer's published declaration or the explicit unknown form, never inferred\npath_template: platform/workloads/keycape/forgejo\nfield: CLIENT_SECRET\ndelegation: native, intended_owner key-cape\nrotation: mint-secret then verification; automatable false until a run has been proven\n",
"description": "One entry per case (ops-mason item 5, ops-warden R4'). It carries the consumer's workload reference, which ADR-0009 needs for zone selection."
}
],
"follow_through": [
{
"id": "ensure-groups",
"owner": "net-kingdom",
"entry_point": "identity-provisioner ensure_service_groups, run inside the provisioner pod through the session's contained cluster access (groups forge-admins,forge-coulomb-owners,forge-coulomb-readers; members exactly forge-admins=tegwick;forge-coulomb-owners=tegwick as recorded in the accepted case, nothing else)",
"auth": "operator cluster access in the attended register-client session; the in-pod call uses the provisioner's own directory binding, and no provisioner or user-engine token is read or sent",
"lane": "key-cape-oidc-login",
"warden_executes": false,
"attended": true,
"value_handling": "none",
"attended_justification": "The provisioner's only HTTP credential is user-engine's runtime token, and its NetworkPolicy admits only user-engine pods. A proxy lane would act as user-engine (ops-warden review R8; canon section 3; ops-warden ADR-0002). Running it in the session register-client already needs adds no attended session. It can become unattended once the provisioner has an operator credential of its own and an admitted path."
},
{
"id": "mint-secret",
"owner": "railiance-platform",
"entry_point": "railiance-platform credential broker: generate a client secret inside custody at bao:platform/workloads/keycape/forgejo",
"auth": "the caller's broker grant for the case's KV path, through its lane; the value is generated in custody and never returned",
"lane": "keycape-client-secret-generate",
"warden_executes": true,
"attended": false,
"value_handling": "generated_in_custody"
},
{
"id": "deliver-secret",
"owner": "railiance-platform",
"entry_point": "ExternalSecret sso/keycape-forgejo-client (key client-secret) from bao:platform/workloads/keycape/forgejo",
"auth": "railiance-platform ESO delivery through role external-secrets-keycape-forgejo; no value leaves custody",
"warden_executes": false,
"attended": false,
"value_handling": "none",
"ready_check": "the delivery for sso/keycape-forgejo-client reports Ready=True (status only, no value read)"
},
{
"id": "bind-env",
"owner": "net-kingdom",
"entry_point": "patch Deployment sso/keycape: env KEYCAPE_FORGEJO_CLIENT_SECRET from sso/keycape-forgejo-client",
"auth": "operator kube credentials for namespace sso",
"lane": "key-cape-oidc-login",
"warden_executes": false,
"attended": true,
"value_handling": "none",
"attended_justification": "A change to the live identity provider's Deployment. It runs in the same attended operator session as register-client, before it, because KeyCape resolves a confidential client's secret at start and fails closed if the binding is missing."
},
{
"id": "register-client",
"owner": "net-kingdom",
"entry_point": "sso-mfa/k8s/keycape/keycape-client.py apply --client-file <client-from-case> --case-acceptance <record> --check-config <keycape binary> (net-kingdom.keycape-client-registration)",
"auth": "operator write access to the live KeyCape client registry in namespace sso",
"lane": "key-cape-oidc-login",
"warden_executes": false,
"attended": true,
"value_handling": "none",
"attended_justification": "The live KeyCape client registry is a Kubernetes Secret, and agents do not read or write Secrets. Until KeyCape loads clients from a non-secret registry with secrets referenced from custody, one attended operator session applies the rendered registration. ops-warden groups all attended steps of a run into that one session. KeyCape loads clients at startup and keeps login, authorization and handoff state in memory, so the restart drops in-flight logins and sessions for every client; runs go in a quiet window (key-cape type review R4). The rendered registry must pass KeyCape's own startup checks (keycape check-config, key-cape c528daa, which keycape-client.py runs before it patches) before the restart: a refused entry stops the single replica and every client loses sign-in (R1)."
},
{
"id": "deliver-consumer-secret",
"owner": "railiance-apps",
"entry_point": "Owner applies dedicated ServiceAccount forgejo/forgejo-keycape-forgejo-eso (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-forgejo-consumer with serviceAccountRef and audiences [openbao], and ExternalSecret forgejo-netkingdom-oidc-client with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/forgejo. Admission validates actual installed ESO TokenRequest support and common controller RBAC before any owner-approved apply.",
"auth": "Consumer owner contained Kubernetes deployment authority; ESO authenticates to OpenBao role external-secrets-keycape-forgejo-forgejo with a TokenRequest for the dedicated namespaced serviceAccountRef; controller remains trusted",
"warden_executes": false,
"attended": false,
"value_handling": "none",
"ready_check": "Namespace SecretStore and ExternalSecret both report Ready=True; dedicated SA has no legacy token refs; status and fixed metadata only, never Secret values."
},
{
"id": "hand-over",
"owner": "railiance-apps",
"entry_point": "configure forgejo with issuer https://kc.coulomb.social, client forgejo and custody bao:platform/workloads/keycape/forgejo, and make it require one of forge-admins,forge-coulomb-owners,forge-coulomb-readers in the groups claim before sign-in completes",
"auth": "the consumer owner's own deployment path",
"warden_executes": false,
"attended": false,
"value_handling": "none"
}
],
"verification": [
{
"id": "authorize-accepts-redirects",
"check": "GET https://kc.coulomb.social/authorize with client forgejo and each of https://forgejo.coulomb.social/user/oauth2/netkingdom/callback returns the login page",
"expect": "present",
"reads_secret_value": false
},
{
"id": "authorize-refuses-other-redirect",
"check": "GET https://kc.coulomb.social/authorize with client forgejo and an unregistered redirect returns an OAuth error",
"expect": "deny",
"reads_secret_value": false
},
{
"id": "custody-denied-to-default",
"check": "a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/forgejo",
"expect": "deny",
"reads_secret_value": false
},
{
"id": "custody-allowed-to-consumer",
"check": "policy workload-kv-read-keycape-forgejo-forgejo grants read on bao:platform/workloads/keycape/forgejo (capabilities check only)",
"expect": "allow",
"reads_secret_value": false
},
{
"id": "groups-present",
"check": "LLDAP groups forge-admins,forge-coulomb-owners,forge-coulomb-readers exist, with exactly the memberships of the accepted case added",
"expect": "present",
"reads_secret_value": false
},
{
"id": "consumer-enforces-groups",
"check": "forgejo's OIDC source for forgejo (owner railiance-apps) requires one of forge-admins,forge-coulomb-owners,forge-coulomb-readers in the groups claim (configuration read, no secret value)",
"expect": "present",
"reads_secret_value": false
},
{
"id": "secret-delivered",
"check": "the delivery for sso/keycape-forgejo-client reports Ready=True (status only)",
"expect": "present",
"reads_secret_value": false
},
{
"id": "mfa-pinned",
"check": "keycape-client.py inspect for forgejo shows the MFA pin on the registered client (non-secret client view)",
"expect": "present",
"reads_secret_value": false
},
{
"id": "consumer-secret-delivered",
"check": "forgejo SecretStore openbao-keycape-forgejo-consumer and ExternalSecret forgejo-netkingdom-oidc-client Ready=True, exact dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value read",
"expect": "present",
"reads_secret_value": false
}
],
"rollback": [
{
"reverses": "hand-over",
"action": "remove the OIDC source for forgejo from forgejo (owner railiance-apps)"
},
{
"reverses": "deliver-consumer-secret",
"action": "After reverting consumer secret reference, delete only authenticated created ExternalSecret/owned target by UID and ownerReference, then SecretStore and dedicated SA; preserve preexisting-matched resources and never delete a shared target. Retain audit and ownership receipts; removing SecretStore/SA alone is not immediate OpenBao token revocation."
},
{
"reverses": "register-client",
"action": "keycape-client.py apply --operation remove for forgejo"
},
{
"reverses": "bind-env",
"action": "remove env KEYCAPE_FORGEJO_CLIENT_SECRET from Deployment sso/keycape, after register-client is reversed"
},
{
"reverses": "deliver-secret",
"action": "delete ExternalSecret sso/keycape-forgejo-client and its target"
},
{
"reverses": "mint-secret",
"action": "covered by client-secret-path: one metadata delete, after deliver-secret is reversed"
},
{
"reverses": "ensure-groups",
"action": "identity-provisioner revoke_service_groups inside the provisioner pod (attended, as ensure-groups) with the case's memberships; delete_if_empty only if no other case uses forge-admins,forge-coulomb-owners,forge-coulomb-readers"
},
{
"reverses": "client-secret-path",
"action": "delete metadata and all versions at platform/workloads/keycape/forgejo, after the ExternalSecret is gone (this also reverses mint-secret)"
},
{
"reverses": "eso-role",
"action": "delete Kubernetes-auth role external-secrets-keycape-forgejo"
},
{
"reverses": "keycape-reader-policy",
"action": "delete policy workload-kv-read-keycape-forgejo-eso, after eso-role"
},
{
"reverses": "consumer-eso-role",
"action": "Delete only this case-created role external-secrets-keycape-forgejo-forgejo, before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or prove expiration before declaring deactivation."
},
{
"reverses": "consumer-reader-policy",
"action": "delete policy workload-kv-read-keycape-forgejo-forgejo"
},
{
"reverses": "catalog-entry",
"action": "remove catalog entry forgejo-keycape-client"
}
],
"execution": {
"permitted": false
},
"digest": "sha256:c0afd5576cdae0cbc807eccb2b9fa87ac668f15e32ec7cf79d295bdb23c75b35"
}

View file

@ -0,0 +1,197 @@
diff --git a/docs/forgejo-structural-materialization-source-v1.md b/docs/forgejo-structural-materialization-source-v1.md
index 9175c5c..c4be07f 100644
--- a/docs/forgejo-structural-materialization-source-v1.md
+++ b/docs/forgejo-structural-materialization-source-v1.md
@@ -115,3 +115,31 @@ binding/application; mint is removed from its structural readiness blocker.
WARDEN-WP-0044-T03 and NK-WP-0048-T06 retain native follow-through, full current
run digest/machine source binding, verification and first live-case acceptance.
No case/type status, credential, policy, catalog activation or workload was changed.
+
+## Explicit source-root selection — 2026-10-16
+
+`plan`, `build` and `structure-request` accept `--types-root` and pass it to
+NetKingdom's existing exact-version loader. The default remains
+`<netkingdom-root>/security-case-types`; a requested version never falls forward
+or backward. Provider declarations still resolve from the NetKingdom repository's
+siblings, independently of the selected type root.
+
+Prepare the acknowledged 1.2 source projection without a live survey:
+
+```bash
+ops-mason plan --case ../net-kingdom/security-case-type-revisions/1.2/oidc-browser-relying-party/examples/forgejo.yaml --types-root ../net-kingdom/security-case-type-revisions/1.2 --no-live --allow-unimplemented
+```
+
+This actual case is proposed and has no acceptance. Its six-object draft includes
+the dedicated consumer ESO role bound to namespace `forgejo` and service account
+`forgejo-keycape-forgejo-eso`. Source-root selection grants no acceptance: normal
+admission still refuses the acknowledged type; `build` refuses the proposed case,
+and `structure-request` refuses the historical 1.1 plan/bundle for it. A current
+accepted case and reviewed plan are needed before regenerating a build/request.
+Do not repin historical acceptance or relabel an offline plan as reviewed.
+
+Six integration checks exercise these paths against the actual owner validator
+and source package, including malformed namespace refusal. This CLI compatibility
+change does not supply the owner-native contracts listed above. Machine descriptors,
+source receipt authentication, current CCR42 binding and native six-object proof
+remain with MASON-WP-0010-T03 / NK-WP-0048-T06 and their existing owners.
diff --git a/src/ops_mason/cli.py b/src/ops_mason/cli.py
index 2f0210a..3731077 100644
--- a/src/ops_mason/cli.py
+++ b/src/ops_mason/cli.py
@@ -53,6 +53,7 @@ def _parser() -> argparse.ArgumentParser:
help="skip the live OpenBao survey (marks the plan's survey source 'none')",
)
plan.add_argument("--netkingdom-root", type=Path, default=None)
+ plan.add_argument("--types-root", type=Path, help="explicit exact-version SecurityCaseType root")
plan.add_argument(
"--allow-unimplemented",
action="store_true",
@@ -74,6 +75,7 @@ def _parser() -> argparse.ArgumentParser:
help="reference case only: build against an acknowledged type",
)
build.add_argument("--netkingdom-root", type=Path, default=None)
+ build.add_argument("--types-root", type=Path, help="explicit exact-version SecurityCaseType root")
structure = families.add_parser(
"structure-request", help="read-only typed structural evidence request"
)
@@ -81,6 +83,7 @@ def _parser() -> argparse.ArgumentParser:
structure.add_argument("--plan", required=True, type=Path)
structure.add_argument("--bundle", required=True, type=Path)
structure.add_argument("--netkingdom-root", type=Path)
+ structure.add_argument("--types-root", type=Path, help="explicit exact-version SecurityCaseType root")
structure.add_argument("--allow-unimplemented", action="store_true")
return parser
@@ -101,6 +104,7 @@ def _build(args) -> int:
args.plan,
out,
nk_root=nk_root,
+ types_root=args.types_root,
allow_unimplemented=args.allow_unimplemented,
)
except (
@@ -134,6 +138,7 @@ def _plan(args) -> int:
doc, collisions, rendered = case_plan.plan_from_case(
args.case,
nk_root=nk_root,
+ types_root=args.types_root,
structure=case_plan.Structure() if args.no_live else None,
allow_unimplemented=args.allow_unimplemented,
)
@@ -171,6 +176,7 @@ def main(argv: Sequence[str] | None = None) -> int:
args.plan,
args.bundle,
nk_root=args.netkingdom_root or case_plan.DEFAULT_NK_ROOT,
+ types_root=args.types_root,
allow_unimplemented=args.allow_unimplemented,
)
except Exception:
diff --git a/tests/test_case_version_source.py b/tests/test_case_version_source.py
new file mode 100644
index 0000000..49aee70
--- /dev/null
+++ b/tests/test_case_version_source.py
@@ -0,0 +1,84 @@
+"""Actual NetKingdom 1.2 source selection; no accepted/native case fixture."""
+import json
+from pathlib import Path
+
+import pytest
+import yaml
+
+from ops_mason import case_plan, cli
+
+NK = case_plan.DEFAULT_NK_ROOT
+TYPES = NK / "security-case-type-revisions/1.2"
+CASE = TYPES / "oidc-browser-relying-party/examples/forgejo.yaml"
+pytestmark = pytest.mark.skipif(not CASE.is_file(), reason="NetKingdom 1.2 source absent")
+
+
+def test_explicit_source_root_renders_actual_six_object_draft(tmp_path, capsys):
+ output = tmp_path / "plan.md"
+ assert cli.main(["plan", "--case", str(CASE), "--types-root", str(TYPES),
+ "--no-live", "--allow-unimplemented", "--out", str(output)]) == 0
+ plan = output.read_text()
+ front = yaml.safe_load(plan.split("---\n", 2)[1])
+ assert front["status"] == "draft"
+ assert front["case_acceptance"] is None
+ assert front["derived_from_type"] == "oidc-browser-relying-party@1.2"
+ assert front["reference_case"] is True
+ case, doc, rendered = case_plan.render(CASE, types_root=TYPES, allow_unimplemented=True)
+ assert case["spec"]["status"] == "proposed"
+ assert "acceptance" not in case["spec"]
+ assert doc["spec"]["status"] == "acknowledged"
+ assert len(rendered["materialization"]) == 6
+ role = next(item for item in rendered["materialization"]
+ if item["id"] == "consumer-eso-role")
+ body = yaml.safe_load(role["body"])
+ assert body["bound_service_account_namespaces"] == ["forgejo"]
+ assert body["bound_service_account_names"] == ["forgejo-keycape-forgejo-eso"]
+ assert front["render_digest"] == rendered["digest"]
+ assert json.loads(capsys.readouterr().err)["ok"] is True
+
+
+def test_explicit_root_does_not_waive_implemented_gate(tmp_path, capsys):
+ output = tmp_path / "plan.md"
+ assert cli.main(["plan", "--case", str(CASE), "--types-root", str(TYPES),
+ "--no-live", "--out", str(output)]) == 2
+ assert not output.exists()
+ assert "only implemented types" in capsys.readouterr().err
+
+
+def test_missing_explicit_root_never_falls_forward_to_12(tmp_path, capsys):
+ output = tmp_path / "plan.md"
+ assert cli.main(["plan", "--case", str(CASE), "--no-live",
+ "--allow-unimplemented", "--out", str(output)]) == 2
+ assert not output.exists()
+ assert "not 1.2" in capsys.readouterr().err
+
+
+def test_proposed_case_cannot_build_even_with_selected_acknowledged_source(tmp_path, capsys):
+ output = tmp_path / "bundle"
+ assert cli.main(["build", "--case", str(CASE), "--types-root", str(TYPES),
+ "--allow-unimplemented", "--plan", str(tmp_path / "absent-plan.md"),
+ "--out", str(output)]) == 2
+ assert not output.exists()
+ assert "case is not accepted" in capsys.readouterr().err
+
+
+def test_historical_bundle_cannot_attest_12_inventory(tmp_path, capsys):
+ owner = Path("/home/worsch/ops-mason")
+ assert cli.main(["structure-request", "--case", str(CASE), "--types-root", str(TYPES),
+ "--allow-unimplemented", "--plan", str(owner / "plans/case-forgejo-netkingdom-oidc.md"),
+ "--bundle", str(owner / "builds/case-forgejo-netkingdom-oidc")]) == 2
+ captured = capsys.readouterr()
+ assert captured.out == ""
+ assert json.loads(captured.err)["error"] == "structural-source-preparation-refused"
+
+
+def test_actual_validator_refuses_invalid_consumer_namespace(tmp_path, capsys):
+ case = yaml.safe_load(CASE.read_text())
+ case["spec"]["parameters"]["consumer_namespace"] = "../../other"
+ source = tmp_path / "case.yaml"
+ source.write_text(yaml.safe_dump(case))
+ assert cli.main(["plan", "--case", str(source), "--types-root", str(TYPES),
+ "--no-live", "--allow-unimplemented"]) == 2
+ captured = capsys.readouterr()
+ assert captured.out == ""
+ assert "consumer_namespace" in captured.err
diff --git a/workplans/MASON-WP-0010-security-case-materialization.md b/workplans/MASON-WP-0010-security-case-materialization.md
index 3fb2e46..4b1a1fd 100644
--- a/workplans/MASON-WP-0010-security-case-materialization.md
+++ b/workplans/MASON-WP-0010-security-case-materialization.md
@@ -247,3 +247,14 @@ parameter/acceptance field; it writes and signs nothing. Details and exact nativ
`docs/forgejo-structural-materialization-source-v1.md`. T03 remains progress;
case remains accepted@1.1, not materialized/live. CCR42 consumer auth remains
in_flight, and native owner evidence is not manufactured by source preparation.
+
+2026-10-16 (T03 source compatibility): `plan`, `build` and
+`structure-request` now expose explicit `--types-root`, passing it to the existing
+exact-version NetKingdom API. Actual acknowledged 1.2 source renders the proposed
+Forgejo case into a six-object **draft**, preserving its absent acceptance.
+Six actual-source integration checks prove the root selection, dedicated consumer
+namespace/SA, ordinary unimplemented refusal, proposed build refusal, historical
+1.1 bundle refusal and malformed namespace refusal. Existing 44 plan/build/
+structure tests pass. No live survey, execution, case/type status, acceptance,
+native owner evidence or CCR was changed. T03 remains progress; actual accepted
+1.2 case/reviewed source and authenticated native owner evidence remain required.

View file

@ -0,0 +1,34 @@
{
"scope": "MASON-WP-0010-T03 source compatibility only",
"owner": "ops-mason",
"base_revision": "e4761b843cd881a672cbb33a5b451de31a61d730",
"isolated_checkout": "/tmp/netkingdom-mason-browser-20261016",
"netkingdom_revision": "c31d21ba7d15624940e8858cd5f31ceb57d82081",
"changed_source": {
"src/ops_mason/cli.py": "sha256:9cb202051a1c329e92a64841655325f7a43d9906d23f62c8bb9c242cd96fe5db",
"tests/test_case_version_source.py": "sha256:e07e6f64cbf0237117cb04dc888f58ef7601d6f50e3accd60a7884c7f19d8a1e",
"docs/forgejo-structural-materialization-source-v1.md": "sha256:7c893fef2bd8c3726f0ed1b7c4e256142cecb3a11c0a25e8c631a850dc3eb71e",
"workplans/MASON-WP-0010-security-case-materialization.md": "sha256:7cfb52adc2e03de92a654678b852f6f35ebdcd60d7309a77bcdac5e972b6c462"
},
"exact_inputs": {
"tools/security-case-types/security_case_validator.py": "sha256:cbb9ab532d328f78cf37aca57891400e2e65d63a8ab3c5fe7bd1a4ff21dee56f",
"security-case-type-revisions/1.2/oidc-browser-relying-party/type.yaml": "sha256:9e60ff569fb0d860a2bbde7bbb0991a54d4b0688f62b56df0fa5d161fd624578",
"security-case-type-revisions/1.2/oidc-browser-relying-party/examples/forgejo.yaml": "sha256:1c868c1da7d9077fae97bc6bc029706ec3dc418cfeaae30dc0f92b5b4ba6f2ab"
},
"artifacts": {
"README.md": "sha256:9d8891682c62b7736f51f2d6f239fa84d47aa147c5f855850ecdcf1388dd7b6a",
"forgejo-1.2-draft-plan.md": "sha256:f61005f9da3651ca4ca4770801d2aa923e80cf1a831fb9f2b692f9b03d5e6066",
"forgejo-1.2-source-render.json": "sha256:c4b57333980f5469953bc7fed1f1c2188b89b06fd69e542fc72b60a782c36cd0",
"implementation.patch": "sha256:cfe7648ed119d83faca228a2f21611c9388de1ba166598cbeb64f40e81620067",
"validation.json": "sha256:44e5d9b8a3b4a4ab13a3a91aaa29927dc9a144217346091fcf2d19287c3731f5"
},
"acceptance_created": false,
"native_materialization_verified": false,
"execution_permitted": false,
"canonical_owner_checkout_edited": false,
"canonical_dirty_paths_preserved": [
".custodian-brief.md",
"WORK-RECORDS.md",
"workplans/MASON-WP-0013-reuse-surface-private-source-reader.md"
]
}