diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/initial-syntax-failure.py.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/initial-syntax-failure.py.txt new file mode 100644 index 0000000..3fb32af --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/initial-syntax-failure.py.txt @@ -0,0 +1,79 @@ +from pathlib import Path +import hashlib,json,subprocess,copy,yaml,re,importlib.util,sys +p=Path(__file__).resolve().parent;r=Path('/home/worsch/net-kingdom');base='342a6f25af5cdc602841948f42759ea52ef1c8f5' +def git(root,*a):return subprocess.check_output(['git','-C',str(root),*a],text=True).strip() +assert git(r,'rev-parse','HEAD')==base +assert git(Path('/home/worsch/railiance-platform'),'rev-parse','HEAD')=='104ed90f42a9a86bf9fda343b49b65a85c9ee4b9' +template=Path('/home/worsch/railiance-platform/manifests/templates/keycape-issuer-eso.yaml') +assert hashlib.sha256(template.read_bytes()).hexdigest()=='e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743' +name='security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml';old=(r/name).read_bytes();old_doc=yaml.safe_load(old) +assert hashlib.sha256(old).hexdigest()=='e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a' +(p/'type.preimage.yaml').write_bytes(old) +patch=p.parent/'nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch' +assert hashlib.sha256(patch.read_bytes()).hexdigest()=='6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61' +subprocess.run(['git','apply','--check',str(patch.resolve())],cwd=r,check=True) +subprocess.run(['git','apply',str(patch.resolve())],cwd=r,check=True) +new=(r/name).read_bytes();new_doc=yaml.safe_load(new) +a=copy.deepcopy(old_doc);b=copy.deepcopy(new_doc) +for doc in [a,b]: + step=next(x for x in doc['spec']['follow_through'] if x['id']=='deliver-secret');step['entry_point']='same-entry-removed-for-comparison' +assert a==b,'only entry_point may change' +assert old_doc['spec']['parameters']==new_doc['spec']['parameters'] +assert old_doc['spec']['execution_descriptors']==new_doc['spec']['execution_descriptors'] +assert new_doc.get('metadata',{}).get('status')==old_doc.get('metadata',{}).get('status') +# Compose by importing the read-only seven-adapter composer, never dispatch. +sys.path.insert(0,'/home/worsch/ops-warden/src');sys.dont_write_bytecode=True +module=r/'sso-mfa/k8s/keycape/compose-1.2-descriptors.py' +spec=importlib.util.spec_from_file_location('nk48_composer',module);c=importlib.util.module_from_spec(spec);spec.loader.exec_module(c) +artifact=r/'sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json';before=artifact.read_bytes();doc=json.loads(before) +regenerated=(json.dumps(c.artifact(doc['check_config_digest'],doc['params'],'1.3'),indent=2,sort_keys=True)+'\n').encode() +assert regenerated==before,'Unexpected descriptor/artifact delta; inspect before writing' +(p/'composition-regenerated.json').write_bytes(regenerated) +wp=r/'workplans/NK-WP-0048-security-case-types.md';text=wp.read_text();(p/'workplan.preimage.md').write_text(text) +for task,status in [('NK-WP-0048-T33','done'),('NK-WP-0048-T34','progress')]: + match=re.search(r'```task\nid: '+task+r'\n.*?```',text,re.S);assert match + before_block=match.group();assert 'status: todo\n' in before_block + text=text[:match.start()]+before_block.replace('status: todo\n','status: '+status+'\n')+text[match.end():] +needle='## T34 — Close the consumer configuration inverse finding' +completion="""2026-10-17 completed exact proposed-source reference correction: Platform +published template, metadata helper and eight focused tests in +`104ed90f42a9a86bf9fda343b49b65a85c9ee4b9`; independent source review +`nk48-r5-f2-review-r1` found no blocking source-fit defect. The proposed 1.3 +entry now names ClusterSecretStore `openbao-keycape-{client_id}-issuer`, cluster +scope restricted to `sso`, and the exact Platform template digest. Only that +entry text changes; parameters and all seven descriptors are unchanged, and +regeneration produces the identical not-an-admission composition artifact. +This closes the coordinator's vague-reference source finding only. It supplies +no formal owner assent, existing live store, installed ESO compatibility, +real delivery/removal/session capability, grant or native acceptance; existing +RPF-1.3-T4 and T16 keep those obligations. F3 is independent and remains open. +Evidence: Custodian `nk48-r5-f2-publication-r1/` and +`nk48-issuer-reference-integration-r1/` under the existing 17 October packet. + +""" +assert text.count(needle)==1;text=text.replace(needle,completion+needle) +needle='## T35 — Align source admission fixtures with the authenticated catalog and attended-session API' +note="""2026-10-17 implementation progress: the fresh independent Apps review +reproduced a missing fresh inverse-session boundary. Three new regressions fail +the original wrapper. Corrected r2 requires a separately authenticated inverse +session before the release-writer lock and revokes it on every exit; 19 tests / +33 subtests pass, and removing the session guard is detected. This remains a +source candidate: direct Apps source inspection found no real owner transaction, +durable journal, all-release-writer exclusion or inverse-session backend to bind. +The four exact missing owner facilities and smallest existing T05 implementation +are retained in Custodian `nk48-r5-f3-implementation-r2/owner-backend-next.md`. +T34 progresses for this correction and investigation; it does not claim the real +backend, accepted type placement, owner assent or F3 closure. The unsafe original +candidate/review remain frozen. Do not integrate its type prose as an implemented +capability or reuse the revoked hand-over session. + +""" +assert text.count(needle)==1;text=text.replace(needle,note+needle) +text=text.replace('and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed.','and `nk48-catalog-fixture-publication-r2/`. Root canonical validation passes +31 tests / 53 subtests against Warden `922c26d` (the same reviewed code plus +one normal allocator-only source UUID commit). Publication receipts belong +in that packet.') +wp.write_text(text) +(p/'verification.json').write_text(json.dumps({'status':'integrated-proposed-source-only','only_type_delta':'deliver-secret.entry_point','type_before_sha256':hashlib.sha256(old).hexdigest(),'type_after_sha256':hashlib.sha256(new).hexdigest(),'parameters_unchanged':True,'seven_descriptors_unchanged':True,'composition_byte_identical':True,'T33':'done: exact source reference only','T34':'progress: real owner backend absent','owner_assent':False,'native_action':False},indent=2)+'\n') +print('Applied exact issuer reference, verified unchanged descriptors/parameters/artifact; recorded bounded T33 completion and T34 progress.') diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/type.preimage.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/type.preimage.yaml new file mode 100644 index 0000000..6ec7560 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/type.preimage.yaml @@ -0,0 +1,495 @@ +apiVersion: netkingdom.io/security-case-type/v0.1 +kind: SecurityCaseType +spec: + id: oidc-browser-relying-party + version: '1.3' + status: proposed + title: Browser OIDC relying party for an internal service + purpose: '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).' + parameters: + - name: client_id + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{1,62}$ + sensitivity: operational + description: OAuth2 client_id, unique in KeyCape. + - name: display_name + type: string + case_settable: true + constraints: + pattern: ^[^\n\r]{1,80}$ + sensitivity: public + description: Name shown on the sign-in page. + - name: redirect_uris + type: string + case_settable: true + constraints: + pattern: ^https://[^*?#\s,]+(,https://[^*?#\s,]+)*$ + sensitivity: security_sensitive + description: 'Comma-separated exact https redirect URIs. No wildcards, no plain + http, no query and no fragment: KeyCape refuses ''*'' and ''?'' at startup, + and OAuth 2.0 forbids fragments (key-cape type review R1).' + - name: allowed_scopes + type: string + case_settable: true + constraints: + enum: + - openid,profile,email,groups + - openid,email,groups + - openid,profile,groups + - openid,groups + sensitivity: security_sensitive + description: Scopes the client may request, openid first. Always includes groups, + because access_groups is required and KeyCape's UserInfo returns groups only + for that scope (key-cape type review R2). + - name: client_type + type: string + case_settable: true + constraints: + enum: + - confidential + - public + sensitivity: security_sensitive + description: confidential for server-side services that can hold a secret; public + for SPAs and native apps (PKCE only). + - name: mfa_required + type: string + case_settable: true + constraints: + enum: + - 'true' + - 'false' + sensitivity: security_sensitive + description: '"true" pins MFA on this client; required when the service grants + administrative or owner rights. "false" is not "no MFA": the client then carries + no mfaRequired field and KeyCape applies the per-user privacyIDEA policy. A + renderer must never emit an explicit mfaRequired false, which would skip that + policy (key-cape type review R3).' + - name: access_groups + type: string + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]{1,62}(,[a-z][a-z0-9-]{1,62})*$ + sensitivity: operational + description: 'Comma-separated LLDAP groups that the service maps to its roles. + Created if absent. KeyCape has no per-client group gate: it issues tokens to + any authenticated user and only carries the groups claim, so the consuming service + enforces the gate itself (for Forgejo, its required-claim setting; key-cape + type review R2).' + - name: initial_members + type: string + required: false + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*(;[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*)*$ + sensitivity: operational + description: Initial memberships as group=user+user;group=user. Usernames are + directory usernames, never secrets. + - name: consuming_service + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{0,62}$ + sensitivity: operational + description: The service that signs users in with this client. + - name: consumer_owner_repo + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]*$ + sensitivity: operational + description: The repository that owns the consuming service's configuration. + - name: consumer_namespace + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ + sensitivity: security_sensitive + description: Consumer namespace for namespaced ESO TokenRequest reader; actual + workload owner must confirm; never infer from service name. + - name: issuer + type: string + case_settable: false + default: https://kc.coulomb.social + sensitivity: security_sensitive + description: The NetKingdom issuer; fixed by the type. + composition: + authority: netkingdom + initial_trust: + - runtime_secret_trust + - runtime_identity_trust + capabilities: + - c1.lightweight-sso + providers: + c1.lightweight-sso: net-kingdom.keycape-client-registration + parameter_bindings: + net-kingdom.keycape-client-registration.client_id: client_id + net-kingdom.keycape-client-registration.display_name: display_name + net-kingdom.keycape-client-registration.redirect_uris: redirect_uris + net-kingdom.keycape-client-registration.allowed_scopes: allowed_scopes + net-kingdom.keycape-client-registration.client_type: client_type + net-kingdom.keycape-client-registration.mfa_required: mfa_required + materialization: + - id: client-secret-path + kind: kv_path + when: client_type == confidential + template: platform/workloads/keycape/{client_id} + body: field CLIENT_SECRET; minted by the credential broker, never written by hand + - id: keycape-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-eso + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\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 + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-{consuming_service} + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\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 + when: client_type == confidential + template: external-secrets-keycape-{client_id} + body: 'auth: kubernetes + + bound_service_account_names: [external-secrets] + + bound_service_account_namespaces: [external-secrets] + + token_policies: [workload-kv-read-keycape-{client_id}-eso] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: The Kubernetes-auth role deliver-secret uses; bound to the ESO service + account only. + - id: consumer-eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id}-{consuming_service} + body: 'auth: kubernetes + + bound_service_account_names: [{consuming_service}-keycape-{client_id}-eso] + + bound_service_account_namespaces: [{consumer_namespace}] + + audience: openbao + + token_policies: [workload-kv-read-keycape-{client_id}-{consuming_service}] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + 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: '{consuming_service}-keycape-client' + body: 'id: {consuming_service}-keycape-client + + owner_repo: key-cape + + warden_executes: false + + risk: high + + status: active once verification passes + + workload_ref: applicable, name {consuming_service}, declaration_ref of the consumer''s + published declaration or the explicit unknown form, never inferred + + path_template: platform/workloads/keycape/{client_id} + + field: CLIENT_SECRET + + delegation: native, intended_owner key-cape + + rotation: mint-secret then verification; automatable false until a run has been + proven + + ' + 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: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change + 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. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: mint-secret + owner: railiance-platform + when: client_type == confidential + entry_point: 'railiance-platform credential broker: generate a client secret inside + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and 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 + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through the ESO store for + role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery + warden_executes: false + attended: false + value_handling: none + ready_check: the delivery for sso/keycape-{client_id}-client reports Ready=True + (status only, no value read) + - id: bind-env + owner: net-kingdom + when: client_type == confidential + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change + 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. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: register-client + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change + 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, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, 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). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' + - id: deliver-consumer-secret + owner: '{consumer_owner_repo}' + when: client_type == confidential + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer + with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client + with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. + Admission validates actual installed ESO TokenRequest support and common controller + RBAC before any owner-approved apply. + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment + warden_executes: false + attended: true + value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. + 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: '{consumer_owner_repo}' + entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} + with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} in the groups claim before sign-in completes. + Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case + kernel inside the attended consumer-owner session, verifies the consumer with + four value-free probes (member-login, non-member-refused, membership-add-grants, + membership-remove-revokes) and records the hand-over receipt. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path + lane: apps-consumer-handover + warden_executes: false + attended: true + value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. + verification: + - id: authorize-accepts-redirects + check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} + returns the login page + expect: present + reads_secret_value: false + - id: authorize-refuses-other-redirect + check: GET {issuer}/authorize with client {client_id} and an unregistered redirect + returns an OAuth error + expect: deny + reads_secret_value: false + - id: custody-denied-to-default + when: client_type == confidential + check: a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/{client_id} + expect: deny + reads_secret_value: false + - id: custody-allowed-to-consumer + when: client_type == confidential + check: policy workload-kv-read-keycape-{client_id}-{consuming_service} grants + read on bao:platform/workloads/keycape/{client_id} (capabilities check only) + expect: allow + reads_secret_value: false + - id: groups-present + check: LLDAP groups {access_groups} exist, with exactly the memberships of the + accepted case added + expect: present + reads_secret_value: false + - id: consumer-enforces-groups + check: '{consuming_service}''s OIDC source for {client_id} (owner {consumer_owner_repo}) + requires one of {access_groups} in the groups claim (configuration read, no + secret value)' + expect: present + reads_secret_value: false + - id: secret-delivered + when: client_type == confidential + check: the delivery for sso/keycape-{client_id}-client reports Ready=True (status + only) + expect: present + reads_secret_value: false + - id: mfa-pinned + when: mfa_required == true + check: keycape-client.py inspect for {client_id} shows the MFA pin on the registered + client (non-secret client view) + expect: present + reads_secret_value: false + - id: consumer-secret-delivered + when: client_type == confidential + check: '{consumer_namespace} SecretStore openbao-keycape-{client_id}-consumer + and ExternalSecret {consuming_service}-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: the adapter removes only its hand-over receipt. Removing the OIDC source + for {client_id} from {consuming_service} is the owner's Helm rollback (owner + {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove + before any consumer object is removed + - reverses: deliver-consumer-secret + when: client_type == confidential + 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 {client_id} + - reverses: bind-env + when: client_type == confidential + action: remove env KEYCAPE_{client_id:env}_CLIENT_SECRET from Deployment sso/keycape, + after register-client is reversed + - reverses: deliver-secret + when: client_type == confidential + action: delete ExternalSecret sso/keycape-{client_id}-client and its target + - reverses: mint-secret + when: client_type == confidential + 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 {access_groups} + - reverses: client-secret-path + when: client_type == confidential + action: delete metadata and all versions at platform/workloads/keycape/{client_id}, + after the ExternalSecret is gone (this also reverses mint-secret) + - reverses: eso-role + when: client_type == confidential + action: delete Kubernetes-auth role external-secrets-keycape-{client_id} + - reverses: keycape-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-eso, after eso-role + - reverses: consumer-eso-role + when: client_type == confidential + action: Delete only this case-created role external-secrets-keycape-{client_id}-{consuming_service}, + before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or + prove expiration before declaring deactivation. + - reverses: consumer-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-{consuming_service} + - reverses: catalog-entry + action: remove catalog entry {consuming_service}-keycape-client + acceptance: + accepted_by: + - owner + - founder + risk_class: medium + # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are + # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. + execution_descriptors: + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 + deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 + mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/verification.json b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/verification.json new file mode 100644 index 0000000..b098063 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/verification.json @@ -0,0 +1,13 @@ +{ + "status": "integrated-proposed-source-only", + "only_type_delta": "deliver-secret.entry_point", + "type_before_sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "type_after_sha256": "ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f", + "parameters_unchanged": true, + "seven_descriptors_unchanged": true, + "composition_byte_identical": true, + "T33": "done: exact source reference only", + "T34": "progress: real owner backend absent", + "owner_assent": false, + "native_action": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/workplan.preimage.md b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/workplan.preimage.md new file mode 100644 index 0000000..4589b2b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/workplan.preimage.md @@ -0,0 +1,1648 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +state_hub_task_id: "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. + +## T35 — Align source admission fixtures with the authenticated catalog and attended-session API + +```task +id: NK-WP-0048-T35 +status: done +priority: high +state_hub_task_id: "cf1d6ab3-846e-53ea-8575-d6347b6f4769" +``` + +Completed 2026-10-17, source fixture compatibility only. Distinct from T32's +independent agent-mint regression repair: both remaining opaque catalog byte +placeholders now use one well-formed synthetic catalog whose exact bytes are +bound by the fixture evidence verifier. The fixture mirrors the six relevant +Warden lane shapes; four draft statuses are hypothetically active, while both +Apps establishing login references remain unresolved. No real catalog entry, +owner login choice or production parameter pin changes. + +Against the reviewed mode-aware Warden source now integrated at +`ae7bc73b4f4bac22454144959b870d161498e0af`, scratch validation passes 31 tests +and 53 subtests. Five resolvable 1.3 descriptors conditionally admit under +fixture-only acknowledgment and pinning; both Apps attended steps refuse before +execution. Changed catalog bytes refuse, all independent mint-guard tests still +reach their intended guard, and the registration fixture's actual kernel +run/rollback and uncertain-result paths pass with synthetic owner callbacks. +The registration fixture explicitly aligns its in-memory proposed type lane +with its current descriptor; canonical 1.1 is unchanged. + +The same tests against predecessor `117fec9241e7b985dfb187b992e173d0a5dc3700` +pass 31 tests and 51 subtests, with only the two new Apps refusal assertions +failing because that predecessor lacks the attended-session catalog check. +Tests target the reviewed current API; the parser or Apps gate is not weakened +for predecessor compatibility. Evidence and root source review: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/` +and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed. This records source +fixture work, not native T03, owner assent, exact-type acknowledgment, PIN-1, +composition/admission completion under T16 or the native first case under T06. +T33 and T34 retain their existing issuer-store and consumer-inverse work. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/README.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/README.md new file mode 100644 index 0000000..597dad8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/README.md @@ -0,0 +1,11 @@ +# NK48-R5-F1 successor coverage, prepared only + +The patch changes only `sso-mfa/k8s/keycape/test_admit_1_3_type.py`; it has not been applied to the owner checkout, committed or published. Source base: `3236d5000e52e0643f33906a26a7825aa0b67465`. Kernel dependency: frozen `117fec9241e7b985dfb187b992e173d0a5dc3700`. + +The committed-map refusal now explicitly includes an agent-actor mint descriptor. A separate fixture-map test acknowledges precisely that changed descriptor digest, allowing admission to reach the independent `agent mint is forbidden` guard. The human descriptor is also tested with an explicit fixture `WARDEN_AGENT_ID`; the helper sets it for that call instead of clearing it. Existing changed-caller coverage remains. + +Focused results: original tests pass against both frozen and scratch guard-removed kernels (8 tests / 31 subtests each). Successor tests pass against the real frozen kernel (10 tests / 32 subtests). Removing only its admission agent-mint guard in a separate scratch kernel makes exactly both new guard tests fail (2 failed, 8 passed / 32 subtests). The dispatch-time guard remains present in the mutant; no adapter is dispatched. + +`test-admit-1-3-agent-mint-coverage.patch` is the proposed owner patch. `scratch-kernel-mutation.patch` is negative-control evidence only and MUST NOT be applied to any canonical owner source. Preimage, successor, exact commands/results and hashes are retained beside this note. The runner requires the exported scratch trees named in scratch.json; it refuses a mismatched guard and modifies only those scratch trees. + +These are fixture admission checks. Type acknowledgment, the 1.3 parameter pin and the changed descriptor-map entry exist only in memory inside tests. No actual type acknowledgment, catalog/pin change, kernel acceptance, native execution or grant is established. R5 F2/F3 and owner/kernel follow-through are unchanged. Root review/integration is the next step; no canonical task refinement is authored in this preparation-only packet. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-application.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-application.json new file mode 100644 index 0000000..ad0abb5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-application.json @@ -0,0 +1,8 @@ +{ + "status": "applied_test_only", + "path": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "preimage_sha256": "sha256:f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "after_sha256": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "native_actions": 0, + "kernel_or_type_pin_changes": 0 +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-net-kingdom-status-after.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-net-kingdom-status-after.txt new file mode 100644 index 0000000..8f7c1ab --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-net-kingdom-status-after.txt @@ -0,0 +1,6 @@ + M .custodian-brief.md + M WORK-RECORDS.md +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/ +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json +?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-validation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-validation.json new file mode 100644 index 0000000..4619718 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-validation.json @@ -0,0 +1,8 @@ +{ + "command": "env PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/home/worsch/ops-warden/src /home/worsch/ops-warden/.venv/bin/python -B -m pytest -q -p no:cacheprovider test_admit_1_3_type.py", + "working_directory": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape", + "result": "10 passed, 32 subtests passed in 5.05s", + "exit_code": 0, + "source_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "publication_status": "not committed; remaining source implementation work retained" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/implementation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/implementation.json new file mode 100644 index 0000000..cef00a9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/implementation.json @@ -0,0 +1,112 @@ +{ + "schema": "custodian.nk48-r5-f1-source-implementation.v1", + "status": "prepared-tested-not-applied", + "finding": "NK48-R5-F1", + "source_base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "reviewed_code_base": "001a38ca2aa85155ce4840c4da22aca6e2346171", + "kernel_base": "117fec9241e7b985dfb187b992e173d0a5dc3700", + "target": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "preimage_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "successor_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "frozen_r5_review_sha256": "e3016352c58178894ccbfc600b960d3f9e2b5d534a05b8df2c5b79737cb0127a", + "canonical_test_still_matches_preimage": true, + "canonical_edits": false, + "canonical_commit_or_publish": false, + "kernel_mutation_scratch_only": true, + "mutant_changed_files": [ + "src/warden/case_run.py" + ], + "native_actions": false, + "grants": false, + "knowledge_orientation": { + "package": "0.2.6", + "digest": "8d1d6a9d4d07fec7b619618de7dfe2254c1790f85f4198c7fe19db9751c02", + "cards": { + "start": "withheld-source-changed", + "identity": "withheld-source-changed" + }, + "direct_source_review": [ + "net-kingdom/AGENTS.md", + "net-kingdom/INTENT.md", + "net-kingdom/SECURITY-COMPANION.md", + "ops-warden/AGENTS.md", + "ops-warden/INTENT.md", + "ops-warden frozen case_run admission guard" + ] + }, + "scope": "Only successor test_admit_1_3_type.py coverage. Fixture type status, map and parameter pin are memory-only test setup. No acknowledgment, pin, catalog, canonical kernel, native capability or owner acceptance change. F2/F3 and independent kernel/catalog/owner reviews remain open.", + "results": [ + { + "name": "preimage-frozen", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "test_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "kernel_case_run_sha256": "77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "preimage-mutant", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-admission-guard-removed/src", + "test_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "kernel_case_run_sha256": "6b0d61fb0da4737f305bb959524540752fbcf0f2993a7969bd7e368bdd03ea5a", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "successor-frozen", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "kernel_case_run_sha256": "77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "successor-mutant", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-admission-guard-removed/src", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "kernel_case_run_sha256": "6b0d61fb0da4737f305bb959524540752fbcf0f2993a7969bd7e368bdd03ea5a", + "exit_code": 1, + "expected_exit_code": 1 + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/manifest.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/manifest.json new file mode 100644 index 0000000..cd3a3c0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/manifest.json @@ -0,0 +1,19 @@ +{ + "status": "prepared-tested-not-applied", + "files": { + "implementation.json": "33d51784fe3ab7d9307292a265b0e684a8bc2fc77564ba58099833e41c849306", + "reproduce-scratch-checks.py": "8f0b01e606c03b3b27b178df60eab664a758df6fa3f9b1bc62ef8b58122c2cd8", + "successor-mutant.txt": "9f03083de5863b3855e533b94671cfbab6fd0dd565e0a0b8c385d51d9510d97c", + "README.md": "78a4dbef0088f18db2b9c1dbe701b3e8a2c20516ae6d2039420a82b8d6b1fe66", + "preimage-frozen.txt": "63dd3d754896c0e8578d82f98ae6f8e460a6ac43f3937a95fa61f65f8aa4accf", + "test_admit_1_3_type.after.py": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "scratch.json": "7e329154c5c60528d9f4387f0bbc9cfb4167c79decd49b14497e67d59e21e469", + "successor-frozen.txt": "275324dcf33eb852eb467832ff18fe9dd09f09d64d6ac37b43bf01766ea745d3", + "canonical-net-kingdom-status-after.txt": "354b3dd65c711df0855c22ba053a4ca295d5491941c5a64c1157a0b7d43cf4cb", + "scratch-kernel-mutation.patch": "dd12c63bd331b8f127149c402077e49fc66d62a452f48044486f58e79e0e29f7", + "test-results.json": "2aab986ed96fb9fa683992051ffe6ec7b2a9327d816542b908dcc66f9dd8df23", + "preimage-mutant.txt": "6c823ed279c355a34169d7753817256890d56fe4a1996ea59aa01fb19e5a5e1f", + "test-admit-1-3-agent-mint-coverage.patch": "5bc1da176836eea65195cfe5fae965284c1f38809a502a0be58c231b60111a75", + "test_admit_1_3_type.before.py": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-frozen.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-frozen.txt new file mode 100644 index 0000000..2cb22b5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-frozen.txt @@ -0,0 +1,2 @@ +........ [100%] +8 passed, 31 subtests passed in 4.98s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-mutant.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-mutant.txt new file mode 100644 index 0000000..708c86a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-mutant.txt @@ -0,0 +1,2 @@ +........ [100%] +8 passed, 31 subtests passed in 4.90s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/reproduce-scratch-checks.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/reproduce-scratch-checks.py new file mode 100644 index 0000000..7087821 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/reproduce-scratch-checks.py @@ -0,0 +1,30 @@ +from pathlib import Path +import os,json,subprocess,shutil,hashlib,difflib +packet=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1') +s=json.loads((packet/'scratch.json').read_text());scratch=Path(s['scratch_root']);nk=scratch/'net-kingdom';frozen=scratch/'warden-frozen';mutant=scratch/'warden-admission-guard-removed';shutil.copytree(frozen,mutant) +p=mutant/'src/warden/case_run.py';before=p.read_text() +guard=''' if descriptor.effect == "mint" and ( + actor != "human" or os.getenv("WARDEN_AGENT_ID") + ): + raise Refused("agent mint is forbidden") +''' +assert before.count(guard)==1 +p.write_text(before.replace(guard,'')) +(packet/'scratch-kernel-mutation.patch').write_text(''.join(difflib.unified_diff(before.splitlines(True),p.read_text().splitlines(True),fromfile='frozen/src/warden/case_run.py',tofile='scratch-mutant/src/warden/case_run.py'))) +test=nk/s['target'];successor=test.read_bytes();original=(packet/'test_admit_1_3_type.before.py').read_bytes() +results=[] +for name,kernel,source,expected in [('preimage-frozen',frozen,original,0),('preimage-mutant',mutant,original,0),('successor-frozen',frozen,successor,0),('successor-mutant',mutant,successor,1)]: + test.write_bytes(source) + env=os.environ.copy();env['PYTHONDONTWRITEBYTECODE']='1';env['PYTHONPATH']=str(kernel/'src') + cmd=['/home/worsch/ops-warden/.venv/bin/python','-m','pytest','-q','-p','no:cacheprovider',test.name] + run=subprocess.run(cmd,cwd=test.parent,env=env,capture_output=True,text=True) + (packet/(name+'.txt')).write_text(run.stdout+run.stderr) + results.append({'name':name,'command':cmd,'cwd':str(test.parent),'pythonpath':env['PYTHONPATH'],'test_sha256':hashlib.sha256(source).hexdigest(),'kernel_case_run_sha256':hashlib.sha256((kernel/'src/warden/case_run.py').read_bytes()).hexdigest(),'exit_code':run.returncode,'expected_exit_code':expected}) + print(name,run.returncode,run.stdout.splitlines()[-1],flush=True) + assert run.returncode==expected + if name=='successor-mutant': + assert 'test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor' in run.stdout + assert 'test_human_mint_is_refused_with_agent_runtime_identity' in run.stdout + assert '2 failed, 8 passed' in run.stdout + test.write_bytes(successor) +(packet/'test-results.json').write_text(json.dumps(results,indent=2)+'\n') diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch-kernel-mutation.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch-kernel-mutation.patch new file mode 100644 index 0000000..0661651 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch-kernel-mutation.patch @@ -0,0 +1,13 @@ +--- frozen/src/warden/case_run.py ++++ scratch-mutant/src/warden/case_run.py +@@ -646,10 +646,6 @@ + or lane != descriptor.lane + ): + raise Refused("principal actor or lane mismatch") +- if descriptor.effect == "mint" and ( +- actor != "human" or os.getenv("WARDEN_AGENT_ID") +- ): +- raise Refused("agent mint is forbidden") + parameters = case.get("parameters", {}) + declared = { + item["name"]: item diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch.json new file mode 100644 index 0000000..aaa0ba2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch.json @@ -0,0 +1,6 @@ +{ + "scratch_root": "/tmp/nk48-r5-f1-ahudwokt", + "target": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "net_kingdom_commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "warden_commit": "117fec9241e7b985dfb187b992e173d0a5dc3700" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-frozen.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-frozen.txt new file mode 100644 index 0000000..7f3e7ca --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-frozen.txt @@ -0,0 +1,2 @@ +.......... [100%] +10 passed, 32 subtests passed in 5.33s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-mutant.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-mutant.txt new file mode 100644 index 0000000..962f8ef --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-mutant.txt @@ -0,0 +1,30 @@ +F....F.... [100%] +=================================== FAILURES =================================== +_ Admit13TypeTests.test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor _ + +self = + + def test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor(self): + # A committed-map mismatch cannot exercise the independent mint guard. + mint = replace(self.descriptors['mint-secret'], actor='agent') + typ = yaml.safe_load(with_status('acknowledged')) + typ['spec']['execution_descriptors']['mint-secret'] = digest(c.encode(mint)) +> with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): +E AssertionError: Refused not raised + +test_admit_1_3_type.py:150: AssertionError +___ Admit13TypeTests.test_human_mint_is_refused_with_agent_runtime_identity ____ + +self = + + def test_human_mint_is_refused_with_agent_runtime_identity(self): + mint = self.descriptors['mint-secret'] + self.assertEqual(mint.actor, 'human') +> with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): +E AssertionError: Refused not raised + +test_admit_1_3_type.py:156: AssertionError +=========================== short test summary info ============================ +FAILED test_admit_1_3_type.py::Admit13TypeTests::test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor +FAILED test_admit_1_3_type.py::Admit13TypeTests::test_human_mint_is_refused_with_agent_runtime_identity +2 failed, 8 passed, 32 subtests passed in 5.72s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-admit-1-3-agent-mint-coverage.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-admit-1-3-agent-mint-coverage.patch new file mode 100644 index 0000000..ec72686 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-admit-1-3-agent-mint-coverage.patch @@ -0,0 +1,68 @@ +--- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py ++++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py +@@ -1,9 +1,9 @@ + """Kernel admission against the real 1.3 successor type (NK-WP-0048-T16 candidate). + +-Unlike test_admit_1_2_adapter_descriptors.py, nothing is patched into the type's +-execution_descriptors: the map tested here is the one committed in +-security-case-type-revisions/1.3. The type is `proposed`, so the kernel must refuse +-it as it stands. Warden's BrowserInputs also pins each type version's parameter ++The committed-map tests use security-case-type-revisions/1.3 unchanged. One ++separate fixture-map test acknowledges only an agent-actor mint descriptor digest ++to reach the independent kernel mint prohibition after map validation. The type ++is `proposed`, so the kernel must refuse it as it stands. Warden's BrowserInputs also pins each type version's parameter + fingerprint and knows only 1.1 and 1.2 (ops-warden case_inputs.PARAMETER_PINS), so an + acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an + ops-warden owner change, and it is not made here. The tests show that 1.3's +@@ -74,7 +74,7 @@ + ARTIFACT['params']['consumer_owner_repo'], + version='1.3')) + +- def admit(self, desc, type_source, *, actor=None, lane=None): ++ def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None): + desc_bytes = c.encode(desc) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(Path(self.temp.name) / desc.step), +@@ -82,7 +82,10 @@ + lambda purpose, source, evidence: evidence in EVIDENCE, + browser_inputs=inputs.BrowserInputs(REPO)) + with mock.patch.dict(os.environ): +- os.environ.pop('WARDEN_AGENT_ID', None) ++ if agent_id is None: ++ os.environ.pop('WARDEN_AGENT_ID', None) ++ else: ++ os.environ['WARDEN_AGENT_ID'] = agent_id + return executor.admit(self.case_source, type_source, desc_bytes, + b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, +@@ -125,7 +128,8 @@ + def test_descriptor_outside_the_map_is_refused(self): + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] +- for changed in (replace(mint, lane='other-lane'), replace(mint, retry_safe=True)): ++ for changed in (replace(mint, lane='other-lane'), replace(mint, retry_safe=True), ++ replace(mint, actor='agent')): + with self.subTest(changed=changed), pinned_13(), self.assertRaisesRegex( + Refused, 'no exact acknowledged machine descriptor'): + self.admit(changed, acknowledged) +@@ -137,6 +141,20 @@ + self.assertEqual((mint.effect, mint.actor), ('mint', 'human')) + with pinned_13(), self.assertRaisesRegex(Refused, 'principal actor or lane mismatch'): + self.admit(mint, acknowledged, actor='agent') ++ ++ def test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor(self): ++ # A committed-map mismatch cannot exercise the independent mint guard. ++ mint = replace(self.descriptors['mint-secret'], actor='agent') ++ typ = yaml.safe_load(with_status('acknowledged')) ++ typ['spec']['execution_descriptors']['mint-secret'] = digest(c.encode(mint)) ++ with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): ++ self.admit(mint, yaml.safe_dump(typ).encode()) ++ ++ def test_human_mint_is_refused_with_agent_runtime_identity(self): ++ mint = self.descriptors['mint-secret'] ++ self.assertEqual(mint.actor, 'human') ++ with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): ++ self.admit(mint, with_status('acknowledged'), agent_id='fixture-agent') + + def test_wrong_lane_is_refused(self): + # Moved from test_admit_1_2_adapter_descriptors.py in r5. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-results.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-results.json new file mode 100644 index 0000000..032f7f3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-results.json @@ -0,0 +1,74 @@ +[ + { + "name": "preimage-frozen", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "test_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "kernel_case_run_sha256": "77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "preimage-mutant", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-admission-guard-removed/src", + "test_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "kernel_case_run_sha256": "6b0d61fb0da4737f305bb959524540752fbcf0f2993a7969bd7e368bdd03ea5a", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "successor-frozen", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "kernel_case_run_sha256": "77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "successor-mutant", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-admission-guard-removed/src", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "kernel_case_run_sha256": "6b0d61fb0da4737f305bb959524540752fbcf0f2993a7969bd7e368bdd03ea5a", + "exit_code": 1, + "expected_exit_code": 1 + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.after.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.after.py new file mode 100644 index 0000000..1b3e537 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.after.py @@ -0,0 +1,175 @@ +"""Kernel admission against the real 1.3 successor type (NK-WP-0048-T16 candidate). + +The committed-map tests use security-case-type-revisions/1.3 unchanged. One +separate fixture-map test acknowledges only an agent-actor mint descriptor digest +to reach the independent kernel mint prohibition after map validation. The type +is `proposed`, so the kernel must refuse it as it stands. Warden's BrowserInputs also pins each type version's parameter +fingerprint and knows only 1.1 and 1.2 (ops-warden case_inputs.PARAMETER_PINS), so an +acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an +ops-warden owner change, and it is not made here. The tests show that 1.3's +parameters carry 1.2's pinned fingerprint, and that with only that one pin added in +memory the committed map admits exactly the composed descriptors and nothing else. +Trust is fixture-only, nothing is dispatched, and none of this is owner admission +or an acknowledgment. +""" +from dataclasses import replace +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import tempfile +import unittest +from unittest import mock + +import yaml + +from warden.case_run import Adapter, Executor, Journal, digest +from warden.case_receipt_state import Refused +import warden.case_inputs as inputs + +ROOT = Path(__file__).parent +REPO = ROOT.parents[2] +spec = importlib.util.spec_from_file_location('compose_13_admit', ROOT / 'compose-1.2-descriptors.py') +c = importlib.util.module_from_spec(spec) +spec.loader.exec_module(c) + +TYPE_DIR = REPO / 'security-case-type-revisions/1.3/oidc-browser-relying-party' +TYPE_BYTES = (TYPE_DIR / 'type.yaml').read_bytes() +ARTIFACT = json.loads((ROOT / 'descriptor-composition-1.3.candidate.json').read_text()) +EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'} + + +def unused(*_): + raise AssertionError('admission must not dispatch') + + +def parameter_fingerprint(type_bytes): + params = yaml.safe_load(type_bytes)['spec']['parameters'] + return hashlib.sha256(json.dumps(params, sort_keys=True, separators=(',', ':')).encode()).hexdigest() + + +def pinned_13(): + """Warden's parameter pins plus the one 1.3 entry ops-warden would have to add.""" + return mock.patch.dict(inputs.PARAMETER_PINS, {'1.3': parameter_fingerprint(TYPE_BYTES)}) + + +def with_status(status): + """The committed 1.3 type with only its status changed.""" + typ = yaml.safe_load(TYPE_BYTES) + typ['spec']['status'] = status + return yaml.safe_dump(typ).encode() + + +class Admit13TypeTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + case = yaml.safe_load((TYPE_DIR / 'examples/forgejo.yaml').read_bytes()) + case['spec'].update(status='materialized', acceptance={ + 'at': '2026-10-17', 'record': 'fixture-only acceptance, not an owner record', + 'by': [{'name': 'fixture', 'role': 'owner'}, {'name': 'fixture', 'role': 'founder'}]}) + self.case_source = yaml.safe_dump(case).encode() + self.descriptors = dict(c.adapter_descriptors(ARTIFACT['check_config_digest'], + ARTIFACT['params']['consumer_owner_repo'], + version='1.3')) + + def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None): + desc_bytes = c.encode(desc) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(Path(self.temp.name) / desc.step), + {adapter.reference: adapter}, + lambda purpose, source, evidence: evidence in EVIDENCE, + browser_inputs=inputs.BrowserInputs(REPO)) + with mock.patch.dict(os.environ): + if agent_id is None: + os.environ.pop('WARDEN_AGENT_ID', None) + else: + os.environ['WARDEN_AGENT_ID'] = agent_id + return executor.admit(self.case_source, type_source, desc_bytes, + b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, + acceptance_evidence='fixture-acceptance', + materialization_evidence='fixture-materialization', + descriptor_evidence='fixture-descriptor', session_evidence='fixture-session') + + def test_committed_map_is_exactly_the_composed_set(self): + committed = yaml.safe_load(TYPE_BYTES)['spec']['execution_descriptors'] + composed = {s: digest(c.encode(d)) for s, d in self.descriptors.items()} + self.assertEqual(len(composed), 7) + self.assertEqual(committed, composed) + self.assertEqual(committed, ARTIFACT['execution_descriptors']) + + def test_proposed_type_is_refused_as_it_stands(self): + self.assertEqual(yaml.safe_load(TYPE_BYTES)['spec']['status'], 'proposed') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), self.assertRaisesRegex(Refused, 'type is not acknowledged'): + self.admit(desc, TYPE_BYTES) + + def test_warden_has_no_1_3_parameter_pin_yet(self): + self.assertNotIn('1.3', inputs.PARAMETER_PINS) + # 1.3 keeps 1.2's parameters, so the missing pin is 1.2's fingerprint. + self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2']) + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), self.assertRaisesRegex( + Refused, 'failed exact-version validation'): + self.admit(desc, acknowledged) + + def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self): + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), pinned_13(): + admitted, invocation, _ = self.admit(desc, acknowledged) + self.assertEqual(admitted, desc) + self.assertEqual((invocation.step, invocation.actor, invocation.target), + (desc.step, desc.actor, 'forgejo')) + + def test_descriptor_outside_the_map_is_refused(self): + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] + for changed in (replace(mint, lane='other-lane'), replace(mint, retry_safe=True), + replace(mint, actor='agent')): + with self.subTest(changed=changed), pinned_13(), self.assertRaisesRegex( + Refused, 'no exact acknowledged machine descriptor'): + self.admit(changed, acknowledged) + + def test_mint_secret_admits_only_a_human_actor(self): + # Moved from test_admit_1_2_adapter_descriptors.py (NK48-R1-F1) in r5. + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] + self.assertEqual((mint.effect, mint.actor), ('mint', 'human')) + with pinned_13(), self.assertRaisesRegex(Refused, 'principal actor or lane mismatch'): + self.admit(mint, acknowledged, actor='agent') + + def test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor(self): + # A committed-map mismatch cannot exercise the independent mint guard. + mint = replace(self.descriptors['mint-secret'], actor='agent') + typ = yaml.safe_load(with_status('acknowledged')) + typ['spec']['execution_descriptors']['mint-secret'] = digest(c.encode(mint)) + with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): + self.admit(mint, yaml.safe_dump(typ).encode()) + + def test_human_mint_is_refused_with_agent_runtime_identity(self): + mint = self.descriptors['mint-secret'] + self.assertEqual(mint.actor, 'human') + with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): + self.admit(mint, with_status('acknowledged'), agent_id='fixture-agent') + + def test_wrong_lane_is_refused(self): + # Moved from test_admit_1_2_adapter_descriptors.py in r5. + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( + Refused, 'principal actor or lane mismatch'): + self.admit(desc, acknowledged, lane='wrong-lane') + + def test_earlier_versions_carry_no_execution_map(self): + for version in ('1.2',): + path = REPO / f'security-case-type-revisions/{version}/oidc-browser-relying-party/type.yaml' + with self.subTest(version=version): + self.assertNotIn('execution_descriptors', yaml.safe_load(path.read_bytes())['spec']) + + +if __name__ == '__main__': + unittest.main()