Commit graph

72 commits

Author SHA1 Message Date
655d8d31b3 Review oidc-service-client 0.1 against KeyCape's client field rules
Answers net-kingdom's NK-WP-0056-T14 review request (message dc359034).
Field rules confirmed: the rendered record passes keycape check-config,
and the token shape was observed on a local synthetic run (assurance is
a fixed aal1/client_secret block). One correction: an excess scope
returns invalid_profile_usage (HTTP 400, feature scope), not
invalid_scope. Notes on the bind-env restart and its Secret
precondition under Recreate, and on the rotation window.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5502@bnt-lap001
Assistant-Session: 68b3b02f-5d0f-4e71-8421-796f62e50196
2026-10-31 23:02:54 +01:00
e2b5fa1442 KEY-WP-0046: directory bind password from KEYCAPE_LLDAP_BIND_PW
Some checks failed
Authentication acceptance / acceptance (push) Failing after 1m12s
Authentication acceptance / provider-contract (push) Successful in 14s
Build and Publish Container Image / build-and-push (push) Successful in 32s
config.Load uses the variable when lldap.bindPW is empty, accepts equal
values (migration, logged as redundant) and refuses differing values with an
error that names neither. check-config and identity-facts report the source,
never the value. For net-kingdom NK-WP-0056-T04 (ESO-delivered svc-keycape bind).

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 896183@bnt-lap001
Assistant-Session: 3a18c01a-3205-4d02-94db-9efff25649a2
2026-10-31 10:24:22 +01:00
9adfb0bf4d KEY-WP-0044: traceparent correlation task and audit-core intake package (NK-WP-0055-T11)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4515@bnt-lap001
Assistant-Session: 0bf96127-6e04-41be-bfda-d11717c3f354
2026-10-27 23:28:54 +01:00
fc9f09a8c2 Record KeyCape owner acceptance of NK-WP-0049 isolated restore (issuer side)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 4515@bnt-lap001
Assistant-Session: 0bf96127-6e04-41be-bfda-d11717c3f354
2026-10-27 21:40:11 +01:00
3acc86fdef KEY-WP-0043: per-run agent identity assessment and proposed workplan
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 328925@bnt-lap001
Assistant-Session: 14b795fd-c632-4c01-8d28-e7e490c7b0ed
2026-10-24 23:49:33 +02:00
e2c5a94782 Bind online human ID tokens to the exact current access grant
Some checks failed
Authentication acceptance / acceptance (push) Failing after 1m4s
Authentication acceptance / provider-contract (push) Successful in 12s
Build and Publish Container Image / build-and-push (push) Successful in 31s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 12:13:56 +02:00
08c083bae9 Share current-token verification and scoped cleanup through one producer client
Some checks failed
Authentication acceptance / acceptance (push) Failing after 59s
Authentication acceptance / provider-contract (push) Successful in 13s
Build and Publish Container Image / build-and-push (push) Successful in 31s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 11:36:26 +02:00
67a0869c1e Add opt-in issuer-owned access-token status and scoped revocation
All checks were successful
Authentication acceptance / acceptance (push) Successful in 59s
Authentication acceptance / provider-contract (push) Successful in 13s
Build and Publish Container Image / build-and-push (push) Successful in 32s
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 10:10:38 +02:00
834b0d06a1 Review exact offline checker behavior for browser registration
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
2026-10-16 22:08:05 +02:00
59c1df71e3 Record the live openbao-admin signInDiagnosis flag (KEY-WP-0041-T02).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1041694@bnt-lap001
Assistant-Session: 7af0bb68-ac4a-4ce3-8639-24ddd84c0b52
2026-10-15 00:25:23 +02:00
3b8d69546b Record live image cdec9dd7 with the sign-in diagnosis page (KEY-WP-0041-T02).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1041694@bnt-lap001
Assistant-Session: 7af0bb68-ac4a-4ce3-8639-24ddd84c0b52
2026-10-15 00:09:11 +02:00
af3b1b0dfb Add opt-in sign-in diagnosis page and uid/assurance on auth log (NK-WP-0051-T02).
A client registration flag, signInDiagnosis (default off), makes the
callback render uid, sorted groups, assurance and client_id before the
redirect, with one continue link to the unchanged code. assurance is aal2
only when this request verified a one-time code. The auth_success log
line now carries uid and assurance beside client_id. Enabled for
openbao-admin in the dev config; the live flag is documented in
docs/operations.md.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1041694@bnt-lap001
Assistant-Session: 7af0bb68-ac4a-4ce3-8639-24ddd84c0b52
2026-10-14 23:45:11 +02:00
21c664643b Record verified KEY39 source acceptance and remaining execution prerequisites
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 19:35:51 +02:00
0f47e0c666 Publish reviewed bounded JWT ready handoff and retained owner history
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 18:31:04 +02:00
f60e7419a9 Preserve JWT input refusal and bounded successor acknowledgment
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 17:30:10 +02:00
a46024bf3e Publish reviewed KEY39 lifecycle source and preserve failed review history
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 15:33:14 +02:00
aef56bcba1 Bind JWT negative probes to exact claim and cleanup evidence
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 02:21:03 +02:00
c528daa5be Add keycape check-config; reject duplicate clientIds
All checks were successful
Authentication acceptance / acceptance (push) Successful in 1m25s
Authentication acceptance / provider-contract (push) Successful in 25s
Build and Publish Container Image / build-and-push (push) Successful in 43s
net-kingdom asked for a way to validate a rendered configuration before
keycape-client.py restarts KeyCape (oidc-browser-relying-party review R4).
check-config runs every startup check that needs no secret, key or upstream:
validation, registry build, global tokenLifetime, policy-store clients and,
with --env-names, the secretRef bindings. The report carries no values.

ValidateConfig now refuses a clientId registered twice. The registry is a
map, so a duplicate silently replaced the earlier registration.

Also records the owners' amended CCR-2026-0018 sequence in KEY-WP-0013.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 779409@bnt-lap001
Assistant-Session: c658ca06-19b5-468d-be64-2985dc20bfd3
2026-10-10 13:28:36 +02:00
bae9f85160 KEY-WP-0039: probe registration packet and attended negative-probe script
T01 done: railiance-platform agreed the case list. The packet adds an
otherwise-valid expiry client and pins audience on the claim probes so each
token fails only the claim under test. The script prints one refused boolean
and refusal class per case, revokes any accepted token, and never counts a
transport failure as a refusal.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 779409@bnt-lap001
Assistant-Session: c658ca06-19b5-468d-be64-2985dc20bfd3
2026-10-10 13:18:24 +02:00
60384834fe Review oidc-browser-relying-party 1.0 against KeyCape's client field rules
Answers net-kingdom's NK-WP-0048-T04 review request. Two blocking findings:
redirect URIs with '?' pass the type but make KeyCape exit at startup, and
access_groups is enforced only by the consumer, never by KeyCape.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 779409@bnt-lap001
Assistant-Session: c658ca06-19b5-468d-be64-2985dc20bfd3
2026-10-10 12:39:31 +02:00
ab5cccdcf3 KEY-WP-0038: Forgejo custody path derived by case type; propose KEY-WP-0039 negative-probe tokens
Forgejo's secret now lives at platform/workloads/keycape/forgejo, as derived by
oidc-browser-relying-party 1.0 (net-kingdom 074b3a0). KEY-WP-0039 answers
railiance-platform's CCR-2026-0038 question with one-mismatch probe registrations
instead of an issuer path that could mint arbitrary claims.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 779409@bnt-lap001
Assistant-Session: c658ca06-19b5-468d-be64-2985dc20bfd3
2026-10-10 12:29:32 +02:00
05e05aa945 KEY-WP-0038-T01 done: founder accepted the Forgejo client request
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5457@bnt-lap001
Assistant-Session: 792bf84a-3783-4f5d-a811-cded61dd2012
2026-10-08 21:26:37 +02:00
73d97e3323 KEY-WP-0038-T01: draft the Forgejo OIDC client request packet
Non-secret claim contract for founder acceptance and railiance-apps agreement.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 5457@bnt-lap001
Assistant-Session: 792bf84a-3783-4f5d-a811-cded61dd2012
2026-10-08 19:32:00 +02:00
4672ef5390 Correct the in-pod verify-client cause in the T04 receipt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 132360@bnt-lap001
Assistant-Session: 769b1afd-4330-4c86-96b2-e510e26db42b
2026-10-07 13:57:05 +02:00
e0b5faea52 KEY-WP-0037-T04 done: live issuance verified for secrets-engine-openbao
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 132360@bnt-lap001
Assistant-Session: 769b1afd-4330-4c86-96b2-e510e26db42b
2026-10-07 02:26:03 +02:00
7d96ee4a43 Confirm groups and MFA for the secrets-engine-openbao operator reader
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 132360@bnt-lap001
Assistant-Session: 769b1afd-4330-4c86-96b2-e510e26db42b
2026-10-06 14:05:37 +02:00
457854fad5 Record prepared KEY-WP-0037-T03 dependencies and the corrected KV path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 132360@bnt-lap001
Assistant-Session: 769b1afd-4330-4c86-96b2-e510e26db42b
2026-10-06 13:47:31 +02:00
44cc067aa2 Name secrets-engine OpenBao registration and custody owner gates
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1046b-5629-7f52-badd-f718eadb6042
2026-10-04 11:15:44 +02:00
717a97408a Correct platform infrastructure client tenant bindings
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e324-abce-7e51-bb2b-496f097afdb0
2026-09-27 16:43:31 +02:00
df823d3950 Record the sign-out page image.
Assistant: grok
Assistant-Session: 01a0d25d-d358-7e13-b84a-d007fbb7e34f
2026-09-27 00:58:22 +02:00
f16559280c Record the completed fresh login and account switch.
Assistant: grok
Assistant-Session: 01a0d25d-d358-7e13-b84a-d007fbb7e34f
2026-09-25 01:49:42 +02:00
004a72cd5c Record the deferred rotation and its triggers (CUST-WP-0073).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 352750@bnt-lap001
Assistant-Session: de41ef1c-2113-4dd2-9b92-f318ffa7f98b
2026-09-24 01:27:36 +02:00
49565a345b Record the Secret template-dump trap and the 2026-09-23 exposure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 352750@bnt-lap001
Assistant-Session: de41ef1c-2113-4dd2-9b92-f318ffa7f98b
2026-09-23 22:12:04 +02:00
51e541c9a1 Adopt the agent environment orientation for KeyCape's live objects.
Write Secrets with kubectl replace in the rotation script, since apply copies
the data into the last-applied annotation. Record in operations.md the
live-change rules that apply here and the open finding: four live Secrets
carry that annotation, and removing it waits for the founder's go-ahead.

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

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 352750@bnt-lap001
Assistant-Session: de41ef1c-2113-4dd2-9b92-f318ffa7f98b
2026-09-23 22:07:23 +02:00
1620ce2edd Register informed-decision-sitting-requester as create-only.
Some checks failed
Authentication acceptance / acceptance (push) Failing after 1m7s
Authentication acceptance / provider-contract (push) Successful in 15s
Build and Publish Container Image / build-and-push (push) Successful in 40s
Pin tenant:platform, approval:create only, and the KeyCape env name.
Live secret custody remains railiance-platform attended apply.

Assistant: grok
Assistant-Session: 01a0a23b-3bf0-7341-b4e5-9dc05f72573a
2026-09-15 20:08:07 +02:00
b854e1ed0c Fix missing live Informed Decision browser registration
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
2026-09-14 02:54:52 +02:00
651625c547 Register create-only Secrets Engine approval requester
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
2026-09-14 02:47:31 +02:00
e17b3f4631 Record deployed P06 policy and completed platform acceptance
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a09cbb-87c6-7900-a145-4ce53ba9f1a6
2026-09-14 00:10:25 +02:00
502a2d5c56 Retain provider acceptance CI and synchronized assurance task binding
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-13 17:06:32 +02:00
ecca6cb4ba Record verified enrollment assurance release and automate provider contract tests
All checks were successful
Authentication acceptance / acceptance (push) Successful in 1m13s
Authentication acceptance / provider-contract (push) Successful in 28s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-13 17:03:44 +02:00
113f3a6296 Verify reader scope before accepting absence of enrolled factors
All checks were successful
Authentication acceptance / acceptance (push) Successful in 1m6s
Build and Publish Container Image / build-and-push (push) Successful in 53s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-13 16:59:09 +02:00
122a0d1369 Require confirmed enrollment and genuine OTP evidence for MFA
All checks were successful
Authentication acceptance / acceptance (push) Successful in 1m10s
Build and Publish Container Image / build-and-push (push) Successful in 41s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-13 16:37:08 +02:00
78f5a718e9 Record provider credential renewal release and remaining owner handoff
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-13 14:27:35 +02:00
632b1f1376 Support provider credential renewal and reject unsuccessful OTP validation
All checks were successful
Authentication acceptance / acceptance (push) Successful in 1m10s
Build and Publish Container Image / build-and-push (push) Successful in 44s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-13 14:23:24 +02:00
ac8ed65203 Support opt-in MFA per browser client with authoritative enrollment checks
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 40s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-13 00:27:28 +02:00
074c2ce498 Add central login recovery and confirmed shared sign-out
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 44s
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-12 10:34:41 +02:00
c9bb7fac58 Register the approver client now its callback exists
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 47s
informed-decision submitted client_id informed-decision-approver and redirect
https://decisions.coulomb.social/auth/callback, with the origin already live and
verified by them rather than reported: both / and /auth/callback return 200 on a
Let's Encrypt certificate valid to 2026-12-09. The host is decisions, not the
decide of an earlier draft. Its path serves a placeholder for now, which does not
matter -- the redirect is matched as an exact string and never fetched.

Published as a public authorization_code client with S256 PKCE, audience
approval-engine, scopes openid/approval:read/approval:approve, mfaRequired true
and a declared tenant:platform. No secretRef, since PKCE is the whole proof.

TestApproverRegistrationShapeIsExact pins every field, so widening a scope or
relaxing MFA fails the build rather than reading as an edit, and asserts the
registration passes startup validation -- proving the KEY-WP-0028 tenant
exemption holds for the registration that actually depends on it.

Two existing guards fired on the way in and neither was loosened. The tenant pin
refused an unreviewed client carrying a tenant, which is its purpose, so the
approver was added to its reviewed set deliberately. And the audience test
panicked slicing secretRef[4:], an assumption that held while the fixture had
only confidential clients; the approver is the first public one, so the loop now
guards on the env: prefix.

The declared tenant reaches the token by the GH-DEC-2026-013 gap route by
construction, and tenant_source says so: registration, never directory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713576@bnt-lap001
Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
2026-09-10 22:50:13 +02:00
f9812ab3b2 Carry approval-engine's store-isolation limit into the tenant contract
approval-engine chose the registration-bound shape for the consuming side and
asked that the limit making it admissible be carried into the contract rather
than left in a message.

Their check is store isolation -- does this caller belong to the store this
engine serves -- not membership. A registration-supplied tenant is admissible for
that question. It is not admissible for any doctrine turning on the approver's own
membership, which is a fact about the person that this claim cannot carry, and an
exact-match check does not become that claim merely by matching.

That is GH-DEC-2026-013 section 1 reached independently from the consuming side,
and it is the practical reason tenant_source exists: a consumer whose check means
store isolation can accept registration, one whose check means membership must
require directory. Recorded next to the provenance table so the two are read
together.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-10 13:44:02 +02:00
63d646b594 Carry the tenant claim's provenance, and correct a guard the ruling voided
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 44s
GH-DEC-2026-013 §5 is a finding nobody asked for and ours to implement: tenant is
a bare string, so a consumer cannot tell a zone the directory asserted about the
person from one a registration supplied about the client they came through.
approval-engine exact-matches that string while its contract reads as though it
relies on the first -- the check is sound and the property a reader infers from
it is absent. gate-house named the property and left the mechanism to us.

Every token now carries tenant_source beside tenant: directory, registration or
default. Advertised in claims_supported, and asserted at the token level on both
grants rather than only in the resolution function, since the claim a consumer
reads is the thing under obligation.

Three values where the ruling names two, which is the judgement here. Labelling
an unasserted profile default as directory would reproduce the same defect one
level down -- a consumer reading an assertion the identity layer never made. The
ruling cites GH-DEC-2026-011 §3 on unknown versus absent for the case it
examined; the same rule applies to our own fallback. The agreement case resolves
to directory deliberately: if a registration declares the zone the directory also
assigned, the directory did assert it, and reporting the weaker source would
understate what is known.

Also corrects the guard shipped in 5f516a0. Its failure message offered two ways
out of adding dynamic registration, and condition (b) voids the second: admitting
dynamic registration voids the registration-bound shape that day, whatever state
the adapter is in. The message named an inadmissible resolution in the exact
place someone would read it while making that change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 713576@bnt-lap001
Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
2026-09-10 07:58:29 +02:00
d29032d6c4 Record the operator withdrawal and the gate-house tenant conditions
approval-engine answered the presenting-actor question we put to them and the
answer withdrew the client: nobody presents approval-engine-operator. Nothing in
their repository obtains an OAuth token, and scope by scope the bundle never
described a single actor -- approve belongs to the human client, emit is
redundant, and no requester identity was ever settled for create. Annotated as
withdrawn in the example config and the packet rather than deleted, because they
asked for it to stay authenticable under CCR-2026-0018 if a presenter appears.
The separation-of-duties observation is kept because it constrains the
re-request: approve must not travel with the operational scopes.

Raised rather than filed: "do not provision" cannot undo a provision. The client
went live in the 2026-09-09 attended rollout, so the estate now holds a
confidential credential that can create and approve approvals, that nobody
presents, and that no consumer awaits. Standing capability with no counterparty
is a worse resting state than either provisioning it for a named presenter or
disabling it. Disablement is KeyCape's to execute and not KeyCape's to decide
alone, so it goes to the owners rather than into a commit.

Also records GH-DEC-2026-013 conditions (a) and (b) in the tenant contract, both
of which strengthen what we had written about ourselves. Row four is normative,
and in the direction we would not have guessed: a future change preferring the
DIRECTORY is also void, because "the directory won" is still a winner and picking
any winner turns a refusal into a silent cross-tenant assertion. And lifting the
dynamic-registration exclusion now VOIDS the registration-bound shape that day
rather than reopening it for review -- our "must be revisited" was too weak.
The shape is recorded as a declared bounded gap, not the terminal state.

Verification note: the Go suite could not be run green for this commit because a
peer session is mid-edit on token.go and human_tenant_test.go for the §5
provenance work. Every file in this commit is YAML or Markdown; both YAML files
were parsed and the example config's scope set and tenant were asserted unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uV8zoCKpA1WRAxsKRYbdH

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1182213@bnt-lap001
Assistant-Session: 966597b9-ae61-46a4-8b9e-1594ab3ec4ad
2026-09-09 23:30:09 +02:00