diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py new file mode 100644 index 00000000..f05f2040 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py @@ -0,0 +1,84 @@ +"""Four-file local source integration only. Default check; --apply writes files. + +No staging, commit, sync, native call, credentials or NetKingdom mutation. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import subprocess + +HERE = Path(__file__).resolve().parent +BASE = '0a9617a62d4696a8070798923b459e8405c3dbf2' +FILES = ('docs/keycape-issuer-eso-contract.md', + 'manifests/templates/keycape-issuer-eso.yaml', + 'scripts/keycape_issuer_eso.py', 'tests/test_keycape_issuer_eso.py') +def sha(data): + return hashlib.sha256(data).hexdigest() +def main(): + parser = argparse.ArgumentParser(__doc__) + parser.add_argument('--repo', type=Path, default=Path('/home/worsch/railiance-platform')) + parser.add_argument('--apply', action='store_true') + args = parser.parse_args() + repo = args.repo.resolve() + env = {'PATH': '/usr/local/bin:/usr/bin:/bin', 'HOME': '/tmp', 'GIT_OPTIONAL_LOCKS': '0'} + def git(*words): + result = subprocess.run(['git', '-C', str(repo), *words], env=env, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode: + raise SystemExit('Refused: local Git check failed; no command output emitted') + return result.stdout + if git('rev-parse', 'HEAD').decode().strip() != BASE: + raise SystemExit('Refused: Platform HEAD changed; rebase/review exact inputs first') + if Path(git('rev-parse', '--show-toplevel').decode().strip()).resolve() != repo: + raise SystemExit('Refused: repository root differs') + manifest = json.loads((HERE / 'manifest.json').read_text()) + entries = {x['path']: x for x in manifest['files']} + if set(entries) != set(FILES) or len(manifest['files']) != 4: + raise SystemExit('Refused: four-file scope differs') + prepared = {} + for name in FILES: + target = repo / name + for parent in (target, *target.parents): + if parent == repo.parent: + break + if parent.is_symlink(): + raise SystemExit('Refused: target or parent is a symlink') + if target.exists() or git('ls-files', '--', name): + raise SystemExit('Refused: expected absent preimage differs: ' + name) + data = (HERE / 'platform-source' / name).read_bytes() + if sha(data) != entries[name]['sha256']: + raise SystemExit('Refused: prepared source hash differs: ' + name) + prepared[name] = data + # Git status gets no lock; do not stage, refresh, reset or replace the index. + excluded = [':(exclude)' + name for name in FILES] + def state(): + index_name = git('rev-parse', '--git-path', 'index').decode().strip() + index = Path(index_name) + if not index.is_absolute(): + index = repo / index + return {'other_status_sha256': sha(git('status', '--porcelain=v1', '-z', + '--untracked-files=all', '--', '.', *excluded)), + 'index_sha256': sha(index.read_bytes()) if index.exists() else None} + before = state() + if not args.apply: + print(json.dumps({'outcome': 'checked-no-write', 'files': list(FILES), + 'preserved_state': before}, indent=2)) + return + # Exclusive creation independently refuses racing target creation. + for name, data in prepared.items(): + target = repo / name + target.parent.mkdir(parents=True, exist_ok=True) + with target.open('xb') as stream: + stream.write(data) + after = state() + if before != after: + raise SystemExit('Refused: unrelated status or index changed during integration; inspect concurrency') + if any(sha((repo / name).read_bytes()) != entries[name]['sha256'] for name in FILES): + raise SystemExit('Refused: post-write verification failed') + print(json.dumps({'outcome': 'applied-source-only', 'files': list(FILES), + 'before': before, 'after': after, 'workplan_changed': False, + 'source_owner_assent_inferred': False}, indent=2)) +if __name__ == '__main__': + main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integration-checks.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integration-checks.json new file mode 100644 index 00000000..21623561 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integration-checks.json @@ -0,0 +1,39 @@ +{ + "check": { + "outcome": "checked-no-write", + "files": [ + "docs/keycape-issuer-eso-contract.md", + "manifests/templates/keycape-issuer-eso.yaml", + "scripts/keycape_issuer_eso.py", + "tests/test_keycape_issuer_eso.py" + ], + "preserved_state": { + "other_status_sha256": "b274ecee79380c3670afa2ff40790064bdbe0565ecea4cfc1eec586deb409990", + "index_sha256": "9c08aefaf66ea19285e143e7c8f0de6402b340307391d7433ba2772b5dd02cf7" + } + }, + "applied": { + "outcome": "applied-source-only", + "files": [ + "docs/keycape-issuer-eso-contract.md", + "manifests/templates/keycape-issuer-eso.yaml", + "scripts/keycape_issuer_eso.py", + "tests/test_keycape_issuer_eso.py" + ], + "before": { + "other_status_sha256": "b274ecee79380c3670afa2ff40790064bdbe0565ecea4cfc1eec586deb409990", + "index_sha256": "9c08aefaf66ea19285e143e7c8f0de6402b340307391d7433ba2772b5dd02cf7" + }, + "after": { + "other_status_sha256": "b274ecee79380c3670afa2ff40790064bdbe0565ecea4cfc1eec586deb409990", + "index_sha256": "9c08aefaf66ea19285e143e7c8f0de6402b340307391d7433ba2772b5dd02cf7" + }, + "workplan_changed": false, + "source_owner_assent_inferred": false + }, + "existing_preimage_refused": true, + "tests_returncode": 0, + "test_output": "test_delivery_document_is_only_external_and_detached (test_keycape_issuer_eso.FixedIssuerContract.test_delivery_document_is_only_external_and_detached) ... ok\ntest_external_drift_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_external_drift_refuses) ... ok\ntest_independent_forgejo_binding (test_keycape_issuer_eso.FixedIssuerContract.test_independent_forgejo_binding) ... ok\ntest_missing_or_unready_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_missing_or_unready_refuses) ... ok\ntest_no_caller_store_or_path_override (test_keycape_issuer_eso.FixedIssuerContract.test_no_caller_store_or_path_override) ... ok\ntest_store_drift_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_store_drift_refuses) ... ok\ntest_type_admitted_trailing_dash_is_preserved (test_keycape_issuer_eso.FixedIssuerContract.test_type_admitted_trailing_dash_is_preserved) ... ok\ntest_valid_ready_metadata_allows_extra_controller_metadata (test_keycape_issuer_eso.FixedIssuerContract.test_valid_ready_metadata_allows_extra_controller_metadata) ... ok\n\n----------------------------------------------------------------------\nRan 8 tests in 0.248s\n\nOK\n", + "canonical_mutation": false, + "native_calls": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/manifest.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/manifest.json new file mode 100644 index 00000000..49240b78 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/manifest.json @@ -0,0 +1,40 @@ +{ + "schema": "custodian.nk48-r5-f2-source-publication.v1", + "status": "prepared-not-executed", + "platform_base": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "platform_source_status": "proposed-source-only", + "source_owner_assent_received": false, + "files": [ + { + "path": "docs/keycape-issuer-eso-contract.md", + "preimage": "absent", + "sha256": "7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68" + }, + { + "path": "manifests/templates/keycape-issuer-eso.yaml", + "preimage": "absent", + "sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743" + }, + { + "path": "scripts/keycape_issuer_eso.py", + "preimage": "absent", + "sha256": "21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87" + }, + { + "path": "tests/test_keycape_issuer_eso.py", + "preimage": "absent", + "sha256": "223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308" + } + ], + "canonical_workplan_delta": null, + "workplan_discovery": "RPF-1.3-T4 exists only in the original Platform owner-review JSON, not in a Platform workplan. No workplan created or repurposed.", + "conditional_netkingdom": { + "base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "patch_sha256": "6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "status": "prepared-only; await F3 combined regeneration before application" + }, + "observed_dirt": [ + "M .custodian-brief.md", + " M WORK-RECORDS.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch new file mode 100644 index 00000000..6a725fe5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch @@ -0,0 +1,16 @@ +--- a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml ++++ b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml +@@ -269,8 +269,11 @@ + 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} ++ sso/keycape-{client_id}-client (key client-secret) through fixed ClusterSecretStore ++ openbao-keycape-{client_id}-issuer (cluster-scoped, restricted to namespace sso), ++ defined by Platform-controlled manifests/templates/keycape-issuer-eso.yaml ++ (sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743), ++ using 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 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json new file mode 100644 index 00000000..b9e65dc6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json @@ -0,0 +1,12 @@ +{ + "README.md": "sha256:522392bd35d2e0b22fd097509361d1476200a361c2ced22603d43e5758aa691d", + "integrate_platform.py": "sha256:ef8a1647e54013c0c6f2c11c8f74ef43fc97ceaca99e8f8caa89ee16ff80efba", + "integration-checks.json": "sha256:0ab8b058414dde1be9c7fd6ee2cb3f246e4deef2036d17472e846fae23bba4b9", + "manifest.json": "sha256:c52692fb154cd46f1bfa62e7a0cda73241716b1024d992449e24e87e18750bbb", + "netkingdom-entry-point.conditional.patch": "sha256:6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "platform-source/docs/keycape-issuer-eso-contract.md": "sha256:7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68", + "platform-source/manifests/templates/keycape-issuer-eso.yaml": "sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "platform-source/scripts/keycape_issuer_eso.py": "sha256:21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87", + "platform-source/tests/test_keycape_issuer_eso.py": "sha256:223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308", + "progress-paragraph.md": "sha256:151199597ba37225e5d46922ae90453f7338c5602ff81b3d870b579278bef2d4" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/docs/keycape-issuer-eso-contract.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/docs/keycape-issuer-eso-contract.md new file mode 100644 index 00000000..56cfa1e6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/docs/keycape-issuer-eso-contract.md @@ -0,0 +1,68 @@ +# Proposed fixed issuer ESO contract — RPF-1.3-T4 / NK48-R5-F2 + +Status: proposed source; not admitted, deployed, or owner-acknowledged. This is a +new Platform declaration for review, not an existing Forgejo grant discovery. + +The only substitution in `manifests/templates/keycape-issuer-eso.yaml` +(sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743) is the admitted `client_id`. The proposed issuer +store is **ClusterSecretStore `openbao-keycape-{client_id}-issuer`**, cluster +scoped (no metadata namespace), restricted by conditions to ExternalSecrets in +`sso`. For Forgejo it is `openbao-keycape-forgejo-issuer`. The suffix makes the +issuer purpose explicit and keeps type-admitted IDs ending in `-` representable. +The store's DNS subdomain name can exceed 63 characters; the existing adapter's +63-character target Secret reference bound is preserved (client ID at most 48). +No type parameter is added or changed. + +The provider is `http://openbao.openbao.svc:8200`, KV v2 mount `platform`, +Kubernetes auth mount `kubernetes`, role `external-secrets-keycape-{client_id}`, +serviceAccountRef `external-secrets/external-secrets`, audience `openbao`. +CCR-2026-0041 already records that Forgejo role/identity/audience, not this new +store name or an unrestricted client family grant. Other clients still require +their own existing owner-controlled role/case admission. + +The ExternalSecret is `sso/keycape-{client_id}-client`, references precisely that +ClusterSecretStore, refreshes every 5m and targets the same name with Owner +creation and Retain remote deletion. Its only data entry maps `client-secret` +from remoteRef `workloads/keycape/{client_id}`, property `CLIENT_SECRET`. The +provider mount plus mount-relative remoteRef equals custody reference +`platform/workloads/keycape/{client_id}`; do not double-prefix `platform/`. +This is distinct from the Apps-owned consumer namespace SecretStore +`openbao-keycape-{client_id}-consumer` and its dedicated consumer identity. + +Store provisioning belongs to Platform's separate owner-controlled path. The +proposed `platform-secret-delivery` lane retains ONLY create/get/watch/delete on +the exact ExternalSecret in `sso`; this source does not delegate store creation +or Secret read/list. Never apply the multi-document template through that lane. +The pure `scripts/keycape_issuer_eso.py` module exposes only the ExternalSecret +document through `external_secret()` and validates fixed public observations +through `require_fixed_store()` and `require_ready_delivery()`. It performs no +API call and issues no evidence/receipt. It is not a real capability or admission +boundary; a caller-supplied observation alone establishes no authenticity. + +The existing RPF-1.3-T4 delivery binding must validate the case/client binding, +verify this exact preexisting store's public identity/spec/Ready state, apply or +compare the exact ExternalSecret, and wait for matching spec plus Ready=True. +The proposed helper accepts extra controller metadata but deliberately refuses +spec drift. Actual API defaulting/normalization and installed ESO schema support +must be checked in that implementation; these fixtures establish no native +compatibility or credential isolation. ESO's shared controller remains trusted. +No Secret bodies, values, arbitrary remoteRefs, or raw errors may enter receipts. + +Existing owner requirements remain: closed `netkingdom.deliver-secret.v1` +metadata receipt with authenticated before/after UID/resourceVersion provenance, +unchanged objects reported NO_CHANGE, attempt-session revocation, and created-only +rollback guarded by exact UID/resourceVersion (drift or uncertain ownership goes +to owner reconciliation). The helper neither deletes a target Secret nor creates +an OpenBao role/policy nor changes CCR-0041. These capabilities and catalog routing +remain unimplemented/unaccepted under RPF-1.3-T4. Independent Platform source +review of this declaration precedes adopting the conditional NetKingdom type +text; no descriptor acceptance, acknowledgment, pin, grant, catalog or native +permission is inferred. + +Grounding: CCR-2026-0041 exact role/identity; the approved historical +`docs/provisioning-windows/2026-10-04-1000/keycape/eso.draft.yaml` issuer +ClusterSecretStore and target pattern; `scripts/catalog_lane_scaffold.py` +ClusterSecretStore/namespace restriction and mount-relative remoteRef model; +RPF-1.3-C1 and RPF-1.3-T4 in the frozen owner review. The scaffold's historical +audience field placement differs from the issuer draft; this candidate uses +the issuer draft's serviceAccountRef.audiences, not a claim of installed support. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/manifests/templates/keycape-issuer-eso.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/manifests/templates/keycape-issuer-eso.yaml new file mode 100644 index 00000000..406a29c7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/manifests/templates/keycape-issuer-eso.yaml @@ -0,0 +1,46 @@ +# PROPOSED SOURCE ONLY — RPF-1.3-T4 / NK48-R5-F2; not admitted or applied. +# Platform controls this template. Only admitted client_id is substituted. +# Provision the store through Platform's separate reviewed owner path; the +# platform-secret-delivery lane applies ONLY the ExternalSecret document. +apiVersion: external-secrets.io/v1 +kind: ClusterSecretStore +metadata: + name: openbao-keycape-{client_id}-issuer +spec: + provider: + vault: + server: http://openbao.openbao.svc:8200 + path: platform + version: v2 + auth: + kubernetes: + mountPath: kubernetes + role: external-secrets-keycape-{client_id} + serviceAccountRef: + name: external-secrets + namespace: external-secrets + audiences: + - openbao + conditions: + - namespaces: + - sso +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: keycape-{client_id}-client + namespace: sso +spec: + refreshInterval: 5m + secretStoreRef: + kind: ClusterSecretStore + name: openbao-keycape-{client_id}-issuer + target: + name: keycape-{client_id}-client + creationPolicy: Owner + deletionPolicy: Retain + data: + - secretKey: client-secret + remoteRef: + key: workloads/keycape/{client_id} + property: CLIENT_SECRET diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/scripts/keycape_issuer_eso.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/scripts/keycape_issuer_eso.py new file mode 100644 index 00000000..8acace38 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/scripts/keycape_issuer_eso.py @@ -0,0 +1,70 @@ +"""Proposed fixed issuer ESO source contract for RPF-1.3-T4 / NK48-R5-F2. + +Pure public metadata only. No API/credential/Secret access or apply path. This +new declaration requires independent Platform review before type integration. +The delivery lane may use only external_secret(); store provisioning is a +separate owner action and is never delegated by this module. +""" +from __future__ import annotations + +from copy import deepcopy +from pathlib import Path +import re + +import yaml + +ROOT = Path(__file__).resolve().parents[1] +TEMPLATE = ROOT / 'manifests/templates/keycape-issuer-eso.yaml' + + +class Refused(ValueError): + """Fixed issuer metadata differs; never return an owner delivery receipt.""" + + +def documents(client_id: str) -> tuple[dict, dict]: + # Match the frozen type client_id pattern and existing adapter's derived bound. + # No caller can supply a store name/kind/namespace or an OpenBao path. + if not isinstance(client_id, str) or not re.fullmatch(r'[a-z0-9][a-z0-9-]{1,62}', client_id): + raise Refused('bounded admitted client_id required') + if len('keycape-' + client_id + '-client') > 63: + raise Refused('derived issuer reference exceeds existing adapter bound') + store, external = yaml.safe_load_all(TEMPLATE.read_text().format(client_id=client_id)) + return store, external + + +def external_secret(client_id: str) -> dict: + """The ONLY document the proposed delivery capability may apply.""" + return deepcopy(documents(client_id)[1]) + + +def _matching_public_resource(observed: dict, expected: dict) -> None: + if not isinstance(observed, dict): + raise Refused('public resource observation required') + metadata = observed.get('metadata') + if not isinstance(metadata, dict): + raise Refused('resource metadata required') + expected_namespace = expected['metadata'].get('namespace') + namespace_matches = (metadata.get('namespace') == expected_namespace + if expected_namespace else metadata.get('namespace') in (None, '')) + if (observed.get('apiVersion') != expected['apiVersion'] + or observed.get('kind') != expected['kind'] + or metadata.get('name') != expected['metadata']['name'] + or not namespace_matches + or observed.get('spec') != expected['spec']): + raise Refused('fixed issuer resource differs') + status = observed.get('status') + conditions = status.get('conditions', []) if isinstance(status, dict) else [] + if not isinstance(conditions, list) or not any( + isinstance(c, dict) and c.get('type') == 'Ready' and c.get('status') == 'True' + for c in conditions): + raise Refused('fixed issuer resource not ready') + + +def require_fixed_store(observed: dict, client_id: str) -> None: + """Refuse a missing/drifted/not-ready store; never create it through delivery.""" + _matching_public_resource(observed, documents(client_id)[0]) + + +def require_ready_delivery(observed: dict, client_id: str) -> None: + """Spec equality and Ready precede a created/NO_CHANGE owner receipt.""" + _matching_public_resource(observed, documents(client_id)[1]) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/tests/test_keycape_issuer_eso.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/tests/test_keycape_issuer_eso.py new file mode 100644 index 00000000..fd6e75db --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/tests/test_keycape_issuer_eso.py @@ -0,0 +1,129 @@ +"""Public metadata fixtures only: no API, credentials, or native calls.""" +import copy +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) +import keycape_issuer_eso as contract + + +class FixedIssuerContract(unittest.TestCase): + def observations(self): + resources = contract.documents("forgejo") + for resource in resources: + resource["metadata"].update(uid="public-fixture-uid", resourceVersion="7") + resource["status"] = {"conditions": [{"type": "Ready", "status": "True"}]} + return resources + + def test_independent_forgejo_binding(self): + # Independent owner-source example: CCR0041 role/SA/audience and the + # existing issuer ESO draft's provider/target/relative key convention. + store, external = contract.documents("forgejo") + self.assertEqual(store["kind"], "ClusterSecretStore") + self.assertEqual(store["metadata"], {"name": "openbao-keycape-forgejo-issuer"}) + self.assertEqual(store["spec"], { + "provider": {"vault": { + "server": "http://openbao.openbao.svc:8200", "path": "platform", "version": "v2", + "auth": {"kubernetes": {"mountPath": "kubernetes", + "role": "external-secrets-keycape-forgejo", + "serviceAccountRef": {"name": "external-secrets", "namespace": "external-secrets", + "audiences": ["openbao"]}}}}}, + "conditions": [{"namespaces": ["sso"]}]}) + self.assertEqual(external["metadata"], {"name": "keycape-forgejo-client", "namespace": "sso"}) + self.assertEqual(external["spec"], { + "refreshInterval": "5m", + "secretStoreRef": {"kind": "ClusterSecretStore", "name": "openbao-keycape-forgejo-issuer"}, + "target": {"name": "keycape-forgejo-client", "creationPolicy": "Owner", "deletionPolicy": "Retain"}, + "data": [{"secretKey": "client-secret", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]}) + + def test_valid_ready_metadata_allows_extra_controller_metadata(self): + store, external = self.observations() + contract.require_fixed_store(store, "forgejo") + contract.require_ready_delivery(external, "forgejo") + + def test_store_drift_refuses(self): + changes = [ + ("kind", "SecretStore"), + ("metadata.namespace", "sso"), + ("metadata.name", "caller-store"), + ("spec.provider.vault.server", "https://wrong.invalid"), + ("spec.provider.vault.path", "tenant"), + ("spec.provider.vault.auth.kubernetes.role", "external-secrets-keycape-sibling"), + ("spec.provider.vault.auth.kubernetes.serviceAccountRef.audiences", ["wrong"]), + ("spec.provider.vault.auth.kubernetes.serviceAccountRef.namespace", "sso"), + ("spec.conditions", [{"namespaces": ["sso", "other"]}]), + ] + for path, value in changes: + with self.subTest(path=path): + store = self.observations()[0] + mutate(store, path, value) + with self.assertRaises(contract.Refused): + contract.require_fixed_store(store, "forgejo") + + def test_external_drift_refuses(self): + changes = [ + ("metadata.namespace", ""), ("metadata.namespace", None), ("metadata.namespace", "other"), + ("metadata.name", "keycape-sibling-client"), + ("spec.secretStoreRef.kind", "SecretStore"), + ("spec.secretStoreRef.name", "openbao-keycape-forgejo-consumer"), + ("spec.target.name", "other"), ("spec.target.creationPolicy", "Merge"), + ("spec.target.deletionPolicy", "Delete"), + ("spec.data", [{"secretKey": "client-secret", "remoteRef": { + "key": "platform/workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]), + ("spec.data", [{"secretKey": "wrong", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]), + ("spec.data", [{"secretKey": "client-secret", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "wrong"}}]), + ] + for path, value in changes: + with self.subTest(path=path, value=value): + external = self.observations()[1] + mutate(external, path, value) + with self.assertRaises(contract.Refused): + contract.require_ready_delivery(external, "forgejo") + + def test_missing_or_unready_refuses(self): + for check, index in [(contract.require_fixed_store, 0), (contract.require_ready_delivery, 1)]: + for status in [None, {}, [], "bad", {"conditions": None}, + {"conditions": [{"type": "Ready", "status": "False"}]}]: + with self.subTest(check=check.__name__, status=status): + observed = self.observations()[index] + observed["status"] = status + with self.assertRaises(contract.Refused): + check(observed, "forgejo") + for malformed in [None, [], {}, {"metadata": None}]: + with self.assertRaises(contract.Refused): + check(malformed, "forgejo") + + def test_no_caller_store_or_path_override(self): + for invalid in ["", "f", "../forgejo", "forgejo/sibling", "Forgejo", "forgejo\n", "-forgejo", "f" * 49, None]: + with self.subTest(invalid=invalid), self.assertRaises(contract.Refused): + contract.documents(invalid) + with self.assertRaises(TypeError): + contract.external_secret("forgejo", store_name="caller-store") + + def test_type_admitted_trailing_dash_is_preserved(self): + self.assertEqual(contract.external_secret("forgejo-")["metadata"]["name"], + "keycape-forgejo--client") + self.assertEqual(contract.documents("forgejo-")[0]["metadata"]["name"], + "openbao-keycape-forgejo--issuer") + + def test_delivery_document_is_only_external_and_detached(self): + external = contract.external_secret("forgejo") + self.assertEqual(external["kind"], "ExternalSecret") + external["spec"]["secretStoreRef"]["name"] = "tampered" + self.assertEqual(contract.external_secret("forgejo")["spec"]["secretStoreRef"]["name"], + "openbao-keycape-forgejo-issuer") + + +def mutate(resource, path, value): + parts = path.split(".") + for key in parts[:-1]: + resource = resource[key] + resource[parts[-1]] = copy.deepcopy(value) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/progress-paragraph.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/progress-paragraph.md new file mode 100644 index 00000000..ab1af2ac --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/progress-paragraph.md @@ -0,0 +1,19 @@ +2026-10-17 source preparation: the proposed fixed issuer ESO template +`manifests/templates/keycape-issuer-eso.yaml`, pure metadata helper +`scripts/keycape_issuer_eso.py`, focused fixtures and owner contract doc passed +independent Platform-scoped source-fit review (Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md`). This +supplies reviewable source for the exact-store-reference gap NK48-R5-F2 under +the existing owner-review obligation RPF-1.3-T4; it establishes no owner assent, +deployment, grant or lane activation. Real authenticated delivery/remove, +attempt-session revocation, authentic before/after provenance and metadata +receipts, preexisting/adoption protection, guarded created-only rollback, +installed ESO compatibility and Warden catalog routing remain incomplete. Do +not mark the delivery obligation done on the strength of template/helper source. + +Placement: reference material only. No legitimate Platform workplan/task for +RPF-1.3-T4 exists in the reviewed current source. The identifier originates in +`nk48-1.3-owner-review/owner-review-railiance-platform.json`, whose review explicitly +created no workplan. This paragraph is retained here for the existing coordinator +work record; it must not be appended to an unrelated finished Platform task or +used to invent a task, status, dependency or owner assent. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.json new file mode 100644 index 00000000..9186ff98 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.json @@ -0,0 +1,443 @@ +{ + "ok": true, + "status": "applied", + "repo_slug": "railiance-platform", + "commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "requests": 2, + "elapsed_ms": 5105.9, + "git": { + "upstream": "origin/main", + "behind": 0, + "ahead": 0 + }, + "identifiers": { + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "railiance-platform", + "namespace": "helixforge", + "assignments": [], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [] + }, + "receipt": { + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "derived_commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "railiance-platform", + "commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "status": "applied", + "counts": { + "created": 0, + "updated": 64, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 0, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 12, + "external_edges": 24 + }, + "created": [], + "updated": [ + "RAIL-PL-WP-0001", + "RAIL-PL-WP-0002", + "RPF-WP-0001", + "RPF-WP-0002", + "RPF-WP-0003", + "RPF-WP-0004", + "RPF-WP-0005", + "RPF-WP-0006", + "RPF-WP-0007", + "RPF-WP-0008", + "RPF-WP-0009", + "RPF-WP-0010", + "RPF-WP-0011", + "RPF-WP-0012", + "RPF-WP-0013", + "RPF-WP-0014", + "RPF-WP-0015", + "RPF-WP-0016", + "RPF-WP-0017", + "RPF-WP-0018", + "RPF-WP-0019", + "RPF-WP-0020", + "RPF-WP-0021", + "RPF-WP-0022", + "RPF-WP-0023", + "RPF-WP-0024", + "RPF-WP-0025", + "RPF-WP-0026", + "RPF-WP-0027", + "RPF-WP-0028", + "RPF-WP-0029", + "RPF-WP-0030", + "RPF-WP-0031", + "RPF-WP-0032", + "RPF-WP-0033", + "RPF-WP-0034", + "RPF-WP-0035", + "RPF-WP-0036", + "RPF-WP-0037", + "RPF-WP-0038", + "RPF-WP-0039", + "RPF-WP-0040", + "RPF-WP-0041", + "RPF-WP-0042", + "RPF-WP-0043", + "RPF-WP-0044", + "RPF-WP-0045", + "RPF-WP-0046", + "RPF-WP-0047", + "RPF-WP-0048", + "RPF-WP-0049", + "RPF-WP-0050", + "RPF-WP-0051", + "RPF-WP-0052", + "RPF-WP-0053", + "RPF-WP-0054", + "RPF-WP-0055", + "RPF-WP-0056", + "RPF-WP-0057", + "RPF-WP-0058", + "RPF-WP-0059", + "RPF-WP-ADHOC-2026-08-23", + "RPF-WP-ADHOC-2026-10-10", + "RPF-WP-ADHOC-2026-10-11" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [ + { + "workplan": "RAIL-PL-WP-0002", + "from": "RAIL-PL-WP-0002", + "target": "RAIL-PL-WP-0001", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0002", + "from": "RPF-WP-0002", + "target": "RAILIANCE-WP-0005", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0002", + "from": "RPF-WP-0002", + "target": "RAILIANCE-WP-0006", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0002", + "from": "RPF-WP-0002", + "target": "RAILIANCE-WP-0007", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0003", + "from": "RPF-WP-0003", + "target": "RAILIANCE-WP-0007", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0003", + "from": "RPF-WP-0003", + "target": "RAILIANCE-WP-0008", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0004", + "from": "RPF-WP-0004", + "target": "RAILIANCE-WP-0007", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0004", + "from": "RPF-WP-0004", + "target": "RAILIANCE-WP-0008", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0005", + "from": "RPF-WP-0005", + "target": "RAILIANCE-WP-0007", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0005", + "from": "RPF-WP-0005", + "target": "RAILIANCE-WP-0008", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0024", + "from": "RPF-WP-0024", + "target": "RAILIANCE-WP-0005", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0024", + "from": "RPF-WP-0024", + "target": "RAILIANCE-WP-0006", + "reason": "missing" + } + ], + "external_edges": [ + { + "workplan": "RPF-WP-0057", + "from": "RPF-WP-0057-T04", + "target": "RPF-WP-0057-T03", + "relationship_type": "starts_after", + "from_workplan_id": "04f7c4aa-0a24-5d61-994d-2487ca6bfb91", + "from_task_id": "94cdfd1c-c639-5c5e-9bd6-7db823ab0265", + "to_workplan_id": null, + "to_task_id": "afa14a91-1ebf-5a45-a28b-380f9a933336" + }, + { + "workplan": "RPF-WP-0003", + "from": "RPF-WP-0003", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "5fb6b6a7-3a40-5658-9804-21c0928c6dd9", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0024", + "from": "RPF-WP-0024", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "6c2e61cf-7aa9-58ee-bb22-3514b9692eb5", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T07", + "target": "SECRETS-WP-0006-T06", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "0dd7c9a5-65a0-53fc-b6c3-5ad96ee56f7b", + "to_workplan_id": null, + "to_task_id": "1431edae-5791-5892-8c4b-829419b537d2" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T12", + "target": "RPF-WP-0035-T11", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "914bf383-c13f-5247-9af0-91d7e3a4784a", + "to_workplan_id": null, + "to_task_id": "7952c8cd-c066-5b72-a025-78d24341c1ab" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T02", + "target": "KEY-WP-0039-T02", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "e0c82ea9-05a5-537e-8fbf-67534263572e", + "to_workplan_id": null, + "to_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T02", + "target": "MASON-WP-0009-T05", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "e0c82ea9-05a5-537e-8fbf-67534263572e", + "to_workplan_id": null, + "to_task_id": "1b1a0566-add7-59c1-94b0-6bb81d2d1537" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T03", + "target": "MASON-WP-0005-T05", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "f47e5bc5-6f3c-54bb-aa35-260ad03892ba", + "to_workplan_id": null, + "to_task_id": "4c205be7-4f1f-5dd1-aafa-fc112fdd640e" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T03", + "target": "FT-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "f47e5bc5-6f3c-54bb-aa35-260ad03892ba", + "to_workplan_id": "f2373858-c932-5a4d-81b6-db9a3595135a", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T06", + "target": "RPF-WP-0044-T04", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "4ca54868-c31f-5e3d-b7d3-a15b84c15f80", + "to_workplan_id": null, + "to_task_id": "0e619658-0453-51ed-b9a0-bed28b5544fd" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T06", + "target": "RPF-WP-0044-T05", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "4ca54868-c31f-5e3d-b7d3-a15b84c15f80", + "to_workplan_id": null, + "to_task_id": "d418068a-6fb0-5416-aac5-d23c93924d9c" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T07", + "target": "RPF-WP-0044-T04", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d", + "to_workplan_id": null, + "to_task_id": "0e619658-0453-51ed-b9a0-bed28b5544fd" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T07", + "target": "RPF-WP-0044-T06", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d", + "to_workplan_id": null, + "to_task_id": "4ca54868-c31f-5e3d-b7d3-a15b84c15f80" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T07", + "target": "RPF-WP-0044-T03", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d", + "to_workplan_id": null, + "to_task_id": "6d5fc801-e361-579b-bcd8-6fe719a82e94" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T07", + "target": "RPF-WP-0044-T05", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d", + "to_workplan_id": null, + "to_task_id": "d418068a-6fb0-5416-aac5-d23c93924d9c" + }, + { + "workplan": "RPF-WP-0004", + "from": "RPF-WP-0004", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "afc66064-7ba4-5f11-b61f-d76890244b7e", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0023", + "from": "RPF-WP-0023", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "b07930f7-ff0e-5fda-b7b9-3b56467173b1", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0001", + "from": "RPF-WP-0001", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "bcc11cf9-d008-5e42-8fff-e2b33a1d0ff2", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0002", + "from": "RPF-WP-0002", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "c69cb4e7-15c8-5858-aebd-73a6e46f0f21", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0036", + "from": "RPF-WP-0036-T04", + "target": "RTEL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "ca639c3d-3a87-5fa4-ad13-6f2e014b0c84", + "from_task_id": "5351e0e4-6263-58f0-afb2-7c78c4cd6f68", + "to_workplan_id": "08a5db92-7293-50d3-b589-55287b9850b3", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0036", + "from": "RPF-WP-0036-T03", + "target": "ACTIVITY-WP-0042", + "relationship_type": "blocks", + "from_workplan_id": "ca639c3d-3a87-5fa4-ad13-6f2e014b0c84", + "from_task_id": "d64446fb-870b-5ae9-9628-f1fb9d06c5a4", + "to_workplan_id": "e1bfb5a4-e647-592d-883f-fe8728ad2f5f", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0043", + "from": "RPF-WP-0043-T03", + "target": "RAPP-POLICY-NEXUS-WP-0002-T02", + "relationship_type": "starts_after", + "from_workplan_id": "ec41a4bd-df18-5b07-9b63-ccb80d9f001c", + "from_task_id": "2afb1aac-66dd-5e1e-92e1-fc020a59a927", + "to_workplan_id": null, + "to_task_id": "18b26ecc-4fb8-516b-8d97-62ff39a85f24" + }, + { + "workplan": "RPF-WP-0043", + "from": "RPF-WP-0043-T04", + "target": "RPF-WP-0043-T03", + "relationship_type": "starts_after", + "from_workplan_id": "ec41a4bd-df18-5b07-9b63-ccb80d9f001c", + "from_task_id": "8638fa73-439d-5dcd-85c7-81aa125170e1", + "to_workplan_id": null, + "to_task_id": "2afb1aac-66dd-5e1e-92e1-fc020a59a927" + }, + { + "workplan": "RPF-WP-0043", + "from": "RPF-WP-0043-T04", + "target": "RPF-WP-0044-T07", + "relationship_type": "starts_after", + "from_workplan_id": "ec41a4bd-df18-5b07-9b63-ccb80d9f001c", + "from_task_id": "8638fa73-439d-5dcd-85c7-81aa125170e1", + "to_workplan_id": null, + "to_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d" + } + ], + "refused": [], + "notes": [] + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-commit.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-commit.json new file mode 100644 index 00000000..50060d0a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-commit.json @@ -0,0 +1,25 @@ +{ + "exit_code": 0, + "commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "base": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "files": [ + { + "path": "docs/keycape-issuer-eso-contract.md", + "sha256": "7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68" + }, + { + "path": "manifests/templates/keycape-issuer-eso.yaml", + "sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743" + }, + { + "path": "scripts/keycape_issuer_eso.py", + "sha256": "21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87" + }, + { + "path": "tests/test_keycape_issuer_eso.py", + "sha256": "223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308" + } + ], + "stdout": "[main 104ed90] feat: declare proposed fixed KeyCape issuer ESO source contract\n 4 files changed, 313 insertions(+)\n create mode 100644 docs/keycape-issuer-eso-contract.md\n create mode 100644 manifests/templates/keycape-issuer-eso.yaml\n create mode 100644 scripts/keycape_issuer_eso.py\n create mode 100644 tests/test_keycape_issuer_eso.py\n", + "stderr": "{\"blocked_blob_ids\": [], \"scanned_blobs\": 4, \"scanner_sha256\": \"88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509\", \"scanner_version\": \"8.30.1\", \"scope\": \"changed-staged-blobs\", \"status\": \"clean\"}\n" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-inputs.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-inputs.json new file mode 100644 index 00000000..067d1216 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-inputs.json @@ -0,0 +1,23 @@ +{ + "owner": "railiance-platform", + "base_revision": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "files": [ + { + "path": "docs/keycape-issuer-eso-contract.md", + "sha256": "7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68" + }, + { + "path": "manifests/templates/keycape-issuer-eso.yaml", + "sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743" + }, + { + "path": "scripts/keycape_issuer_eso.py", + "sha256": "21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87" + }, + { + "path": "tests/test_keycape_issuer_eso.py", + "sha256": "223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308" + } + ], + "message": "feat: declare proposed fixed KeyCape issuer ESO source contract" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md new file mode 100644 index 00000000..86517bad --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md @@ -0,0 +1,70 @@ +# Independent Platform-scoped source review: NK48-R5-F2 + +Decision: **accept the exact Platform candidate as reviewable source**. No blocking +source-fit defect was reproduced. This is an independent reviewer decision, not +Platform owner assent, source adoption, descriptor acceptance or execution authority. +The conditional NetKingdom patch may be incorporated into the coordinator's +combined candidate; report F2's exact-store-reference requirement closed only +after the Platform source is actually adopted. No canonical files were edited. + +The proposed `ClusterSecretStore openbao-keycape-{client_id}-issuer` is a new, +explicit owner-controlled declaration. It is not a store name granted by +CCR-2026-0041. For Forgejo, its role, Kubernetes auth mount, service account, +audience and exact KV path agree with that CCR. The `platform` provider mount +and `workloads/keycape/forgejo` remoteRef correctly form the custody path without +double-prefixing it. The `sso` condition and cluster scope agree with Platform's +historical issuer draft. The consumer namespace SecretStore is never substituted. +No role, policy, grant, catalog or caller-selectable store/path is added. + +The pure helper is appropriate preparation under existing RPF-1.3-T4. It checks +fixed identity, complete spec equality and Ready status; it exposes only the +ExternalSecret through its delivery-facing function. It neither provisions the +store nor issues receipts. Admitted IDs ending in `-` remain representable because +the fixed suffix completes the resource name. The adapter's effective bound is +client ID length 48: `keycape-` + ID + `-client` is at most 63 characters. The +store reaches 71 characters and is a DNS subdomain resource name, so a separate +63-character store label bound would incorrectly narrow the type. + +Both patches applied to clean archives of the exact specified commits, all nine +source-pins matched, the eight supplied tests passed, and independent boundary +checks accepted IDs of length 2, 47 and 48 while rejecting 1, 49 and 63. Parsing +the patched type proved that only +`spec.follow_through[id=deliver-secret].entry_point` changed. The pinned template +hash matches its exact content. See `evidence.json`, `focused-tests.txt` and the +replayable `reproduce.py`. Archive subprocesses and tests received a minimal +environment with no ambient credentials; no native infrastructure calls occurred. + +These source checks establish neither the installed ESO CRD shape/defaulting nor +trusted live resource observations. Strict spec equality may reject harmless API +defaults; the actual owner implementation must handle them deliberately without +allowing semantic drift. The audience placement follows the historical issuer +draft, not the older scaffold's differing placement. No installed schema proof is +claimed. The `sso` condition limits store users by namespace; the shared ESO +controller and writers in that namespace remain within the documented trust +boundary. + +The concrete next implementation is the existing RPF-1.3-T4 binding: authenticate +the admitted case/client and attempt session; retrieve and validate this exact +preexisting store through a contained owner path; create the exact ExternalSecret +or refuse a differing one; issue NO_CHANGE for an exact preexisting match only +after Ready readback; and publish/authenticate the closed metadata receipt. Its +before/after UID and resourceVersion must come from that trusted operation, not +from caller-supplied fixture dictionaries. Preserve the preexisting/adoption guard: +these helpers confer no ownership on a preexisting ExternalSecret or target Secret, +and no created receipt may be fabricated from matching spec or Ready alone. The +real binding must handle preexisting target collisions through the owner's +containment/adoption policy, without broad Secret reads or inferred adoption. + +Implement remove using authenticated case-created provenance plus exact UID and +resourceVersion delete preconditions, allow owned target garbage collection, +revoke the parent session per attempt, and route drift or uncertain ownership to +owner reconciliation. Catalog routing remains the existing Warden owner work. +These are carried implementation obligations, not new tasks or permission gates. + +Inputs: Platform `0a9617a62d4696a8070798923b459e8405c3dbf2`, NetKingdom +`3236d5000e52e0643f33906a26a7825aa0b67465`, and the adjacent +`nk48-r5-f2-implementation-r1` packet. Exact packet file hashes and archive hashes +are in `evidence.json`. Review also read Platform AGENTS/INTENT, the original +Platform owner contract and CCR0041, issuer ESO draft, lane design and scaffold, +plus r5's F2 finding and the NetKingdom security companion. No separate fix was +needed and the author's record was not rewritten. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json new file mode 100644 index 00000000..9ac2fad3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json @@ -0,0 +1,39 @@ +{ + "source_only": true, + "ambient_credentials_passed": false, + "native_calls": false, + "bases": { + "railiance-platform": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "net-kingdom": "3236d5000e52e0643f33906a26a7825aa0b67465" + }, + "input_hashes": { + "OWNER-REVIEW.md": "sha256:7983fd841f09bc7584047426912c33d568c97b4c1126776f99aac42b34d2b742", + "canonical-source-readonly-state.json": "sha256:129d6926bb996e510ab4b41d30e738096b53b71de1fb2dd470fe0ac94848cbec", + "focused-tests.json": "sha256:8de5862a8d752dce0b3b96a9275e42952a99415a18d3f67c06bf9f5b6008d0e3", + "focused-tests.txt": "sha256:aab46c51d87198ad4be6dd3078f61286e2faf1ef4d76fa14298698388f78ab24", + "manifest.json": "sha256:754531fbef468bc0e432bd26d4dbed992294c34d57e23fe7c3b7f829f7a24ba4", + "netkingdom-entry-point.conditional.patch": "sha256:6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "platform-candidate/docs/keycape-issuer-eso-contract.md": "sha256:7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68", + "platform-candidate/manifests/templates/keycape-issuer-eso.yaml": "sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "platform-candidate/scripts/keycape_issuer_eso.py": "sha256:21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87", + "platform-candidate/tests/test_keycape_issuer_eso.py": "sha256:223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308", + "platform.candidate.patch": "sha256:a490df7dee41e15351da8d2906e1342edebb533984befe3d9ae74d952dbb091b", + "review.json": "sha256:d99db9ab4f326308cde464129bbbb7573434e500fd03c130a37a590bac3643a4", + "source-pins.json": "sha256:6e896b6196a0aca01ddaf433b4d14cc041fc0296c5dd6404c3a698b941b61bd8", + "structural-and-patch-checks.json": "sha256:a3dc4cd405c412d4369986cc6736cd2d9eed02d4b86d95d62d5a9914106fdb14", + "type.before.yaml": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "type.candidate.yaml": "sha256:ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f" + }, + "checks": { + "all_source_pins_verified": true, + "both_patches_apply_to_exact_archives": true, + "focused_tests": "8 tests passed", + "only_type_delta": "spec.follow_through[id=deliver-secret].entry_point", + "independent_length_boundaries": "2,47,48 accepted; 1,49,63 refused", + "template_hash_matches_type": true + }, + "archive_sha256": { + "railiance-platform": "sha256:0798af8b0171a5925a10b520269c45b83b03eaa97650831e17c2aaaeb696bbd9", + "net-kingdom": "sha256:e2852f444ed3c1501107b3cc1aef088e7d30f953284ca310149cc46424da12b3" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/focused-tests.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/focused-tests.txt new file mode 100644 index 00000000..97063167 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/focused-tests.txt @@ -0,0 +1,13 @@ +test_delivery_document_is_only_external_and_detached (test_keycape_issuer_eso.FixedIssuerContract.test_delivery_document_is_only_external_and_detached) ... ok +test_external_drift_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_external_drift_refuses) ... ok +test_independent_forgejo_binding (test_keycape_issuer_eso.FixedIssuerContract.test_independent_forgejo_binding) ... ok +test_missing_or_unready_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_missing_or_unready_refuses) ... ok +test_no_caller_store_or_path_override (test_keycape_issuer_eso.FixedIssuerContract.test_no_caller_store_or_path_override) ... ok +test_store_drift_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_store_drift_refuses) ... ok +test_type_admitted_trailing_dash_is_preserved (test_keycape_issuer_eso.FixedIssuerContract.test_type_admitted_trailing_dash_is_preserved) ... ok +test_valid_ready_metadata_allows_extra_controller_metadata (test_keycape_issuer_eso.FixedIssuerContract.test_valid_ready_metadata_allows_extra_controller_metadata) ... ok + +---------------------------------------------------------------------- +Ran 8 tests in 0.294s + +OK diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/reproduce.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/reproduce.py new file mode 100644 index 00000000..90d2a10d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/reproduce.py @@ -0,0 +1,87 @@ +"""Independent source-only review; archived trees and public synthetic fixtures.""" +import hashlib +import importlib.util +import io +import json +import os +from pathlib import Path +import subprocess +import tarfile +import tempfile +import yaml + +OUT = Path(__file__).resolve().parent +PACKET = OUT.parent / 'nk48-r5-f2-implementation-r1' +BASES = {'railiance-platform': '0a9617a62d4696a8070798923b459e8405c3dbf2', + 'net-kingdom': '3236d5000e52e0643f33906a26a7825aa0b67465'} +ENV = {'PATH': '/usr/local/bin:/usr/bin:/bin', 'HOME': '/tmp', 'PYTHONDONTWRITEBYTECODE': '1'} +def run(args, cwd): + return subprocess.run(args, cwd=cwd, env=ENV, capture_output=True, text=True, check=True).stdout +def sha(data): + return hashlib.sha256(data).hexdigest() + +result = {'source_only': True, 'ambient_credentials_passed': False, 'native_calls': False, + 'bases': BASES, 'input_hashes': {}, 'checks': {}} +for p in sorted(PACKET.rglob('*')): + if p.is_file(): + result['input_hashes'][str(p.relative_to(PACKET))] = sha(p.read_bytes()) +with tempfile.TemporaryDirectory(prefix='platform-f2-independent-') as scratch: + roots = {} + for repo, commit in BASES.items(): + dest = Path(scratch) / repo + dest.mkdir() + archive = subprocess.run(['git', '-C', '/home/worsch/' + repo, 'archive', commit], + env=ENV, capture_output=True, check=True).stdout + with tarfile.open(fileobj=io.BytesIO(archive)) as tf: + tf.extractall(dest, filter='data') + roots[repo] = dest + result.setdefault('archive_sha256', {})[repo] = sha(archive) + for pin in json.loads((PACKET / 'source-pins.json').read_text()): + repo = pin['repo'] + root = roots.get(repo, Path('/home/worsch/the-custodian')) + actual = sha((root / pin['path']).read_bytes()) + assert actual == pin['sha256'], pin['path'] + result['checks']['all_source_pins_verified'] = True + for repo, patch in [('railiance-platform', 'platform.candidate.patch'), + ('net-kingdom', 'netkingdom-entry-point.conditional.patch')]: + run(['git', 'apply', '--check', str(PACKET / patch)], roots[repo]) + run(['git', 'apply', str(PACKET / patch)], roots[repo]) + result['checks']['both_patches_apply_to_exact_archives'] = True + platform = roots['railiance-platform'] + tests = subprocess.run(['/usr/bin/python3', '-m', 'unittest', 'discover', '-s', 'tests', + '-p', 'test_keycape_issuer_eso.py', '-v'], cwd=platform, + env=ENV, capture_output=True, text=True) + (OUT / 'focused-tests.txt').write_text(tests.stdout + tests.stderr) + assert tests.returncode == 0 + result['checks']['focused_tests'] = '8 tests passed' + type_path = roots['net-kingdom'] / 'security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml' + before = yaml.safe_load((PACKET / 'type.before.yaml').read_text()) + after = yaml.safe_load(type_path.read_text()) + before_step = next(s for s in before['spec']['follow_through'] if s['id'] == 'deliver-secret') + after_step = next(s for s in after['spec']['follow_through'] if s['id'] == 'deliver-secret') + assert before_step['entry_point'] != after_step['entry_point'] + after_step['entry_point'] = before_step['entry_point'] + assert before == after + result['checks']['only_type_delta'] = 'spec.follow_through[id=deliver-secret].entry_point' + spec = importlib.util.spec_from_file_location('issuer', platform / 'scripts/keycape_issuer_eso.py') + issuer = importlib.util.module_from_spec(spec) + spec.loader.exec_module(issuer) + for n in (2, 47, 48): + store, external = issuer.documents('a' * n) + assert len(external['metadata']['name']) <= 63 + assert len(store['metadata']['name']) <= 253 + for n in (1, 49, 63): + try: + issuer.documents('a' * n) + except issuer.Refused: + pass + else: + raise AssertionError(n) + result['checks']['independent_length_boundaries'] = '2,47,48 accepted; 1,49,63 refused' + result['checks']['template_hash_matches_type'] = ( + sha((platform / 'manifests/templates/keycape-issuer-eso.yaml').read_bytes()) + in yaml.safe_load(type_path.read_text())['spec']['follow_through'][ + next(i for i,s in enumerate(before['spec']['follow_through']) if s['id']=='deliver-secret')]['entry_point']) + assert result['checks']['template_hash_matches_type'] +(OUT / 'evidence.json').write_text(json.dumps(result, indent=2) + '\n') +print(json.dumps(result['checks'], indent=2)) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/review.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/review.json new file mode 100644 index 00000000..a571cf6d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/review.json @@ -0,0 +1,23 @@ +{ + "schema": "custodian.nk48-r5-f2-independent-source-review.v1", + "date": "2026-10-17", + "reviewer": "independent Platform-scoped Codex subagent", + "finding": "NK48-R5-F2", + "decision": "accept-for-reviewable-source", + "blocking_source_findings": [], + "source_owner_assent_received": false, + "source_adoption_performed": false, + "f2_closed": false, + "f2_closure_condition": "Actual Platform adoption of the explicit source declaration followed by integration of its exact conditional entry_point into the successor candidate.", + "platform_base": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "netkingdom_base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "accepted_platform_patch_sha256": "a490df7dee41e15351da8d2906e1342edebb533984befe3d9ae74d952dbb091b", + "conditional_netkingdom_patch_sha256": "6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "template_sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "evidence": ["evidence.json", "focused-tests.txt", "reproduce.py", "REVIEW.md"], + "next_implementation": "Existing RPF-1.3-T4 authenticated deliver/remove, trusted metadata readback and closed receipts, attempt-session revocation, created-only rollback and catalog routing; preserve preexisting/adoption guards and confirm installed ESO schema/defaulting.", + "native_execution_performed": false, + "canonical_edits_or_commits": false, + "new_grants_or_workplans": false, + "candidate_fix_needed": false +}