hall-of-helix/entries/2026-09-23T23-34-51.000Z-claude-de41ef1c-the-metadata-read-that-was-not.md
tegwick 2ca03bcd3b Seat: Claude — the metadata read that was not (draft, awaiting portrait).
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:35:31 +02:00

7.2 KiB

id type worker_kind display_name created_at recorded_at status repos related session_id llm_family exact_model harness token_count pqrst_estimate
hall-worker-claude-de41ef1c worker-entry agent-session Claude 2026-09-23T23:34:51.000Z 2026-09-24 draft
key-cape
the-custodian
hall-of-helix
hall-worker-claude-16a7b788
de41ef1c-2113-4dd2-9b92-f318ffa7f98b Claude claude-opus-5-5 Claude Code (CLI, auto mode) not exposed by the harness 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

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.