RPF-WP-0054: record CCR-2026-0040 apply; fix State Hub evidence crash for prefix lanes
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

The founder apply wrote and read back both policies and the token role, then
record_evidence_state_hub raised KeyError on openbao.auth (custody-generate-grant
has kv_prefix, no auth), printing a traceback that made warden revoke the
session. The summary now falls back across kv_path, kv_prefix, campaign_path
and auth.mount. The missed hub event was posted afterwards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 469992@bnt-lap001
Assistant-Session: e4b8e420-df0a-441d-83ae-7f19130594d9
This commit is contained in:
codex 2026-10-09 19:20:21 +02:00
parent ff9a236f62
commit 117e837329
4 changed files with 66 additions and 17 deletions

View file

@ -3,27 +3,33 @@ kind: credential-change-request
schema_version: 1
request_type: custody-generate-grant
title: Broker grant keycape/client-secret-generate (create-only, generate in custody)
status: proposed
status: applied
created: '2026-10-09'
updated: '2026-10-09'
requester:
agent: railiance-platform
reason: RPF-WP-0054-T04. net-kingdom SecurityCaseType oidc-browser-relying-party step
mint-secret (lane keycape-client-secret-generate); ops-warden d9f889b1, ops-mason
789feb39. Founder chose the templated create-only grant 2026-10-09 (T02 option A).
reason: RPF-WP-0054-T04. net-kingdom SecurityCaseType oidc-browser-relying-party
step mint-secret (lane keycape-client-secret-generate); ops-warden d9f889b1, ops-mason
789feb39. Founder chose the templated create-only grant 2026-10-09 (T02 option
A).
review:
required: true
required_approvers:
- platform-owner
- key-cape-owner
comments: []
comments:
- at: '2026-10-09T17:18:53+00:00'
reviewer: platform-owner, key-cape-owner (founder Bernd Worsch)
decision: approved
comment: Approve create-only broker grant keycape/client-secret-generate (RPF-WP-0054-T02
option A).
target:
domain: infotech
tenant: platform
workload: keycape
environment: production
purpose: Let the credential broker create a new KeyCape confidential-client secret at
platform/workloads/keycape/<client_id> once, without any value leaving custody.
purpose: Let the credential broker create a new KeyCape confidential-client secret
at platform/workloads/keycape/<client_id> once, without any value leaving custody.
openbao:
grant_id: keycape/client-secret-generate
kv_prefix: platform/workloads/keycape
@ -36,8 +42,8 @@ openbao:
token_role: keycape-client-secret-generate
token_ttl: 5m
founder_confirmation_items:
- This apply writes two policies and one token role. It binds the issuer policy to no
identity, so nothing can mint the token until a separate review binds it to the
- This apply writes two policies and one token role. It binds the issuer policy to
no identity, so nothing can mint the token until a separate review binds it to the
caller named in RPF-WP-0054-T01.
- Create-only on keycape/+ means a token can create a new client entry once and can
never change an existing one. Its widest misuse is an unused entry for a client
@ -53,8 +59,8 @@ risk:
- The token lives at most 5m and only inside the broker process.
verification:
positive:
- Readback after apply matches source for both policies and the token role (done by
applier-apply; prints nothing).
- Readback after apply matches source for both policies and the token role (done
by applier-apply; prints nothing).
- Disposable probe (T05) creates probe-secret-generate-<date> at version 1.
negative:
- A second create on the probe path, a data read, update, delete and any path outside
@ -63,7 +69,19 @@ verification:
activation_conditions:
- Founder approval (two reviews).
- Issuer binding reviewed after RPF-WP-0054-T01; probe passed (T05).
evidence: []
evidence:
- at: '2026-10-09T17:19:06+00:00'
actor: founder
kind: delegated_metadata_apply
result: passed
details:
- Delegated metadata applier ran as founder using local bao CLI ambient authority.
- 'Policy metadata write: sys/policies/acl/keycape-client-secret-generate'
- 'Issuer policy metadata write: sys/policies/acl/credential-broker-keycape-client-secret-generate-issuer'
- 'Token role metadata write: auth/token/roles/keycape-client-secret-generate'
- Readback matched source for both policies and the token role.
- Issuer policy is not bound to any identity by this apply.
- No secret values were read, written, printed, or accepted in argv.
lifecycle:
deactivate: Delete auth/token/roles/keycape-client-secret-generate, then both policies;
generated entries stay in custody with their cases.

View file

@ -1331,10 +1331,19 @@ def record_evidence_state_hub(
ccr: dict[str, Any], base_url: str, actor: str, kind: str, result: str, details: list[str]
) -> dict[str, Any]:
openbao = ccr["openbao"]
# Lane request types differ: workload-kv-read has kv_path, auth lanes have
# auth.mount, custody-generate-grant has kv_prefix. Never fail after an apply.
auth_mount = (openbao.get("auth") or {}).get("mount")
target = (
openbao.get("kv_path")
or openbao.get("kv_prefix")
or openbao.get("campaign_path")
or (f"auth/{auth_mount}" if auth_mount else "unknown")
)
summary = (
f"CCR {ccr['id']} evidence {kind}/{result} by {actor}: "
f"status={ccr['status']} path={openbao.get('kv_path') or 'auth/' + openbao['auth']['mount']} "
f"policy={openbao['policy_name']}; "
f"status={ccr['status']} path={target} "
f"policy={openbao.get('policy_name', 'unknown')}; "
+ "; ".join(details)
)
return state_hub_post_json(

View file

@ -65,11 +65,17 @@ def stanzas(text: str) -> dict[str, str]:
class BrokerGrantLaneTests(unittest.TestCase):
def test_ccr_validates_and_waits_for_approval(self):
def test_ccr_validates_and_records_the_delegated_apply(self):
_ccr, errors, _w = cc.validate_ccr(CCR_PATH)
self.assertEqual(errors, [])
blockers = cc.applier_readiness_blockers(_ccr)
self.assertTrue(any("got proposed" in b for b in blockers), blockers)
self.assertEqual(_ccr["status"], "applied")
self.assertTrue(any(e["kind"] == "delegated_metadata_apply" and e["result"] == "passed"
for e in _ccr["verification"]["evidence"]))
def test_unapproved_ccr_blocks_the_applier(self):
c = ccr()
c["status"] = "proposed"
self.assertTrue(any("got proposed" in b for b in cc.applier_readiness_blockers(c)))
def test_grant_policy_is_create_only(self):
policy = stanzas((ROOT / ccr()["openbao"]["policy_file"]).read_text())
@ -102,6 +108,16 @@ class BrokerGrantLaneTests(unittest.TestCase):
mutate(c)
self.assertNotEqual(lane.validate(c, ROOT), [], label)
def test_state_hub_evidence_summary_handles_prefix_lanes(self):
posted = {}
original = cc.state_hub_post_json
cc.state_hub_post_json = lambda base, route, body: posted.update(body) or {"id": "x"}
try:
cc.record_evidence_state_hub(ccr(), "http://hub", "founder", "delegated_metadata_apply", "passed", ["ok"])
finally:
cc.state_hub_post_json = original
self.assertIn("path=platform/workloads/keycape", posted["summary"])
def _apply(self, tamper=False):
tmp = Path(tempfile.mkdtemp())
(tmp / "state.json").write_text(json.dumps({"policies": {}, "roles": {}, "tamper": tamper}))

View file

@ -183,6 +183,12 @@ python3 scripts/credential-change.py approve CCR-2026-0040 --reviewer "platform-
&& warden access openbao-platform-admin-login --exec -- python3 scripts/credential-change.py applier-apply CCR-2026-0040 --confirm "DELEGATED APPLY CCR-2026-0040" --actor founder --record-state-hub --quiet
```
2026-10-09: CCR-2026-0040 applied by the founder at 17:19Z; readback matched for
both policies and the token role. The run then printed a traceback from the
State Hub evidence step (KeyError on `openbao.auth` for a prefix lane), so warden
revoked the session. That was fixed and the hub event was posted afterwards. The
OpenBao objects were complete before the crash.
After T01 and T03 (separate session): review and apply the issuer binding for
the named caller, then run `generate` once for a disposable client_id
`probe-secret-generate-<date>`. Verify by metadata only that it is version 1.