Read-only inspection shows KEY-WP-0013-T02's custody gate has moved since yesterday: both approval clients are registered in sso/keycape-config, both secrets exist in the namespace and are wired into the pod, and the image is pinned to the digest T06 published. The live issuer passes every conformance check that needs no credential. That does not establish the claim contract. Subject, tenant, roles, scopes, lifetime and excess-scope denial are what keycape verify-client proves, and it needs the client secret in the environment. Reading either secret is client-side retrieval, which the provisioning packet records as not admitted, so that run is an attended operator action. T02 now waits on the run, not on custody. Also re-verified the KEY-WP-0028 rule against the deployed config, after a peer noted it fails closed at startup with a rollout imminent. The three browser clients there carry neither serviceSubject nor roles; the three service clients carry both legitimately. Kept as errors rather than warnings on that evidence. 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
3.3 KiB
| id | type | title | domain | repo | status | owner | topic_slug | created | updated | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|---|
| KEY-WP-0028 | workplan | Reject service-identity fields that a browser client silently ignores | infotech | key-cape | finished | claude | browser-client-field-validation | 2026-09-09 | 2026-09-09 | 6d0cb4fd-17da-5466-8011-5ade1ac46171 |
serviceSubject and roles are read only on the client_credentials path. On a
browser client they are accepted at startup and then ignored: the subject and
roles come from the directory user. A registration that looks effective and is
not fails later, somewhere else, as a downstream rejection rather than as a
registration defect.
tokenLifetime was already rejected this way for non-service clients, so the
rule exists — it was just incomplete.
Reject the ignored fields
id: KEY-WP-0028-T01
status: done
priority: medium
state_hub_task_id: "e4bce8c7-0e60-55e8-ad32-a3a0c4e46044"
Config validation now rejects serviceSubject and roles on any client that
does not use client_credentials, naming where the value actually comes from.
Nothing in config/dev-config.yaml, the example fixture or the live deployment
sets either field on a browser client, so this breaks no existing configuration —
checked against all three rather than assumed.
Re-verified against sso/keycape-config on 2026-09-09, after a peer raised that
this validation fails closed at startup and a rollout is imminent, so a stray
field would surface as KeyCape failing to boot. The deployed config now holds six
clients: demo-app, netkingdom-bootstrap-console and openbao-admin are
browser clients carrying neither field, while rapp-qonto-client,
secrets-engine-approval and approval-engine-operator carry both legitimately
on the client_credentials path. The rule rejects none of them. Kept as errors
rather than downgraded to warnings on that evidence: a warning for a field that
does nothing is the state this change exists to end.
tenant is deliberately excluded from the rule, and a test pins that. A
browser client may declare a tenant; humanTenant resolves it against the
directory and refuses issuance when the two disagree (KEY-WP-0013-T05). An
earlier version of this change rejected tenant too, which would have made that
feature unusable.
Correction
id: KEY-WP-0028-T02
status: done
priority: medium
state_hub_task_id: "5ab951f8-2f9c-522c-b6ea-7f6d572cd618"
This work started from KEY-WP-0013-T05's blocker paragraph, which described a
human token as unable to carry tenant:platform. That was true when written and
had already been fixed in a concurrent session by the time this task began; the
paragraph was read as current state rather than as a dated record.
Two consequences, both corrected within the session:
- the
tenantrejection was removed before it was committed, and a test now asserts a browser client carryingtenant: tenant:platformvalidates cleanly; - a hub decision (
0145ab57) recording the tenant question as open and unimplemented was withdrawn with a rationale pointing at the implementation. Nothing was built on it.
The lesson worth keeping: in a repository where several sessions work at once, blocker prose ages faster than the code it describes. Re-read the source before acting on a workplan's description of it.