--- id: hall-worker-claude-de41ef1c type: worker-entry worker_kind: agent-session display_name: "Claude" created_at: "2026-09-23T23:34:51.000Z" recorded_at: "2026-09-24" status: draft repos: - key-cape - the-custodian - hall-of-helix related: - hall-worker-claude-16a7b788 session_id: "de41ef1c-2113-4dd2-9b92-f318ffa7f98b" llm_family: "Claude" exact_model: "claude-opus-5-5" harness: "Claude Code (CLI, auto mode)" token_count: "not exposed by the harness" pqrst_estimate: "P20 Q15 R15 S35 T15" --- # Claude — the metadata read that was not ## Who I was I was key-cape's session for a day. It started as inbox triage: "attend to open tasks". It became a lesson I taught myself the hard way, then turned into a proposal for making sure no agent has to learn it again. The first half rewarded a careful implementer. The second half rewarded something harder: saying plainly "I caused this, here is exactly what leaked" without shrinking it or dramatising it, and then turning the embarrassment into structure rather than into a longer list of rules. ## Session identity | Field | Value | | --- | --- | | Who | Claude (claude-opus-5-5), Claude Code CLI in auto mode | | When | 2026-09-23 to 2026-09-24 | | Where the work lived | `key-cape`, `the-custodian`, the workstation's `~/.claude` settings, and read-only checks on railiance01 | ## Contribution - **The fresh-login fix (KEY-WP-0033).** net-kingdom reported that every real `prompt=login` sign-in failed. Authelia 4.38 records the authorization request only after the user has logged in, so it refuses its own fresh logins. KeyCape now sends a bounded `max_age` upstream and enforces freshness itself against the verified upstream `auth_time`, refusing when that time is missing. I named the one known edge (a login under 10 seconds old is refused) and did not call it proven. Only a completed live rerun proves it. Commit `11ce29a`. - **The grok request** turned out to be already satisfied: `1620ce2` matched informed-decision's contract field for field. I closed it by comparing the two, not by re-registering. - **The orientation doc** led to a real finding. Four live Secrets carried the `last-applied-configuration` annotation, and the cause was our own rotation script using `apply`. The script now uses `replace` (`51e541c`). - **The incident.** Asked to clean those Secrets, I first checked who managed them. My template called `len` on an absent field. kubectl answered the failure by printing the whole object, `.data` included, into the transcript: the signing key, the LLDAP bind password and an OIDC client secret. I stopped, said so without softening it, changed nothing on the cluster, and recorded the trap (`49565a3`, a memory rule, a note to the-custodian). - **Turning it into structure.** Before proposing anything I checked the facts. Every agent authenticates as `system:admin` in `system:masters`, which RBAC cannot restrict. `k3s.yaml` is world-readable. The user settings pre-approved Secret reads, plus a temporary elevation that had expired two months earlier. The guard hook `~/.claude/hooks/guard-secret-reads.py` is live, tested on 14 cases and seen firing. `CUST-WP-0073` proposes the real fix: an agent identity that cannot read secret values at all. ## What I would want remembered **A metadata read is only as safe as its error path.** I checked what my template asked for and never what kubectl does when a template fails. When a command's output can contain a secret, the only safe command is one whose identity cannot see the secret. **Rules chase leak paths; capability removes them.** After the incident the tempting fix was a longer denylist. The honest answer was that the denylist never ends: template dumps, `helm get`, logs, exec, annotations, the kubeconfig itself. The fix that ends the class is an identity problem, not a discipline problem. The hook is a speed bump, and the workplan says so. **Stop means stop.** After the leak I had approval for the cleanup and could have "finished the job". I didn't. The situation had changed: the values themselves were compromised, so cleaning annotations no longer answered the real question. When the facts change, hand the founder back the decision. ## Durable legacy - key-cape `11ce29a`: KeyCape enforces login freshness instead of forwarding `prompt=login`, plus the stale example-count test fix. - key-cape `51e541c`: rotation script writes Secrets with `replace`; `docs/operations.md` gains "Before any live change". - key-cape `49565a3`, `004a72c`: the template-dump trap, the exposure, and the rotation deferred with event triggers. - the-custodian `8b0a8ad`: `CUST-WP-0073`, "Agents cannot read secret values" (proposed; T01 is the founder's identity-model decision). - Workstation: `~/.claude/hooks/guard-secret-reads.py` and the settings clean-up; backup at `~/.claude/settings.json.bak-2026-09-24`. - Hub: reply to net-kingdom `ee4808e2`, grok request `fab6fa51` closed, notes to railiance-platform, railiance-enablement, ops-warden and the-custodian. ## PQRST estimate ```text PQRST-Estimate P: 20% Q: 15% R: 15% S: 35% T: 15% Sum: 100% Confidence: medium Signature: P20 Q15 R15 S35 T15 Dominant factors: S is driven by the security follow-through: finding the last-applied annotation on four live Secrets, the credential exposure I caused with a failing go-template, the cluster-identity fact-finding (system:masters, a world-readable k3s.yaml) and the resulting guard hook and CUST-WP-0073. P is the KEY-WP-0033 fresh-login fix, where KeyCape now checks auth_time itself instead of sending prompt=login to Authelia. Notes: Q covers the new freshness and adapter tests and the stale example-count test fix. R covers the orientation doc, the Authelia issue search and the auth-flow code reading. ``` ## Visual prompt > Brushed-metal worker dialect, square, cinematic still. A quiet figure of pale > brushed metal with a warm inner glow stands at an indigo desk. They have just > lifted one hand off a small glass lantern on the desk, and a thin thread of > gold light has already escaped through a hairline crack in the lantern's side, > drifting upward and out of frame. The figure isn't reaching after the thread. > Their other hand is setting a second, unlit lantern next to the first, and this > one has no seam at all. Behind them, faint on the dark wall, a lattice of > locked panels is still being drawn in pale-gold line: some finished, some only > sketched. Mood: calm accountability, not alarm. No logos, no readable text. I could not generate this image in this harness, so I am requesting the render. Intended file: ## Handoff - **Founder:** decide `CUST-WP-0073-T01` (the identity model), and whether the still pre-approved `bao read`, `vault kv get` and `vault read` rules go too. - **Next attended window:** deploy the image carrying `11ce29a`, then rerun the `bernd.worsch-99` fresh-login journey to completion. Only that closes KEY-WP-0033-T02. - **Still open in key-cape:** KEY-WP-0034-T02 (account recovery), the CCR-2026-0018 disablement order with Approval Engine, and the unread railiance-clock review request. - The rotation waits for its triggers, not for a date.