Record reviewed case admission fixes, exact issuer source and isolated unit checks
This commit is contained in:
parent
d68d131e32
commit
a69be6cb07
238 changed files with 38749 additions and 0 deletions
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86",
|
||||
"id": "ebeaa526-9ece-45ca-a4cb-c8afd4c863c4",
|
||||
"topic_id": null,
|
||||
"task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3",
|
||||
"decision_id": null,
|
||||
"event_type": "note",
|
||||
"summary": "Rechecked KEY-WP-0039 against current infrastructure source review and primary State Hub: all 14 executable/test/producer-decision pins and 3 Platform pins match; T01/T03 done, T02 wait, parent active; KEY38 T02/T03 remain wait. Existing finalization assessment remains valid. Source-only approval is not live readiness/custody or a negative/expiry/cleanup receipt. No workplan/status edits, credentials, live changes or renewed approval request. Recheck evidence: docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck.json.",
|
||||
"detail": null,
|
||||
"author": "codex",
|
||||
"session_id": null,
|
||||
"created_at": "2026-10-17T17:27:33.253863Z",
|
||||
"workstream_id": "09c3bddf-6058-5603-8488-cccdb7be4f86"
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
{
|
||||
"checked_at": "2026-10-17T17:27:31.694709+00:00",
|
||||
"owner_head": "d803e9282a47b49daf930fb905312fa5104c0ace",
|
||||
"source_pin_checks": [
|
||||
{
|
||||
"path": "net-kingdom/sso-mfa/k8s/keycape/openbao-negative-node-route.md",
|
||||
"expected_sha256": "cad3e3dc481d45295cc014e48285e81614911dfb4a10523883f3044f6085f363",
|
||||
"actual_sha256": "cad3e3dc481d45295cc014e48285e81614911dfb4a10523883f3044f6085f363",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "net-kingdom/sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py",
|
||||
"expected_sha256": "76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1",
|
||||
"actual_sha256": "76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "net-kingdom/sso-mfa/k8s/keycape/openbao-negative-session.py",
|
||||
"expected_sha256": "24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd",
|
||||
"actual_sha256": "24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "net-kingdom/sso-mfa/k8s/keycape/test_openbao_negative_session.py",
|
||||
"expected_sha256": "01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c",
|
||||
"actual_sha256": "01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "net-kingdom/sso-mfa/k8s/keycape/test_openbao_node_route.py",
|
||||
"expected_sha256": "1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6",
|
||||
"actual_sha256": "1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "net-kingdom/sso-mfa/k8s/keycape/test_openbao_private_input.py",
|
||||
"expected_sha256": "84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1",
|
||||
"actual_sha256": "84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "net-kingdom/sso-mfa/k8s/keycape/test_openbao_private_ready.py",
|
||||
"expected_sha256": "8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277",
|
||||
"actual_sha256": "8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "key-cape/docs/openbao-negative-token-producer-decision.json",
|
||||
"expected_sha256": "7b3e4a53ac4af33c780f16c3f1aa9cde0154040ad2747a8d361dd79bbb7f435c",
|
||||
"actual_sha256": "7b3e4a53ac4af33c780f16c3f1aa9cde0154040ad2747a8d361dd79bbb7f435c",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "key-cape/docs/openbao-negative-token-producer-decision.md",
|
||||
"expected_sha256": "38e4c9660ca57a1fb6e5ff04f214af61dd1a58abf564a5ed22216d909e27fc73",
|
||||
"actual_sha256": "38e4c9660ca57a1fb6e5ff04f214af61dd1a58abf564a5ed22216d909e27fc73",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "key-cape/scripts/openbao_negative_private_handoff.py",
|
||||
"expected_sha256": "faf63a3e9504dcbb46847a114275f83d508521e095410a47f92f02b86ba82b1b",
|
||||
"actual_sha256": "faf63a3e9504dcbb46847a114275f83d508521e095410a47f92f02b86ba82b1b",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "key-cape/scripts/openbao_negative_private_launcher.py",
|
||||
"expected_sha256": "46e57504b3185cc13f1656901a0ca02cc3887527cb09566e270f57b76099ab9c",
|
||||
"actual_sha256": "46e57504b3185cc13f1656901a0ca02cc3887527cb09566e270f57b76099ab9c",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "key-cape/scripts/test_openbao_negative_private_handoff.py",
|
||||
"expected_sha256": "e4eb600718e7d93b0e801ac3ab1427af9895a6e5d877c1134369e049e0849797",
|
||||
"actual_sha256": "e4eb600718e7d93b0e801ac3ab1427af9895a6e5d877c1134369e049e0849797",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "key-cape/scripts/test_openbao_negative_private_launcher.py",
|
||||
"expected_sha256": "243426a85075b3447adeb2ca320a13711409744f71837a06699de6ad5b04ee9d",
|
||||
"actual_sha256": "243426a85075b3447adeb2ca320a13711409744f71837a06699de6ad5b04ee9d",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "key-cape/scripts/test_openbao_negative_private_preparation.py",
|
||||
"expected_sha256": "8e4dd327c4a339a0151ecba57db5987965b5d536ca18123fb7c74a047a241548",
|
||||
"actual_sha256": "8e4dd327c4a339a0151ecba57db5987965b5d536ca18123fb7c74a047a241548",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "railiance-platform/helm/openbao-ui-overlay-k8s.yaml",
|
||||
"expected_sha256": "7b345359906bf1ecc4cb8e61de7a9f1a121068a66bd919e606190a9217a82daf",
|
||||
"actual_sha256": "7b345359906bf1ecc4cb8e61de7a9f1a121068a66bd919e606190a9217a82daf",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "railiance-platform/helm/openbao-ui-overlay/nginx.conf",
|
||||
"expected_sha256": "464f7f5cecc69ea343c1e2acc5932e832b2a9c0e6a5099113634a12abc80ccb3",
|
||||
"actual_sha256": "464f7f5cecc69ea343c1e2acc5932e832b2a9c0e6a5099113634a12abc80ccb3",
|
||||
"match": true
|
||||
},
|
||||
{
|
||||
"path": "railiance-platform/helm/openbao-values.yaml",
|
||||
"expected_sha256": "a10239851277889dd69dacb8636109dddd095551d85b46ca4ed2c141944e8d1e",
|
||||
"actual_sha256": "a10239851277889dd69dacb8636109dddd095551d85b46ca4ed2c141944e8d1e",
|
||||
"match": true
|
||||
}
|
||||
],
|
||||
"primary_task_readback": [
|
||||
{
|
||||
"id": "4d06b059-3768-53bb-83a4-a9a71c0850b8",
|
||||
"record_id": "KEY-WP-0039-T01",
|
||||
"status": "done",
|
||||
"blocking_reason": null,
|
||||
"needs_human": false
|
||||
},
|
||||
{
|
||||
"id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3",
|
||||
"record_id": "KEY-WP-0039-T02",
|
||||
"status": "wait",
|
||||
"blocking_reason": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established.",
|
||||
"needs_human": false
|
||||
},
|
||||
{
|
||||
"id": "54d37ad8-2352-51c2-bb2e-af6070dbbd73",
|
||||
"record_id": "KEY-WP-0039-T03",
|
||||
"status": "done",
|
||||
"blocking_reason": null,
|
||||
"needs_human": false
|
||||
},
|
||||
{
|
||||
"id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30",
|
||||
"record_id": "KEY-WP-0038-T02",
|
||||
"status": "wait",
|
||||
"blocking_reason": "Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout.",
|
||||
"needs_human": false
|
||||
},
|
||||
{
|
||||
"id": "40ddaa29-7245-508c-ad48-ddc063e25115",
|
||||
"record_id": "KEY-WP-0038-T03",
|
||||
"status": "wait",
|
||||
"blocking_reason": "Claims are verified against the registered client.",
|
||||
"needs_human": false
|
||||
}
|
||||
],
|
||||
"parent_status": "active",
|
||||
"disposition": "Source review finalized; native acceptance outstanding. Preserve active parent and T02 wait. No new approval request, credential access or native action.",
|
||||
"assessment": "ASSESSMENT.md"
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
"""Prepared source integration only. Root runs --execute; no commit/projection here."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
PACKET = Path(__file__).resolve().parent
|
||||
ROOT = Path('/home/worsch/net-kingdom')
|
||||
WARDEN = Path('/home/worsch/ops-warden')
|
||||
|
||||
def sha(data):
|
||||
return 'sha256:' + hashlib.sha256(data).hexdigest()
|
||||
|
||||
def git(root, *args):
|
||||
return subprocess.check_output(['git', '-C', str(root), *args], text=True).strip()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--execute', action='store_true')
|
||||
args = parser.parse_args()
|
||||
manifest = json.loads((PACKET / 'publication-inputs.json').read_text())
|
||||
assert git(ROOT, 'rev-parse', '--abbrev-ref', 'HEAD') == 'main'
|
||||
assert git(ROOT, 'rev-parse', 'HEAD') == manifest['base_revision']
|
||||
assert git(WARDEN, 'rev-parse', 'HEAD') == manifest['warden_revision']
|
||||
workplan = ROOT / manifest['workplan']
|
||||
original = workplan.read_bytes()
|
||||
candidate = (PACKET / 'workplan.candidate.md').read_bytes()
|
||||
assert sha(original) == manifest['workplan_preimage_sha256']
|
||||
assert sha(candidate) == manifest['workplan_candidate_sha256']
|
||||
assert sha((PACKET / 'task-T35.md').read_bytes()) == manifest['task_addition_sha256']
|
||||
assert candidate == original + (PACKET / 'task-T35.md').read_bytes()
|
||||
assert len(re.findall(rb'^```task$', original, re.M)) == 34
|
||||
assert len(re.findall(rb'^```task$', candidate, re.M)) == 35
|
||||
assert b'id: NK-WP-0048-T35' not in original
|
||||
assert sha((PACKET / 'candidate.patch').read_bytes()) == manifest['patch_sha256']
|
||||
for row in manifest['files']:
|
||||
path = ROOT / row['path']
|
||||
if row['preimage_sha256'] is None:
|
||||
assert not path.exists() and not path.is_symlink(), str(path)
|
||||
else:
|
||||
assert sha(path.read_bytes()) == row['preimage_sha256'], str(path)
|
||||
protected_before = {name: sha((ROOT / name).read_bytes()) for name in manifest['protected_files']}
|
||||
assert protected_before == manifest['protected_files'], 'Operator brief/index changed: refresh the prepared inputs first.'
|
||||
# Preserve the original index exactly; this script never stages anything.
|
||||
index = git(ROOT, 'ls-files', '--stage', '-z')
|
||||
assert sha(Path('/home/worsch/repo-manager/src/repo_manager/identifiers.py').read_bytes()) == manifest['repo_manager_identifiers_sha256']
|
||||
sys.path.insert(0, '/home/worsch/repo-manager/src')
|
||||
from repo_manager.identifiers import ensure_missing_work_record_identifiers
|
||||
before = ensure_missing_work_record_identifiers(ROOT, execute=False)
|
||||
assert before['ok'] and before['assignments'] == [] and before['files_changed'] == []
|
||||
subprocess.run(['git', 'apply', '--check', str(PACKET / 'candidate.patch')], cwd=ROOT, check=True)
|
||||
if not args.execute:
|
||||
print(json.dumps({'prepared_only': True, 'preimages_verified': True, 'files': 4,
|
||||
'next': 'Run this script with --execute, then run_validation.py --execute.'}))
|
||||
return
|
||||
subprocess.run(['git', 'apply', str(PACKET / 'candidate.patch')], cwd=ROOT, check=True)
|
||||
for row in manifest['files']:
|
||||
assert sha((ROOT / row['path']).read_bytes()) == row['sha256'], row['path']
|
||||
workplan.write_bytes(candidate)
|
||||
preview = ensure_missing_work_record_identifiers(ROOT, execute=False)
|
||||
(PACKET / 'identifier-preview.json').write_text(json.dumps(preview, indent=2) + '\n')
|
||||
assert preview['ok'] and preview['files_changed'] == [manifest['workplan']]
|
||||
assert len(preview['assignments']) == 1
|
||||
assignment = preview['assignments'][0]
|
||||
assert (assignment['kind'], assignment['record_id'], assignment['path']) == (
|
||||
'task', 'NK-WP-0048-T35', manifest['workplan'])
|
||||
# UUID comes only from the ordinary allocator, never from this packet.
|
||||
actual = ensure_missing_work_record_identifiers(ROOT, execute=True)
|
||||
assert actual['assignments'] == preview['assignments']
|
||||
assert actual['files_changed'] == [manifest['workplan']]
|
||||
(PACKET / 'identifier-assignment.json').write_text(json.dumps(actual, indent=2) + '\n')
|
||||
allocated = workplan.read_bytes()
|
||||
assert allocated.startswith(original), 'Existing workplan bytes must be preserved.'
|
||||
assert len(re.findall(rb'^```task$', allocated, re.M)) == 35
|
||||
assert allocated.count(b'id: NK-WP-0048-T35\n') == 1
|
||||
assert not ensure_missing_work_record_identifiers(ROOT, execute=False)['assignments']
|
||||
assert git(ROOT, 'ls-files', '--stage', '-z') == index, 'Index must remain unchanged.'
|
||||
assert {name: sha((ROOT / name).read_bytes()) for name in manifest['protected_files']} == protected_before
|
||||
(PACKET / 'workplan.allocated.md').write_bytes(allocated)
|
||||
publication = {'owner': 'net-kingdom', 'base_revision': manifest['base_revision'],
|
||||
'message': manifest['message'], 'files': [
|
||||
{'path': row['path'], 'sha256': sha((ROOT / row['path']).read_bytes()).removeprefix('sha256:')}
|
||||
for row in manifest['files'] + [{'path': manifest['workplan']}]]}
|
||||
(PACKET / 'publication-inputs.allocated.json').write_text(json.dumps(publication, indent=2) + '\n')
|
||||
print(json.dumps({'source_applied': True, 'task_count': 35, 'allocated_only': assignment['record_id'],
|
||||
'files': len(publication['files']), 'commit_and_projection': 'Root handles separately.'}))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
diff --git a/sso-mfa/k8s/keycape/test_admit_1_3_type.py b/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
--- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
+++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
@@ -8,7 +8,9 @@
|
||||
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.
|
||||
+memory the five resolvable steps admit. The two Apps attended steps remain refused
|
||||
+because their establishing login is unresolved in the synthetic catalog. Four real
|
||||
+draft lane shapes are hypothetically active in that fixture; no catalog is promoted.
|
||||
Trust is fixture-only, nothing is dispatched, and none of this is owner admission
|
||||
or an acknowledgment.
|
||||
"""
|
||||
@@ -37,6 +39,8 @@
|
||||
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())
|
||||
+CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()
|
||||
+UNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'}
|
||||
EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'}
|
||||
|
||||
|
||||
@@ -74,12 +78,13 @@
|
||||
ARTIFACT['params']['consumer_owner_repo'],
|
||||
version='1.3'))
|
||||
|
||||
- def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None):
|
||||
+ def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG):
|
||||
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,
|
||||
+ lambda purpose, source, evidence: (evidence in EVIDENCE and
|
||||
+ (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))),
|
||||
browser_inputs=inputs.BrowserInputs(REPO))
|
||||
with mock.patch.dict(os.environ):
|
||||
if agent_id is None:
|
||||
@@ -87,7 +92,7 @@
|
||||
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,
|
||||
+ catalog, principal='founder', actor=actor or desc.actor,
|
||||
lane=lane or desc.lane,
|
||||
acceptance_evidence='fixture-acceptance',
|
||||
materialization_evidence='fixture-materialization',
|
||||
@@ -112,18 +117,33 @@
|
||||
self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2'])
|
||||
acknowledged = with_status('acknowledged')
|
||||
for step, desc in self.descriptors.items():
|
||||
+ if step in UNRESOLVED_APPS:
|
||||
+ continue # The attended session gate correctly precedes parameter validation.
|
||||
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):
|
||||
+ def test_committed_map_conditionally_admits_five_resolvable_steps(self):
|
||||
acknowledged = with_status('acknowledged')
|
||||
for step, desc in self.descriptors.items():
|
||||
+ if step in UNRESOLVED_APPS:
|
||||
+ continue
|
||||
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_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self):
|
||||
+ for step in sorted(UNRESOLVED_APPS):
|
||||
+ with self.subTest(step=step), pinned_13(), self.assertRaisesRegex(
|
||||
+ Refused, 'does not resolve to an active, reviewed login lane'):
|
||||
+ self.admit(self.descriptors[step], with_status('acknowledged'))
|
||||
+
|
||||
+ def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self):
|
||||
+ mint = self.descriptors['mint-secret']
|
||||
+ with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'):
|
||||
+ self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\n# changed source\n')
|
||||
|
||||
def test_descriptor_outside_the_map_is_refused(self):
|
||||
acknowledged = with_status('acknowledged')
|
||||
diff --git a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
--- a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
+++ b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
@@ -259,8 +259,13 @@
|
||||
case_source = yaml.safe_dump(case).encode()
|
||||
typ = yaml.safe_load((root / 'security-case-types/oidc-browser-relying-party/type.yaml').read_bytes())
|
||||
typ['spec']['execution_descriptors'] = {'register-client': digest(self.desc_bytes)}
|
||||
+ # Fixture-only successor lane for the adapter's current descriptor.
|
||||
+ # Canonical 1.1 still names the earlier login lane and is not edited.
|
||||
+ typ['spec']['follow_through'] = [
|
||||
+ dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step
|
||||
+ for step in typ['spec']['follow_through']]
|
||||
type_source = yaml.safe_dump(typ).encode()
|
||||
- catalog = b'fixture-only-catalog'
|
||||
+ catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()
|
||||
sheet = json.dumps(['register-client', 'net-kingdom', 'forgejo', 'human',
|
||||
'founder', 'keycape-attended-case-change', 'fixture-acceptance', 'fixture-materialization',
|
||||
'fixture-descriptor', 'fixture-session'], separators=(',', ':')).encode()
|
||||
@@ -270,6 +275,8 @@
|
||||
adapter = a.build(**dict(self.kw, admitted=invocation, case_source=case_source,
|
||||
case_acceptance='fixture-acceptance'))
|
||||
def fixture_verifier(purpose, source, evidence):
|
||||
+ if purpose == 'catalog-routing-acceptance':
|
||||
+ return source == digest(catalog) and evidence == 'fixture-descriptor'
|
||||
if purpose.startswith('session-revocation:'):
|
||||
_, lane, operation, attempt = purpose.split(':')
|
||||
return source == digest(case_source) and lane == invocation.lane and evidence == (
|
||||
diff --git a/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml
|
||||
@@ -0,0 +1,128 @@
|
||||
+# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog.
|
||||
+# Four draft entries are hypothetically active; Apps session_lane remains unresolved.
|
||||
+# Not an authenticated owner catalog, promotion, login choice or native authority.
|
||||
+entries:
|
||||
+- id: key-cape-oidc-login
|
||||
+ risk: standard
|
||||
+ owner_repo: key-cape
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-06-27'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: interim
|
||||
+ intended_owner: key-cape
|
||||
+ reviewed: '2026-09-08'
|
||||
+ blocked_on: fixture-only; no native authority
|
||||
+ lane: login
|
||||
+ title: Synthetic key-cape-oidc-login
|
||||
+ need_keywords:
|
||||
+ - key-cape-oidc-login
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: keycape-client-secret-generate
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-platform
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-10'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-platform
|
||||
+ reviewed: '2026-10-10'
|
||||
+ lane: secret
|
||||
+ title: Synthetic keycape-client-secret-generate
|
||||
+ need_keywords:
|
||||
+ - keycape-client-secret-generate
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: keycape-attended-case-change
|
||||
+ lane: attended-change
|
||||
+ session_lane: key-cape-oidc-login
|
||||
+ risk: high
|
||||
+ owner_repo: key-cape/net-kingdom
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: interim
|
||||
+ intended_owner: key-cape
|
||||
+ reviewed: '2026-10-17'
|
||||
+ blocked_on: fixture-only; no native authority
|
||||
+ title: Synthetic keycape-attended-case-change
|
||||
+ need_keywords:
|
||||
+ - keycape-attended-case-change
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: platform-secret-delivery
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-platform
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-platform
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic platform-secret-delivery
|
||||
+ need_keywords:
|
||||
+ - platform-secret-delivery
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: consumer-owner-deployment
|
||||
+ lane: attended-change
|
||||
+ session_lane: pending-owner-confirmation
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-apps
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-apps
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic consumer-owner-deployment
|
||||
+ need_keywords:
|
||||
+ - consumer-owner-deployment
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: apps-consumer-handover
|
||||
+ lane: attended-change
|
||||
+ session_lane: pending-owner-confirmation
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-apps
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-apps
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic apps-consumer-handover
|
||||
+ need_keywords:
|
||||
+ - apps-consumer-handover
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"schema": "custodian.nk48-catalog-fixture-publication-preparation.v1",
|
||||
"status": "prepared only; canonical apply and validation runners not executed, including dry-run",
|
||||
"owner_base": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"warden_base": "ae7bc73b4f4bac22454144959b870d161498e0af",
|
||||
"root_source_review": "Root explicitly accepted fixture patch SHA256 df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501 for source integration; no owner/native assent inferred.",
|
||||
"task_refinement": "Append completed T35 for distinct current authenticated-catalog/mode-aware fixture compatibility; preserve exact original workplan bytes, all34 existing task blocks/frontmatter/status/UUID. Normal allocator preview must yield exactlyT35 before execution; no manual UUID.",
|
||||
"root_commands": [
|
||||
"python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py --execute",
|
||||
"python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py --execute",
|
||||
"Root normal scoped commit using publication-inputs.allocated.json; normal owner projection afterwards."
|
||||
],
|
||||
"mutation_scope": [
|
||||
"Exactly the three reviewed fixture files plus the appended T35 workplan task.",
|
||||
"No git stage,commit,push,projection,brief refresh or index generation in prepared scripts.",
|
||||
"Preexisting operator .custodian-brief.md/WORK-RECORDS.md remain byte-identical; unrelated dirty/untracked paths are never read or written by the apply script."
|
||||
],
|
||||
"validation_scope": "Canonical NK test and source files against Warden mainae7bc73;105 explicit public source hashes checked before and after. Two scoped test modules only; expected31 tests/53 subtests; no skip/deselection accepted. EmptyHOME and allowlisted environment; fresh redirected Python caches; pytest cache/conftest discovery disabled. Network denied. Native processes denied except exact pinned offline metadata validator with guarded empty-environment child. Reads from /home or /root denied except exact prehashed public source files; canonical writes denied.",
|
||||
"unverified_limits": [
|
||||
"Prepared runner has only been AST-parsed; root must execute and inspect logs.",
|
||||
"Python audit controls are bounded to reviewed test execution and do not claim operating-system credential isolation or an autopilot grant.",
|
||||
"Warden code is source-only; 1.3 parameter pin, draft promotion, pending Apps login, nativeT03 and downstream acceptance remain gated.",
|
||||
"Existing adverse review history remains unchanged."
|
||||
],
|
||||
"files": {
|
||||
"apply_prepare.py": "sha256:b294e6339e8c9af9ba911667646dd0ab52e75cf798de93238d84d1057c8a9e70",
|
||||
"run_validation.py": "sha256:16236aa55a774f8ea6d66da4ee17f7737c6d1b013d844ca9b9759e02b615af51",
|
||||
"validation-guard.py": "sha256:13611f4de25dc7d01348905c4b365a0ffbb95fd93a61c75c8faf38d705c54f50",
|
||||
"workplan.preimage.md": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9",
|
||||
"publication-inputs.json": "sha256:d9bf5555013aa9e399037672c80a72af23c6815db03088bee3db4c8583197146",
|
||||
"candidate.patch": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501",
|
||||
"task-T35.md": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f",
|
||||
"validation-child.py": "sha256:6bd4e3cda22e65f90bbb4006b403270405947293be2ae8bf8fdade0abed61f45",
|
||||
"workplan.candidate.md": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
{
|
||||
"schema": "custodian.nk48-catalog-fixture-publication-inputs.v1",
|
||||
"prepared_only": true,
|
||||
"owner": "net-kingdom",
|
||||
"base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"warden_revision": "ae7bc73b4f4bac22454144959b870d161498e0af",
|
||||
"message": "test: align security case fixtures with authenticated catalog admission",
|
||||
"workplan": "workplans/NK-WP-0048-security-case-types.md",
|
||||
"workplan_preimage_sha256": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9",
|
||||
"workplan_candidate_sha256": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981",
|
||||
"task_addition_sha256": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f",
|
||||
"patch_sha256": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501",
|
||||
"files": [
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"preimage_sha256": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0",
|
||||
"sha256": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"preimage_sha256": "sha256:4d0126416522e54eec1ae4c83b5488e09ff11ffb97ec1ee6d234548780c60f63",
|
||||
"sha256": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"preimage_sha256": null,
|
||||
"sha256": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53"
|
||||
}
|
||||
],
|
||||
"allocation": {
|
||||
"method": "repo_manager.identifiers.ensure_missing_work_record_identifiers",
|
||||
"manual_uuid": false,
|
||||
"preexisting_assignments": [],
|
||||
"expected_new_record_ids": [
|
||||
"NK-WP-0048-T35"
|
||||
],
|
||||
"expected_files_changed": [
|
||||
"workplans/NK-WP-0048-security-case-types.md"
|
||||
],
|
||||
"expected_task_count": 35,
|
||||
"concrete_postallocation_manifest": "publication-inputs.allocated.json"
|
||||
},
|
||||
"protected_files": {
|
||||
".custodian-brief.md": "sha256:3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1",
|
||||
"WORK-RECORDS.md": "sha256:a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a"
|
||||
},
|
||||
"validation": {
|
||||
"tests": [
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py"
|
||||
],
|
||||
"expected_result": "31 passed,53 subtests passed; no skipped or deselected tests",
|
||||
"python": "/tmp/warden-kernel-review-r1/final/.venv/bin/python",
|
||||
"source_hashes": {
|
||||
"net-kingdom": {
|
||||
"sso-mfa/k8s/keycape/test_vergabe_client_rollout.py": "sha256:f9b7defc312b0fea24ded92ef479dcc8aef063f7a66a8245760918bcf44c4af1",
|
||||
"sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py": "sha256:9a4b9fb0141221d6e5bcb72b3656613afda54d16ede300bffa8f71956ae6fd2c",
|
||||
"sso-mfa/k8s/keycape/test_keycape_client.py": "sha256:9572044679feaedfe732e15a2b6f8ef6b41a8fe2e4430257f309a3e83e97736c",
|
||||
"sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py": "sha256:76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1",
|
||||
"sso-mfa/k8s/keycape/portal-client-rollout.py": "sha256:158e1d36eb881cdd0a2eda91b472adea5e8fde5061e00d26dda368580aeb871f",
|
||||
"sso-mfa/k8s/keycape/test_playwright_activation.py": "sha256:6fc22d091f27a76c32d2fb0a7fc3c6e18e54ff98d242699c518b2f9dc9fd4cbd",
|
||||
"sso-mfa/k8s/keycape/exercise-approval-clients.py": "sha256:e2d3dd741ac52af28dce752344227a4443b78fedd4cfcc06ba902e679dfc9351",
|
||||
"sso-mfa/k8s/keycape/keycape-client.py": "sha256:4b63161cf3365d34dd684a3116e2c935baaf7f6aa4b30983f6883546db3d6591",
|
||||
"sso-mfa/k8s/keycape/test_openbao_negative_session.py": "sha256:01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c",
|
||||
"sso-mfa/k8s/keycape/authentication-policy-rollout.py": "sha256:2c1bd23fded4855bae1eb3a08dbc4510d7f60f2bc4d625d60e471ab78f6f0251",
|
||||
"sso-mfa/k8s/keycape/handover-adapter.py": "sha256:bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832",
|
||||
"sso-mfa/k8s/keycape/openbao-signin-diagnosis-rollout.py": "sha256:a57b957d6eed9e8fb09d2a7aa7405a39deeafd9aaf57a9e6dc907afabd380371",
|
||||
"sso-mfa/k8s/keycape/test_handover_adapter.py": "sha256:cd1fb0eb64a777699221ebdfcd6fea96bafb9b27ec388e95dcfe66189eac8ca2",
|
||||
"sso-mfa/k8s/keycape/test_authentication_policy_rollout.py": "sha256:11c10039d99a34d2819b09146a966c8b7cba6095e7a8b51c055a01e7167e3ed5",
|
||||
"sso-mfa/k8s/keycape/test_deliver_consumer_secret_adapter.py": "sha256:f4e6c6acd75a36f675007d2f62a7c7c4f7bfe2951cbb5e3e4cdde8b53f17f473",
|
||||
"sso-mfa/k8s/keycape/bind-env-adapter.py": "sha256:fa17fae21776b5c620ee9782adf050649c3a28719a6687d2524ff67ca7ad1925",
|
||||
"sso-mfa/k8s/keycape/test_keycape_checker_snapshot.py": "sha256:9271f25c760ad732c257d59d259b5c767436a2dc2d5ba24fca259bc57761d6d1",
|
||||
"sso-mfa/k8s/keycape/test_openbao_signin_diagnosis_rollout.py": "sha256:25d46fe7caca2ad4c262bd2a25188c91847304b6b051c4f3b3705c69f2803f01",
|
||||
"sso-mfa/k8s/keycape/test_openbao_private_ready.py": "sha256:8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277",
|
||||
"sso-mfa/k8s/keycape/test_mint_secret_adapter.py": "sha256:8110b6a8abd8e9a1f5e1feae11ec09c4e28cb7486c759b0e8459da1fb1b7144d",
|
||||
"sso-mfa/k8s/keycape/playwright-activation.py": "sha256:5ac683b3324b00e6253f6040f4c73057027b1f98f7071160a6153794ba522757",
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019",
|
||||
"sso-mfa/k8s/keycape/test_openbao_node_route.py": "sha256:1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6",
|
||||
"sso-mfa/k8s/keycape/test_secrets_engine_openbao_client_rollout.py": "sha256:e6f86fb7712c1b9c01d9b684f26d5cc949649b823d2732eced132d59acd3fe01",
|
||||
"sso-mfa/k8s/keycape/openbao-client-config.py": "sha256:1b908b4c36134212d78f9fbe7a2a201eb3ced06d31bb1dcea4a59d80689d84b1",
|
||||
"sso-mfa/k8s/keycape/test_registration_original_intent_reconcile.py": "sha256:e2897b8200ec97445fc4a26c55ad82651fcb1a4d22efe7fe59d0fcfc5cd8ec71",
|
||||
"sso-mfa/k8s/keycape/test_ensure_groups_adapter.py": "sha256:c8754a2ba735116edc6fe257aa993d0530cea9ca9f954ea5a621e38694906be2",
|
||||
"sso-mfa/k8s/keycape/test_keycape_checker_environment.py": "sha256:35f316eed168d3df588498faf24ab9377733a7d857ddfa5e0e4322a6287c93ff",
|
||||
"sso-mfa/k8s/keycape/test_bind_env_adapter.py": "sha256:a1e5e7a631a4843f9a0d2310a3942b26936103d786aeae47a142ff7c7b2fdbdb",
|
||||
"sso-mfa/k8s/keycape/create-secrets.py": "sha256:5da672fff36eb94101741ad99e2a1a96ae81fea15ed1871304c40e63ce27c787",
|
||||
"sso-mfa/k8s/keycape/approval-clients-rollout.py": "sha256:488d5cc59e081993dd2b5fc6d9e46bbddcaec4f5170b8f7a5ad0aae3a211a790",
|
||||
"sso-mfa/k8s/keycape/vergabe-client-rollout.py": "sha256:8cee6cc5ff3e3e8617304055263d04b393d0e005c34d9af12cb7e8bc685bac1a",
|
||||
"sso-mfa/k8s/keycape/keycape-registration-adapter.py": "sha256:a53beea4dbbe373ce7a5b84d55e79be8407ab9688037c7ee057afcc7be576047",
|
||||
"sso-mfa/k8s/keycape/compose-1.2-descriptors.py": "sha256:39021cac900b11a59d00c5a1d5491b1ee4c639955cde07d07854c3db8bc04958",
|
||||
"sso-mfa/k8s/keycape/test_approval_clients_rollout.py": "sha256:745316d42c3aba6b59b562d3bbcd4b2cb92871fb5e51a954e2254e2e550bb408",
|
||||
"sso-mfa/k8s/keycape/register-user-engine-portal.py": "sha256:67173edc44f5457e7b6a3e0fb2321b398159cfce5dea8ead01eabf835fb38d76",
|
||||
"sso-mfa/k8s/keycape/test_issuer_pin.py": "sha256:e471ed815af246633c0b21ac2cf5cae94d0dbdeff9e6ce801b4ddd033097dc20",
|
||||
"sso-mfa/k8s/keycape/openbao-negative-session.py": "sha256:24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a",
|
||||
"sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py": "sha256:65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42",
|
||||
"sso-mfa/k8s/keycape/test_portal_client_rollout.py": "sha256:a16cd4bf278970c10de2e1b1025597b3872c9515713dd947ac6b50cb78feea1f",
|
||||
"sso-mfa/k8s/keycape/test_deliver_secret_adapter.py": "sha256:24cedec29071b62fc2c2458950ed244feeff542bc20a7d480667dd99cda16b03",
|
||||
"sso-mfa/k8s/keycape/deliver-secret-adapter.py": "sha256:cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d",
|
||||
"sso-mfa/k8s/keycape/test_openbao_private_input.py": "sha256:84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1",
|
||||
"sso-mfa/k8s/keycape/register-coulomb-social.py": "sha256:b40ff4bdba71b797f8f393e79cb3c9d73b790045a0f7df6ecea35803369793c7",
|
||||
"sso-mfa/k8s/keycape/mint-secret-adapter.py": "sha256:246323c7243ded18229ab063956577e151d56d9be485d3bd44fc38e5af0edf3c",
|
||||
"sso-mfa/k8s/keycape/test_adapter_source_closure.py": "sha256:63cb442636d996fcf1c36b7b746ed860106a7da35dbaeb1c5129a41420ca4155",
|
||||
"sso-mfa/k8s/keycape/secrets-engine-openbao-client-rollout.py": "sha256:8b834e5f0353a5e11797637420609e06e7bf7de47777c7e78b774f74d4a6afbd",
|
||||
"sso-mfa/k8s/keycape/test_openbao_client_config.py": "sha256:7104a73fb936aca4829ef8046e39497d0b7347d3e74810f672e4d4ddc3346457",
|
||||
"sso-mfa/k8s/keycape/ensure-groups-adapter.py": "sha256:6cba5b07a9262a8e0bf445dc4095ba03b31ddc15e2efbfe374e93f4f78b6aed4",
|
||||
"sso-mfa/k8s/keycape/verify-user-engine-portal.py": "sha256:ccd5106395bd5d7b4f3dbbcbad449e3793aff217d4429b5d5af9e52ab9a58103",
|
||||
"sso-mfa/k8s/keycape/test_playwright_registration.py": "sha256:b97ea8619ac768f520e4058aeb50b0b1e8ef6d2e9a1a0fff932a151a3d4b61ae",
|
||||
"sso-mfa/k8s/keycape/playwright-registration.py": "sha256:bf2d6b83d93629df8ddaf6c31d6c7408ae3866969d63895714df0e08a570da99",
|
||||
"identity-provisioner/group_provenance_postgres.py": "sha256:2a0111e83ad7b611076a1b45634e7f698b3d2e9acc473320b4a8ba009ebd0932",
|
||||
"identity-provisioner/password_setup.py": "sha256:53012fc708384fc9cd0fff4f3fa5f204ab85866db91d726457dd130799e88ca0",
|
||||
"identity-provisioner/provisioner.py": "sha256:19d1b837cbafeeacb1aa815856cd68091e6702281d72b1d860230fa320e1725b",
|
||||
"identity-provisioner/copy-client-secret.py": "sha256:0f1907a9f16688148aede9fceb53a55cfd0504bcb8552f158d0fa77e9ad960c8",
|
||||
"identity-provisioner/group_provenance.py": "sha256:405e08eaaab7c21d618dff4db5970607806f32ab6c461effb81c737e32182b10",
|
||||
"identity-provisioner/server.py": "sha256:b61c83da6c7ce20fa2790d5bb00aeca6a91dccd133526bc559ed26562f90eb29",
|
||||
"security-case-types/oidc-browser-relying-party/type.yaml": "sha256:f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c",
|
||||
"security-case-type-revisions/1.2/oidc-browser-relying-party/type.yaml": "sha256:9e60ff569fb0d860a2bbde7bbb0991a54d4b0688f62b56df0fa5d161fd624578",
|
||||
"security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a",
|
||||
"security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml": "sha256:546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94",
|
||||
"security-cases/forgejo-netkingdom-oidc.yaml": "sha256:bcc2c6d5bae0d28242b3cdb71e74d8ca297c2afe04766b55d53597f86893351d",
|
||||
"sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json": "sha256:773daa1df72cc7cf6a6d8aa082208181ca2438cdecc9179b1bc5b1c3a09099c2",
|
||||
"sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53",
|
||||
"tools/security-case-types/security_case_evidence.py": "sha256:da8efa59c52ad73283397ebe639ae259d0cb9c6348b5cb829135c24230dcdc75",
|
||||
"tools/security-case-types/security_case_validator.py": "sha256:1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b",
|
||||
"tools/security-scenario-composer/security_scenario_composer.py": "sha256:9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f",
|
||||
"tools/playbook-capability-contract/playbook_contract_validator.py": "sha256:2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d"
|
||||
},
|
||||
"ops-warden": {
|
||||
"src/warden/ca.py": "sha256:d5282819c5d29386fd59a7dc46d1abdc01e99fc6838a1a6637f90868389fe557",
|
||||
"src/warden/inventory.py": "sha256:9401db92d7cbf3bea962b97e83ef2c252a19c69ab94ce4836ba58f68638cb311",
|
||||
"src/warden/memory.py": "sha256:ecf5f7ccabb669b56bfca3a109b10bbe8437bdd2ea30a47a4c08570f97d7be2a",
|
||||
"src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7",
|
||||
"src/warden/audit.py": "sha256:6b5b8fe053064d3905294bfde1b691fcca8a0806c889e52a49494470d29c24a0",
|
||||
"src/warden/case_type.py": "sha256:7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7",
|
||||
"src/warden/vault.py": "sha256:860b8c9f498b3955b363a74dd74087b6d0c399599ef22b951a076ee33c95929f",
|
||||
"src/warden/taint.py": "sha256:8c3d161d18d429d026aad715ac01e2d33e0f4e84ac03fbaea3cea15017ad39a1",
|
||||
"src/warden/case_run.py": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a",
|
||||
"src/warden/vault_hints.py": "sha256:0d158ffcc3a93d5442df5fbe13d4c1e3b3159dd60301530bbdae6db9bc9c709e",
|
||||
"src/warden/access.py": "sha256:258d45d2f84b54b2464373ad4d7d6df6ff95b8e8d60f5e85ceb2d962fcfa8a61",
|
||||
"src/warden/clock_attribution.py": "sha256:5fcf07a2525a6a250442e14b6f6229f7db2a6ca02fc6aac23cdaa4c1a78c63eb",
|
||||
"src/warden/case_plan.py": "sha256:9352ae8683d1181980c4ff38524582e4be448d33c85b558f60582c1e5bb5bbaa",
|
||||
"src/warden/caller_identity.py": "sha256:747edfaceecd470981949253bda0c3bd2f487d40a9a2f77c2cc1eff3f34f04f7",
|
||||
"src/warden/policy.py": "sha256:aac46f538e986731cd841581c91efe004b8c1940966b954ef635780a719dada4",
|
||||
"src/warden/case_receipt_state.py": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc",
|
||||
"src/warden/config.py": "sha256:8a857faa3811b00303d4d5c904d69255ebdaff3239e8516f97e6606e53b46924",
|
||||
"src/warden/openbao_caller.py": "sha256:7e6d07827d00eae7de3b2367c97bdcda1a13d6559627201a4ba09a0e42d46a17",
|
||||
"src/warden/doubles.py": "sha256:5460e07f5bd0f51a81d3f3c0ac84efde65ca9130b1039f94ee22ca484976a9de",
|
||||
"src/warden/posture.py": "sha256:1bb60c4dd4634ea42cb130ae1325d7415adf43096906bde2515396c368256e33",
|
||||
"src/warden/cli.py": "sha256:196cd829d3dbfbd2333598a983687bde1f9bcdfadef95b2fd1d237c4a5f060a9",
|
||||
"src/warden/proxy.py": "sha256:69649bcf8979e4ed7f9104e927b458827a355c4846143367f4cd49c8b89b96da",
|
||||
"src/warden/plan.py": "sha256:001b3197e644221344d52e6b1594eb8eae78d10e73d18ed31273505909f3916b",
|
||||
"src/warden/worker.py": "sha256:d7c2b391b36033d44d8c6841b9bce18a05c74b41ef2910b91689820b6a1cb44e",
|
||||
"src/warden/mask.py": "sha256:0876412d453fa84109dc8ee0468449f40cf4d3421b056f30fc205f0f59ec494b",
|
||||
"src/warden/models.py": "sha256:f4100b02c5d96d537980e00d4b874159cd5822ba8219f1f533a2516912b7bd85",
|
||||
"src/warden/__init__.py": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a",
|
||||
"src/warden/scorecard.py": "sha256:0fa5aef34aeb004fa7fcfabc287759cc47421ca5244c1f31fc467b22a653353c",
|
||||
"src/warden/desk.py": "sha256:2499120392a5a1c0fd8f423d5b5c787c75a4efb9a986a1b7bcb85eae13423e85",
|
||||
"src/warden/routing/scaffold.py": "sha256:5079c8e51f8d487eae3f322a66324bd70cefb637bb28f46c861032de8ec54aa2",
|
||||
"src/warden/routing/models.py": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e",
|
||||
"src/warden/routing/__init__.py": "sha256:e9124811ebe11ad32c98ab462b2eed634d4ddca08792981d0f353b223a239902",
|
||||
"src/warden/routing/catalog.py": "sha256:5b95810df9e6102a33d3a116a096cd75292c8572bad721fe6baa772caacc9bc3",
|
||||
"src/warden/scripts/ops_ssh_wrapper.py": "sha256:e8c289882f2ad439d2eb32f9a871967420fefaffe745c9271c1a2e948fbdfe50",
|
||||
"src/warden/scripts/__init__.py": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo_manager_identifiers_sha256": "sha256:4e35fcd009c20822574fd33171176bd14a1ff2dd5af8b0bb2820954974593a2a",
|
||||
"authorization": "Root accepted the exact source fixture patch for source integration. This packet adds no owner/native authority or catalog/pin promotion. No prepared script has been executed.",
|
||||
"publishable": false,
|
||||
"requires_allocated_manifest": true,
|
||||
"publication_file_paths": [
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"workplans/NK-WP-0048-security-case-types.md"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
"""Prepared canonical source validation; --execute launches only the two scoped tests."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
PACKET = Path(__file__).resolve().parent
|
||||
ROOTS = {'net-kingdom': Path('/home/worsch/net-kingdom'), 'ops-warden': Path('/home/worsch/ops-warden')}
|
||||
|
||||
def sha(data):
|
||||
return 'sha256:' + hashlib.sha256(data).hexdigest()
|
||||
|
||||
def sources(manifest):
|
||||
allowed = []
|
||||
for owner, rows in manifest['validation']['source_hashes'].items():
|
||||
for relative, expected in rows.items():
|
||||
path = ROOTS[owner] / relative
|
||||
assert not path.is_symlink(), str(path)
|
||||
assert sha(path.read_bytes()) == expected, str(path)
|
||||
allowed.append(str(path.resolve()))
|
||||
return allowed
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--execute', action='store_true')
|
||||
args = parser.parse_args()
|
||||
manifest = json.loads((PACKET / 'publication-inputs.json').read_text())
|
||||
allowed = sources(manifest)
|
||||
revision = subprocess.check_output(['git', '-C', str(ROOTS['ops-warden']), 'rev-parse', 'HEAD'], text=True).strip()
|
||||
assert revision == manifest['warden_revision']
|
||||
if not args.execute:
|
||||
print(json.dumps({'prepared_only': True, 'public_source_files_verified': len(allowed),
|
||||
'tests': manifest['validation']['tests']}))
|
||||
return
|
||||
protected = {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']}
|
||||
scratch = Path(tempfile.mkdtemp(prefix='nk48-catalog-canonical-validation-'))
|
||||
for name in ['home', 'tmp', 'guard', 'pycache']:
|
||||
(scratch / name).mkdir()
|
||||
shutil.copyfile(PACKET / 'validation-guard.py', scratch / 'guard/sitecustomize.py')
|
||||
shutil.copyfile(PACKET / 'validation-child.py', scratch / 'child.py')
|
||||
(scratch / 'allowed.json').write_text(json.dumps(allowed))
|
||||
(scratch / 'pytest.ini').write_text('[pytest]\n')
|
||||
environment = {
|
||||
'HOME': str(scratch / 'home'), 'TMPDIR': str(scratch / 'tmp'), 'PATH': '/usr/bin:/bin',
|
||||
'PYTHONPATH': str(scratch / 'guard') + ':' + str(ROOTS['ops-warden'] / 'src'),
|
||||
'PYTHONDONTWRITEBYTECODE': '1', 'PYTHONPYCACHEPREFIX': str(scratch / 'pycache'),
|
||||
'LANG': 'C.UTF-8', 'WARDEN_REVIEW_ALLOWED_SOURCE_LIST': str(scratch / 'allowed.json'),
|
||||
'WARDEN_REVIEW_VALIDATOR_PREFIX': str(scratch / 'tmp/warden-case-validator-'),
|
||||
}
|
||||
with (PACKET / 'canonical-validation.log').open('w') as log:
|
||||
result = subprocess.run([manifest['validation']['python'], str(scratch / 'child.py')],
|
||||
cwd=ROOTS['net-kingdom'], env=environment,
|
||||
stdout=log, stderr=subprocess.STDOUT)
|
||||
sources(manifest)
|
||||
assert protected == {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']}
|
||||
log_text = (PACKET / 'canonical-validation.log').read_text()
|
||||
counts_verified = bool(re.search(r'\b31 passed, 53 subtests passed\b', log_text))
|
||||
counts_verified = counts_verified and not re.search(r'\b[1-9][0-9]* (?:skipped|deselected)\b', log_text)
|
||||
receipt = {'executed': True, 'counts_verified': counts_verified, 'returncode': result.returncode, 'warden_revision': revision,
|
||||
'net_kingdom_head': subprocess.check_output(['git', '-C', str(ROOTS['net-kingdom']), 'rev-parse', 'HEAD'], text=True).strip(),
|
||||
'verified_public_source_files': len(allowed), 'tests': manifest['validation']['tests'],
|
||||
'log_sha256': sha((PACKET / 'canonical-validation.log').read_bytes()), 'scratch': str(scratch),
|
||||
'limitations': 'Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin.'}
|
||||
(PACKET / 'canonical-validation.json').write_text(json.dumps(receipt, indent=2) + '\n')
|
||||
print(json.dumps(receipt, indent=2))
|
||||
raise SystemExit(result.returncode or (0 if counts_verified else 1))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
## T35 — Align source admission fixtures with the authenticated catalog and attended-session API
|
||||
|
||||
```task
|
||||
id: NK-WP-0048-T35
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
"""Focused canonical-source test process. Network/native/estate-read guard required."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import sitecustomize
|
||||
|
||||
assert sitecustomize.ACTIVE is True
|
||||
assert not {'BAO_TOKEN', 'VAULT_TOKEN', 'WARDEN_AGENT_ID'} & os.environ.keys()
|
||||
assert list(Path.home().iterdir()) == []
|
||||
try:
|
||||
socket.socket()
|
||||
except RuntimeError as error:
|
||||
assert str(error) == 'fixture validation: network disabled'
|
||||
else:
|
||||
raise AssertionError('network guard inactive')
|
||||
|
||||
import warden.case_inputs as inputs
|
||||
import pytest
|
||||
|
||||
assert Path(inputs.__file__).resolve() == Path('/home/worsch/ops-warden/src/warden/case_inputs.py')
|
||||
PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n'''
|
||||
old_run = subprocess.run
|
||||
|
||||
def run_pinned(arguments, *positional, **keywords):
|
||||
if (type(arguments) is not list or len(arguments) != 5
|
||||
or arguments[:3] != [sys.executable, '-I', '-c']
|
||||
or arguments[3] != inputs._VALIDATE
|
||||
or not str(arguments[4]).startswith(sitecustomize.VALIDATOR_PREFIX)
|
||||
or keywords.get('env') != {}):
|
||||
raise RuntimeError('fixture validation: unexpected process refused')
|
||||
return old_run([*arguments[:3], PREAMBLE + arguments[3], arguments[4]],
|
||||
*positional, **keywords)
|
||||
|
||||
inputs.subprocess.run = run_pinned
|
||||
raise SystemExit(pytest.main([
|
||||
'-q', '-p', 'no:cacheprovider', '--noconftest',
|
||||
'-c', str(Path(os.environ['TMPDIR']).parent / 'pytest.ini'),
|
||||
'--rootdir=/home/worsch/net-kingdom',
|
||||
'/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_admit_1_3_type.py',
|
||||
'/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py',
|
||||
]))
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
"""Copied as sitecustomize.py into a fresh scratch directory by run_validation."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
ALLOWED = frozenset(json.loads(Path(os.environ['WARDEN_REVIEW_ALLOWED_SOURCE_LIST']).read_text()))
|
||||
VALIDATOR_PREFIX = os.environ['WARDEN_REVIEW_VALIDATOR_PREFIX']
|
||||
|
||||
def guard(event, args):
|
||||
if event.startswith('socket.'):
|
||||
raise RuntimeError('fixture validation: network disabled')
|
||||
if event == 'subprocess.Popen':
|
||||
command = args[1]
|
||||
if (type(command) is list and len(command) == 5
|
||||
and command[:3] == [sys.executable, '-I', '-c']
|
||||
and command[3].startswith('import sys as _review_sys\ndef _review_guard(')
|
||||
and str(command[4]).startswith(VALIDATOR_PREFIX) and args[3] == {}):
|
||||
return
|
||||
raise RuntimeError('fixture validation: native process disabled')
|
||||
if event in {'os.system', 'os.exec', 'os.posix_spawn'}:
|
||||
raise RuntimeError('fixture validation: native process disabled')
|
||||
if event == 'open' and isinstance(args[0], str):
|
||||
path = str(Path(args[0]).absolute())
|
||||
if path.startswith(('/home/', '/root/')):
|
||||
# Only exact prehashed public source may be read from the estate.
|
||||
if str(Path(path).resolve()) not in ALLOWED:
|
||||
raise PermissionError('fixture validation: unlisted estate file disabled')
|
||||
mode, flags = args[1:3]
|
||||
if ((isinstance(mode, str) and any(c in mode for c in 'wax+'))
|
||||
or (flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND))):
|
||||
raise PermissionError('fixture validation: canonical source writes disabled')
|
||||
|
||||
sys.addaudithook(guard)
|
||||
ACTIVE = True
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,92 @@
|
|||
"""Prepared source integration only. Root runs --execute; no commit/projection here."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
PACKET = Path(__file__).resolve().parent
|
||||
ROOT = Path('/home/worsch/net-kingdom')
|
||||
WARDEN = Path('/home/worsch/ops-warden')
|
||||
|
||||
def sha(data):
|
||||
return 'sha256:' + hashlib.sha256(data).hexdigest()
|
||||
|
||||
def git(root, *args):
|
||||
return subprocess.check_output(['git', '-C', str(root), *args], text=True).strip()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--execute', action='store_true')
|
||||
args = parser.parse_args()
|
||||
manifest = json.loads((PACKET / 'publication-inputs.json').read_text())
|
||||
assert git(ROOT, 'rev-parse', '--abbrev-ref', 'HEAD') == 'main'
|
||||
assert git(ROOT, 'rev-parse', 'HEAD') == manifest['base_revision']
|
||||
assert git(WARDEN, 'rev-parse', 'HEAD') == manifest['warden_revision']
|
||||
workplan = ROOT / manifest['workplan']
|
||||
original = workplan.read_bytes()
|
||||
candidate = (PACKET / 'workplan.candidate.md').read_bytes()
|
||||
assert sha(original) == manifest['workplan_preimage_sha256']
|
||||
assert sha(candidate) == manifest['workplan_candidate_sha256']
|
||||
assert sha((PACKET / 'task-T35.md').read_bytes()) == manifest['task_addition_sha256']
|
||||
assert candidate == original + (PACKET / 'task-T35.md').read_bytes()
|
||||
assert len(re.findall(rb'^```task$', original, re.M)) == 34
|
||||
assert len(re.findall(rb'^```task$', candidate, re.M)) == 35
|
||||
assert b'id: NK-WP-0048-T35' not in original
|
||||
assert sha((PACKET / 'candidate.patch').read_bytes()) == manifest['patch_sha256']
|
||||
for row in manifest['files']:
|
||||
path = ROOT / row['path']
|
||||
if row['preimage_sha256'] is None:
|
||||
assert not path.exists() and not path.is_symlink(), str(path)
|
||||
else:
|
||||
assert sha(path.read_bytes()) == row['preimage_sha256'], str(path)
|
||||
protected_before = {name: sha((ROOT / name).read_bytes()) for name in manifest['protected_files']}
|
||||
assert protected_before == manifest['protected_files'], 'Operator brief/index changed: refresh the prepared inputs first.'
|
||||
# Preserve the original index exactly; this script never stages anything.
|
||||
index = git(ROOT, 'ls-files', '--stage', '-z')
|
||||
assert sha(Path('/home/worsch/repo-manager/src/repo_manager/identifiers.py').read_bytes()) == manifest['repo_manager_identifiers_sha256']
|
||||
sys.path.insert(0, '/home/worsch/repo-manager/src')
|
||||
from repo_manager.identifiers import ensure_missing_work_record_identifiers
|
||||
before = ensure_missing_work_record_identifiers(ROOT, execute=False)
|
||||
assert before['ok'] and before['assignments'] == [] and before['files_changed'] == []
|
||||
subprocess.run(['git', 'apply', '--check', str(PACKET / 'candidate.patch')], cwd=ROOT, check=True)
|
||||
if not args.execute:
|
||||
print(json.dumps({'prepared_only': True, 'preimages_verified': True, 'files': 4,
|
||||
'next': 'Run this script with --execute, then run_validation.py --execute.'}))
|
||||
return
|
||||
subprocess.run(['git', 'apply', str(PACKET / 'candidate.patch')], cwd=ROOT, check=True)
|
||||
for row in manifest['files']:
|
||||
assert sha((ROOT / row['path']).read_bytes()) == row['sha256'], row['path']
|
||||
workplan.write_bytes(candidate)
|
||||
preview = ensure_missing_work_record_identifiers(ROOT, execute=False)
|
||||
(PACKET / 'identifier-preview.json').write_text(json.dumps(preview, indent=2) + '\n')
|
||||
assert preview['ok'] and preview['files_changed'] == [manifest['workplan']]
|
||||
assert len(preview['assignments']) == 1
|
||||
assignment = preview['assignments'][0]
|
||||
assert (assignment['kind'], assignment['record_id'], assignment['path']) == (
|
||||
'task', 'NK-WP-0048-T35', manifest['workplan'])
|
||||
# UUID comes only from the ordinary allocator, never from this packet.
|
||||
actual = ensure_missing_work_record_identifiers(ROOT, execute=True)
|
||||
assert actual['assignments'] == preview['assignments']
|
||||
assert actual['files_changed'] == [manifest['workplan']]
|
||||
(PACKET / 'identifier-assignment.json').write_text(json.dumps(actual, indent=2) + '\n')
|
||||
allocated = workplan.read_bytes()
|
||||
assert allocated.startswith(original), 'Existing workplan bytes must be preserved.'
|
||||
assert len(re.findall(rb'^```task$', allocated, re.M)) == 35
|
||||
assert allocated.count(b'id: NK-WP-0048-T35\n') == 1
|
||||
assert not ensure_missing_work_record_identifiers(ROOT, execute=False)['assignments']
|
||||
assert git(ROOT, 'ls-files', '--stage', '-z') == index, 'Index must remain unchanged.'
|
||||
assert {name: sha((ROOT / name).read_bytes()) for name in manifest['protected_files']} == protected_before
|
||||
(PACKET / 'workplan.allocated.md').write_bytes(allocated)
|
||||
publication = {'owner': 'net-kingdom', 'base_revision': manifest['base_revision'],
|
||||
'message': manifest['message'], 'files': [
|
||||
{'path': row['path'], 'sha256': sha((ROOT / row['path']).read_bytes()).removeprefix('sha256:')}
|
||||
for row in manifest['files'] + [{'path': manifest['workplan']}]]}
|
||||
(PACKET / 'publication-inputs.allocated.json').write_text(json.dumps(publication, indent=2) + '\n')
|
||||
print(json.dumps({'source_applied': True, 'task_count': 35, 'allocated_only': assignment['record_id'],
|
||||
'files': len(publication['files']), 'commit_and_projection': 'Root handles separately.'}))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
diff --git a/sso-mfa/k8s/keycape/test_admit_1_3_type.py b/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
--- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
+++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
@@ -8,7 +8,9 @@
|
||||
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.
|
||||
+memory the five resolvable steps admit. The two Apps attended steps remain refused
|
||||
+because their establishing login is unresolved in the synthetic catalog. Four real
|
||||
+draft lane shapes are hypothetically active in that fixture; no catalog is promoted.
|
||||
Trust is fixture-only, nothing is dispatched, and none of this is owner admission
|
||||
or an acknowledgment.
|
||||
"""
|
||||
@@ -37,6 +39,8 @@
|
||||
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())
|
||||
+CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()
|
||||
+UNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'}
|
||||
EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'}
|
||||
|
||||
|
||||
@@ -74,12 +78,13 @@
|
||||
ARTIFACT['params']['consumer_owner_repo'],
|
||||
version='1.3'))
|
||||
|
||||
- def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None):
|
||||
+ def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG):
|
||||
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,
|
||||
+ lambda purpose, source, evidence: (evidence in EVIDENCE and
|
||||
+ (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))),
|
||||
browser_inputs=inputs.BrowserInputs(REPO))
|
||||
with mock.patch.dict(os.environ):
|
||||
if agent_id is None:
|
||||
@@ -87,7 +92,7 @@
|
||||
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,
|
||||
+ catalog, principal='founder', actor=actor or desc.actor,
|
||||
lane=lane or desc.lane,
|
||||
acceptance_evidence='fixture-acceptance',
|
||||
materialization_evidence='fixture-materialization',
|
||||
@@ -112,18 +117,33 @@
|
||||
self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2'])
|
||||
acknowledged = with_status('acknowledged')
|
||||
for step, desc in self.descriptors.items():
|
||||
+ if step in UNRESOLVED_APPS:
|
||||
+ continue # The attended session gate correctly precedes parameter validation.
|
||||
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):
|
||||
+ def test_committed_map_conditionally_admits_five_resolvable_steps(self):
|
||||
acknowledged = with_status('acknowledged')
|
||||
for step, desc in self.descriptors.items():
|
||||
+ if step in UNRESOLVED_APPS:
|
||||
+ continue
|
||||
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_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self):
|
||||
+ for step in sorted(UNRESOLVED_APPS):
|
||||
+ with self.subTest(step=step), pinned_13(), self.assertRaisesRegex(
|
||||
+ Refused, 'does not resolve to an active, reviewed login lane'):
|
||||
+ self.admit(self.descriptors[step], with_status('acknowledged'))
|
||||
+
|
||||
+ def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self):
|
||||
+ mint = self.descriptors['mint-secret']
|
||||
+ with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'):
|
||||
+ self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\n# changed source\n')
|
||||
|
||||
def test_descriptor_outside_the_map_is_refused(self):
|
||||
acknowledged = with_status('acknowledged')
|
||||
diff --git a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
--- a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
+++ b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
@@ -259,8 +259,13 @@
|
||||
case_source = yaml.safe_dump(case).encode()
|
||||
typ = yaml.safe_load((root / 'security-case-types/oidc-browser-relying-party/type.yaml').read_bytes())
|
||||
typ['spec']['execution_descriptors'] = {'register-client': digest(self.desc_bytes)}
|
||||
+ # Fixture-only successor lane for the adapter's current descriptor.
|
||||
+ # Canonical 1.1 still names the earlier login lane and is not edited.
|
||||
+ typ['spec']['follow_through'] = [
|
||||
+ dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step
|
||||
+ for step in typ['spec']['follow_through']]
|
||||
type_source = yaml.safe_dump(typ).encode()
|
||||
- catalog = b'fixture-only-catalog'
|
||||
+ catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()
|
||||
sheet = json.dumps(['register-client', 'net-kingdom', 'forgejo', 'human',
|
||||
'founder', 'keycape-attended-case-change', 'fixture-acceptance', 'fixture-materialization',
|
||||
'fixture-descriptor', 'fixture-session'], separators=(',', ':')).encode()
|
||||
@@ -270,6 +275,8 @@
|
||||
adapter = a.build(**dict(self.kw, admitted=invocation, case_source=case_source,
|
||||
case_acceptance='fixture-acceptance'))
|
||||
def fixture_verifier(purpose, source, evidence):
|
||||
+ if purpose == 'catalog-routing-acceptance':
|
||||
+ return source == digest(catalog) and evidence == 'fixture-descriptor'
|
||||
if purpose.startswith('session-revocation:'):
|
||||
_, lane, operation, attempt = purpose.split(':')
|
||||
return source == digest(case_source) and lane == invocation.lane and evidence == (
|
||||
diff --git a/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml
|
||||
@@ -0,0 +1,128 @@
|
||||
+# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog.
|
||||
+# Four draft entries are hypothetically active; Apps session_lane remains unresolved.
|
||||
+# Not an authenticated owner catalog, promotion, login choice or native authority.
|
||||
+entries:
|
||||
+- id: key-cape-oidc-login
|
||||
+ risk: standard
|
||||
+ owner_repo: key-cape
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-06-27'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: interim
|
||||
+ intended_owner: key-cape
|
||||
+ reviewed: '2026-09-08'
|
||||
+ blocked_on: fixture-only; no native authority
|
||||
+ lane: login
|
||||
+ title: Synthetic key-cape-oidc-login
|
||||
+ need_keywords:
|
||||
+ - key-cape-oidc-login
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: keycape-client-secret-generate
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-platform
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-10'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-platform
|
||||
+ reviewed: '2026-10-10'
|
||||
+ lane: secret
|
||||
+ title: Synthetic keycape-client-secret-generate
|
||||
+ need_keywords:
|
||||
+ - keycape-client-secret-generate
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: keycape-attended-case-change
|
||||
+ lane: attended-change
|
||||
+ session_lane: key-cape-oidc-login
|
||||
+ risk: high
|
||||
+ owner_repo: key-cape/net-kingdom
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: interim
|
||||
+ intended_owner: key-cape
|
||||
+ reviewed: '2026-10-17'
|
||||
+ blocked_on: fixture-only; no native authority
|
||||
+ title: Synthetic keycape-attended-case-change
|
||||
+ need_keywords:
|
||||
+ - keycape-attended-case-change
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: platform-secret-delivery
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-platform
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-platform
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic platform-secret-delivery
|
||||
+ need_keywords:
|
||||
+ - platform-secret-delivery
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: consumer-owner-deployment
|
||||
+ lane: attended-change
|
||||
+ session_lane: pending-owner-confirmation
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-apps
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-apps
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic consumer-owner-deployment
|
||||
+ need_keywords:
|
||||
+ - consumer-owner-deployment
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: apps-consumer-handover
|
||||
+ lane: attended-change
|
||||
+ session_lane: pending-owner-confirmation
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-apps
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-apps
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic apps-consumer-handover
|
||||
+ need_keywords:
|
||||
+ - apps-consumer-handover
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"executed": true,
|
||||
"counts_verified": true,
|
||||
"returncode": 0,
|
||||
"warden_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580",
|
||||
"net_kingdom_head": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"verified_public_source_files": 105,
|
||||
"tests": [
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py"
|
||||
],
|
||||
"log_sha256": "sha256:2ac155ce37c15bfd4f24483304036fec725bc3f166c0963d56280dea1d43ae73",
|
||||
"scratch": "/tmp/nk48-catalog-canonical-validation-ntdpunql",
|
||||
"limitations": "Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin."
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"schema": "repo-manager.identifier-assignment.v1",
|
||||
"ok": true,
|
||||
"executed": true,
|
||||
"repo": "net-kingdom",
|
||||
"namespace": "helixforge",
|
||||
"assignments": [
|
||||
{
|
||||
"kind": "task",
|
||||
"record_id": "NK-WP-0048-T35",
|
||||
"uuid": "cf1d6ab3-846e-53ea-8575-d6347b6f4769",
|
||||
"path": "workplans/NK-WP-0048-security-case-types.md"
|
||||
}
|
||||
],
|
||||
"historical_invalid_identifiers": [],
|
||||
"historical_identity_collisions": [],
|
||||
"files_changed": [
|
||||
"workplans/NK-WP-0048-security-case-types.md"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"schema": "repo-manager.identifier-assignment.v1",
|
||||
"ok": true,
|
||||
"executed": false,
|
||||
"repo": "net-kingdom",
|
||||
"namespace": "helixforge",
|
||||
"assignments": [
|
||||
{
|
||||
"kind": "task",
|
||||
"record_id": "NK-WP-0048-T35",
|
||||
"uuid": "cf1d6ab3-846e-53ea-8575-d6347b6f4769",
|
||||
"path": "workplans/NK-WP-0048-security-case-types.md"
|
||||
}
|
||||
],
|
||||
"historical_invalid_identifiers": [],
|
||||
"historical_identity_collisions": [],
|
||||
"files_changed": [
|
||||
"workplans/NK-WP-0048-security-case-types.md"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"schema": "custodian.nk48-catalog-fixture-publication-preparation.v1",
|
||||
"status": "prepared only; canonical apply and validation runners not executed, including dry-run",
|
||||
"owner_base": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"warden_base": "ae7bc73b4f4bac22454144959b870d161498e0af",
|
||||
"root_source_review": "Root explicitly accepted fixture patch SHA256 df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501 for source integration; no owner/native assent inferred.",
|
||||
"task_refinement": "Append completed T35 for distinct current authenticated-catalog/mode-aware fixture compatibility; preserve exact original workplan bytes, all34 existing task blocks/frontmatter/status/UUID. Normal allocator preview must yield exactlyT35 before execution; no manual UUID.",
|
||||
"root_commands": [
|
||||
"python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py --execute",
|
||||
"python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py --execute",
|
||||
"Root normal scoped commit using publication-inputs.allocated.json; normal owner projection afterwards."
|
||||
],
|
||||
"mutation_scope": [
|
||||
"Exactly the three reviewed fixture files plus the appended T35 workplan task.",
|
||||
"No git stage,commit,push,projection,brief refresh or index generation in prepared scripts.",
|
||||
"Preexisting operator .custodian-brief.md/WORK-RECORDS.md remain byte-identical; unrelated dirty/untracked paths are never read or written by the apply script."
|
||||
],
|
||||
"validation_scope": "Canonical NK test and source files against Warden mainae7bc73;105 explicit public source hashes checked before and after. Two scoped test modules only; expected31 tests/53 subtests; no skip/deselection accepted. EmptyHOME and allowlisted environment; fresh redirected Python caches; pytest cache/conftest discovery disabled. Network denied. Native processes denied except exact pinned offline metadata validator with guarded empty-environment child. Reads from /home or /root denied except exact prehashed public source files; canonical writes denied.",
|
||||
"unverified_limits": [
|
||||
"Prepared runner has only been AST-parsed; root must execute and inspect logs.",
|
||||
"Python audit controls are bounded to reviewed test execution and do not claim operating-system credential isolation or an autopilot grant.",
|
||||
"Warden code is source-only; 1.3 parameter pin, draft promotion, pending Apps login, nativeT03 and downstream acceptance remain gated.",
|
||||
"Existing adverse review history remains unchanged."
|
||||
],
|
||||
"files": {
|
||||
"apply_prepare.py": "sha256:b294e6339e8c9af9ba911667646dd0ab52e75cf798de93238d84d1057c8a9e70",
|
||||
"run_validation.py": "sha256:16236aa55a774f8ea6d66da4ee17f7737c6d1b013d844ca9b9759e02b615af51",
|
||||
"validation-guard.py": "sha256:13611f4de25dc7d01348905c4b365a0ffbb95fd93a61c75c8faf38d705c54f50",
|
||||
"workplan.preimage.md": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9",
|
||||
"publication-inputs.json": "sha256:d9bf5555013aa9e399037672c80a72af23c6815db03088bee3db4c8583197146",
|
||||
"candidate.patch": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501",
|
||||
"task-T35.md": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f",
|
||||
"validation-child.py": "sha256:6bd4e3cda22e65f90bbb4006b403270405947293be2ae8bf8fdade0abed61f45",
|
||||
"workplan.candidate.md": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"owner": "net-kingdom",
|
||||
"base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"message": "test: align security case fixtures with authenticated catalog admission",
|
||||
"files": [
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sha256": "2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"sha256": "86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"sha256": "7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53"
|
||||
},
|
||||
{
|
||||
"path": "workplans/NK-WP-0048-security-case-types.md",
|
||||
"sha256": "53f9e5826bd7e34287d59f46089ee50c31598ba1a56bcf6d3249525f7e23bb9b"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
{
|
||||
"schema": "custodian.nk48-catalog-fixture-publication-inputs.v1",
|
||||
"prepared_only": true,
|
||||
"owner": "net-kingdom",
|
||||
"base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"warden_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580",
|
||||
"message": "test: align security case fixtures with authenticated catalog admission",
|
||||
"workplan": "workplans/NK-WP-0048-security-case-types.md",
|
||||
"workplan_preimage_sha256": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9",
|
||||
"workplan_candidate_sha256": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981",
|
||||
"task_addition_sha256": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f",
|
||||
"patch_sha256": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501",
|
||||
"files": [
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"preimage_sha256": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0",
|
||||
"sha256": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"preimage_sha256": "sha256:4d0126416522e54eec1ae4c83b5488e09ff11ffb97ec1ee6d234548780c60f63",
|
||||
"sha256": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"preimage_sha256": null,
|
||||
"sha256": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53"
|
||||
}
|
||||
],
|
||||
"allocation": {
|
||||
"method": "repo_manager.identifiers.ensure_missing_work_record_identifiers",
|
||||
"manual_uuid": false,
|
||||
"preexisting_assignments": [],
|
||||
"expected_new_record_ids": [
|
||||
"NK-WP-0048-T35"
|
||||
],
|
||||
"expected_files_changed": [
|
||||
"workplans/NK-WP-0048-security-case-types.md"
|
||||
],
|
||||
"expected_task_count": 35,
|
||||
"concrete_postallocation_manifest": "publication-inputs.allocated.json"
|
||||
},
|
||||
"protected_files": {
|
||||
".custodian-brief.md": "sha256:3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1",
|
||||
"WORK-RECORDS.md": "sha256:a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a"
|
||||
},
|
||||
"validation": {
|
||||
"tests": [
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py"
|
||||
],
|
||||
"expected_result": "31 passed,53 subtests passed; no skipped or deselected tests",
|
||||
"python": "/tmp/warden-kernel-review-r1/final/.venv/bin/python",
|
||||
"source_hashes": {
|
||||
"net-kingdom": {
|
||||
"sso-mfa/k8s/keycape/test_vergabe_client_rollout.py": "sha256:f9b7defc312b0fea24ded92ef479dcc8aef063f7a66a8245760918bcf44c4af1",
|
||||
"sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py": "sha256:9a4b9fb0141221d6e5bcb72b3656613afda54d16ede300bffa8f71956ae6fd2c",
|
||||
"sso-mfa/k8s/keycape/test_keycape_client.py": "sha256:9572044679feaedfe732e15a2b6f8ef6b41a8fe2e4430257f309a3e83e97736c",
|
||||
"sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py": "sha256:76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1",
|
||||
"sso-mfa/k8s/keycape/portal-client-rollout.py": "sha256:158e1d36eb881cdd0a2eda91b472adea5e8fde5061e00d26dda368580aeb871f",
|
||||
"sso-mfa/k8s/keycape/test_playwright_activation.py": "sha256:6fc22d091f27a76c32d2fb0a7fc3c6e18e54ff98d242699c518b2f9dc9fd4cbd",
|
||||
"sso-mfa/k8s/keycape/exercise-approval-clients.py": "sha256:e2d3dd741ac52af28dce752344227a4443b78fedd4cfcc06ba902e679dfc9351",
|
||||
"sso-mfa/k8s/keycape/keycape-client.py": "sha256:4b63161cf3365d34dd684a3116e2c935baaf7f6aa4b30983f6883546db3d6591",
|
||||
"sso-mfa/k8s/keycape/test_openbao_negative_session.py": "sha256:01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c",
|
||||
"sso-mfa/k8s/keycape/authentication-policy-rollout.py": "sha256:2c1bd23fded4855bae1eb3a08dbc4510d7f60f2bc4d625d60e471ab78f6f0251",
|
||||
"sso-mfa/k8s/keycape/handover-adapter.py": "sha256:bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832",
|
||||
"sso-mfa/k8s/keycape/openbao-signin-diagnosis-rollout.py": "sha256:a57b957d6eed9e8fb09d2a7aa7405a39deeafd9aaf57a9e6dc907afabd380371",
|
||||
"sso-mfa/k8s/keycape/test_handover_adapter.py": "sha256:cd1fb0eb64a777699221ebdfcd6fea96bafb9b27ec388e95dcfe66189eac8ca2",
|
||||
"sso-mfa/k8s/keycape/test_authentication_policy_rollout.py": "sha256:11c10039d99a34d2819b09146a966c8b7cba6095e7a8b51c055a01e7167e3ed5",
|
||||
"sso-mfa/k8s/keycape/test_deliver_consumer_secret_adapter.py": "sha256:f4e6c6acd75a36f675007d2f62a7c7c4f7bfe2951cbb5e3e4cdde8b53f17f473",
|
||||
"sso-mfa/k8s/keycape/bind-env-adapter.py": "sha256:fa17fae21776b5c620ee9782adf050649c3a28719a6687d2524ff67ca7ad1925",
|
||||
"sso-mfa/k8s/keycape/test_keycape_checker_snapshot.py": "sha256:9271f25c760ad732c257d59d259b5c767436a2dc2d5ba24fca259bc57761d6d1",
|
||||
"sso-mfa/k8s/keycape/test_openbao_signin_diagnosis_rollout.py": "sha256:25d46fe7caca2ad4c262bd2a25188c91847304b6b051c4f3b3705c69f2803f01",
|
||||
"sso-mfa/k8s/keycape/test_openbao_private_ready.py": "sha256:8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277",
|
||||
"sso-mfa/k8s/keycape/test_mint_secret_adapter.py": "sha256:8110b6a8abd8e9a1f5e1feae11ec09c4e28cb7486c759b0e8459da1fb1b7144d",
|
||||
"sso-mfa/k8s/keycape/playwright-activation.py": "sha256:5ac683b3324b00e6253f6040f4c73057027b1f98f7071160a6153794ba522757",
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019",
|
||||
"sso-mfa/k8s/keycape/test_openbao_node_route.py": "sha256:1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6",
|
||||
"sso-mfa/k8s/keycape/test_secrets_engine_openbao_client_rollout.py": "sha256:e6f86fb7712c1b9c01d9b684f26d5cc949649b823d2732eced132d59acd3fe01",
|
||||
"sso-mfa/k8s/keycape/openbao-client-config.py": "sha256:1b908b4c36134212d78f9fbe7a2a201eb3ced06d31bb1dcea4a59d80689d84b1",
|
||||
"sso-mfa/k8s/keycape/test_registration_original_intent_reconcile.py": "sha256:e2897b8200ec97445fc4a26c55ad82651fcb1a4d22efe7fe59d0fcfc5cd8ec71",
|
||||
"sso-mfa/k8s/keycape/test_ensure_groups_adapter.py": "sha256:c8754a2ba735116edc6fe257aa993d0530cea9ca9f954ea5a621e38694906be2",
|
||||
"sso-mfa/k8s/keycape/test_keycape_checker_environment.py": "sha256:35f316eed168d3df588498faf24ab9377733a7d857ddfa5e0e4322a6287c93ff",
|
||||
"sso-mfa/k8s/keycape/test_bind_env_adapter.py": "sha256:a1e5e7a631a4843f9a0d2310a3942b26936103d786aeae47a142ff7c7b2fdbdb",
|
||||
"sso-mfa/k8s/keycape/create-secrets.py": "sha256:5da672fff36eb94101741ad99e2a1a96ae81fea15ed1871304c40e63ce27c787",
|
||||
"sso-mfa/k8s/keycape/approval-clients-rollout.py": "sha256:488d5cc59e081993dd2b5fc6d9e46bbddcaec4f5170b8f7a5ad0aae3a211a790",
|
||||
"sso-mfa/k8s/keycape/vergabe-client-rollout.py": "sha256:8cee6cc5ff3e3e8617304055263d04b393d0e005c34d9af12cb7e8bc685bac1a",
|
||||
"sso-mfa/k8s/keycape/keycape-registration-adapter.py": "sha256:a53beea4dbbe373ce7a5b84d55e79be8407ab9688037c7ee057afcc7be576047",
|
||||
"sso-mfa/k8s/keycape/compose-1.2-descriptors.py": "sha256:39021cac900b11a59d00c5a1d5491b1ee4c639955cde07d07854c3db8bc04958",
|
||||
"sso-mfa/k8s/keycape/test_approval_clients_rollout.py": "sha256:745316d42c3aba6b59b562d3bbcd4b2cb92871fb5e51a954e2254e2e550bb408",
|
||||
"sso-mfa/k8s/keycape/register-user-engine-portal.py": "sha256:67173edc44f5457e7b6a3e0fb2321b398159cfce5dea8ead01eabf835fb38d76",
|
||||
"sso-mfa/k8s/keycape/test_issuer_pin.py": "sha256:e471ed815af246633c0b21ac2cf5cae94d0dbdeff9e6ce801b4ddd033097dc20",
|
||||
"sso-mfa/k8s/keycape/openbao-negative-session.py": "sha256:24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a",
|
||||
"sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py": "sha256:65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42",
|
||||
"sso-mfa/k8s/keycape/test_portal_client_rollout.py": "sha256:a16cd4bf278970c10de2e1b1025597b3872c9515713dd947ac6b50cb78feea1f",
|
||||
"sso-mfa/k8s/keycape/test_deliver_secret_adapter.py": "sha256:24cedec29071b62fc2c2458950ed244feeff542bc20a7d480667dd99cda16b03",
|
||||
"sso-mfa/k8s/keycape/deliver-secret-adapter.py": "sha256:cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d",
|
||||
"sso-mfa/k8s/keycape/test_openbao_private_input.py": "sha256:84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1",
|
||||
"sso-mfa/k8s/keycape/register-coulomb-social.py": "sha256:b40ff4bdba71b797f8f393e79cb3c9d73b790045a0f7df6ecea35803369793c7",
|
||||
"sso-mfa/k8s/keycape/mint-secret-adapter.py": "sha256:246323c7243ded18229ab063956577e151d56d9be485d3bd44fc38e5af0edf3c",
|
||||
"sso-mfa/k8s/keycape/test_adapter_source_closure.py": "sha256:63cb442636d996fcf1c36b7b746ed860106a7da35dbaeb1c5129a41420ca4155",
|
||||
"sso-mfa/k8s/keycape/secrets-engine-openbao-client-rollout.py": "sha256:8b834e5f0353a5e11797637420609e06e7bf7de47777c7e78b774f74d4a6afbd",
|
||||
"sso-mfa/k8s/keycape/test_openbao_client_config.py": "sha256:7104a73fb936aca4829ef8046e39497d0b7347d3e74810f672e4d4ddc3346457",
|
||||
"sso-mfa/k8s/keycape/ensure-groups-adapter.py": "sha256:6cba5b07a9262a8e0bf445dc4095ba03b31ddc15e2efbfe374e93f4f78b6aed4",
|
||||
"sso-mfa/k8s/keycape/verify-user-engine-portal.py": "sha256:ccd5106395bd5d7b4f3dbbcbad449e3793aff217d4429b5d5af9e52ab9a58103",
|
||||
"sso-mfa/k8s/keycape/test_playwright_registration.py": "sha256:b97ea8619ac768f520e4058aeb50b0b1e8ef6d2e9a1a0fff932a151a3d4b61ae",
|
||||
"sso-mfa/k8s/keycape/playwright-registration.py": "sha256:bf2d6b83d93629df8ddaf6c31d6c7408ae3866969d63895714df0e08a570da99",
|
||||
"identity-provisioner/group_provenance_postgres.py": "sha256:2a0111e83ad7b611076a1b45634e7f698b3d2e9acc473320b4a8ba009ebd0932",
|
||||
"identity-provisioner/password_setup.py": "sha256:53012fc708384fc9cd0fff4f3fa5f204ab85866db91d726457dd130799e88ca0",
|
||||
"identity-provisioner/provisioner.py": "sha256:19d1b837cbafeeacb1aa815856cd68091e6702281d72b1d860230fa320e1725b",
|
||||
"identity-provisioner/copy-client-secret.py": "sha256:0f1907a9f16688148aede9fceb53a55cfd0504bcb8552f158d0fa77e9ad960c8",
|
||||
"identity-provisioner/group_provenance.py": "sha256:405e08eaaab7c21d618dff4db5970607806f32ab6c461effb81c737e32182b10",
|
||||
"identity-provisioner/server.py": "sha256:b61c83da6c7ce20fa2790d5bb00aeca6a91dccd133526bc559ed26562f90eb29",
|
||||
"security-case-types/oidc-browser-relying-party/type.yaml": "sha256:f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c",
|
||||
"security-case-type-revisions/1.2/oidc-browser-relying-party/type.yaml": "sha256:9e60ff569fb0d860a2bbde7bbb0991a54d4b0688f62b56df0fa5d161fd624578",
|
||||
"security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a",
|
||||
"security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml": "sha256:546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94",
|
||||
"security-cases/forgejo-netkingdom-oidc.yaml": "sha256:bcc2c6d5bae0d28242b3cdb71e74d8ca297c2afe04766b55d53597f86893351d",
|
||||
"sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json": "sha256:773daa1df72cc7cf6a6d8aa082208181ca2438cdecc9179b1bc5b1c3a09099c2",
|
||||
"sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53",
|
||||
"tools/security-case-types/security_case_evidence.py": "sha256:da8efa59c52ad73283397ebe639ae259d0cb9c6348b5cb829135c24230dcdc75",
|
||||
"tools/security-case-types/security_case_validator.py": "sha256:1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b",
|
||||
"tools/security-scenario-composer/security_scenario_composer.py": "sha256:9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f",
|
||||
"tools/playbook-capability-contract/playbook_contract_validator.py": "sha256:2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d"
|
||||
},
|
||||
"ops-warden": {
|
||||
"src/warden/ca.py": "sha256:d5282819c5d29386fd59a7dc46d1abdc01e99fc6838a1a6637f90868389fe557",
|
||||
"src/warden/inventory.py": "sha256:9401db92d7cbf3bea962b97e83ef2c252a19c69ab94ce4836ba58f68638cb311",
|
||||
"src/warden/memory.py": "sha256:ecf5f7ccabb669b56bfca3a109b10bbe8437bdd2ea30a47a4c08570f97d7be2a",
|
||||
"src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7",
|
||||
"src/warden/audit.py": "sha256:6b5b8fe053064d3905294bfde1b691fcca8a0806c889e52a49494470d29c24a0",
|
||||
"src/warden/case_type.py": "sha256:7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7",
|
||||
"src/warden/vault.py": "sha256:860b8c9f498b3955b363a74dd74087b6d0c399599ef22b951a076ee33c95929f",
|
||||
"src/warden/taint.py": "sha256:8c3d161d18d429d026aad715ac01e2d33e0f4e84ac03fbaea3cea15017ad39a1",
|
||||
"src/warden/case_run.py": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a",
|
||||
"src/warden/vault_hints.py": "sha256:0d158ffcc3a93d5442df5fbe13d4c1e3b3159dd60301530bbdae6db9bc9c709e",
|
||||
"src/warden/access.py": "sha256:258d45d2f84b54b2464373ad4d7d6df6ff95b8e8d60f5e85ceb2d962fcfa8a61",
|
||||
"src/warden/clock_attribution.py": "sha256:5fcf07a2525a6a250442e14b6f6229f7db2a6ca02fc6aac23cdaa4c1a78c63eb",
|
||||
"src/warden/case_plan.py": "sha256:9352ae8683d1181980c4ff38524582e4be448d33c85b558f60582c1e5bb5bbaa",
|
||||
"src/warden/caller_identity.py": "sha256:747edfaceecd470981949253bda0c3bd2f487d40a9a2f77c2cc1eff3f34f04f7",
|
||||
"src/warden/policy.py": "sha256:aac46f538e986731cd841581c91efe004b8c1940966b954ef635780a719dada4",
|
||||
"src/warden/case_receipt_state.py": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc",
|
||||
"src/warden/config.py": "sha256:8a857faa3811b00303d4d5c904d69255ebdaff3239e8516f97e6606e53b46924",
|
||||
"src/warden/openbao_caller.py": "sha256:7e6d07827d00eae7de3b2367c97bdcda1a13d6559627201a4ba09a0e42d46a17",
|
||||
"src/warden/doubles.py": "sha256:5460e07f5bd0f51a81d3f3c0ac84efde65ca9130b1039f94ee22ca484976a9de",
|
||||
"src/warden/posture.py": "sha256:1bb60c4dd4634ea42cb130ae1325d7415adf43096906bde2515396c368256e33",
|
||||
"src/warden/cli.py": "sha256:196cd829d3dbfbd2333598a983687bde1f9bcdfadef95b2fd1d237c4a5f060a9",
|
||||
"src/warden/proxy.py": "sha256:69649bcf8979e4ed7f9104e927b458827a355c4846143367f4cd49c8b89b96da",
|
||||
"src/warden/plan.py": "sha256:001b3197e644221344d52e6b1594eb8eae78d10e73d18ed31273505909f3916b",
|
||||
"src/warden/worker.py": "sha256:d7c2b391b36033d44d8c6841b9bce18a05c74b41ef2910b91689820b6a1cb44e",
|
||||
"src/warden/mask.py": "sha256:0876412d453fa84109dc8ee0468449f40cf4d3421b056f30fc205f0f59ec494b",
|
||||
"src/warden/models.py": "sha256:f4100b02c5d96d537980e00d4b874159cd5822ba8219f1f533a2516912b7bd85",
|
||||
"src/warden/__init__.py": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a",
|
||||
"src/warden/scorecard.py": "sha256:0fa5aef34aeb004fa7fcfabc287759cc47421ca5244c1f31fc467b22a653353c",
|
||||
"src/warden/desk.py": "sha256:2499120392a5a1c0fd8f423d5b5c787c75a4efb9a986a1b7bcb85eae13423e85",
|
||||
"src/warden/routing/scaffold.py": "sha256:5079c8e51f8d487eae3f322a66324bd70cefb637bb28f46c861032de8ec54aa2",
|
||||
"src/warden/routing/models.py": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e",
|
||||
"src/warden/routing/__init__.py": "sha256:e9124811ebe11ad32c98ab462b2eed634d4ddca08792981d0f353b223a239902",
|
||||
"src/warden/routing/catalog.py": "sha256:5b95810df9e6102a33d3a116a096cd75292c8572bad721fe6baa772caacc9bc3",
|
||||
"src/warden/scripts/ops_ssh_wrapper.py": "sha256:e8c289882f2ad439d2eb32f9a871967420fefaffe745c9271c1a2e948fbdfe50",
|
||||
"src/warden/scripts/__init__.py": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo_manager_identifiers_sha256": "sha256:4e35fcd009c20822574fd33171176bd14a1ff2dd5af8b0bb2820954974593a2a",
|
||||
"authorization": "Root accepted the exact source fixture patch for source integration. This packet adds no owner/native authority or catalog/pin promotion. No prepared script has been executed.",
|
||||
"publishable": false,
|
||||
"requires_allocated_manifest": true,
|
||||
"publication_file_paths": [
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"workplans/NK-WP-0048-security-case-types.md"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"original_packet": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1",
|
||||
"reason": "Normal Warden allocator committed one previously missing historical UUID; no code/source fixture changes. Original apply stopped before any writes.",
|
||||
"previous_warden_head": "ae7bc73b4f4bac22454144959b870d161498e0af",
|
||||
"current_warden_head": "922c26da0f3fcb0182352ea83e6ae06f29878580",
|
||||
"only_head_delta_path": "workplans/WARDEN-WP-0038-plan-mutation-intent.md",
|
||||
"all_validation_source_hashes_unchanged": true
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
"""Prepared canonical source validation; --execute launches only the two scoped tests."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
PACKET = Path(__file__).resolve().parent
|
||||
ROOTS = {'net-kingdom': Path('/home/worsch/net-kingdom'), 'ops-warden': Path('/home/worsch/ops-warden')}
|
||||
|
||||
def sha(data):
|
||||
return 'sha256:' + hashlib.sha256(data).hexdigest()
|
||||
|
||||
def sources(manifest):
|
||||
allowed = []
|
||||
for owner, rows in manifest['validation']['source_hashes'].items():
|
||||
for relative, expected in rows.items():
|
||||
path = ROOTS[owner] / relative
|
||||
assert not path.is_symlink(), str(path)
|
||||
assert sha(path.read_bytes()) == expected, str(path)
|
||||
allowed.append(str(path.resolve()))
|
||||
return allowed
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--execute', action='store_true')
|
||||
args = parser.parse_args()
|
||||
manifest = json.loads((PACKET / 'publication-inputs.json').read_text())
|
||||
allowed = sources(manifest)
|
||||
revision = subprocess.check_output(['git', '-C', str(ROOTS['ops-warden']), 'rev-parse', 'HEAD'], text=True).strip()
|
||||
assert revision == manifest['warden_revision']
|
||||
if not args.execute:
|
||||
print(json.dumps({'prepared_only': True, 'public_source_files_verified': len(allowed),
|
||||
'tests': manifest['validation']['tests']}))
|
||||
return
|
||||
protected = {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']}
|
||||
scratch = Path(tempfile.mkdtemp(prefix='nk48-catalog-canonical-validation-'))
|
||||
for name in ['home', 'tmp', 'guard', 'pycache']:
|
||||
(scratch / name).mkdir()
|
||||
shutil.copyfile(PACKET / 'validation-guard.py', scratch / 'guard/sitecustomize.py')
|
||||
shutil.copyfile(PACKET / 'validation-child.py', scratch / 'child.py')
|
||||
(scratch / 'allowed.json').write_text(json.dumps(allowed))
|
||||
(scratch / 'pytest.ini').write_text('[pytest]\n')
|
||||
environment = {
|
||||
'HOME': str(scratch / 'home'), 'TMPDIR': str(scratch / 'tmp'), 'PATH': '/usr/bin:/bin',
|
||||
'PYTHONPATH': str(scratch / 'guard') + ':' + str(ROOTS['ops-warden'] / 'src'),
|
||||
'PYTHONDONTWRITEBYTECODE': '1', 'PYTHONPYCACHEPREFIX': str(scratch / 'pycache'),
|
||||
'LANG': 'C.UTF-8', 'WARDEN_REVIEW_ALLOWED_SOURCE_LIST': str(scratch / 'allowed.json'),
|
||||
'WARDEN_REVIEW_VALIDATOR_PREFIX': str(scratch / 'tmp/warden-case-validator-'),
|
||||
}
|
||||
with (PACKET / 'canonical-validation.log').open('w') as log:
|
||||
result = subprocess.run([manifest['validation']['python'], str(scratch / 'child.py')],
|
||||
cwd=ROOTS['net-kingdom'], env=environment,
|
||||
stdout=log, stderr=subprocess.STDOUT)
|
||||
sources(manifest)
|
||||
assert protected == {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']}
|
||||
log_text = (PACKET / 'canonical-validation.log').read_text()
|
||||
counts_verified = bool(re.search(r'\b31 passed, 53 subtests passed\b', log_text))
|
||||
counts_verified = counts_verified and not re.search(r'\b[1-9][0-9]* (?:skipped|deselected)\b', log_text)
|
||||
receipt = {'executed': True, 'counts_verified': counts_verified, 'returncode': result.returncode, 'warden_revision': revision,
|
||||
'net_kingdom_head': subprocess.check_output(['git', '-C', str(ROOTS['net-kingdom']), 'rev-parse', 'HEAD'], text=True).strip(),
|
||||
'verified_public_source_files': len(allowed), 'tests': manifest['validation']['tests'],
|
||||
'log_sha256': sha((PACKET / 'canonical-validation.log').read_bytes()), 'scratch': str(scratch),
|
||||
'limitations': 'Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin.'}
|
||||
(PACKET / 'canonical-validation.json').write_text(json.dumps(receipt, indent=2) + '\n')
|
||||
print(json.dumps(receipt, indent=2))
|
||||
raise SystemExit(result.returncode or (0 if counts_verified else 1))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
## T35 — Align source admission fixtures with the authenticated catalog and attended-session API
|
||||
|
||||
```task
|
||||
id: NK-WP-0048-T35
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
"""Focused canonical-source test process. Network/native/estate-read guard required."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import sitecustomize
|
||||
|
||||
assert sitecustomize.ACTIVE is True
|
||||
assert not {'BAO_TOKEN', 'VAULT_TOKEN', 'WARDEN_AGENT_ID'} & os.environ.keys()
|
||||
assert list(Path.home().iterdir()) == []
|
||||
try:
|
||||
socket.socket()
|
||||
except RuntimeError as error:
|
||||
assert str(error) == 'fixture validation: network disabled'
|
||||
else:
|
||||
raise AssertionError('network guard inactive')
|
||||
|
||||
import warden.case_inputs as inputs
|
||||
import pytest
|
||||
|
||||
assert Path(inputs.__file__).resolve() == Path('/home/worsch/ops-warden/src/warden/case_inputs.py')
|
||||
PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n'''
|
||||
old_run = subprocess.run
|
||||
|
||||
def run_pinned(arguments, *positional, **keywords):
|
||||
if (type(arguments) is not list or len(arguments) != 5
|
||||
or arguments[:3] != [sys.executable, '-I', '-c']
|
||||
or arguments[3] != inputs._VALIDATE
|
||||
or not str(arguments[4]).startswith(sitecustomize.VALIDATOR_PREFIX)
|
||||
or keywords.get('env') != {}):
|
||||
raise RuntimeError('fixture validation: unexpected process refused')
|
||||
return old_run([*arguments[:3], PREAMBLE + arguments[3], arguments[4]],
|
||||
*positional, **keywords)
|
||||
|
||||
inputs.subprocess.run = run_pinned
|
||||
raise SystemExit(pytest.main([
|
||||
'-q', '-p', 'no:cacheprovider', '--noconftest',
|
||||
'-c', str(Path(os.environ['TMPDIR']).parent / 'pytest.ini'),
|
||||
'--rootdir=/home/worsch/net-kingdom',
|
||||
'/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_admit_1_3_type.py',
|
||||
'/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py',
|
||||
]))
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
"""Copied as sitecustomize.py into a fresh scratch directory by run_validation."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
ALLOWED = frozenset(json.loads(Path(os.environ['WARDEN_REVIEW_ALLOWED_SOURCE_LIST']).read_text()))
|
||||
VALIDATOR_PREFIX = os.environ['WARDEN_REVIEW_VALIDATOR_PREFIX']
|
||||
|
||||
def guard(event, args):
|
||||
if event.startswith('socket.'):
|
||||
raise RuntimeError('fixture validation: network disabled')
|
||||
if event == 'subprocess.Popen':
|
||||
command = args[1]
|
||||
if (type(command) is list and len(command) == 5
|
||||
and command[:3] == [sys.executable, '-I', '-c']
|
||||
and command[3].startswith('import sys as _review_sys\ndef _review_guard(')
|
||||
and str(command[4]).startswith(VALIDATOR_PREFIX) and args[3] == {}):
|
||||
return
|
||||
raise RuntimeError('fixture validation: native process disabled')
|
||||
if event in {'os.system', 'os.exec', 'os.posix_spawn'}:
|
||||
raise RuntimeError('fixture validation: native process disabled')
|
||||
if event == 'open' and isinstance(args[0], str):
|
||||
path = str(Path(args[0]).absolute())
|
||||
if path.startswith(('/home/', '/root/')):
|
||||
# Only exact prehashed public source may be read from the estate.
|
||||
if str(Path(path).resolve()) not in ALLOWED:
|
||||
raise PermissionError('fixture validation: unlisted estate file disabled')
|
||||
mode, flags = args[1:3]
|
||||
if ((isinstance(mode, str) and any(c in mode for c in 'wax+'))
|
||||
or (flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND))):
|
||||
raise PermissionError('fixture validation: canonical source writes disabled')
|
||||
|
||||
sys.addaudithook(guard)
|
||||
ACTIVE = True
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,243 @@
|
|||
diff --git a/sso-mfa/k8s/keycape/test_admit_1_3_type.py b/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
--- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
+++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py
|
||||
@@ -8,7 +8,9 @@
|
||||
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.
|
||||
+memory the five resolvable steps admit. The two Apps attended steps remain refused
|
||||
+because their establishing login is unresolved in the synthetic catalog. Four real
|
||||
+draft lane shapes are hypothetically active in that fixture; no catalog is promoted.
|
||||
Trust is fixture-only, nothing is dispatched, and none of this is owner admission
|
||||
or an acknowledgment.
|
||||
"""
|
||||
@@ -37,6 +39,8 @@
|
||||
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())
|
||||
+CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()
|
||||
+UNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'}
|
||||
EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'}
|
||||
|
||||
|
||||
@@ -74,12 +78,13 @@
|
||||
ARTIFACT['params']['consumer_owner_repo'],
|
||||
version='1.3'))
|
||||
|
||||
- def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None):
|
||||
+ def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG):
|
||||
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,
|
||||
+ lambda purpose, source, evidence: (evidence in EVIDENCE and
|
||||
+ (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))),
|
||||
browser_inputs=inputs.BrowserInputs(REPO))
|
||||
with mock.patch.dict(os.environ):
|
||||
if agent_id is None:
|
||||
@@ -87,7 +92,7 @@
|
||||
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,
|
||||
+ catalog, principal='founder', actor=actor or desc.actor,
|
||||
lane=lane or desc.lane,
|
||||
acceptance_evidence='fixture-acceptance',
|
||||
materialization_evidence='fixture-materialization',
|
||||
@@ -112,18 +117,33 @@
|
||||
self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2'])
|
||||
acknowledged = with_status('acknowledged')
|
||||
for step, desc in self.descriptors.items():
|
||||
+ if step in UNRESOLVED_APPS:
|
||||
+ continue # The attended session gate correctly precedes parameter validation.
|
||||
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):
|
||||
+ def test_committed_map_conditionally_admits_five_resolvable_steps(self):
|
||||
acknowledged = with_status('acknowledged')
|
||||
for step, desc in self.descriptors.items():
|
||||
+ if step in UNRESOLVED_APPS:
|
||||
+ continue
|
||||
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_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self):
|
||||
+ for step in sorted(UNRESOLVED_APPS):
|
||||
+ with self.subTest(step=step), pinned_13(), self.assertRaisesRegex(
|
||||
+ Refused, 'does not resolve to an active, reviewed login lane'):
|
||||
+ self.admit(self.descriptors[step], with_status('acknowledged'))
|
||||
+
|
||||
+ def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self):
|
||||
+ mint = self.descriptors['mint-secret']
|
||||
+ with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'):
|
||||
+ self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\n# changed source\n')
|
||||
|
||||
def test_descriptor_outside_the_map_is_refused(self):
|
||||
acknowledged = with_status('acknowledged')
|
||||
diff --git a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
--- a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
+++ b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py
|
||||
@@ -259,8 +259,13 @@
|
||||
case_source = yaml.safe_dump(case).encode()
|
||||
typ = yaml.safe_load((root / 'security-case-types/oidc-browser-relying-party/type.yaml').read_bytes())
|
||||
typ['spec']['execution_descriptors'] = {'register-client': digest(self.desc_bytes)}
|
||||
+ # Fixture-only successor lane for the adapter's current descriptor.
|
||||
+ # Canonical 1.1 still names the earlier login lane and is not edited.
|
||||
+ typ['spec']['follow_through'] = [
|
||||
+ dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step
|
||||
+ for step in typ['spec']['follow_through']]
|
||||
type_source = yaml.safe_dump(typ).encode()
|
||||
- catalog = b'fixture-only-catalog'
|
||||
+ catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()
|
||||
sheet = json.dumps(['register-client', 'net-kingdom', 'forgejo', 'human',
|
||||
'founder', 'keycape-attended-case-change', 'fixture-acceptance', 'fixture-materialization',
|
||||
'fixture-descriptor', 'fixture-session'], separators=(',', ':')).encode()
|
||||
@@ -270,6 +275,8 @@
|
||||
adapter = a.build(**dict(self.kw, admitted=invocation, case_source=case_source,
|
||||
case_acceptance='fixture-acceptance'))
|
||||
def fixture_verifier(purpose, source, evidence):
|
||||
+ if purpose == 'catalog-routing-acceptance':
|
||||
+ return source == digest(catalog) and evidence == 'fixture-descriptor'
|
||||
if purpose.startswith('session-revocation:'):
|
||||
_, lane, operation, attempt = purpose.split(':')
|
||||
return source == digest(case_source) and lane == invocation.lane and evidence == (
|
||||
diff --git a/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml
|
||||
@@ -0,0 +1,128 @@
|
||||
+# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog.
|
||||
+# Four draft entries are hypothetically active; Apps session_lane remains unresolved.
|
||||
+# Not an authenticated owner catalog, promotion, login choice or native authority.
|
||||
+entries:
|
||||
+- id: key-cape-oidc-login
|
||||
+ risk: standard
|
||||
+ owner_repo: key-cape
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-06-27'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: interim
|
||||
+ intended_owner: key-cape
|
||||
+ reviewed: '2026-09-08'
|
||||
+ blocked_on: fixture-only; no native authority
|
||||
+ lane: login
|
||||
+ title: Synthetic key-cape-oidc-login
|
||||
+ need_keywords:
|
||||
+ - key-cape-oidc-login
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: keycape-client-secret-generate
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-platform
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-10'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-platform
|
||||
+ reviewed: '2026-10-10'
|
||||
+ lane: secret
|
||||
+ title: Synthetic keycape-client-secret-generate
|
||||
+ need_keywords:
|
||||
+ - keycape-client-secret-generate
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: keycape-attended-case-change
|
||||
+ lane: attended-change
|
||||
+ session_lane: key-cape-oidc-login
|
||||
+ risk: high
|
||||
+ owner_repo: key-cape/net-kingdom
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: interim
|
||||
+ intended_owner: key-cape
|
||||
+ reviewed: '2026-10-17'
|
||||
+ blocked_on: fixture-only; no native authority
|
||||
+ title: Synthetic keycape-attended-case-change
|
||||
+ need_keywords:
|
||||
+ - keycape-attended-case-change
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: platform-secret-delivery
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-platform
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-platform
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic platform-secret-delivery
|
||||
+ need_keywords:
|
||||
+ - platform-secret-delivery
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: consumer-owner-deployment
|
||||
+ lane: attended-change
|
||||
+ session_lane: pending-owner-confirmation
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-apps
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-apps
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic consumer-owner-deployment
|
||||
+ need_keywords:
|
||||
+ - consumer-owner-deployment
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
+- id: apps-consumer-handover
|
||||
+ lane: attended-change
|
||||
+ session_lane: pending-owner-confirmation
|
||||
+ risk: high
|
||||
+ owner_repo: railiance-apps
|
||||
+ warden_executes: false
|
||||
+ reviewed: '2026-10-17'
|
||||
+ status: active
|
||||
+ delegation:
|
||||
+ mode: native
|
||||
+ intended_owner: railiance-apps
|
||||
+ reviewed: '2026-10-17'
|
||||
+ title: Synthetic apps-consumer-handover
|
||||
+ need_keywords:
|
||||
+ - apps-consumer-handover
|
||||
+ subsystem: synthetic source test
|
||||
+ wiki_ref: fixture-only
|
||||
+ canon_ref: fixture-only
|
||||
+ workload_ref:
|
||||
+ applicability: not-applicable
|
||||
+ reason: Synthetic source test; no workload or native authority.
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
from pathlib import Path
|
||||
import yaml
|
||||
r=Path('/tmp/nk48-catalog-fixture-update-r1/net-kingdom/sso-mfa/k8s/keycape')
|
||||
original=Path('/tmp/warden-kernel-review-r2/candidate/registry/routing/catalog.yaml')
|
||||
entries=yaml.safe_load(original.read_bytes())['entries']
|
||||
ids={'keycape-attended-case-change','keycape-client-secret-generate','key-cape-oidc-login','platform-secret-delivery','consumer-owner-deployment','apps-consumer-handover'}
|
||||
keep=('id','lane','session_lane','owner_repo','risk','status','reviewed','warden_executes','delegation')
|
||||
rows=[]
|
||||
for entry in entries:
|
||||
if entry['id'] not in ids:continue
|
||||
item={k:v for k,v in entry.items() if k in keep}
|
||||
item.update(title='Synthetic '+entry['id'],need_keywords=[entry['id']],subsystem='synthetic source test',wiki_ref='fixture-only',canon_ref='fixture-only',workload_ref={'applicability':'not-applicable','reason':'Synthetic source test; no workload or native authority.'})
|
||||
if item['status']=='draft':item['status']='active'
|
||||
# Keep delegation mode/owner; do not copy actual runtime commands or claim owner acceptance.
|
||||
item['delegation']={k:v for k,v in item['delegation'].items() if k in ('mode','intended_owner','reviewed')}
|
||||
if item['delegation']['mode']=='interim':item['delegation']['blocked_on']='fixture-only; no native authority'
|
||||
rows.append(item)
|
||||
assert len(rows)==6
|
||||
(r/'kernel-catalog.fixture.yaml').write_text('# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog.\n# Four draft entries are hypothetically active; Apps session_lane remains unresolved.\n# Not an authenticated owner catalog, promotion, login choice or native authority.\n'+yaml.safe_dump({'entries':rows},sort_keys=False))
|
||||
p=r/'test_admit_1_3_type.py';s=p.read_text()
|
||||
s=s.replace("memory the committed map admits exactly the composed descriptors and nothing else.","memory the five resolvable steps admit. The two Apps attended steps remain refused\nbecause their establishing login is unresolved in the synthetic catalog. Four real\ndraft lane shapes are hypothetically active in that fixture; no catalog is promoted.")
|
||||
s=s.replace("EVIDENCE =", "CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()\nUNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'}\nEVIDENCE =")
|
||||
s=s.replace("def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None):","def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG):")
|
||||
s=s.replace("lambda purpose, source, evidence: evidence in EVIDENCE,", "lambda purpose, source, evidence: (evidence in EVIDENCE and\n (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))),")
|
||||
s=s.replace("b'fixture-only-catalog', principal=", "catalog, principal=")
|
||||
s=s.replace("for step, desc in self.descriptors.items():\n with self.subTest(step=step), self.assertRaisesRegex(\n Refused, 'failed exact-version validation'):","for step, desc in self.descriptors.items():\n if step in UNRESOLVED_APPS:\n continue # The attended session gate correctly precedes parameter validation.\n with self.subTest(step=step), self.assertRaisesRegex(\n Refused, 'failed exact-version validation'):")
|
||||
s=s.replace("def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self):", "def test_committed_map_conditionally_admits_five_resolvable_steps(self):")
|
||||
s=s.replace("with self.subTest(step=step), pinned_13():\n admitted, invocation, _ =", "with self.subTest(step=step), pinned_13():\n if step in UNRESOLVED_APPS:\n continue\n admitted, invocation, _ =")
|
||||
pos=s.index(' def test_descriptor_outside_the_map_is_refused')
|
||||
s=s[:pos]+''' def test_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self):
|
||||
for step in sorted(UNRESOLVED_APPS):
|
||||
with self.subTest(step=step), pinned_13(), self.assertRaisesRegex(
|
||||
Refused, 'does not resolve to an active, reviewed login lane'):
|
||||
self.admit(self.descriptors[step], with_status('acknowledged'))
|
||||
|
||||
def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self):
|
||||
mint = self.descriptors['mint-secret']
|
||||
with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'):
|
||||
self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\\n# changed source\\n')
|
||||
|
||||
'''+s[pos:]
|
||||
p.write_text(s)
|
||||
p=r/'test_keycape_registration_adapter.py';s=p.read_text()
|
||||
s=s.replace(" catalog = b'fixture-only-catalog'", " # Fixture-only successor lane for the adapter's current descriptor.\n # Canonical 1.1 still names the earlier login lane and is not edited.\n typ['spec']['follow_through'] = [\n dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step\n for step in typ['spec']['follow_through']]\n type_source = yaml.safe_dump(typ).encode()\n catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()")
|
||||
s=s.replace(" def fixture_verifier(purpose, source, evidence):\n", " def fixture_verifier(purpose, source, evidence):\n if purpose == 'catalog-routing-acceptance':\n return source == digest(catalog) and evidence == 'fixture-descriptor'\n")
|
||||
p.write_text(s)
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"schema": "custodian.nk48-catalog-fixture-update.v1",
|
||||
"date": "2026-10-17",
|
||||
"role": "implementation proposal; this successor task is not independent review",
|
||||
"status": "candidate prepared in scratch only; no canonical edits/commits",
|
||||
"base_net_kingdom": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"kernel_current_candidate": "5abde817775d6304e3cf5a46948717a1606981bf plus reviewed candidate.patch SHA2561a3530a1e469fc03c2c29f1b66fa3ba1904e56bf2abcfb242c7a2ed015fb3e74",
|
||||
"kernel_predecessor": "117fec9241e7b985dfb187b992e173d0a5dc3700",
|
||||
"patch": {
|
||||
"file": "candidate.patch",
|
||||
"sha256": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501",
|
||||
"check": "git apply --check passed against fresh frozen342a6f25 archive"
|
||||
},
|
||||
"files": {
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py": {
|
||||
"base": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0",
|
||||
"candidate": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019"
|
||||
},
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": {
|
||||
"base": "sha256:4d0126416522e54eec1ae4c83b5488e09ff11ffb97ec1ee6d234548780c60f63",
|
||||
"candidate": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a"
|
||||
},
|
||||
"sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml": {
|
||||
"base": null,
|
||||
"candidate": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53"
|
||||
}
|
||||
},
|
||||
"changes": [
|
||||
"Replace both remaining fixture-only-catalog byte placeholders with one static well-formed synthetic catalog shaped after Warden5abde817. Four draft entries hypothetically active; no real catalog change. Preserve real owner/risk/delegation/lane/session kinds, including unresolved Apps sessions. Runtime commands and real workload paths are omitted.",
|
||||
"Bind catalog-routing-acceptance fixture evidence to digest of the exact catalog bytes in both fixtures; add changed-source refusal test. Fixture trust remains synthetic and supplies no owner authentication.",
|
||||
"For the current kernel expect five resolvable1.3 descriptors to admit under simulated acknowledgment/pin; expect both Apps attended descriptors to refuse unresolved establishing login. Missing-pin tests exercise the five descriptors that can reach parameter validation.",
|
||||
"Keep every committed-map and mint-guard test: wrong actor, agent-actor descriptor after exact fixture-map acknowledgment, and agent runtime all reach their intended guard.",
|
||||
"Registration fixture explicitly supplies its proposed current-descriptor lane in its in-memory type step; canonical1.1 remains unchanged. This fixes the previously unexamined canonical lane/descriptor disagreement for the mode-aware API. Existing synthetic kernel dispatch, rollback, lost-readback and external-revision tests pass."
|
||||
],
|
||||
"path_correction": "test_admit_1_2_adapter_descriptors.py does not exist at342a6f25; its corrected mint tests already moved into test_admit_1_3_type.py. The second remaining fake catalog is test_keycape_registration_adapter.py, which is included.",
|
||||
"validation": {
|
||||
"command": "python3 /tmp/nk48-catalog-fixture-update-r1/run.py <candidate|frozen117> final",
|
||||
"candidate": "31 passed,53 subtests passed (see candidate-final.log)",
|
||||
"frozen117": "2 failed subtests,31 passed,51 subtests passed (see frozen117-final.log). Only unresolved Apps refusal assertions fail; predecessor117 lacks the attended lane/session enforcement added after it.",
|
||||
"compatibility": "Selected tests now target the reviewed mode-aware catalog API.117 remains useful as a differential predecessor; no version-based skipping, expected-failure marking or weakened production parser is proposed. All common mint guard and registration execution tests still pass with117.",
|
||||
"isolation": "Empty synthetic HOME; allowlisted environment. Python socket audit guard denies all network. Native subprocesses denied except exact pinned BrowserInputs offline validator child, launched with empty environment and injected child socket/process/ambient-home-read audit guards. Registration Kubernetes/checker owner calls are synthetic mocks. No ambient tokens, native credentials, live owner calls or owner acceptance.",
|
||||
"full_suite": "Not run; focused scope only.",
|
||||
"packaging_initial_attempt": "First generated diff omitted new file mode for synthetic YAML and git apply --check refused dev/null; corrected new-file metadata, no source change or canonical apply."
|
||||
},
|
||||
"limitations": [
|
||||
"Four source draft lane statuses, unresolved Apps login choice, founder exact-type acknowledgment,1.3 PARAMETER_PINS, native T03 and downstream acceptance remain separate gates.",
|
||||
"Synthetic catalog is deliberately hypothetical data; its fixture evidence and bytes do not imply authentic owner approval.",
|
||||
"This implementation packet requires source review before canonical application.",
|
||||
"No workplan edits or State Hub changes; parent handles task/progress records."
|
||||
],
|
||||
"boundary": {
|
||||
"canonical_edits": false,
|
||||
"new_commits": false,
|
||||
"catalog_promotion": false,
|
||||
"parameter_pin_added": false,
|
||||
"native_actions": false,
|
||||
"credential_access": false,
|
||||
"external_owner_assent": false
|
||||
},
|
||||
"knowledge_orientation": {
|
||||
"package": "0.2.6",
|
||||
"digest": "8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02",
|
||||
"status": "start withheld/orientation-only; actual owner AGENTS.md, scoped source and source contract reviewed directly; no inferred action authority"
|
||||
},
|
||||
"packet_hashes": {
|
||||
"edit.py": "sha256:da1fef62073b92b59307e9f52115fa4fa784b260b5202db9d28fc62938982040",
|
||||
"candidate-tests.log": "sha256:dbc8706c1b29c6fb08821670d1e4a923e1b0a8db56b039937e346bd7581090c1",
|
||||
"frozen117-final.log": "sha256:81185ef66ed4ed5f836dc214899911af3a7a8b9f53b25b12130d8d8b6d8eafcf",
|
||||
"run.py": "sha256:8ad707bd9bb509e4b27503c284cee8aff3a7205882ca87febdace0a050c5f749",
|
||||
"frozen117-tests.log": "sha256:bd012a45cfeb9bba992b79a89a12a1397eea22169740aca2830d136d4a410ea2",
|
||||
"candidate-final.log": "sha256:2669471008859e15ce9b3bd92baf4e759672e96048ef221f90b19daf84dc3a28",
|
||||
"candidate.patch": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501",
|
||||
"test_runner.py": "sha256:d6f4e8f0f5c34557bf678543ae2c69202dc321f1226c2e1a2d462b2d81efc9c5"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from pathlib import Path
|
||||
import os,subprocess,sys
|
||||
r=Path('/tmp/nk48-catalog-fixture-update-r1');(r/'home').mkdir(exist_ok=True)
|
||||
kernel='/tmp/warden-kernel-review-r2/candidate/src' if sys.argv[1]=='candidate' else str(r/'warden117/src')
|
||||
env={'HOME':str(r/'home'),'PATH':'/usr/bin:/bin','PYTHONPATH':'/tmp/warden-kernel-review-r2/guard:'+kernel,'PYTHONDONTWRITEBYTECODE':'1','LANG':'C.UTF-8'}
|
||||
with (r/(sys.argv[1]+'-'+sys.argv[2]+'.log')).open('w') as log:
|
||||
result=subprocess.run(['/tmp/warden-kernel-review-r1/final/.venv/bin/python',str(r/'test_runner.py')],cwd=r/'net-kingdom',env=env,stdout=log,stderr=subprocess.STDOUT)
|
||||
print(result.returncode);sys.exit(result.returncode)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
"""Invoke only pinned, offline validator child; keep socket/native denial in child."""
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import subprocess
|
||||
import warden.case_inputs as inputs
|
||||
import pytest
|
||||
|
||||
PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n'''
|
||||
old_run = subprocess.run
|
||||
|
||||
def run_pinned(args,*pos,**kw):
|
||||
if (type(args) is not list or len(args)!=5 or args[:3]!=[sys.executable,'-I','-c']
|
||||
or args[3]!=inputs._VALIDATE or not str(args[4]).startswith('/tmp/warden-case-validator-')
|
||||
or kw.get('env')!={}):
|
||||
raise RuntimeError('review validator: unexpected process refused')
|
||||
return old_run([*args[:3],PREAMBLE+args[3],args[4]],*pos,**kw)
|
||||
inputs.subprocess.run=run_pinned
|
||||
raise SystemExit(pytest.main(['-q','sso-mfa/k8s/keycape/test_admit_1_3_type.py','sso-mfa/k8s/keycape/test_keycape_registration_adapter.py']))
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"executed": true,
|
||||
"counts_verified": true,
|
||||
"returncode": 0,
|
||||
"warden_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580",
|
||||
"net_kingdom_head": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"verified_public_source_files": 105,
|
||||
"tests": [
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py"
|
||||
],
|
||||
"log_sha256": "sha256:7bb78da51b547a05c66ffeb1e592c23317be29f6705d3e359c2ae370b86aedd4",
|
||||
"scratch": "/tmp/nk48-catalog-canonical-validation-5ghz6p1c",
|
||||
"limitations": "Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin."
|
||||
}
|
||||
|
|
@ -0,0 +1,365 @@
|
|||
{
|
||||
"repo_slug": "net-kingdom",
|
||||
"repo_path": "/home/worsch/net-kingdom",
|
||||
"source_revision": "befb0bc68cd25b625804fce559476eefd702521d",
|
||||
"source_revision_reason": null,
|
||||
"issues": [
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-24",
|
||||
"message": "Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)",
|
||||
"file_path": "",
|
||||
"db_id": "",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-35",
|
||||
"message": "Repo Manager warning: workplans/: repository uses more than one workplan prefix: ['NET-WP', 'NK-WP']",
|
||||
"file_path": "workplans/",
|
||||
"db_id": "",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-33",
|
||||
"message": "WORK-RECORDS.md is missing or stale \u2014 regenerate via fix-consistency (CUST-WP-0061-T04 generated work-record index)",
|
||||
"file_path": "WORK-RECORDS.md",
|
||||
"db_id": "",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": true
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-37",
|
||||
"message": "Residual workplan 'nk-wp-0011' has no origin/origin_ref provenance (not invented)",
|
||||
"file_path": "workplans/NK-WP-0011-enterprise-federation-saml.md",
|
||||
"db_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md",
|
||||
"db_id": "",
|
||||
"file_value": "USER-WP-0020",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md",
|
||||
"db_id": "",
|
||||
"file_value": "KEY-WP-0004",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md",
|
||||
"db_id": "",
|
||||
"file_value": "USER-WP-0020",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md",
|
||||
"db_id": "",
|
||||
"file_value": "KEY-WP-0004",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'USER-WP-0021' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md",
|
||||
"db_id": "",
|
||||
"file_value": "USER-WP-0021",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'FLEX-WP-0009' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md",
|
||||
"db_id": "",
|
||||
"file_value": "FLEX-WP-0009",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'AUDIT-WP-0003' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md",
|
||||
"db_id": "",
|
||||
"file_value": "AUDIT-WP-0003",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'EMAIL-WP-0004' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md",
|
||||
"db_id": "",
|
||||
"file_value": "EMAIL-WP-0004",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'USER-WP-0022' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md",
|
||||
"db_id": "",
|
||||
"file_value": "USER-WP-0022",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'KEY-WP-0008' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md",
|
||||
"db_id": "",
|
||||
"file_value": "KEY-WP-0008",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'CSOC-WP-0003' is not linked to State Hub",
|
||||
"file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md",
|
||||
"db_id": "",
|
||||
"file_value": "CSOC-WP-0003",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-37",
|
||||
"message": "Residual workplan 'nk-wp-0027' has no origin/origin_ref provenance (not invented)",
|
||||
"file_path": "workplans/NK-WP-0027-reef-placement-reconciliation.md",
|
||||
"db_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-37",
|
||||
"message": "Residual workplan 'nk-wp-0031' has no origin/origin_ref provenance (not invented)",
|
||||
"file_path": "workplans/NK-WP-0031-deterministic-posture-feedback.md",
|
||||
"db_id": "9d7b04f9-3803-5613-b7a5-8bd606c77f5a",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-37",
|
||||
"message": "Residual workplan 'nk-wp-0040' has no origin/origin_ref provenance (not invented)",
|
||||
"file_path": "workplans/NK-WP-0040-execution-attribution-receipt.md",
|
||||
"db_id": "e2533f3a-aa43-59b3-bff3-8e64b6149487",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'NK-WP-0048-T15' in workplan 'NK-WP-0048' \u2014 every depends_on target is terminal (NK-WP-0048-T31)",
|
||||
"file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T15",
|
||||
"db_id": "",
|
||||
"file_value": "NK-WP-0048-T31",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-10",
|
||||
"message": "Task status drift 'NK-WP-0048-T33': file='done' db='todo' (file wins)",
|
||||
"file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T33",
|
||||
"db_id": "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5",
|
||||
"file_value": "done",
|
||||
"db_value": "todo",
|
||||
"fixable": true
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-10",
|
||||
"message": "Task status drift 'NK-WP-0048-T34': file='progress' db='todo' (file wins)",
|
||||
"file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T34",
|
||||
"db_id": "37f43c40-d014-544e-aa0b-5219dd4a6b93",
|
||||
"file_value": "progress",
|
||||
"db_value": "todo",
|
||||
"fixable": true
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-11",
|
||||
"message": "Derived task 'NK-WP-0048-T35' is absent from this projection",
|
||||
"file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T35",
|
||||
"db_id": "cf1d6ab3-846e-53ea-8575-d6347b6f4769",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": true
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAIL-PL-WP-0002' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAIL-PL-WP-0002",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'nk-wp-0035@retired-20260908' (id=064c5e8b\u2026, status=archived) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "064c5e8b-7852-4d7f-a5fa-e26e406ab8e0",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'adhoc-2026-08-14@retired-20260827' (id=a5097121\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "a5097121-1461-59f8-81fa-e6ecef527337",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'adhoc-2026-08-23@retired-20260827' (id=e25ba5fa\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "e25ba5fa-eaae-5039-8d93-a69c6d837fc4",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'net-kingdom-adhoc-2026-07-02@retired-20260827' (id=67c1c7ac\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "67c1c7ac-d8b1-41dc-a81a-4e43f1afd068",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'net-kingdom-adhoc-2026-06-14@retired-20260827' (id=c6f3d6bf\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "c6f3d6bf-4916-490d-96ce-092d2776d7e7",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-26",
|
||||
"message": "Workplan id 'NET-WP-0020' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
|
||||
"file_path": "workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md",
|
||||
"db_id": "",
|
||||
"file_value": "NET-WP-0020",
|
||||
"db_value": "NK-WP",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-26",
|
||||
"message": "Workplan id 'NET-WP-0015' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
|
||||
"file_path": "workplans/archived/260603-NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap.md",
|
||||
"db_id": "",
|
||||
"file_value": "NET-WP-0015",
|
||||
"db_value": "NK-WP",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-26",
|
||||
"message": "Workplan id 'NET-WP-0016' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
|
||||
"file_path": "workplans/archived/260603-NET-WP-0016-guided-security-bootstrap-experience.md",
|
||||
"db_id": "",
|
||||
"file_value": "NET-WP-0016",
|
||||
"db_value": "NK-WP",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-26",
|
||||
"message": "Workplan id 'NET-WP-0018' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
|
||||
"file_path": "workplans/archived/260604-NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md",
|
||||
"db_id": "",
|
||||
"file_value": "NET-WP-0018",
|
||||
"db_value": "NK-WP",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-26",
|
||||
"message": "Workplan id 'NET-WP-0017' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
|
||||
"file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md",
|
||||
"db_id": "",
|
||||
"file_value": "NET-WP-0017",
|
||||
"db_value": "NK-WP",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-26",
|
||||
"message": "Workplan id 'NET-WP-0019' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform",
|
||||
"file_path": "workplans/archived/260702-NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md",
|
||||
"db_id": "",
|
||||
"file_value": "NET-WP-0019",
|
||||
"db_value": "NK-WP",
|
||||
"fixable": false
|
||||
}
|
||||
],
|
||||
"fixes_applied": [
|
||||
"bindings: synced 64/64",
|
||||
"C-17 cleared: pushed 1 backlogged commit(s)",
|
||||
"C-33 fixed: regenerated WORK-RECORDS.md",
|
||||
"C-10 fixed: task 5ace28ff\u2026 status \u2192 'done'",
|
||||
"C-10 fixed: task 37f43c40\u2026 status \u2192 'progress'",
|
||||
"C-11 fixed: task 'NK-WP-0048-T35' \u2192 cf1d6ab3\u2026",
|
||||
"brief: .custodian-brief.md updated",
|
||||
"push: pushed"
|
||||
],
|
||||
"summary": {
|
||||
"fail": 0,
|
||||
"automation_error": 0,
|
||||
"warn": 29,
|
||||
"info": 5
|
||||
},
|
||||
"result": "warn"
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
WARN: git commit failed for writeback: Generated-only commits rejected: staged
|
||||
|
|
@ -0,0 +1,989 @@
|
|||
{
|
||||
"ok": true,
|
||||
"status": "applied",
|
||||
"repo_slug": "net-kingdom",
|
||||
"commit": "befb0bc68cd25b625804fce559476eefd702521d",
|
||||
"requests": 2,
|
||||
"elapsed_ms": 7743.0,
|
||||
"git": {
|
||||
"upstream": "origin/main",
|
||||
"behind": 0,
|
||||
"ahead": 0
|
||||
},
|
||||
"identifiers": {
|
||||
"schema": "repo-manager.identifier-assignment.v1",
|
||||
"ok": true,
|
||||
"executed": true,
|
||||
"repo": "net-kingdom",
|
||||
"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": "befb0bc68cd25b625804fce559476eefd702521d",
|
||||
"derived_commit": "befb0bc68cd25b625804fce559476eefd702521d",
|
||||
"outcome": {
|
||||
"schema": "state-hub.projection-reset.v1",
|
||||
"repo_slug": "net-kingdom",
|
||||
"commit": "befb0bc68cd25b625804fce559476eefd702521d",
|
||||
"status": "applied",
|
||||
"counts": {
|
||||
"created": 0,
|
||||
"updated": 64,
|
||||
"retired": 0,
|
||||
"refused": 0,
|
||||
"released": 0,
|
||||
"created_tasks": 0,
|
||||
"updated_tasks": 1,
|
||||
"cancelled_tasks": 0,
|
||||
"created_edges": 0,
|
||||
"removed_edges": 0,
|
||||
"unresolved_edges": 1,
|
||||
"external_edges": 85
|
||||
},
|
||||
"created": [],
|
||||
"updated": [
|
||||
"NET-WP-0015",
|
||||
"NET-WP-0016",
|
||||
"NET-WP-0017",
|
||||
"NET-WP-0018",
|
||||
"NET-WP-0019",
|
||||
"NET-WP-0020",
|
||||
"NK-WP-0001",
|
||||
"NK-WP-0002",
|
||||
"NK-WP-0003",
|
||||
"NK-WP-0004",
|
||||
"NK-WP-0005",
|
||||
"NK-WP-0006",
|
||||
"NK-WP-0007",
|
||||
"NK-WP-0008",
|
||||
"NK-WP-0009",
|
||||
"NK-WP-0010",
|
||||
"NK-WP-0011",
|
||||
"NK-WP-0012",
|
||||
"NK-WP-0013",
|
||||
"NK-WP-0014",
|
||||
"NK-WP-0015",
|
||||
"NK-WP-0016",
|
||||
"NK-WP-0017",
|
||||
"NK-WP-0018",
|
||||
"NK-WP-0019",
|
||||
"NK-WP-0021",
|
||||
"NK-WP-0022",
|
||||
"NK-WP-0023",
|
||||
"NK-WP-0024",
|
||||
"NK-WP-0025",
|
||||
"NK-WP-0026",
|
||||
"NK-WP-0027",
|
||||
"NK-WP-0028",
|
||||
"NK-WP-0029",
|
||||
"NK-WP-0030",
|
||||
"NK-WP-0031",
|
||||
"NK-WP-0032",
|
||||
"NK-WP-0033",
|
||||
"NK-WP-0034",
|
||||
"NK-WP-0035",
|
||||
"NK-WP-0036",
|
||||
"NK-WP-0037",
|
||||
"NK-WP-0038",
|
||||
"NK-WP-0039",
|
||||
"NK-WP-0040",
|
||||
"NK-WP-0041",
|
||||
"NK-WP-0042",
|
||||
"NK-WP-0043",
|
||||
"NK-WP-0044",
|
||||
"NK-WP-0045",
|
||||
"NK-WP-0046",
|
||||
"NK-WP-0047",
|
||||
"NK-WP-0048",
|
||||
"NK-WP-0049",
|
||||
"NK-WP-0050",
|
||||
"NK-WP-0051",
|
||||
"NK-WP-ADHOC-2026-06-14",
|
||||
"NK-WP-ADHOC-2026-07-02",
|
||||
"NK-WP-ADHOC-2026-08-14",
|
||||
"NK-WP-ADHOC-2026-08-23",
|
||||
"NK-WP-ADHOC-2026-10-03",
|
||||
"NK-WP-ADHOC-2026-10-10",
|
||||
"NK-WP-ADHOC-2026-10-11",
|
||||
"NK-WP-ADHOC-2026-10-16"
|
||||
],
|
||||
"retired": [],
|
||||
"released": [],
|
||||
"created_tasks": [],
|
||||
"updated_tasks": [
|
||||
"NK-WP-0048-T35"
|
||||
],
|
||||
"cancelled_tasks": [],
|
||||
"created_edges": [],
|
||||
"removed_edges": [],
|
||||
"unresolved_edges": [
|
||||
{
|
||||
"workplan": "NK-WP-0006",
|
||||
"from": "NK-WP-0006",
|
||||
"target": "NK-WP-0001",
|
||||
"reason": "missing"
|
||||
}
|
||||
],
|
||||
"external_edges": [
|
||||
{
|
||||
"workplan": "NK-WP-0008",
|
||||
"from": "NK-WP-0008",
|
||||
"target": "NK-WP-0006",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0050",
|
||||
"from": "NK-WP-0050-T03",
|
||||
"target": "RPF-WP-0056-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377",
|
||||
"from_task_id": "5a3aaf57-de5c-5a03-bc99-9bcf2fc5d95e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "a82762eb-bb6d-5600-89ca-a81ada2eba9f"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0050",
|
||||
"from": "NK-WP-0050-T03",
|
||||
"target": "NK-WP-0050-T02",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377",
|
||||
"from_task_id": "5a3aaf57-de5c-5a03-bc99-9bcf2fc5d95e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "a9dadd07-f136-5dc7-9229-7a9dbb941f0d"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0050",
|
||||
"from": "NK-WP-0050-T02",
|
||||
"target": "NK-WP-0050-T01",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377",
|
||||
"from_task_id": "a9dadd07-f136-5dc7-9229-7a9dbb941f0d",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "64893d94-9717-5812-b8e3-7cabe095fcdc"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0017",
|
||||
"from": "NK-WP-0017",
|
||||
"target": "NK-WP-0015",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "08398d26-cadf-44bc-97ee-67da790040e6",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011",
|
||||
"target": "NK-WP-0006",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011",
|
||||
"target": "NK-WP-0004",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011",
|
||||
"target": "NK-WP-0003",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "f24cefd4-a09b-4fa1-9b25-94bf783b425e",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T03",
|
||||
"target": "NK-WP-0011-T02",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "2fe6f100-3a5d-563e-b033-7d7b846ae487"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T07",
|
||||
"target": "NK-WP-0011-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T07",
|
||||
"target": "NK-WP-0011-T04",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T07",
|
||||
"target": "NK-WP-0011-T05",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T04",
|
||||
"target": "NK-WP-0011-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T06",
|
||||
"target": "NK-WP-0011-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T06",
|
||||
"target": "NK-WP-0011-T04",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T06",
|
||||
"target": "NK-WP-0011-T05",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T05",
|
||||
"target": "NK-WP-0011-T04",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T09",
|
||||
"target": "NK-WP-0011-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T09",
|
||||
"target": "NK-WP-0011-T07",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T09",
|
||||
"target": "NK-WP-0011-T08",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T08",
|
||||
"target": "NK-WP-0011-T02",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "2fe6f100-3a5d-563e-b033-7d7b846ae487"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0011",
|
||||
"from": "NK-WP-0011-T08",
|
||||
"target": "NK-WP-0011-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07",
|
||||
"from_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908"
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0016",
|
||||
"from": "NET-WP-0016",
|
||||
"target": "NET-WP-0015",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0016",
|
||||
"from": "NET-WP-0016",
|
||||
"target": "NK-WP-0012",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T18",
|
||||
"target": "NK-WP-0049-T33",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "141dd773-c1fa-56c1-a852-bbe2372e5125",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T18",
|
||||
"target": "NK-WP-0049-T32",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "141dd773-c1fa-56c1-a852-bbe2372e5125",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "cd78dbb7-a8f2-5754-8788-6eb1b7d97741"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T35",
|
||||
"target": "NK-WP-0049-T34",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "1bfd1703-589f-5511-bad5-812d860e4a71",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T33",
|
||||
"target": "NK-WP-0049-T35",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "1bfd1703-589f-5511-bad5-812d860e4a71"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T33",
|
||||
"target": "NK-WP-0049-T34",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T33",
|
||||
"target": "NK-WP-0049-T36",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "8b72334b-fc27-5707-a189-bad345eaf3bc"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T33",
|
||||
"target": "NK-WP-0049-T32",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "cd78dbb7-a8f2-5754-8788-6eb1b7d97741"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T36",
|
||||
"target": "NK-WP-0049-T34",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "8b72334b-fc27-5707-a189-bad345eaf3bc",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0049",
|
||||
"from": "NK-WP-0049-T03",
|
||||
"target": "NK-WP-0049-T02",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804",
|
||||
"from_task_id": "d68cd4d3-1ba3-530e-88f9-26ebb8dd6eac",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "479d6970-6516-50f5-9436-bb5b56d58aad"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0025",
|
||||
"from": "NK-WP-0025",
|
||||
"target": "NK-WP-0024",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "2412a251-d6b5-5f05-8082-3eb7697c51dc",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "635a73d4-b3cb-5c38-af22-56a6a66beb0e",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0016",
|
||||
"from": "NK-WP-0016",
|
||||
"target": "NK-WP-0015",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "2d592e18-e63d-4856-97a1-f8c3e019e150",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0006",
|
||||
"from": "NK-WP-0006",
|
||||
"target": "NK-WP-0005",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0006",
|
||||
"from": "NK-WP-0006",
|
||||
"target": "NK-WP-0004",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0013",
|
||||
"from": "NK-WP-0013",
|
||||
"target": "NK-WP-0006",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "32a54d8e-8633-42a6-8ec1-104842c581c1",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0015",
|
||||
"from": "NK-WP-0015",
|
||||
"target": "NK-WP-0014",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0017",
|
||||
"from": "NET-WP-0017",
|
||||
"target": "NET-WP-0016",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0017",
|
||||
"from": "NET-WP-0017",
|
||||
"target": "NET-WP-0015",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0007",
|
||||
"from": "NK-WP-0007",
|
||||
"target": "NK-WP-0006",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0007",
|
||||
"from": "NK-WP-0007",
|
||||
"target": "NK-WP-0005",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0007",
|
||||
"from": "NK-WP-0007",
|
||||
"target": "NK-WP-0004",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0024",
|
||||
"from": "NK-WP-0024",
|
||||
"target": "NK-WP-0023",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "635a73d4-b3cb-5c38-af22-56a6a66beb0e",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "fdb3deee-06c3-5d12-91ce-9c50b2ac0618",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0015",
|
||||
"from": "NET-WP-0015",
|
||||
"target": "NK-WP-0006",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0015",
|
||||
"from": "NET-WP-0015",
|
||||
"target": "NK-WP-0012",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0018",
|
||||
"from": "NK-WP-0018",
|
||||
"target": "NK-WP-0017",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "08398d26-cadf-44bc-97ee-67da790040e6",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0018",
|
||||
"from": "NK-WP-0018",
|
||||
"target": "NK-WP-0016",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "2d592e18-e63d-4856-97a1-f8c3e019e150",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0005",
|
||||
"from": "NK-WP-0005",
|
||||
"target": "NK-WP-0004",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0019",
|
||||
"from": "NET-WP-0019",
|
||||
"target": "NET-WP-0017",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "75d388b6-7ec1-4e1b-8c87-6ff44f953210",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0019",
|
||||
"from": "NET-WP-0019",
|
||||
"target": "NET-WP-0018",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "75d388b6-7ec1-4e1b-8c87-6ff44f953210",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0018",
|
||||
"from": "NET-WP-0018",
|
||||
"target": "NET-WP-0017",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NET-WP-0018",
|
||||
"from": "NET-WP-0018",
|
||||
"target": "NET-WP-0015",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0027",
|
||||
"from": "NK-WP-0027-T03",
|
||||
"target": "NK-WP-0027-T02",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4",
|
||||
"from_task_id": "e3f0bb0f-0c85-5cb9-a5ab-75f668cd3447",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "5b35b646-a525-55da-852a-6005613d42ea"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0012",
|
||||
"from": "NK-WP-0012",
|
||||
"target": "NK-WP-0006",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0014",
|
||||
"from": "NK-WP-0014",
|
||||
"target": "NK-WP-0013",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "32a54d8e-8633-42a6-8ec1-104842c581c1",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0014",
|
||||
"from": "NK-WP-0014",
|
||||
"target": "NK-WP-0012",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0019",
|
||||
"from": "NK-WP-0019",
|
||||
"target": "NK-WP-0018",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "d2daa8b4-8ecf-4377-b382-492e653735f7",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0009",
|
||||
"from": "NK-WP-0009",
|
||||
"target": "NK-WP-0008",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "d4d02dbf-3974-502d-8b87-b776fc63e17e",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0022",
|
||||
"from": "NK-WP-0022",
|
||||
"target": "NK-WP-0023",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "d76ddccc-00c8-548a-b141-2cd660fa38da",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "fdb3deee-06c3-5d12-91ce-9c50b2ac0618",
|
||||
"to_task_id": null
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T15",
|
||||
"target": "NK-WP-0048-T31",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "1b825705-4848-5edc-832f-b1622cd9bdb7",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T16",
|
||||
"target": "NK-WP-0048-T25",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "0de2a7d9-874d-578b-a076-f653fe42e30b"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T16",
|
||||
"target": "NK-WP-0048-T29",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "129db41d-4560-5e20-9f3b-c3c91e0a5601"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T16",
|
||||
"target": "NK-WP-0048-T24",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "1a3931ee-3b5f-52fc-9295-4073804f4bd2"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T16",
|
||||
"target": "NK-WP-0048-T28",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T16",
|
||||
"target": "NK-WP-0048-T30",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "6f6073ac-52dd-5b70-8874-6d76d4c072e4"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T16",
|
||||
"target": "NK-WP-0048-T26",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "94b3cbf0-c908-5ac6-b0db-67385c3e021c"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T16",
|
||||
"target": "NK-WP-0048-T27",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "e56365f8-29b5-5c89-a329-8115f9f3b2a7"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T07",
|
||||
"target": "NK-WP-0048-T06",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T09",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "0ee12dfc-8260-5038-bd39-cc7fb4113be0"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T15",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "1b825705-4848-5edc-832f-b1622cd9bdb7"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T16",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T05",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "3d987d3d-0797-538b-86e4-51185a327ae2"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "MASON-WP-0010-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "56cea6be-c613-586c-80cd-125340b374da"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T12",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T20",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T17",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "8c3e4baa-a218-5996-af9e-fe1153a46dac"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T08",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "8e519082-dc8b-5e46-b660-03d03e55dcf6"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T18",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "9c45da71-fb4c-5f20-9065-90f448f37c64"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T13",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "b264c10b-565c-5d29-b788-03cf5fbd2883"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "WARDEN-WP-0044-T03",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T06",
|
||||
"target": "NK-WP-0048-T14",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "fce95440-a25b-5f27-8ef3-a3d182099113"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0048",
|
||||
"from": "NK-WP-0048-T14",
|
||||
"target": "NK-WP-0048-T20",
|
||||
"relationship_type": "starts_after",
|
||||
"from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb",
|
||||
"from_task_id": "fce95440-a25b-5f27-8ef3-a3d182099113",
|
||||
"to_workplan_id": null,
|
||||
"to_task_id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8"
|
||||
},
|
||||
{
|
||||
"workplan": "NK-WP-0010",
|
||||
"from": "NK-WP-0010",
|
||||
"target": "NK-WP-0008",
|
||||
"relationship_type": "blocks",
|
||||
"from_workplan_id": "f4faf8b4-ae57-40cf-a881-6fe66ca6ad74",
|
||||
"from_task_id": null,
|
||||
"to_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d",
|
||||
"to_task_id": null
|
||||
}
|
||||
],
|
||||
"refused": [],
|
||||
"notes": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"exit_code": 0,
|
||||
"commit": "befb0bc68cd25b625804fce559476eefd702521d",
|
||||
"base": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"files": [
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sha256": "2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"sha256": "86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"sha256": "7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53"
|
||||
},
|
||||
{
|
||||
"path": "workplans/NK-WP-0048-security-case-types.md",
|
||||
"sha256": "80279bbdf6d36e2321ecfeb5a3f996a8345e6da2ceb9b26167e52b3668ba7e85"
|
||||
},
|
||||
{
|
||||
"path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml",
|
||||
"sha256": "ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f"
|
||||
}
|
||||
],
|
||||
"stdout": "[main befb0bc] fix: bind issuer template exactly and align catalog admission fixtures\n 5 files changed, 209 insertions(+), 10 deletions(-)\n create mode 100644 sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml\n",
|
||||
"stderr": "{\"blocked_blob_ids\": [], \"scanned_blobs\": 5, \"scanner_sha256\": \"88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509\", \"scanner_version\": \"8.30.1\", \"scope\": \"changed-staged-blobs\", \"status\": \"clean\"}\n"
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"owner": "net-kingdom",
|
||||
"base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"message": "fix: bind issuer template exactly and align catalog admission fixtures",
|
||||
"files": [
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sha256": "2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"sha256": "86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"sha256": "7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53"
|
||||
},
|
||||
{
|
||||
"path": "workplans/NK-WP-0048-security-case-types.md",
|
||||
"sha256": "80279bbdf6d36e2321ecfeb5a3f996a8345e6da2ceb9b26167e52b3668ba7e85"
|
||||
},
|
||||
{
|
||||
"path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml",
|
||||
"sha256": "ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
{
|
||||
"schema": "custodian.nk48-catalog-fixture-publication-inputs.v1",
|
||||
"prepared_only": true,
|
||||
"owner": "net-kingdom",
|
||||
"base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5",
|
||||
"warden_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580",
|
||||
"message": "test: align security case fixtures with authenticated catalog admission",
|
||||
"workplan": "workplans/NK-WP-0048-security-case-types.md",
|
||||
"workplan_preimage_sha256": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9",
|
||||
"workplan_candidate_sha256": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981",
|
||||
"task_addition_sha256": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f",
|
||||
"patch_sha256": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501",
|
||||
"files": [
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"preimage_sha256": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0",
|
||||
"sha256": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"preimage_sha256": "sha256:4d0126416522e54eec1ae4c83b5488e09ff11ffb97ec1ee6d234548780c60f63",
|
||||
"sha256": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"preimage_sha256": null,
|
||||
"sha256": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53"
|
||||
}
|
||||
],
|
||||
"allocation": {
|
||||
"method": "repo_manager.identifiers.ensure_missing_work_record_identifiers",
|
||||
"manual_uuid": false,
|
||||
"preexisting_assignments": [],
|
||||
"expected_new_record_ids": [
|
||||
"NK-WP-0048-T35"
|
||||
],
|
||||
"expected_files_changed": [
|
||||
"workplans/NK-WP-0048-security-case-types.md"
|
||||
],
|
||||
"expected_task_count": 35,
|
||||
"concrete_postallocation_manifest": "publication-inputs.allocated.json"
|
||||
},
|
||||
"protected_files": {
|
||||
".custodian-brief.md": "sha256:3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1",
|
||||
"WORK-RECORDS.md": "sha256:a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a"
|
||||
},
|
||||
"validation": {
|
||||
"tests": [
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py"
|
||||
],
|
||||
"expected_result": "31 passed,53 subtests passed; no skipped or deselected tests",
|
||||
"python": "/tmp/warden-kernel-review-r1/final/.venv/bin/python",
|
||||
"source_hashes": {
|
||||
"net-kingdom": {
|
||||
"sso-mfa/k8s/keycape/test_vergabe_client_rollout.py": "sha256:f9b7defc312b0fea24ded92ef479dcc8aef063f7a66a8245760918bcf44c4af1",
|
||||
"sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py": "sha256:9a4b9fb0141221d6e5bcb72b3656613afda54d16ede300bffa8f71956ae6fd2c",
|
||||
"sso-mfa/k8s/keycape/test_keycape_client.py": "sha256:9572044679feaedfe732e15a2b6f8ef6b41a8fe2e4430257f309a3e83e97736c",
|
||||
"sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py": "sha256:76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1",
|
||||
"sso-mfa/k8s/keycape/portal-client-rollout.py": "sha256:158e1d36eb881cdd0a2eda91b472adea5e8fde5061e00d26dda368580aeb871f",
|
||||
"sso-mfa/k8s/keycape/test_playwright_activation.py": "sha256:6fc22d091f27a76c32d2fb0a7fc3c6e18e54ff98d242699c518b2f9dc9fd4cbd",
|
||||
"sso-mfa/k8s/keycape/exercise-approval-clients.py": "sha256:e2d3dd741ac52af28dce752344227a4443b78fedd4cfcc06ba902e679dfc9351",
|
||||
"sso-mfa/k8s/keycape/keycape-client.py": "sha256:4b63161cf3365d34dd684a3116e2c935baaf7f6aa4b30983f6883546db3d6591",
|
||||
"sso-mfa/k8s/keycape/test_openbao_negative_session.py": "sha256:01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c",
|
||||
"sso-mfa/k8s/keycape/authentication-policy-rollout.py": "sha256:2c1bd23fded4855bae1eb3a08dbc4510d7f60f2bc4d625d60e471ab78f6f0251",
|
||||
"sso-mfa/k8s/keycape/handover-adapter.py": "sha256:bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832",
|
||||
"sso-mfa/k8s/keycape/openbao-signin-diagnosis-rollout.py": "sha256:a57b957d6eed9e8fb09d2a7aa7405a39deeafd9aaf57a9e6dc907afabd380371",
|
||||
"sso-mfa/k8s/keycape/test_handover_adapter.py": "sha256:cd1fb0eb64a777699221ebdfcd6fea96bafb9b27ec388e95dcfe66189eac8ca2",
|
||||
"sso-mfa/k8s/keycape/test_authentication_policy_rollout.py": "sha256:11c10039d99a34d2819b09146a966c8b7cba6095e7a8b51c055a01e7167e3ed5",
|
||||
"sso-mfa/k8s/keycape/test_deliver_consumer_secret_adapter.py": "sha256:f4e6c6acd75a36f675007d2f62a7c7c4f7bfe2951cbb5e3e4cdde8b53f17f473",
|
||||
"sso-mfa/k8s/keycape/bind-env-adapter.py": "sha256:fa17fae21776b5c620ee9782adf050649c3a28719a6687d2524ff67ca7ad1925",
|
||||
"sso-mfa/k8s/keycape/test_keycape_checker_snapshot.py": "sha256:9271f25c760ad732c257d59d259b5c767436a2dc2d5ba24fca259bc57761d6d1",
|
||||
"sso-mfa/k8s/keycape/test_openbao_signin_diagnosis_rollout.py": "sha256:25d46fe7caca2ad4c262bd2a25188c91847304b6b051c4f3b3705c69f2803f01",
|
||||
"sso-mfa/k8s/keycape/test_openbao_private_ready.py": "sha256:8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277",
|
||||
"sso-mfa/k8s/keycape/test_mint_secret_adapter.py": "sha256:8110b6a8abd8e9a1f5e1feae11ec09c4e28cb7486c759b0e8459da1fb1b7144d",
|
||||
"sso-mfa/k8s/keycape/playwright-activation.py": "sha256:5ac683b3324b00e6253f6040f4c73057027b1f98f7071160a6153794ba522757",
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019",
|
||||
"sso-mfa/k8s/keycape/test_openbao_node_route.py": "sha256:1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6",
|
||||
"sso-mfa/k8s/keycape/test_secrets_engine_openbao_client_rollout.py": "sha256:e6f86fb7712c1b9c01d9b684f26d5cc949649b823d2732eced132d59acd3fe01",
|
||||
"sso-mfa/k8s/keycape/openbao-client-config.py": "sha256:1b908b4c36134212d78f9fbe7a2a201eb3ced06d31bb1dcea4a59d80689d84b1",
|
||||
"sso-mfa/k8s/keycape/test_registration_original_intent_reconcile.py": "sha256:e2897b8200ec97445fc4a26c55ad82651fcb1a4d22efe7fe59d0fcfc5cd8ec71",
|
||||
"sso-mfa/k8s/keycape/test_ensure_groups_adapter.py": "sha256:c8754a2ba735116edc6fe257aa993d0530cea9ca9f954ea5a621e38694906be2",
|
||||
"sso-mfa/k8s/keycape/test_keycape_checker_environment.py": "sha256:35f316eed168d3df588498faf24ab9377733a7d857ddfa5e0e4322a6287c93ff",
|
||||
"sso-mfa/k8s/keycape/test_bind_env_adapter.py": "sha256:a1e5e7a631a4843f9a0d2310a3942b26936103d786aeae47a142ff7c7b2fdbdb",
|
||||
"sso-mfa/k8s/keycape/create-secrets.py": "sha256:5da672fff36eb94101741ad99e2a1a96ae81fea15ed1871304c40e63ce27c787",
|
||||
"sso-mfa/k8s/keycape/approval-clients-rollout.py": "sha256:488d5cc59e081993dd2b5fc6d9e46bbddcaec4f5170b8f7a5ad0aae3a211a790",
|
||||
"sso-mfa/k8s/keycape/vergabe-client-rollout.py": "sha256:8cee6cc5ff3e3e8617304055263d04b393d0e005c34d9af12cb7e8bc685bac1a",
|
||||
"sso-mfa/k8s/keycape/keycape-registration-adapter.py": "sha256:a53beea4dbbe373ce7a5b84d55e79be8407ab9688037c7ee057afcc7be576047",
|
||||
"sso-mfa/k8s/keycape/compose-1.2-descriptors.py": "sha256:39021cac900b11a59d00c5a1d5491b1ee4c639955cde07d07854c3db8bc04958",
|
||||
"sso-mfa/k8s/keycape/test_approval_clients_rollout.py": "sha256:745316d42c3aba6b59b562d3bbcd4b2cb92871fb5e51a954e2254e2e550bb408",
|
||||
"sso-mfa/k8s/keycape/register-user-engine-portal.py": "sha256:67173edc44f5457e7b6a3e0fb2321b398159cfce5dea8ead01eabf835fb38d76",
|
||||
"sso-mfa/k8s/keycape/test_issuer_pin.py": "sha256:e471ed815af246633c0b21ac2cf5cae94d0dbdeff9e6ce801b4ddd033097dc20",
|
||||
"sso-mfa/k8s/keycape/openbao-negative-session.py": "sha256:24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a",
|
||||
"sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py": "sha256:65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42",
|
||||
"sso-mfa/k8s/keycape/test_portal_client_rollout.py": "sha256:a16cd4bf278970c10de2e1b1025597b3872c9515713dd947ac6b50cb78feea1f",
|
||||
"sso-mfa/k8s/keycape/test_deliver_secret_adapter.py": "sha256:24cedec29071b62fc2c2458950ed244feeff542bc20a7d480667dd99cda16b03",
|
||||
"sso-mfa/k8s/keycape/deliver-secret-adapter.py": "sha256:cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d",
|
||||
"sso-mfa/k8s/keycape/test_openbao_private_input.py": "sha256:84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1",
|
||||
"sso-mfa/k8s/keycape/register-coulomb-social.py": "sha256:b40ff4bdba71b797f8f393e79cb3c9d73b790045a0f7df6ecea35803369793c7",
|
||||
"sso-mfa/k8s/keycape/mint-secret-adapter.py": "sha256:246323c7243ded18229ab063956577e151d56d9be485d3bd44fc38e5af0edf3c",
|
||||
"sso-mfa/k8s/keycape/test_adapter_source_closure.py": "sha256:63cb442636d996fcf1c36b7b746ed860106a7da35dbaeb1c5129a41420ca4155",
|
||||
"sso-mfa/k8s/keycape/secrets-engine-openbao-client-rollout.py": "sha256:8b834e5f0353a5e11797637420609e06e7bf7de47777c7e78b774f74d4a6afbd",
|
||||
"sso-mfa/k8s/keycape/test_openbao_client_config.py": "sha256:7104a73fb936aca4829ef8046e39497d0b7347d3e74810f672e4d4ddc3346457",
|
||||
"sso-mfa/k8s/keycape/ensure-groups-adapter.py": "sha256:6cba5b07a9262a8e0bf445dc4095ba03b31ddc15e2efbfe374e93f4f78b6aed4",
|
||||
"sso-mfa/k8s/keycape/verify-user-engine-portal.py": "sha256:ccd5106395bd5d7b4f3dbbcbad449e3793aff217d4429b5d5af9e52ab9a58103",
|
||||
"sso-mfa/k8s/keycape/test_playwright_registration.py": "sha256:b97ea8619ac768f520e4058aeb50b0b1e8ef6d2e9a1a0fff932a151a3d4b61ae",
|
||||
"sso-mfa/k8s/keycape/playwright-registration.py": "sha256:bf2d6b83d93629df8ddaf6c31d6c7408ae3866969d63895714df0e08a570da99",
|
||||
"identity-provisioner/group_provenance_postgres.py": "sha256:2a0111e83ad7b611076a1b45634e7f698b3d2e9acc473320b4a8ba009ebd0932",
|
||||
"identity-provisioner/password_setup.py": "sha256:53012fc708384fc9cd0fff4f3fa5f204ab85866db91d726457dd130799e88ca0",
|
||||
"identity-provisioner/provisioner.py": "sha256:19d1b837cbafeeacb1aa815856cd68091e6702281d72b1d860230fa320e1725b",
|
||||
"identity-provisioner/copy-client-secret.py": "sha256:0f1907a9f16688148aede9fceb53a55cfd0504bcb8552f158d0fa77e9ad960c8",
|
||||
"identity-provisioner/group_provenance.py": "sha256:405e08eaaab7c21d618dff4db5970607806f32ab6c461effb81c737e32182b10",
|
||||
"identity-provisioner/server.py": "sha256:b61c83da6c7ce20fa2790d5bb00aeca6a91dccd133526bc559ed26562f90eb29",
|
||||
"security-case-types/oidc-browser-relying-party/type.yaml": "sha256:f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c",
|
||||
"security-case-type-revisions/1.2/oidc-browser-relying-party/type.yaml": "sha256:9e60ff569fb0d860a2bbde7bbb0991a54d4b0688f62b56df0fa5d161fd624578",
|
||||
"security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml": "sha256:ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f",
|
||||
"security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml": "sha256:546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94",
|
||||
"security-cases/forgejo-netkingdom-oidc.yaml": "sha256:bcc2c6d5bae0d28242b3cdb71e74d8ca297c2afe04766b55d53597f86893351d",
|
||||
"sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json": "sha256:773daa1df72cc7cf6a6d8aa082208181ca2438cdecc9179b1bc5b1c3a09099c2",
|
||||
"sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53",
|
||||
"tools/security-case-types/security_case_evidence.py": "sha256:da8efa59c52ad73283397ebe639ae259d0cb9c6348b5cb829135c24230dcdc75",
|
||||
"tools/security-case-types/security_case_validator.py": "sha256:1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b",
|
||||
"tools/security-scenario-composer/security_scenario_composer.py": "sha256:9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f",
|
||||
"tools/playbook-capability-contract/playbook_contract_validator.py": "sha256:2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d"
|
||||
},
|
||||
"ops-warden": {
|
||||
"src/warden/ca.py": "sha256:d5282819c5d29386fd59a7dc46d1abdc01e99fc6838a1a6637f90868389fe557",
|
||||
"src/warden/inventory.py": "sha256:9401db92d7cbf3bea962b97e83ef2c252a19c69ab94ce4836ba58f68638cb311",
|
||||
"src/warden/memory.py": "sha256:ecf5f7ccabb669b56bfca3a109b10bbe8437bdd2ea30a47a4c08570f97d7be2a",
|
||||
"src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7",
|
||||
"src/warden/audit.py": "sha256:6b5b8fe053064d3905294bfde1b691fcca8a0806c889e52a49494470d29c24a0",
|
||||
"src/warden/case_type.py": "sha256:7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7",
|
||||
"src/warden/vault.py": "sha256:860b8c9f498b3955b363a74dd74087b6d0c399599ef22b951a076ee33c95929f",
|
||||
"src/warden/taint.py": "sha256:8c3d161d18d429d026aad715ac01e2d33e0f4e84ac03fbaea3cea15017ad39a1",
|
||||
"src/warden/case_run.py": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a",
|
||||
"src/warden/vault_hints.py": "sha256:0d158ffcc3a93d5442df5fbe13d4c1e3b3159dd60301530bbdae6db9bc9c709e",
|
||||
"src/warden/access.py": "sha256:258d45d2f84b54b2464373ad4d7d6df6ff95b8e8d60f5e85ceb2d962fcfa8a61",
|
||||
"src/warden/clock_attribution.py": "sha256:5fcf07a2525a6a250442e14b6f6229f7db2a6ca02fc6aac23cdaa4c1a78c63eb",
|
||||
"src/warden/case_plan.py": "sha256:9352ae8683d1181980c4ff38524582e4be448d33c85b558f60582c1e5bb5bbaa",
|
||||
"src/warden/caller_identity.py": "sha256:747edfaceecd470981949253bda0c3bd2f487d40a9a2f77c2cc1eff3f34f04f7",
|
||||
"src/warden/policy.py": "sha256:aac46f538e986731cd841581c91efe004b8c1940966b954ef635780a719dada4",
|
||||
"src/warden/case_receipt_state.py": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc",
|
||||
"src/warden/config.py": "sha256:8a857faa3811b00303d4d5c904d69255ebdaff3239e8516f97e6606e53b46924",
|
||||
"src/warden/openbao_caller.py": "sha256:7e6d07827d00eae7de3b2367c97bdcda1a13d6559627201a4ba09a0e42d46a17",
|
||||
"src/warden/doubles.py": "sha256:5460e07f5bd0f51a81d3f3c0ac84efde65ca9130b1039f94ee22ca484976a9de",
|
||||
"src/warden/posture.py": "sha256:1bb60c4dd4634ea42cb130ae1325d7415adf43096906bde2515396c368256e33",
|
||||
"src/warden/cli.py": "sha256:196cd829d3dbfbd2333598a983687bde1f9bcdfadef95b2fd1d237c4a5f060a9",
|
||||
"src/warden/proxy.py": "sha256:69649bcf8979e4ed7f9104e927b458827a355c4846143367f4cd49c8b89b96da",
|
||||
"src/warden/plan.py": "sha256:001b3197e644221344d52e6b1594eb8eae78d10e73d18ed31273505909f3916b",
|
||||
"src/warden/worker.py": "sha256:d7c2b391b36033d44d8c6841b9bce18a05c74b41ef2910b91689820b6a1cb44e",
|
||||
"src/warden/mask.py": "sha256:0876412d453fa84109dc8ee0468449f40cf4d3421b056f30fc205f0f59ec494b",
|
||||
"src/warden/models.py": "sha256:f4100b02c5d96d537980e00d4b874159cd5822ba8219f1f533a2516912b7bd85",
|
||||
"src/warden/__init__.py": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a",
|
||||
"src/warden/scorecard.py": "sha256:0fa5aef34aeb004fa7fcfabc287759cc47421ca5244c1f31fc467b22a653353c",
|
||||
"src/warden/desk.py": "sha256:2499120392a5a1c0fd8f423d5b5c787c75a4efb9a986a1b7bcb85eae13423e85",
|
||||
"src/warden/routing/scaffold.py": "sha256:5079c8e51f8d487eae3f322a66324bd70cefb637bb28f46c861032de8ec54aa2",
|
||||
"src/warden/routing/models.py": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e",
|
||||
"src/warden/routing/__init__.py": "sha256:e9124811ebe11ad32c98ab462b2eed634d4ddca08792981d0f353b223a239902",
|
||||
"src/warden/routing/catalog.py": "sha256:5b95810df9e6102a33d3a116a096cd75292c8572bad721fe6baa772caacc9bc3",
|
||||
"src/warden/scripts/ops_ssh_wrapper.py": "sha256:e8c289882f2ad439d2eb32f9a871967420fefaffe745c9271c1a2e948fbdfe50",
|
||||
"src/warden/scripts/__init__.py": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repo_manager_identifiers_sha256": "sha256:4e35fcd009c20822574fd33171176bd14a1ff2dd5af8b0bb2820954974593a2a",
|
||||
"authorization": "Root accepted the exact source fixture patch for source integration. This packet adds no owner/native authority or catalog/pin promotion. No prepared script has been executed.",
|
||||
"publishable": false,
|
||||
"requires_allocated_manifest": true,
|
||||
"publication_file_paths": [
|
||||
"sso-mfa/k8s/keycape/test_admit_1_3_type.py",
|
||||
"sso-mfa/k8s/keycape/test_keycape_registration_adapter.py",
|
||||
"sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml",
|
||||
"workplans/NK-WP-0048-security-case-types.md"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
"""Prepared canonical source validation; --execute launches only the two scoped tests."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
PACKET = Path(__file__).resolve().parent
|
||||
ROOTS = {'net-kingdom': Path('/home/worsch/net-kingdom'), 'ops-warden': Path('/home/worsch/ops-warden')}
|
||||
|
||||
def sha(data):
|
||||
return 'sha256:' + hashlib.sha256(data).hexdigest()
|
||||
|
||||
def sources(manifest):
|
||||
allowed = []
|
||||
for owner, rows in manifest['validation']['source_hashes'].items():
|
||||
for relative, expected in rows.items():
|
||||
path = ROOTS[owner] / relative
|
||||
assert not path.is_symlink(), str(path)
|
||||
assert sha(path.read_bytes()) == expected, str(path)
|
||||
allowed.append(str(path.resolve()))
|
||||
return allowed
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--execute', action='store_true')
|
||||
args = parser.parse_args()
|
||||
manifest = json.loads((PACKET / 'publication-inputs.json').read_text())
|
||||
allowed = sources(manifest)
|
||||
revision = subprocess.check_output(['git', '-C', str(ROOTS['ops-warden']), 'rev-parse', 'HEAD'], text=True).strip()
|
||||
assert revision == manifest['warden_revision']
|
||||
if not args.execute:
|
||||
print(json.dumps({'prepared_only': True, 'public_source_files_verified': len(allowed),
|
||||
'tests': manifest['validation']['tests']}))
|
||||
return
|
||||
protected = {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']}
|
||||
scratch = Path(tempfile.mkdtemp(prefix='nk48-catalog-canonical-validation-'))
|
||||
for name in ['home', 'tmp', 'guard', 'pycache']:
|
||||
(scratch / name).mkdir()
|
||||
shutil.copyfile(PACKET / 'validation-guard.py', scratch / 'guard/sitecustomize.py')
|
||||
shutil.copyfile(PACKET / 'validation-child.py', scratch / 'child.py')
|
||||
(scratch / 'allowed.json').write_text(json.dumps(allowed))
|
||||
(scratch / 'pytest.ini').write_text('[pytest]\n')
|
||||
environment = {
|
||||
'HOME': str(scratch / 'home'), 'TMPDIR': str(scratch / 'tmp'), 'PATH': '/usr/bin:/bin',
|
||||
'PYTHONPATH': str(scratch / 'guard') + ':' + str(ROOTS['ops-warden'] / 'src'),
|
||||
'PYTHONDONTWRITEBYTECODE': '1', 'PYTHONPYCACHEPREFIX': str(scratch / 'pycache'),
|
||||
'LANG': 'C.UTF-8', 'WARDEN_REVIEW_ALLOWED_SOURCE_LIST': str(scratch / 'allowed.json'),
|
||||
'WARDEN_REVIEW_VALIDATOR_PREFIX': str(scratch / 'tmp/warden-case-validator-'),
|
||||
}
|
||||
with (PACKET / 'canonical-validation.log').open('w') as log:
|
||||
result = subprocess.run([manifest['validation']['python'], str(scratch / 'child.py')],
|
||||
cwd=ROOTS['net-kingdom'], env=environment,
|
||||
stdout=log, stderr=subprocess.STDOUT)
|
||||
sources(manifest)
|
||||
assert protected == {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']}
|
||||
log_text = (PACKET / 'canonical-validation.log').read_text()
|
||||
counts_verified = bool(re.search(r'\b31 passed, 53 subtests passed\b', log_text))
|
||||
counts_verified = counts_verified and not re.search(r'\b[1-9][0-9]* (?:skipped|deselected)\b', log_text)
|
||||
receipt = {'executed': True, 'counts_verified': counts_verified, 'returncode': result.returncode, 'warden_revision': revision,
|
||||
'net_kingdom_head': subprocess.check_output(['git', '-C', str(ROOTS['net-kingdom']), 'rev-parse', 'HEAD'], text=True).strip(),
|
||||
'verified_public_source_files': len(allowed), 'tests': manifest['validation']['tests'],
|
||||
'log_sha256': sha((PACKET / 'canonical-validation.log').read_bytes()), 'scratch': str(scratch),
|
||||
'limitations': 'Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin.'}
|
||||
(PACKET / 'canonical-validation.json').write_text(json.dumps(receipt, indent=2) + '\n')
|
||||
print(json.dumps(receipt, indent=2))
|
||||
raise SystemExit(result.returncode or (0 if counts_verified else 1))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
"""Focused canonical-source test process. Network/native/estate-read guard required."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import sitecustomize
|
||||
|
||||
assert sitecustomize.ACTIVE is True
|
||||
assert not {'BAO_TOKEN', 'VAULT_TOKEN', 'WARDEN_AGENT_ID'} & os.environ.keys()
|
||||
assert list(Path.home().iterdir()) == []
|
||||
try:
|
||||
socket.socket()
|
||||
except RuntimeError as error:
|
||||
assert str(error) == 'fixture validation: network disabled'
|
||||
else:
|
||||
raise AssertionError('network guard inactive')
|
||||
|
||||
import warden.case_inputs as inputs
|
||||
import pytest
|
||||
|
||||
assert Path(inputs.__file__).resolve() == Path('/home/worsch/ops-warden/src/warden/case_inputs.py')
|
||||
PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n'''
|
||||
old_run = subprocess.run
|
||||
|
||||
def run_pinned(arguments, *positional, **keywords):
|
||||
if (type(arguments) is not list or len(arguments) != 5
|
||||
or arguments[:3] != [sys.executable, '-I', '-c']
|
||||
or arguments[3] != inputs._VALIDATE
|
||||
or not str(arguments[4]).startswith(sitecustomize.VALIDATOR_PREFIX)
|
||||
or keywords.get('env') != {}):
|
||||
raise RuntimeError('fixture validation: unexpected process refused')
|
||||
return old_run([*arguments[:3], PREAMBLE + arguments[3], arguments[4]],
|
||||
*positional, **keywords)
|
||||
|
||||
inputs.subprocess.run = run_pinned
|
||||
raise SystemExit(pytest.main([
|
||||
'-q', '-p', 'no:cacheprovider', '--noconftest',
|
||||
'-c', str(Path(os.environ['TMPDIR']).parent / 'pytest.ini'),
|
||||
'--rootdir=/home/worsch/net-kingdom',
|
||||
'/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_admit_1_3_type.py',
|
||||
'/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py',
|
||||
]))
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
"""Copied as sitecustomize.py into a fresh scratch directory by run_validation."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
ALLOWED = frozenset(json.loads(Path(os.environ['WARDEN_REVIEW_ALLOWED_SOURCE_LIST']).read_text()))
|
||||
VALIDATOR_PREFIX = os.environ['WARDEN_REVIEW_VALIDATOR_PREFIX']
|
||||
|
||||
def guard(event, args):
|
||||
if event.startswith('socket.'):
|
||||
raise RuntimeError('fixture validation: network disabled')
|
||||
if event == 'subprocess.Popen':
|
||||
command = args[1]
|
||||
if (type(command) is list and len(command) == 5
|
||||
and command[:3] == [sys.executable, '-I', '-c']
|
||||
and command[3].startswith('import sys as _review_sys\ndef _review_guard(')
|
||||
and str(command[4]).startswith(VALIDATOR_PREFIX) and args[3] == {}):
|
||||
return
|
||||
raise RuntimeError('fixture validation: native process disabled')
|
||||
if event in {'os.system', 'os.exec', 'os.posix_spawn'}:
|
||||
raise RuntimeError('fixture validation: native process disabled')
|
||||
if event == 'open' and isinstance(args[0], str):
|
||||
path = str(Path(args[0]).absolute())
|
||||
if path.startswith(('/home/', '/root/')):
|
||||
# Only exact prehashed public source may be read from the estate.
|
||||
if str(Path(path).resolve()) not in ALLOWED:
|
||||
raise PermissionError('fixture validation: unlisted estate file disabled')
|
||||
mode, flags = args[1:3]
|
||||
if ((isinstance(mode, str) and any(c in mode for c in 'wax+'))
|
||||
or (flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND))):
|
||||
raise PermissionError('fixture validation: canonical source writes disabled')
|
||||
|
||||
sys.addaudithook(guard)
|
||||
ACTIVE = True
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"previous": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2",
|
||||
"only_source_pin_deltas": [
|
||||
{
|
||||
"repo": "net-kingdom",
|
||||
"path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml",
|
||||
"before": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a",
|
||||
"after": "sha256:ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f"
|
||||
}
|
||||
],
|
||||
"reason": "Independently reviewed exact issuer-template entry text integrated; descriptors, parameters and artifact unchanged."
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"status": "verified",
|
||||
"commit": "befb0bc68cd25b625804fce559476eefd702521d",
|
||||
"source_and_primary_task_count": 35,
|
||||
"original31bindings_unchanged": true,
|
||||
"new_records": [
|
||||
{
|
||||
"record_id": "NK-WP-0048-T32",
|
||||
"status": "done",
|
||||
"uuid": "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5"
|
||||
},
|
||||
{
|
||||
"record_id": "NK-WP-0048-T33",
|
||||
"status": "done",
|
||||
"uuid": "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5"
|
||||
},
|
||||
{
|
||||
"record_id": "NK-WP-0048-T34",
|
||||
"status": "progress",
|
||||
"uuid": "37f43c40-d014-544e-aa0b-5219dd4a6b93"
|
||||
},
|
||||
{
|
||||
"record_id": "NK-WP-0048-T35",
|
||||
"status": "done",
|
||||
"uuid": "cf1d6ab3-846e-53ea-8575-d6347b6f4769"
|
||||
}
|
||||
],
|
||||
"native_gates_closed": 0,
|
||||
"source_corrections_only": true
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
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('and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication\nreceipts belong in the latter packet when performed.','and `nk48-catalog-fixture-publication-r2/`. Root canonical validation passes\n31 tests / 53 subtests against Warden `922c26d` (the same reviewed code plus\none normal allocator-only source UUID commit). Publication receipts belong\nin 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.')
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
{
|
||||
"admitted": false,
|
||||
"canonical_default": {
|
||||
"type": "oidc-browser-relying-party@1.1",
|
||||
"type_yaml_digest": "sha256:f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c",
|
||||
"unchanged": true
|
||||
},
|
||||
"check_config_digest": "sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5",
|
||||
"complete_executable_descriptor_set": true,
|
||||
"dispatch_permitted": false,
|
||||
"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"
|
||||
},
|
||||
"owner_review": false,
|
||||
"params": {
|
||||
"consumer_owner_repo": "railiance-apps"
|
||||
},
|
||||
"refusal": null,
|
||||
"review_requirement": "successor candidate only: new descriptor bytes need a reviewed successor revision under canon section 6 and owner review (NK-WP-0048-T16) before pinning",
|
||||
"schema": "netkingdom.descriptor-composition-candidate.v1",
|
||||
"selection": "explicit version root 1.3=security-case-type-revisions/1.3; default root unchanged",
|
||||
"status": "not_an_admission",
|
||||
"steps": [
|
||||
{
|
||||
"candidate_descriptor_digest": "sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a",
|
||||
"descriptor": {
|
||||
"actor": "human",
|
||||
"adapter": "net-kingdom/ensure-groups@v1",
|
||||
"adapter_source": "sha256:f93e68e1932c7ca293c9e69e2d0e1843d58d06a8d4892158fe7d1e0393a21ba0",
|
||||
"allowed_created": [
|
||||
"service-group",
|
||||
"service-group-membership"
|
||||
],
|
||||
"effect": "change",
|
||||
"lane": "keycape-attended-case-change",
|
||||
"owner": "net-kingdom",
|
||||
"retry_safe": false,
|
||||
"revocation_scopes": [
|
||||
"parent-session"
|
||||
],
|
||||
"step": "ensure-groups",
|
||||
"target_parameter": "client_id",
|
||||
"version": "warden.case.execution.v1"
|
||||
},
|
||||
"findings": [],
|
||||
"names_confirmed": null,
|
||||
"owner_confirmation_required": null,
|
||||
"step": "ensure-groups",
|
||||
"type_lane": "keycape-attended-case-change",
|
||||
"type_owner": "net-kingdom"
|
||||
},
|
||||
{
|
||||
"candidate_descriptor_digest": "sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70",
|
||||
"descriptor": {
|
||||
"actor": "human",
|
||||
"adapter": "net-kingdom/keycape-mint-secret@v1",
|
||||
"adapter_source": "sha256:34f79944204a2cf5b2a22baecbee4bde77cbdc0e3f9fc87bc98fa12a9c2c0bc1",
|
||||
"allowed_created": [
|
||||
"custody-secret-version"
|
||||
],
|
||||
"effect": "mint",
|
||||
"lane": "keycape-client-secret-generate",
|
||||
"owner": "railiance-platform",
|
||||
"retry_safe": false,
|
||||
"revocation_scopes": [
|
||||
"parent-session",
|
||||
"broker-grant"
|
||||
],
|
||||
"step": "mint-secret",
|
||||
"target_parameter": "client_id",
|
||||
"version": "warden.case.execution.v1"
|
||||
},
|
||||
"findings": [],
|
||||
"names_confirmed": {
|
||||
"by": "founder",
|
||||
"on": "2026-10-17",
|
||||
"schema": "railiance-platform.custody-mint.v1",
|
||||
"task": "NK-WP-0048-T25"
|
||||
},
|
||||
"owner_confirmation_required": null,
|
||||
"step": "mint-secret",
|
||||
"type_lane": "keycape-client-secret-generate",
|
||||
"type_owner": "railiance-platform"
|
||||
},
|
||||
{
|
||||
"candidate_descriptor_digest": "sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85",
|
||||
"descriptor": {
|
||||
"actor": "human",
|
||||
"adapter": "net-kingdom/deliver-secret@v1",
|
||||
"adapter_source": "sha256:df609bb93ebdfffb12cde213381a77eb7ac82670f814ff73623c1d98262cc90e",
|
||||
"allowed_created": [
|
||||
"delivered-secret"
|
||||
],
|
||||
"effect": "change",
|
||||
"lane": "platform-secret-delivery",
|
||||
"owner": "railiance-platform",
|
||||
"retry_safe": false,
|
||||
"revocation_scopes": [
|
||||
"parent-session"
|
||||
],
|
||||
"step": "deliver-secret",
|
||||
"target_parameter": "client_id",
|
||||
"version": "warden.case.execution.v1"
|
||||
},
|
||||
"findings": [],
|
||||
"names_confirmed": {
|
||||
"by": "founder",
|
||||
"lane": "platform-secret-delivery",
|
||||
"on": "2026-10-17",
|
||||
"schema": "netkingdom.deliver-secret.v1",
|
||||
"task": "NK-WP-0048-T26"
|
||||
},
|
||||
"owner_confirmation_required": null,
|
||||
"step": "deliver-secret",
|
||||
"type_lane": "platform-secret-delivery",
|
||||
"type_owner": "railiance-platform"
|
||||
},
|
||||
{
|
||||
"candidate_descriptor_digest": "sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0",
|
||||
"descriptor": {
|
||||
"actor": "human",
|
||||
"adapter": "net-kingdom/keycape-bind-env@v1",
|
||||
"adapter_source": "sha256:dcd43f5ce1c4e829e1198faffc6bfb9cc610509f5176658f42c133accc0a7305",
|
||||
"allowed_created": [
|
||||
"keycape-env-binding"
|
||||
],
|
||||
"effect": "change",
|
||||
"lane": "keycape-attended-case-change",
|
||||
"owner": "net-kingdom",
|
||||
"retry_safe": false,
|
||||
"revocation_scopes": [
|
||||
"parent-session"
|
||||
],
|
||||
"step": "bind-env",
|
||||
"target_parameter": "client_id",
|
||||
"version": "warden.case.execution.v1"
|
||||
},
|
||||
"findings": [],
|
||||
"names_confirmed": {
|
||||
"by": "founder",
|
||||
"on": "2026-10-17",
|
||||
"schema": "netkingdom.keycape-bind-env.attempt.v1",
|
||||
"task": "NK-WP-0048-T27"
|
||||
},
|
||||
"owner_confirmation_required": null,
|
||||
"step": "bind-env",
|
||||
"type_lane": "keycape-attended-case-change",
|
||||
"type_owner": "net-kingdom"
|
||||
},
|
||||
{
|
||||
"candidate_descriptor_digest": "sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7",
|
||||
"descriptor": {
|
||||
"actor": "human",
|
||||
"adapter": "net-kingdom/keycape-registration@v1",
|
||||
"adapter_source": "sha256:b30f7abfbb5bf439c5ce067325b01c8b711076a38a6fb8484f55d5c8912921f3",
|
||||
"allowed_created": [
|
||||
"keycape-client"
|
||||
],
|
||||
"effect": "change",
|
||||
"lane": "keycape-attended-case-change",
|
||||
"owner": "net-kingdom",
|
||||
"retry_safe": false,
|
||||
"revocation_scopes": [
|
||||
"parent-session"
|
||||
],
|
||||
"step": "register-client",
|
||||
"target_parameter": "client_id",
|
||||
"version": "warden.case.execution.v1"
|
||||
},
|
||||
"findings": [],
|
||||
"names_confirmed": null,
|
||||
"owner_confirmation_required": null,
|
||||
"step": "register-client",
|
||||
"type_lane": "keycape-attended-case-change",
|
||||
"type_owner": "net-kingdom"
|
||||
},
|
||||
{
|
||||
"candidate_descriptor_digest": "sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7",
|
||||
"descriptor": {
|
||||
"actor": "human",
|
||||
"adapter": "net-kingdom/deliver-consumer-secret@v1",
|
||||
"adapter_source": "sha256:66f16dcb665216d09ef63a5ca654283a0292005ba4fb4d79c27eeb54b886a385",
|
||||
"allowed_created": [
|
||||
"consumer-eso-sa",
|
||||
"consumer-secretstore",
|
||||
"consumer-externalsecret"
|
||||
],
|
||||
"effect": "change",
|
||||
"lane": "consumer-owner-deployment",
|
||||
"owner": "railiance-apps",
|
||||
"retry_safe": false,
|
||||
"revocation_scopes": [
|
||||
"parent-session"
|
||||
],
|
||||
"step": "deliver-consumer-secret",
|
||||
"target_parameter": "client_id",
|
||||
"version": "warden.case.execution.v1"
|
||||
},
|
||||
"findings": [],
|
||||
"names_confirmed": {
|
||||
"by": "founder",
|
||||
"lane": "consumer-owner-deployment",
|
||||
"on": "2026-10-17",
|
||||
"schema": "netkingdom.consumer-secret-delivery.v1",
|
||||
"task": "NK-WP-0048-T28"
|
||||
},
|
||||
"owner_confirmation_required": null,
|
||||
"step": "deliver-consumer-secret",
|
||||
"type_lane": "consumer-owner-deployment",
|
||||
"type_owner": "{consumer_owner_repo}"
|
||||
},
|
||||
{
|
||||
"candidate_descriptor_digest": "sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224",
|
||||
"descriptor": {
|
||||
"actor": "human",
|
||||
"adapter": "net-kingdom/apps-handover@v1",
|
||||
"adapter_source": "sha256:4439305b3edcd5d51c73e6438744d8895d69c1ccb58854aac79bb489d868cf48",
|
||||
"allowed_created": [
|
||||
"handover-receipt"
|
||||
],
|
||||
"effect": "change",
|
||||
"lane": "apps-consumer-handover",
|
||||
"owner": "railiance-apps",
|
||||
"retry_safe": false,
|
||||
"revocation_scopes": [
|
||||
"parent-session"
|
||||
],
|
||||
"step": "hand-over",
|
||||
"target_parameter": "client_id",
|
||||
"version": "warden.case.execution.v1"
|
||||
},
|
||||
"findings": [],
|
||||
"names_confirmed": {
|
||||
"by": "founder",
|
||||
"lane": "apps-consumer-handover",
|
||||
"on": "2026-10-17",
|
||||
"schema": "netkingdom.apps-handover.attempt.v1",
|
||||
"task": "NK-WP-0048-T29"
|
||||
},
|
||||
"owner_confirmation_required": null,
|
||||
"step": "hand-over",
|
||||
"type_lane": "apps-consumer-handover",
|
||||
"type_owner": "{consumer_owner_repo}"
|
||||
}
|
||||
],
|
||||
"task": "NK-WP-0048-T30",
|
||||
"type": "oidc-browser-relying-party@1.3"
|
||||
}
|
||||
|
|
@ -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.')
|
||||
|
|
@ -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 <client-from-case> --case-acceptance <record> --check-config
|
||||
<keycape binary> (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
|
||||
|
|
@ -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
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
........ [100%]
|
||||
8 passed, 31 subtests passed in 4.98s
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
........ [100%]
|
||||
8 passed, 31 subtests passed in 4.90s
|
||||
|
|
@ -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')
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.......... [100%]
|
||||
10 passed, 32 subtests passed in 5.33s
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
F....F.... [100%]
|
||||
=================================== FAILURES ===================================
|
||||
_ Admit13TypeTests.test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor _
|
||||
|
||||
self = <test_admit_1_3_type.Admit13TypeTests testMethod=test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor>
|
||||
|
||||
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 = <test_admit_1_3_type.Admit13TypeTests testMethod=test_human_mint_is_refused_with_agent_runtime_identity>
|
||||
|
||||
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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
}
|
||||
]
|
||||
|
|
@ -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()
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
"""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
|
||||
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):
|
||||
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):
|
||||
os.environ.pop('WARDEN_AGENT_ID', None)
|
||||
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)):
|
||||
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_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()
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# NK48-R5-F2 issuer store implementation candidate
|
||||
|
||||
The missing issuer store is now a concrete **proposed Platform source contract**,
|
||||
not a discovered existing grant. Neither patch has been applied to a canonical
|
||||
checkout. Independent Platform review remains necessary before the conditional
|
||||
NetKingdom patch can be integrated; F2 is not declared closed here.
|
||||
|
||||
The proposed reference is cluster-scoped `ClusterSecretStore
|
||||
openbao-keycape-{client_id}-issuer`, with no metadata namespace and use restricted
|
||||
to `sso`. For Forgejo, it binds role `external-secrets-keycape-forgejo` to the
|
||||
existing CCR0041 identity `external-secrets/external-secrets`, audience `openbao`.
|
||||
The exact store/provider/ExternalSecret/key/property/target source is in
|
||||
`platform-candidate/manifests/templates/keycape-issuer-eso.yaml`; its hash is
|
||||
recorded in `review.json` and the conditional type text. The new `-issuer` name
|
||||
is an implementation proposal: it does not reuse the Apps consumer SecretStore
|
||||
or claim that CCR0041 admitted this store name. It preserves type IDs ending in
|
||||
`-` without changing parameters. ClusterSecretStore names use DNS subdomain
|
||||
names; the existing adapter's 63-character target reference bound is retained.
|
||||
|
||||
`platform.candidate.patch` adds only that explicit template, a pure metadata
|
||||
render/validation helper, eight focused fixture tests and an owner contract doc.
|
||||
It contains no Kubernetes/OpenBao API client, apply/delete command, credentials,
|
||||
receipt issuer or catalog grant. Store provisioning is separate owner work; the
|
||||
delivery lane gets only the ExternalSecret document. The template is multi-doc
|
||||
review source and must not be applied through the delivery lane. Its strict spec
|
||||
comparison intentionally refuses drift; installed ESO schema/defaulting and
|
||||
trusted observation authenticity still belong to the real owner binding.
|
||||
|
||||
`netkingdom-entry-point.conditional.patch` changes **only** the issuer
|
||||
`deliver-secret.entry_point` at NetKingdom 3236d500. The prior type is byte-equal
|
||||
to r5's e75fe941 reviewed type. It names the proposed exact kind/name/scope and
|
||||
owner template digest. Parameters, descriptors, modes, lanes, other follow-through,
|
||||
materialization, verification, acceptance, and every rollback entry are unchanged.
|
||||
The root coordinator can combine this text with F3 after Platform source review,
|
||||
then regenerate the existing combined artifact. No source owner acknowledgment,
|
||||
case/type pin, catalog mutation or default revision switch has occurred.
|
||||
|
||||
Eight tests / 42 negative subcases passed using public synthetic metadata:
|
||||
independently fixed Forgejo mapping; wrong kind/name/namespace; role, server,
|
||||
mount, SA/audience or allowed-namespace drift; consumer-store substitution;
|
||||
remoteRef/property/target drift; malformed/unready observations; rejected caller
|
||||
store/path overrides; detached ExternalSecret-only render; type-compatible trailing
|
||||
dash. Both exact patches passed `git apply --check` in disposable scratch.
|
||||
See `focused-tests.*` and `structural-and-patch-checks.json`. These checks are not
|
||||
native delivery, signed evidence, API defaulting, issuer auth or isolation proof.
|
||||
|
||||
The source basis is pinned in `source-pins.json`: r5 F2 at coordinator review
|
||||
lines 429–439; Platform RPF-1.3-C1 explicitly requires a named store and the
|
||||
lane definition explicitly calls for a fixed client-derived Platform constant;
|
||||
CCR0041 grants the Forgejo role/SA/audience but declares no store name;
|
||||
Platform's historical issuer `eso.draft.yaml` supplies ClusterSecretStore,
|
||||
namespace restriction, serviceAccountRef.audiences and fixed target/remoteRef
|
||||
conventions; the scaffold also renders ClusterSecretStore and mount-relative
|
||||
remoteRefs but requires a caller-supplied owner config store_name. The scaffold's
|
||||
historical audience location differs: the candidate follows the issuer draft,
|
||||
and makes no claim about the actual installed schema.
|
||||
|
||||
Bounded source search: Platform, NetKingdom and Mason public YAML/Python/JSON/MD
|
||||
for `openbao-keycape-forgejo` and `external-secrets-keycape-forgejo`, plus Platform
|
||||
issuer/store/scaffold files. Existing hits name the separate consumer store,
|
||||
role/policy or old type text. No fixed Forgejo issuer store declaration was found.
|
||||
This is a public-source finding, not a live-cluster absence claim. No Kubernetes,
|
||||
SSH, Bao, credential route, Secret or private archive was accessed.
|
||||
|
||||
The next source step is independent Platform review of the exact new declaration,
|
||||
then coordinator integration of the accepted text with F3. RPF-1.3-T4's actual
|
||||
capabilities, authentic metadata receipts, per-attempt revocation, guarded
|
||||
created-only rollback and Warden catalog routing remain its existing owner work.
|
||||
No new task, UUID, protocol, policy gate or human approval request is created.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"net-kingdom": {
|
||||
"HEAD": "3236d5000e52e0643f33906a26a7825aa0b67465",
|
||||
"status_porcelain_v1": " M .custodian-brief.md\n M WORK-RECORDS.md\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json\n?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md\n",
|
||||
"index_sha256": "47bcf3291b9613706337c2493c33a065ab089c0c5bbbe545de5f462b67b007a3"
|
||||
},
|
||||
"railiance-platform": {
|
||||
"HEAD": "0a9617a62d4696a8070798923b459e8405c3dbf2",
|
||||
"status_porcelain_v1": "M .custodian-brief.md\n M WORK-RECORDS.md\n",
|
||||
"index_sha256": "40f766948f7774c6215ab562e50b4154e9bbdcfbf3a14488625c95afc9cf6fe6"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"schema": "custodian.source-test-result.v1",
|
||||
"scope": "synthetic public metadata only; no API, native, credential, receipt-authenticity or isolation proof",
|
||||
"command": "python3 -B platform-candidate/tests/test_keycape_issuer_eso.py",
|
||||
"tests": 8,
|
||||
"subtests": 42,
|
||||
"passed": true,
|
||||
"python": "3.12.3",
|
||||
"pyyaml": "6.0.1"
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
test_delivery_document_is_only_external_and_detached (fixture.FixedIssuerContract.test_delivery_document_is_only_external_and_detached) ... ok
|
||||
test_external_drift_refuses (fixture.FixedIssuerContract.test_external_drift_refuses) ... ok
|
||||
test_independent_forgejo_binding (fixture.FixedIssuerContract.test_independent_forgejo_binding) ... ok
|
||||
test_missing_or_unready_refuses (fixture.FixedIssuerContract.test_missing_or_unready_refuses) ... ok
|
||||
test_no_caller_store_or_path_override (fixture.FixedIssuerContract.test_no_caller_store_or_path_override) ... ok
|
||||
test_store_drift_refuses (fixture.FixedIssuerContract.test_store_drift_refuses) ... ok
|
||||
test_type_admitted_trailing_dash_is_preserved (fixture.FixedIssuerContract.test_type_admitted_trailing_dash_is_preserved) ... ok
|
||||
test_valid_ready_metadata_allows_extra_controller_metadata (fixture.FixedIssuerContract.test_valid_ready_metadata_allows_extra_controller_metadata) ... ok
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 8 tests in 0.247s
|
||||
|
||||
OK
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"schema": "custodian.source-packet-manifest.v1",
|
||||
"sha256": {
|
||||
"OWNER-REVIEW.md": "sha256:7983fd841f09bc7584047426912c33d568c97b4c1126776f99aac42b34d2b742",
|
||||
"canonical-source-readonly-state.json": "sha256:129d6926bb996e510ab4b41d30e738096b53b71de1fb2dd470fe0ac94848cbec",
|
||||
"focused-tests.json": "sha256:8de5862a8d752dce0b3b96a9275e42952a99415a18d3f67c06bf9f5b6008d0e3",
|
||||
"focused-tests.txt": "sha256:aab46c51d87198ad4be6dd3078f61286e2faf1ef4d76fa14298698388f78ab24",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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])
|
||||
|
|
@ -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)
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
--- /dev/null
|
||||
+++ b/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.
|
||||
--- /dev/null
|
||||
+++ b/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
|
||||
--- /dev/null
|
||||
+++ b/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])
|
||||
--- /dev/null
|
||||
+++ b/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)
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"schema": "custodian.nk48-r5-f2-implementation-candidate.v1",
|
||||
"status": "proposed-source-not-admitted",
|
||||
"date": "2026-10-17",
|
||||
"scope": "NK-WP-0048-T16; existing RPF-1.3-T4 implementation preparation only",
|
||||
"source_owner_assent_received": false,
|
||||
"finding": "NK48-R5-F2",
|
||||
"owner_requirement": "RPF-1.3-C1",
|
||||
"netkingdom_base": "3236d5000e52e0643f33906a26a7825aa0b67465",
|
||||
"reviewed_code": "001a38ca2aa85155ce4840c4da22aca6e2346171",
|
||||
"platform_base": "0a9617a62d4696a8070798923b459e8405c3dbf2",
|
||||
"discovery": "No existing Forgejo issuer store declaration found in bounded public source search. No claim about live absence.",
|
||||
"new_proposed_binding": {
|
||||
"kind": "ClusterSecretStore",
|
||||
"name": "openbao-keycape-{client_id}-issuer",
|
||||
"namespace": null,
|
||||
"allowed_external_secret_namespace": "sso",
|
||||
"role": "external-secrets-keycape-{client_id}",
|
||||
"service_account_ref": "external-secrets/external-secrets",
|
||||
"audiences": [
|
||||
"openbao"
|
||||
],
|
||||
"template_path": "manifests/templates/keycape-issuer-eso.yaml",
|
||||
"template_sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743",
|
||||
"external_secret": "sso/keycape-{client_id}-client",
|
||||
"secret_key": "client-secret",
|
||||
"provider_mount": "platform",
|
||||
"remote_ref_key": "workloads/keycape/{client_id}",
|
||||
"remote_ref_property": "CLIENT_SECRET",
|
||||
"target_creation_policy": "Owner",
|
||||
"remote_deletion_policy": "Retain"
|
||||
},
|
||||
"candidate_patches": {
|
||||
"platform.candidate.patch": "a490df7dee41e15351da8d2906e1342edebb533984befe3d9ae74d952dbb091b",
|
||||
"netkingdom-entry-point.conditional.patch": "6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61"
|
||||
},
|
||||
"checks": {
|
||||
"tests_passed": 8,
|
||||
"subtests_passed": 42,
|
||||
"patch_dryruns_passed": true,
|
||||
"only_type_structural_delta": "spec.follow_through[id=deliver-secret].entry_point",
|
||||
"parameters_bytes_unchanged": true
|
||||
},
|
||||
"remaining_existing_owner_work": [
|
||||
"Independent Platform review/adoption of explicit template and metadata helper under RPF-1.3-T4; no assent inferred.",
|
||||
"After that review, integrate the conditional NetKingdom text with F3 and regenerate combined composition artifact through existing owner path.",
|
||||
"RPF-1.3-T4 real authenticated deliver/remove/evidence/session capabilities and catalog routing acceptance remain separate existing requirements.",
|
||||
"Actual approved owner execution must establish installed ESO compatibility, authentic resource observation, fixed preexisting store and receipt/rollback semantics; source fixtures prove none of these."
|
||||
],
|
||||
"changes_not_made": [
|
||||
"canonical checkouts",
|
||||
"workplans/tasks/UUIDs",
|
||||
"descriptors",
|
||||
"catalog",
|
||||
"CCR0041/grants",
|
||||
"pins/acknowledgments",
|
||||
"default 1.1 or revision 1.2",
|
||||
"native resources",
|
||||
"credentials/Secret values"
|
||||
],
|
||||
"knowledge": {
|
||||
"version": "0.2.6",
|
||||
"digest": "8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02",
|
||||
"status": "withheld-orientation-only; current source SECURITY-COMPANION and actual owner contracts reviewed; no authority inferred"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
[
|
||||
{
|
||||
"repo": "net-kingdom",
|
||||
"commit": "3236d5000e52e0643f33906a26a7825aa0b67465",
|
||||
"path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml",
|
||||
"sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a",
|
||||
"working_tree_equals_commit": true,
|
||||
"line_anchors": [
|
||||
14,
|
||||
17,
|
||||
269,
|
||||
270,
|
||||
273
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "net-kingdom",
|
||||
"commit": "3236d5000e52e0643f33906a26a7825aa0b67465",
|
||||
"path": "sso-mfa/k8s/keycape/deliver-secret-adapter.py",
|
||||
"sha256": "cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d",
|
||||
"working_tree_equals_commit": true,
|
||||
"line_anchors": [
|
||||
47,
|
||||
60,
|
||||
94,
|
||||
98
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "railiance-platform",
|
||||
"commit": "0a9617a62d4696a8070798923b459e8405c3dbf2",
|
||||
"path": "credential-change-requests/CCR-2026-0041-forgejo-netkingdom-oidc-keycape-eso-reader.yaml",
|
||||
"sha256": "e69077ad29166770e4a57b4c4105615e272822d8fde11df591d0c127b158ac67",
|
||||
"working_tree_equals_commit": true,
|
||||
"line_anchors": [
|
||||
40,
|
||||
44,
|
||||
52,
|
||||
56,
|
||||
66,
|
||||
89
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "railiance-platform",
|
||||
"commit": "0a9617a62d4696a8070798923b459e8405c3dbf2",
|
||||
"path": "docs/provisioning-windows/2026-10-04-1000/keycape/eso.draft.yaml",
|
||||
"sha256": "c8c334fae9d7e7e2f74ac30095d2266d626e857f7c5a85ad4e20b28543678c5d",
|
||||
"working_tree_equals_commit": true,
|
||||
"line_anchors": [
|
||||
3,
|
||||
5,
|
||||
16,
|
||||
18,
|
||||
19,
|
||||
22,
|
||||
26,
|
||||
33,
|
||||
43,
|
||||
55
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "railiance-platform",
|
||||
"commit": "0a9617a62d4696a8070798923b459e8405c3dbf2",
|
||||
"path": "docs/credential-lane-designs/keycape-secrets-engine-openbao-issuer-input.yaml",
|
||||
"sha256": "ade2e10b0f65341051b955900c68931bdd56fd3a81bf452ae81229cc2390ad63",
|
||||
"working_tree_equals_commit": true,
|
||||
"line_anchors": [
|
||||
44,
|
||||
56
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "railiance-platform",
|
||||
"commit": "0a9617a62d4696a8070798923b459e8405c3dbf2",
|
||||
"path": "scripts/catalog_lane_scaffold.py",
|
||||
"sha256": "96d60cc47aa78728f5ba96ce1fd63033adf23c05641de93ebbdda3902082d2d1",
|
||||
"working_tree_equals_commit": true,
|
||||
"line_anchors": [
|
||||
137,
|
||||
144,
|
||||
145,
|
||||
149,
|
||||
154
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "railiance-platform",
|
||||
"commit": "0a9617a62d4696a8070798923b459e8405c3dbf2",
|
||||
"path": "argocd/platform-addons/openbao-secretstore/openbao-keycape-approval-clients.clustersecretstore.yaml",
|
||||
"sha256": "9ce6533e652db5428248c37319b6096a5f8f4af82138141473de4d4ca809e692",
|
||||
"working_tree_equals_commit": true,
|
||||
"line_anchors": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"repo": "the-custodian",
|
||||
"path": "docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r5.json",
|
||||
"sha256": "e3016352c58178894ccbfc600b960d3f9e2b5d534a05b8df2c5b79737cb0127a"
|
||||
},
|
||||
{
|
||||
"repo": "the-custodian",
|
||||
"path": "docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-platform.json",
|
||||
"sha256": "d3f523b60924b530376b7aac4459cecda5da183848dd6e35889c1013c73439d9"
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"only_structural_delta": "spec.follow_through[id=deliver-secret].entry_point",
|
||||
"parameters_raw_bytes_identical": true,
|
||||
"descriptor_changes": false,
|
||||
"canonical_1_1_1_2_changes": false,
|
||||
"base": "3236d5000e52e0643f33906a26a7825aa0b67465",
|
||||
"both_patch_dryruns_exit": 0
|
||||
}
|
||||
|
|
@ -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 <client-from-case> --case-acceptance <record> --check-config
|
||||
<keycape binary> (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
|
||||
|
|
@ -0,0 +1,498 @@
|
|||
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 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
|
||||
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 <client-from-case> --case-acceptance <record> --check-config
|
||||
<keycape binary> (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
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# F2 proposed source publication packet
|
||||
|
||||
Prepared local integration only; not executed against either canonical checkout.
|
||||
`manifest.json` lists exact source hashes and absent preimages. Platform HEAD was
|
||||
`0a9617a62d4696a8070798923b459e8405c3dbf2`; staged `.custodian-brief.md` and
|
||||
unstaged `WORK-RECORDS.md` already existed. Preserve them.
|
||||
|
||||
Check, then apply the four reviewed files:
|
||||
|
||||
```bash
|
||||
python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py
|
||||
python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py --apply
|
||||
```
|
||||
|
||||
The script checks the exact HEAD, absent tracked/worktree preimages, hashes and
|
||||
symlink safety; it writes only the four manifest paths. It never stages, commits,
|
||||
syncs, applies native resources or edits NetKingdom. The source remains explicitly
|
||||
proposed/source-only, with no inferred owner assent. It verifies that unrelated
|
||||
Git status and index bytes are unchanged. A concurrent failure may leave a
|
||||
partial set of newly created files; inspect it rather than reset unrelated dirt.
|
||||
|
||||
Run the focused test in Platform, then stage only these paths. To keep the
|
||||
preexisting staged brief out of the source commit, use a path-scoped commit:
|
||||
|
||||
```bash
|
||||
git add -- docs/keycape-issuer-eso-contract.md manifests/templates/keycape-issuer-eso.yaml scripts/keycape_issuer_eso.py tests/test_keycape_issuer_eso.py
|
||||
git commit --only -m "Prepare proposed fixed KeyCape issuer ESO source contract" -- docs/keycape-issuer-eso-contract.md manifests/templates/keycape-issuer-eso.yaml scripts/keycape_issuer_eso.py tests/test_keycape_issuer_eso.py
|
||||
```
|
||||
|
||||
Root handles the already-authorized normal repository sync and its exact-commit
|
||||
receipt. No task is marked done. `progress-paragraph.md` explains the completed
|
||||
source-fit work and remaining real implementation. No legitimate Platform
|
||||
workplan task was found: `RPF-1.3-T4` lives in the original owner-review JSON only.
|
||||
Do not append the paragraph to an unrelated finished workplan or create a new one.
|
||||
|
||||
`netkingdom-entry-point.conditional.patch` is the unchanged exact proposed-1.3
|
||||
entry text from the reviewed packet. Keep it unapplied until F3 is combined and
|
||||
the existing combined artifact regenerated. It edits only the issuer delivery
|
||||
entry point, pins the exact template digest and grants no adoption or activation.
|
||||
|
||||
`integration-checks.json` records the isolated scratch application, preservation
|
||||
of synthetic staged/unstaged dirt, rejection of non-absent preimages, and eight
|
||||
passing focused tests. Tests used a minimal environment and local Git only.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"first_attempt": "missing /home/worsch/railiance-platform/.venv/bin/python; exit127; tests did not run",
|
||||
"rerun": "env PYTHONDONTWRITEBYTECODE=1 HOME=/tmp PATH=/usr/bin:/bin python3 -B -m unittest discover -s tests -p test_keycape_issuer_eso.py -q",
|
||||
"result": "8 tests passed; fixture-only metadata, no native/API calls"
|
||||
}
|
||||
|
|
@ -0,0 +1,604 @@
|
|||
{
|
||||
"repo_slug": "railiance-platform",
|
||||
"repo_path": "/home/worsch/railiance-platform",
|
||||
"source_revision": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9",
|
||||
"source_revision_reason": null,
|
||||
"issues": [
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-35",
|
||||
"message": "Repo Manager missing: README.md: README.md is required for flavor tooling",
|
||||
"file_path": "README.md",
|
||||
"db_id": "",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0015-T03' in workplan 'RPF-WP-0015' \u2014 decision 2fd5849c-240e-4d0a-bf07-5ebf8dfce3fd is resolved",
|
||||
"file_path": "workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md#RPF-WP-0015-T03",
|
||||
"db_id": "",
|
||||
"file_value": "2fd5849c-240e-4d0a-bf07-5ebf8dfce3fd",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0035-T02' in workplan 'RPF-WP-0035' \u2014 decision 62aa95e4-3ccc-4867-8806-5e3db005b483 is resolved",
|
||||
"file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T02",
|
||||
"db_id": "",
|
||||
"file_value": "62aa95e4-3ccc-4867-8806-5e3db005b483",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0035-T03' in workplan 'RPF-WP-0035' \u2014 decision 62aa95e4-3ccc-4867-8806-5e3db005b483 is resolved",
|
||||
"file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T03",
|
||||
"db_id": "",
|
||||
"file_value": "62aa95e4-3ccc-4867-8806-5e3db005b483",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0035-T07' in workplan 'RPF-WP-0035' \u2014 decision 1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa is resolved",
|
||||
"file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T07",
|
||||
"db_id": "",
|
||||
"file_value": "1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0035-T08' in workplan 'RPF-WP-0035' \u2014 decision ab85ae8a-12a0-4945-ae64-1d3d86d5b26f is resolved",
|
||||
"file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T08",
|
||||
"db_id": "",
|
||||
"file_value": "ab85ae8a-12a0-4945-ae64-1d3d86d5b26f",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0036-T03' in workplan 'RPF-WP-0036' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved",
|
||||
"file_path": "workplans/RPF-WP-0036-platform-service-assurance.md#RPF-WP-0036-T03",
|
||||
"db_id": "",
|
||||
"file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-39",
|
||||
"message": "Wait task 'RPF-WP-0038-T04' in workplan 'RPF-WP-0038' has neither depends_on, needs_human: true, blocked_on: message-from:\u2026 nor wait_until \u2014 unqualified wait",
|
||||
"file_path": "workplans/RPF-WP-0038-forgejo-scaleway-primary-coverage.md#RPF-WP-0038-T04",
|
||||
"db_id": "",
|
||||
"file_value": "wait",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0043-T03' in workplan 'RPF-WP-0043' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved",
|
||||
"file_path": "workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md#RPF-WP-0043-T03",
|
||||
"db_id": "",
|
||||
"file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0043-T04' in workplan 'RPF-WP-0043' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved",
|
||||
"file_path": "workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md#RPF-WP-0043-T04",
|
||||
"db_id": "",
|
||||
"file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0044-T04' in workplan 'RPF-WP-0044' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved",
|
||||
"file_path": "workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md#RPF-WP-0044-T04",
|
||||
"db_id": "",
|
||||
"file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0044-T06' in workplan 'RPF-WP-0044' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved",
|
||||
"file_path": "workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md#RPF-WP-0044-T06",
|
||||
"db_id": "",
|
||||
"file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0044-T07' in workplan 'RPF-WP-0044' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved",
|
||||
"file_path": "workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md#RPF-WP-0044-T07",
|
||||
"db_id": "",
|
||||
"file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-40",
|
||||
"message": "blocker satisfied, task still wait: 'RPF-WP-0048-T02' in workplan 'RPF-WP-0048' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved",
|
||||
"file_path": "workplans/RPF-WP-0048-activity-core-gitops-adoption.md#RPF-WP-0048-T02",
|
||||
"db_id": "",
|
||||
"file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-23",
|
||||
"message": "Lifecycle drift in 'rpf-wp-0053': workplan status 'proposed' has an active task \u2014 report only (ADR-007: do not auto-promote proposed)",
|
||||
"file_path": "workplans/RPF-WP-0053-coulomb-paired-backup-admission.md",
|
||||
"db_id": "cb55e474-c7a5-5c6f-bd72-ce65f853e3e3",
|
||||
"file_value": "proposed",
|
||||
"db_value": "proposed",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-39",
|
||||
"message": "Wait task 'RPF-WP-0058-T05' in workplan 'RPF-WP-0058' has neither depends_on, needs_human: true, blocked_on: message-from:\u2026 nor wait_until \u2014 unqualified wait",
|
||||
"file_path": "workplans/RPF-WP-0058-reuse-private-source-reader.md#RPF-WP-0058-T05",
|
||||
"db_id": "",
|
||||
"file_value": "wait",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0004' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260702-RPF-WP-0023-workload-kv-access-lanes.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0004",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0005' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260702-RPF-WP-0024-credential-change-approval-workflow.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0005",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0006' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260702-RPF-WP-0024-credential-change-approval-workflow.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0006",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0005' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0002-openbao-approved-automation-delegation.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0005",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0006' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0002-openbao-approved-automation-delegation.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0006",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0007' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0002-openbao-approved-automation-delegation.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0007",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0004' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0003-issue-core-runtime-ingestion-key-lane.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0004",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0007' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0003-issue-core-runtime-ingestion-key-lane.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0007",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0008' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0003-issue-core-runtime-ingestion-key-lane.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0008",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0004' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0004-llm-connect-openrouter-provider-key-lane.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0004",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0007' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0004-llm-connect-openrouter-provider-key-lane.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0007",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0008' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0004-llm-connect-openrouter-provider-key-lane.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0008",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0007' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0005-reuse-surface-runtime-secrets-openbao-lane.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0007",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-20",
|
||||
"message": "Workplan dependency target 'RAILIANCE-WP-0008' is not linked to State Hub",
|
||||
"file_path": "workplans/archived/260905-RPF-WP-0005-reuse-surface-runtime-secrets-openbao-lane.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAILIANCE-WP-0008",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0003@retired-20260826' (id=665b3b9b\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "665b3b9b-608a-4be4-84b6-dcb8261ff57b",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-platform-railiance-wp-0004@retired-20260826' (id=e57e487b\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "e57e487b-8557-439d-8093-0457c73ede93",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-platform-railiance-wp-0005@retired-20260826' (id=2731fece\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "2731fece-6c49-45b8-ab8a-4ea6c04ac603",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-platform-railiance-wp-0006@retired-20260826' (id=96c8a93d\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "96c8a93d-7a5a-4fa9-8f7b-865119551da3",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-platform-railiance-wp-0007@retired-20260826' (id=4d7ce243\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "4d7ce243-f40a-4249-a46a-a24f75d6fe4c",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-platform-railiance-wp-0008@retired-20260826' (id=671898ef\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "671898ef-2378-4814-b8f6-066148cdad46",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-platform-railiance-wp-0009@retired-20260826' (id=b059c81d\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "b059c81d-96f1-451f-896f-a05cd73744a1",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-platform-railiance-wp-0010@retired-20260826' (id=f364d405\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "f364d405-a85d-4b89-b600-1964ab436cad",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0029@retired-20260826' (id=038bc3c0\u2026, status=blocked) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "038bc3c0-4492-5b91-95eb-ae515ca205df",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0024@retired-20260826' (id=88c4ef7f\u2026, status=blocked) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "88c4ef7f-0af8-580e-90dc-a2bae2675a4d",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-platform-railiance-wp-0011@retired-20260826' (id=3f9fd8de\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "3f9fd8de-7235-4486-aaa7-634dba2bb6fa",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0027@retired-20260826' (id=6f8a6cbc\u2026, status=blocked) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "6f8a6cbc-c076-5f0a-ade2-281a7ec71360",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0017@retired-20260826' (id=16e8f234\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "16e8f234-9305-5ff3-af82-4639215a0c54",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0028@retired-20260826' (id=022dda1f\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "022dda1f-8772-57ad-862e-672924455f11",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0015@retired-20260826' (id=1c73af72\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "1c73af72-fd4b-5ab6-8ff7-a3c302bf55a5",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0022@retired-20260826' (id=a7977cbf\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "a7977cbf-ab43-56b7-b7a9-ceb52382b341",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0025@retired-20260826' (id=2422d4e7\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "2422d4e7-2125-591e-8e95-80ecc6e09da0",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0016@retired-20260826' (id=857ca302\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "857ca302-bb94-5d41-a649-1aa966368df4",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0026@retired-20260826' (id=73878f8d\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "73878f8d-d8bb-5845-8a5c-91cdd83c1066",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "INFO",
|
||||
"check_id": "C-08",
|
||||
"message": "Retired DB workstream 'railiance-wp-0023@retired-20260826' (id=ec52fbe6\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work",
|
||||
"file_path": "",
|
||||
"db_id": "ec52fbe6-571b-570e-884b-c7a32ff7bcc8",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-25",
|
||||
"message": "Owner-return task 'RPF-WP-0035-T08' waiting on audit-core has 9 reply/replies after its last dated note (or unread when undated) \u2014 owner action needed: 570ca050-0316-4e38-9c16-be41057c2330, 2de1c6e7-9602-48e5-868d-0048fbbef150, e7c36b17-d8f7-4d86-af09-02111289d1f5, 5fe769a7-d35b-4e37-a671-673d13cf8948, b3c564a6-93f9-4c07-872b-7feb22cccb73, ec4995b5-e1b2-4c66-9f8d-5ac8d6ceb149, 7c4019f2-348d-475e-9c96-e1ff6d0d27f5, ff43fd2c-7633-4b34-9ae6-2483ba03a271, 0d0da0d2-36a1-4ffc-b7bf-7896fad691f6",
|
||||
"file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T08",
|
||||
"db_id": "",
|
||||
"file_value": "message-from:audit-core",
|
||||
"db_value": "",
|
||||
"fixable": false,
|
||||
"repo_slug": "railiance-platform",
|
||||
"source_revision": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9",
|
||||
"source_revision_reason": null,
|
||||
"owner_return": {
|
||||
"task_record_id": "RPF-WP-0035-T08",
|
||||
"message_ids": [
|
||||
"570ca050-0316-4e38-9c16-be41057c2330",
|
||||
"2de1c6e7-9602-48e5-868d-0048fbbef150",
|
||||
"e7c36b17-d8f7-4d86-af09-02111289d1f5",
|
||||
"5fe769a7-d35b-4e37-a671-673d13cf8948",
|
||||
"b3c564a6-93f9-4c07-872b-7feb22cccb73",
|
||||
"ec4995b5-e1b2-4c66-9f8d-5ac8d6ceb149",
|
||||
"7c4019f2-348d-475e-9c96-e1ff6d0d27f5",
|
||||
"ff43fd2c-7633-4b34-9ae6-2483ba03a271",
|
||||
"0d0da0d2-36a1-4ffc-b7bf-7896fad691f6"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-25",
|
||||
"message": "Owner-return task 'RPF-WP-0056-T03' waiting on railiance-cluster has 9 reply/replies after its last dated note (or unread when undated) \u2014 owner action needed: 4e9d4e58-71b3-497b-8954-cedc30740529, 13acb774-8fbe-4ca3-b44d-89a4742138bd, c9435491-8545-4d48-a57a-1773f2789092, fb8f25df-57ce-40e5-ae67-0c0b392746e8, 4bb0d7af-b644-44f3-acc8-69c7321e9824, 5301d4fb-0631-4ac5-8471-531e5362b2ba, 2087b9ac-c980-4881-89f1-6b1088132f5b, 78b89fb2-ced6-45f1-9718-0670b8257d45, 110e8dea-ce81-40ff-8161-325ddb89cd67",
|
||||
"file_path": "workplans/RPF-WP-0056-playwright-consumer-admission.md#RPF-WP-0056-T03",
|
||||
"db_id": "",
|
||||
"file_value": "message-from:railiance-cluster",
|
||||
"db_value": "",
|
||||
"fixable": false,
|
||||
"repo_slug": "railiance-platform",
|
||||
"source_revision": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9",
|
||||
"source_revision_reason": null,
|
||||
"owner_return": {
|
||||
"task_record_id": "RPF-WP-0056-T03",
|
||||
"message_ids": [
|
||||
"4e9d4e58-71b3-497b-8954-cedc30740529",
|
||||
"13acb774-8fbe-4ca3-b44d-89a4742138bd",
|
||||
"c9435491-8545-4d48-a57a-1773f2789092",
|
||||
"fb8f25df-57ce-40e5-ae67-0c0b392746e8",
|
||||
"4bb0d7af-b644-44f3-acc8-69c7321e9824",
|
||||
"5301d4fb-0631-4ac5-8471-531e5362b2ba",
|
||||
"2087b9ac-c980-4881-89f1-6b1088132f5b",
|
||||
"78b89fb2-ced6-45f1-9718-0670b8257d45",
|
||||
"110e8dea-ce81-40ff-8161-325ddb89cd67"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-28",
|
||||
"message": "14 unread message(s) lack thread_id for supersession tracking: the-custodian:836a8502, the-custodian:72c6a148, audit-core:0d0da0d2, rapp-postgres:6d047a08, railiance-platform:c4a80a60",
|
||||
"file_path": "",
|
||||
"db_id": "",
|
||||
"file_value": "",
|
||||
"db_value": "",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-26",
|
||||
"message": "Workplan id 'RAIL-PL-WP-0002' uses prefix 'RAIL-PL-WP' but repo canonical prefix is 'RPF-WP' \u2014 new plans must conform",
|
||||
"file_path": "workplans/archived/260702-RAIL-PL-WP-0002-openbao-platform-secrets-service.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAIL-PL-WP-0002",
|
||||
"db_value": "RPF-WP",
|
||||
"fixable": false
|
||||
},
|
||||
{
|
||||
"severity": "WARN",
|
||||
"check_id": "C-26",
|
||||
"message": "Workplan id 'RAIL-PL-WP-0001' uses prefix 'RAIL-PL-WP' but repo canonical prefix is 'RPF-WP' \u2014 new plans must conform",
|
||||
"file_path": "workplans/archived/RAIL-PL-WP-0001-platform-baseline.md",
|
||||
"db_id": "",
|
||||
"file_value": "RAIL-PL-WP-0001",
|
||||
"db_value": "RPF-WP",
|
||||
"fixable": false
|
||||
}
|
||||
],
|
||||
"fixes_applied": [
|
||||
"bindings: synced 64/64",
|
||||
"C-17 cleared: pushed 1 backlogged commit(s)",
|
||||
"push: pushed"
|
||||
],
|
||||
"summary": {
|
||||
"fail": 0,
|
||||
"automation_error": 0,
|
||||
"warn": 35,
|
||||
"info": 20
|
||||
},
|
||||
"result": "warn"
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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])
|
||||
|
|
@ -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)
|
||||
|
|
@ -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.
|
||||
|
|
@ -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": []
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue