Bind mint broker validation to exact reviewed sources and track successor obligations
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a1468b-63fa-7710-85dc-8f2e16b3ebfa
This commit is contained in:
parent
104ed90f42
commit
d35c0f9d4b
5 changed files with 337 additions and 8 deletions
|
|
@ -137,9 +137,9 @@ grants:
|
|||
requires_human_above: 5m
|
||||
actors:
|
||||
allowed_types:
|
||||
- approved-agent
|
||||
- human-operator
|
||||
required_subject_binding: keycape-or-kubernetes-service-account
|
||||
required_subject_binding: auth/netkingdom/role/credential-broker-keycape-client-secret-generate
|
||||
# CCR-2026-0043: human net-kingdom-admins membership and /assurance/level=aal2.
|
||||
authorization:
|
||||
flex_auth_required: false
|
||||
flex_auth_mode: optional-preflight
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Steps (every one fails closed):
|
|||
the broker issuer policy for this grant.
|
||||
2. Re-derive the case reference from ``--case-file`` (sha256 over its exact
|
||||
bytes) and require: it equals ``--case-ref``; type
|
||||
``oidc-browser-relying-party@1.1``; status ``materialized``; client_type
|
||||
an exactly source-pinned acknowledged browser revision; status ``materialized``; client_type
|
||||
``confidential``; ``parameters.client_id`` equals ``--client-id``, which
|
||||
matches the type's pattern and is not a reserved entry.
|
||||
3. Mint a 5-minute grant token through token role
|
||||
|
|
@ -46,6 +46,7 @@ import re
|
|||
import secrets
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.request
|
||||
|
||||
import yaml
|
||||
|
|
@ -57,7 +58,39 @@ ISSUER_POLICY = 'credential-broker-keycape-client-secret-generate-issuer'
|
|||
PREFIX = 'workloads/keycape'
|
||||
RESERVED = {'secrets-engine-openbao'}
|
||||
FIELD = 'CLIENT_SECRET'
|
||||
CASE_TYPE = 'oidc-browser-relying-party@1.1'
|
||||
# Owner configuration, never a caller-supplied root or a version search/fallback.
|
||||
# The sibling source checkout must contain these exact reviewed bytes.
|
||||
NETKINGDOM_ROOT = Path(__file__).resolve().parents[2] / 'net-kingdom'
|
||||
ACKNOWLEDGED_TYPES = {
|
||||
'oidc-browser-relying-party@1.1': (
|
||||
'security-case-types/oidc-browser-relying-party/type.yaml',
|
||||
'f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c'),
|
||||
}
|
||||
# Same authenticated producer and parameter projection as ops-warden case_inputs
|
||||
# at b593c879, restricted to this broker's currently admitted 1.1. Source-only
|
||||
# acknowledgment (including 1.2) never expands broker admission. A future 1.3
|
||||
# entry requires real acknowledgment and a separately reviewed admission binding.
|
||||
# Neither source status nor caller-supplied YAML expands this set.
|
||||
SOURCE_PINS = {
|
||||
'tools/security-case-types/security_case_validator.py':
|
||||
'1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b',
|
||||
'tools/security-scenario-composer/security_scenario_composer.py':
|
||||
'9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f',
|
||||
'tools/playbook-capability-contract/playbook_contract_validator.py':
|
||||
'2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d',
|
||||
}
|
||||
PARAMETER_PINS = {
|
||||
'1.1': '9ec2d6113bf70dbd35cfc247412363ff187e792bc82e24159a6f78e090aa3e4c',
|
||||
}
|
||||
_VALIDATE = """
|
||||
import json, sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, str(Path(sys.argv[1]) / 'tools/security-case-types'))
|
||||
import security_case_validator as v
|
||||
case, typ = json.load(sys.stdin)
|
||||
issues = v.validate_type(typ) + v.validate_case(case, typ, require_implemented=False)
|
||||
print('valid' if not v.has_errors(issues) else 'invalid')
|
||||
"""
|
||||
# oidc-browser-relying-party@1.1 parameter client_id.
|
||||
CLIENT_ID_RE = re.compile(r'^[a-z0-9][a-z0-9-]{1,62}$')
|
||||
CASE_REF_RE = re.compile(r'^[a-z0-9][a-z0-9-]*@sha256:[0-9a-f]{64}$')
|
||||
|
|
@ -95,6 +128,53 @@ def read_json(result, code):
|
|||
raise Exit(code, 'unparseable_bao_output') from None
|
||||
|
||||
|
||||
def authenticated_source(relative, expected):
|
||||
path = NETKINGDOM_ROOT / relative
|
||||
# Refuse symlinks at every selected component, including the configured root.
|
||||
if any(part.is_symlink() for part in (path, *path.parents)):
|
||||
raise ValueError
|
||||
raw = path.read_bytes()
|
||||
if hashlib.sha256(raw).hexdigest() != expected:
|
||||
raise ValueError
|
||||
return raw
|
||||
|
||||
|
||||
def check_acknowledged_case(doc):
|
||||
# Authenticates the selected TYPE/producer bytes and validates case schema.
|
||||
# The caller's self-hashed case reference is not authenticated case admission
|
||||
# or owner acceptance. Those require independent Warden/native evidence;
|
||||
# this source-only hardening does not complete the real admission binding.
|
||||
binding = ACKNOWLEDGED_TYPES.get(doc['spec'].get('type'))
|
||||
if binding is None:
|
||||
raise Exit(2, 'case_type_unacknowledged')
|
||||
try:
|
||||
typ = yaml.safe_load(authenticated_source(*binding))
|
||||
spec = typ['spec']
|
||||
version = str(spec['version'])
|
||||
fingerprint = hashlib.sha256(json.dumps(
|
||||
spec['parameters'], sort_keys=True, separators=(',', ':')).encode()).hexdigest()
|
||||
if (spec['status'] not in ('acknowledged', 'implemented')
|
||||
or f"{spec['id']}@{version}" != doc['spec']['type']
|
||||
or PARAMETER_PINS.get(version) != fingerprint):
|
||||
raise ValueError
|
||||
with tempfile.TemporaryDirectory(prefix='platform-case-validator-') as directory:
|
||||
for relative, expected in SOURCE_PINS.items():
|
||||
raw = authenticated_source(relative, expected)
|
||||
dest = Path(directory) / relative
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
dest.write_bytes(raw)
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-I', '-c', _VALIDATE, directory],
|
||||
input=json.dumps([doc, typ]), text=True,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
|
||||
env={}, timeout=10, check=True)
|
||||
if result.stdout != 'valid\n':
|
||||
raise ValueError
|
||||
except (OSError, ValueError, TypeError, KeyError, yaml.YAMLError,
|
||||
subprocess.SubprocessError):
|
||||
raise Exit(2, 'case_source_validation_failed') from None
|
||||
|
||||
|
||||
def check_case(args, receipt):
|
||||
if not CLIENT_ID_RE.match(args.client_id) or args.client_id in RESERVED:
|
||||
raise Exit(2, 'client_id_invalid_or_reserved')
|
||||
|
|
@ -112,14 +192,15 @@ def check_case(args, receipt):
|
|||
derived = f"{spec.get('id')}@sha256:{hashlib.sha256(raw).hexdigest()}"
|
||||
if derived != args.case_ref:
|
||||
raise Exit(2, 'case_ref_mismatch')
|
||||
if spec.get('type') != CASE_TYPE:
|
||||
raise Exit(2, 'case_type_mismatch')
|
||||
if not isinstance(spec.get('type'), str):
|
||||
raise Exit(2, 'case_type_unacknowledged')
|
||||
if spec.get('status') != 'materialized':
|
||||
raise Exit(2, 'case_not_materialized')
|
||||
if params.get('client_type') != 'confidential':
|
||||
raise Exit(2, 'case_client_not_confidential')
|
||||
if params.get('client_id') != args.client_id:
|
||||
raise Exit(2, 'case_client_id_mismatch')
|
||||
check_acknowledged_case(doc)
|
||||
receipt['case_verified'] = True
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,20 @@ class CredentialGrantBindTests(unittest.TestCase):
|
|||
grants.validate_grant(grant, index, catalog, errors)
|
||||
self.assertEqual(errors, [])
|
||||
|
||||
def test_keycape_generator_actor_matches_applied_issuer_binding(self) -> None:
|
||||
catalog = grants.yaml.safe_load(
|
||||
(REPO_DIR / 'credential-grants/catalog.yaml').read_text())
|
||||
grant = next(g for g in catalog['grants'] if g['id'] == 'keycape/client-secret-generate')
|
||||
ccr = grants.yaml.safe_load((REPO_DIR / 'credential-change-requests' /
|
||||
'CCR-2026-0043-keycape-client-secret-generate-issuer-binding.yaml').read_text())
|
||||
role = grants.yaml.safe_load((REPO_DIR / ccr['openbao']['auth']['role_file']).read_text())
|
||||
auth = ccr['openbao']['auth']
|
||||
self.assertEqual(grant['actors']['allowed_types'], ['human-operator'])
|
||||
self.assertEqual(grant['actors']['required_subject_binding'],
|
||||
f"auth/{auth['mount']}/role/{auth['role']}")
|
||||
self.assertEqual(role['bound_claims'],
|
||||
{'groups': ['net-kingdom-admins'], '/assurance/level': 'aal2'})
|
||||
|
||||
def test_postgres_grants_bind_rapp(self) -> None:
|
||||
catalog = grants.yaml.safe_load(
|
||||
(REPO_DIR / "credential-grants/catalog.yaml").read_text()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@ token each call used: KV calls must use the minted grant token, never the
|
|||
caller's session.
|
||||
"""
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import shutil
|
||||
from unittest import mock
|
||||
|
||||
import yaml
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
|
@ -75,7 +80,9 @@ class Generate(unittest.TestCase):
|
|||
def run_cli(self, *extra, client_id='forgejo', ref=None, env_extra=None):
|
||||
(self.tmp / 'state.json').write_text(json.dumps(self.state))
|
||||
receipt = self.tmp / f'r{len(list(self.tmp.glob("r*.json")))}.json'
|
||||
env = {k: v for k, v in os.environ.items() if k not in ('BAO_TOKEN', 'VAULT_TOKEN', 'WARDEN_AGENT_ID')}
|
||||
empty_home = self.tmp / 'empty-home'
|
||||
empty_home.mkdir(exist_ok=True)
|
||||
env = {'PATH': os.defpath, 'HOME': str(empty_home)}
|
||||
env.update(PATH=f'{self.tmp / "bin"}:{env["PATH"]}', FAKE_BAO_STATE=str(self.tmp / 'state.json'),
|
||||
**(env_extra or {}))
|
||||
proc = subprocess.run([sys.executable, str(CLI), 'generate', '--grant', 'keycape/client-secret-generate',
|
||||
|
|
@ -149,15 +156,124 @@ class Generate(unittest.TestCase):
|
|||
def test_only_exact_acknowledged_type_version_is_admitted(self):
|
||||
for type_ref in ('oidc-browser-relying-party@1.0',
|
||||
'oidc-browser-relying-party@1.2',
|
||||
'oidc-browser-relying-party@1.3',
|
||||
'oidc-browser-relying-party@99.0',
|
||||
'confidential-service-client@1.1'):
|
||||
with self.subTest(type_ref=type_ref):
|
||||
self.write_case(CASE.replace('oidc-browser-relying-party@1.1', type_ref))
|
||||
self.state['calls'] = []
|
||||
code, receipt = self.run_cli()
|
||||
self.assertEqual((code, receipt['outcome']), (2, 'case_type_mismatch'))
|
||||
self.assertEqual((code, receipt['outcome']), (2, 'case_type_unacknowledged'))
|
||||
self.assertEqual(self.state['calls'], [])
|
||||
|
||||
def test_source_acknowledgment_alone_does_not_admit_12(self):
|
||||
self.write_case(CASE.replace('oidc-browser-relying-party@1.1',
|
||||
'oidc-browser-relying-party@1.2')
|
||||
+ ' consumer_namespace: forgejo\n')
|
||||
code, receipt = self.run_cli()
|
||||
self.assertEqual((code, receipt['outcome']), (2, 'case_type_unacknowledged'))
|
||||
self.assertEqual(self.state['calls'], [])
|
||||
|
||||
def test_caller_environment_cannot_select_type_or_source_root(self):
|
||||
code, receipt = self.run_cli(env_extra={
|
||||
'NETKINGDOM_ROOT': '/nonexistent',
|
||||
'WARDEN_NETKINGDOM_ROOT': '/nonexistent',
|
||||
'PYTHONPATH': '/nonexistent',
|
||||
})
|
||||
self.assertEqual((code, receipt['outcome']), (0, 'generated'))
|
||||
|
||||
def test_acknowledged_11_still_requires_its_exact_parameters(self):
|
||||
self.write_case(CASE + ' unexpected_target: forgejo\n')
|
||||
code, receipt = self.run_cli()
|
||||
self.assertEqual((code, receipt['outcome']), (2, 'case_source_validation_failed'))
|
||||
self.assertEqual(self.state['calls'], [])
|
||||
|
||||
def test_future_revision_requires_explicit_owner_binding_and_acknowledgment(self):
|
||||
# Synthetic owner configuration exercises the extension mechanism only.
|
||||
# This is not the actual proposed 1.3 type, acknowledgment or parameter pin.
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
'future_fixture_generator', ROOT / 'scripts/keycape_client_secret_generate.py')
|
||||
broker = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(broker)
|
||||
source = self.tmp / 'synthetic-owner-root'
|
||||
source.mkdir()
|
||||
relative, digest = broker.ACKNOWLEDGED_TYPES['oidc-browser-relying-party@1.1']
|
||||
for rel in (relative, *broker.SOURCE_PINS):
|
||||
dest = source / rel
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(broker.NETKINGDOM_ROOT / rel, dest)
|
||||
typ = source / relative
|
||||
original = typ.read_bytes()
|
||||
doc = yaml.safe_load(CASE)
|
||||
doc['spec']['type'] = 'oidc-browser-relying-party@1.3'
|
||||
typ.write_bytes(original.replace(b'version: "1.1"', b'version: "1.3"'))
|
||||
# YAML numeric spelling is owner-authored, never an inferred revision.
|
||||
type_doc = yaml.safe_load(original)
|
||||
type_doc['spec']['version'] = '1.3'
|
||||
typ.write_text(yaml.safe_dump(type_doc))
|
||||
binding = {doc['spec']['type']: (relative, hashlib.sha256(typ.read_bytes()).hexdigest())}
|
||||
with mock.patch.object(broker, 'NETKINGDOM_ROOT', source):
|
||||
with self.assertRaises(broker.Exit):
|
||||
broker.check_acknowledged_case(doc) # no explicit binding
|
||||
with mock.patch.dict(broker.ACKNOWLEDGED_TYPES, binding):
|
||||
with self.assertRaises(broker.Exit):
|
||||
broker.check_acknowledged_case(doc) # no parameter admission
|
||||
with mock.patch.dict(broker.PARAMETER_PINS, {'1.3': broker.PARAMETER_PINS['1.1']}):
|
||||
broker.check_acknowledged_case(doc)
|
||||
type_doc['spec']['status'] = 'proposed'
|
||||
typ.write_text(yaml.safe_dump(type_doc))
|
||||
proposed = {doc['spec']['type']: (relative, hashlib.sha256(typ.read_bytes()).hexdigest())}
|
||||
with mock.patch.dict(broker.ACKNOWLEDGED_TYPES, proposed):
|
||||
with self.assertRaises(broker.Exit) as caught:
|
||||
broker.check_acknowledged_case(doc)
|
||||
self.assertEqual(caught.exception.reason, 'case_source_validation_failed')
|
||||
|
||||
def test_wrong_sources_proposed_unknown_versions_and_symlinks_fail_closed(self):
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
'candidate_generator', ROOT / 'scripts/keycape_client_secret_generate.py')
|
||||
broker = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(broker)
|
||||
source = self.tmp / 'net-kingdom'
|
||||
source.mkdir()
|
||||
relative, digest = broker.ACKNOWLEDGED_TYPES['oidc-browser-relying-party@1.1']
|
||||
for rel in (relative, *broker.SOURCE_PINS):
|
||||
dest = source / rel
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(broker.NETKINGDOM_ROOT / rel, dest)
|
||||
doc = yaml.safe_load(CASE)
|
||||
with mock.patch.object(broker, 'NETKINGDOM_ROOT', source):
|
||||
broker.check_acknowledged_case(doc)
|
||||
producer = source / next(iter(broker.SOURCE_PINS))
|
||||
raw = producer.read_bytes()
|
||||
producer.write_bytes(raw + b'\n# drift\n')
|
||||
with self.assertRaises(broker.Exit) as caught:
|
||||
broker.check_acknowledged_case(doc)
|
||||
self.assertEqual(caught.exception.reason, 'case_source_validation_failed')
|
||||
producer.write_bytes(raw)
|
||||
typ = source / relative
|
||||
raw = typ.read_bytes()
|
||||
typ.write_bytes(raw.replace(b'status: acknowledged', b'status: proposed'))
|
||||
# Even a hypothetical owner pin update cannot admit proposed status.
|
||||
with mock.patch.dict(broker.ACKNOWLEDGED_TYPES, {
|
||||
doc['spec']['type']: (relative, hashlib.sha256(typ.read_bytes()).hexdigest())}):
|
||||
with self.assertRaises(broker.Exit) as caught:
|
||||
broker.check_acknowledged_case(doc)
|
||||
self.assertEqual(caught.exception.reason, 'case_source_validation_failed')
|
||||
typ.write_bytes(raw + b'\n# source drift\n')
|
||||
with self.assertRaises(broker.Exit) as caught:
|
||||
broker.check_acknowledged_case(doc)
|
||||
self.assertEqual(caught.exception.reason, 'case_source_validation_failed')
|
||||
typ.unlink()
|
||||
typ.symlink_to(broker.__file__)
|
||||
with self.assertRaises(broker.Exit) as caught:
|
||||
broker.check_acknowledged_case(doc)
|
||||
self.assertEqual(caught.exception.reason, 'case_source_validation_failed')
|
||||
for ref in ('oidc-browser-relying-party@1.3', 'oidc-browser-relying-party@99.0'):
|
||||
doc['spec']['type'] = ref
|
||||
with self.assertRaises(broker.Exit) as caught:
|
||||
broker.check_acknowledged_case(doc)
|
||||
self.assertEqual(caught.exception.reason, 'case_type_unacknowledged')
|
||||
|
||||
def test_exact_accepted_case_still_requires_materialization(self):
|
||||
self.write_case(CASE.replace('status: materialized', 'status: accepted'))
|
||||
code, receipt = self.run_cli()
|
||||
|
|
|
|||
118
workplans/RPF-WP-0060-browser-case-platform-bindings.md
Normal file
118
workplans/RPF-WP-0060-browser-case-platform-bindings.md
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
id: RPF-WP-0060
|
||||
type: workplan
|
||||
title: "Implement Platform bindings for the browser SecurityCase successor"
|
||||
domain: financials
|
||||
repo: railiance-platform
|
||||
status: active
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-17"
|
||||
updated: "2026-10-17"
|
||||
flavor: implementation
|
||||
origin: cross-repo
|
||||
origin_ref: NK-WP-0048-T16
|
||||
state_hub_workstream_id: "43110f82-ef9f-5a46-a305-f0e287b7b226"
|
||||
---
|
||||
|
||||
Demand: Platform's source review of the proposed browser SecurityCase 1.3
|
||||
requires four concrete owner obligations before owner-descriptor acceptance.
|
||||
The review labels `RPF-1.3-T1` through `T4` were not live work records; this
|
||||
workplan makes them ordinary owner tasks without changing RPF-WP-0054's
|
||||
completed first-generation lane or implementing its residual rotation work.
|
||||
|
||||
Review: `the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-platform.json`.
|
||||
The 1.3 type remains proposed. This workplan grants no native access, case
|
||||
acceptance, owner acknowledgment, catalog activation or policy change.
|
||||
|
||||
## T01 — Bind the broker to reviewed acknowledged type sources
|
||||
|
||||
```task
|
||||
id: RPF-WP-0060-T01
|
||||
status: progress
|
||||
priority: high
|
||||
state_hub_task_id: "afec2053-1995-506d-a112-cda1de552c1c"
|
||||
```
|
||||
|
||||
Implements review obligation RPF-1.3-T1. The broker's explicit source map admits
|
||||
only its existing 1.1 revision. Exact type and producer bytes, parameter
|
||||
fingerprint, acknowledgment state and case schema are checked before any Bao
|
||||
call. Source-only 1.2 and proposed 1.3 remain refused. The fixed installation
|
||||
layout is `railiance-platform` and `net-kingdom` as real sibling directories;
|
||||
missing, symlinked or drifted selected sources refuse rather than fall back.
|
||||
|
||||
2026-10-17: source hardening and catalog regressions independently reviewed in
|
||||
`the-custodian/docs/changes/netkingdom-target-2026-10-17/platform-broker-review-r1/`.
|
||||
The exact case reference binds caller-supplied bytes; schema validation does
|
||||
not authenticate case acceptance. Existing Warden admission/native evidence
|
||||
must establish that independently. This task remains open for the real binding
|
||||
and explicit 1.3 source/parameter pins after genuine acknowledgment and review.
|
||||
No speculative successor pin or acknowledgment is added.
|
||||
|
||||
Done when the actual admitted-case binding is verified, the genuinely
|
||||
acknowledged successor is explicitly selected, and refusal tests cover unknown,
|
||||
unacknowledged, wrong-case and drifted-source inputs before native effects.
|
||||
|
||||
## T02 — Correct the documented mint caller and issuer binding
|
||||
|
||||
```task
|
||||
id: RPF-WP-0060-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "1a764c10-3a41-5eff-af96-c9e33a6e2c8b"
|
||||
```
|
||||
|
||||
Implements review obligation RPF-1.3-T2. `credential-grants/catalog.yaml` now
|
||||
names human-operator only and the exact existing issuer role
|
||||
`auth/netkingdom/role/credential-broker-keycape-client-secret-generate`.
|
||||
Regression checks bind this statement to CCR-2026-0043's role source,
|
||||
net-kingdom-admins membership and aal2 claim. No OpenBao role, policy, grant,
|
||||
credential or live configuration changes.
|
||||
|
||||
## T03 — Implement mint, guarded destruction and authenticated evidence
|
||||
|
||||
```task
|
||||
id: RPF-WP-0060-T03
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "b5d73ec6-c593-50a9-8367-f899edec6f5f"
|
||||
```
|
||||
|
||||
Implements review obligation RPF-1.3-T3. Bind the actual generator to the closed
|
||||
`railiance-platform.custody-mint.v1` projection and authenticated publication.
|
||||
Capture grant accessor and revocation evidence and revoke the issuer session.
|
||||
Destruction uses attended platform-admin authority and compares the original
|
||||
CAS-0 version-1 creation time, current version and absence of other versions
|
||||
before metadata deletion. No standing family-wide delete grant is introduced.
|
||||
Unavailable authority, uncertain effects or failed revocation remain UNCERTAIN
|
||||
for owner reconciliation. Verify with the frozen adapter, then obtain the
|
||||
required native authority and actual execution receipts.
|
||||
|
||||
## T04 — Implement and admit the delivery lane and capability
|
||||
|
||||
```task
|
||||
id: RPF-WP-0060-T04
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "84f8b0a4-8450-5623-8608-dc277978383b"
|
||||
```
|
||||
|
||||
Implements review obligation RPF-1.3-T4. Reuse the fixed proposed issuer Store
|
||||
contract; validate the preexisting Store and only create the case-derived
|
||||
ExternalSecret in `sso`. Refuse conflicts; report NO_CHANGE only for matching
|
||||
preexisting configuration; establish current Ready status before success.
|
||||
Return the closed metadata-only `netkingdom.deliver-secret.v1` receipt and use
|
||||
UID and resourceVersion preconditions to remove only authenticated case-created
|
||||
resources. Never read Secret values or create the Store through this lane.
|
||||
|
||||
Bind real human net-kingdom-admins/aal2 admission, per-attempt session and
|
||||
revocation, durable pre-intent/recovery journal and authenticated owner evidence.
|
||||
The separate command-mechanics candidate is preparation, not this capability;
|
||||
its production factory refuses. Review freshness/defaulting/transport failure
|
||||
cases before integrating it. Obtain ops-warden's catalog routing acceptance for
|
||||
`platform-secret-delivery` through an authorized handoff before recording owner
|
||||
acceptance. No routing handoff or native action is implied by this work record.
|
||||
|
||||
The parent stays active until these obligations and their required owner
|
||||
acceptance are proved. Source tests do not complete native acceptance or
|
||||
KEY-WP-0039's negative/expiry/cleanup exercise.
|
||||
Loading…
Add table
Add a link
Reference in a new issue