fix(secret-rotator): revoke old HA token over a new-token session #321

Merged
k.renner merged 1 commits from fix/ha-revoke-new-session into master 2026-07-21 10:16:12 +00:00
Member

Third and final correctness fix for ha-token mode, found by running the rotation end-to-end against live HA (the previous two — timeouts/floor/orphan in #318, unique-name in #320 — were also found by testing rather than review).

Symptom

The rotation ran correctly — new token minted, stored, old token revoked — but the job logged a false warning:

warning: ... the OLD token 'Shelly Manager' (d0159a29...) REMAINS VALID: connection closed by HA

Checked directly afterward: d0159a29 was already gone from HA. The revoke had succeeded; only the report was wrong.

Root cause

HA binds a WebSocket session to the refresh token behind its access token. The rotator authenticated with the old token and revoked that same token over that same session — so HA tore the session down while processing the delete. The delete committed server-side, but the ack couldn't be read → "connection closed by HA".

Fix

After storing the new token, drop the old session and reconnect with the new token to do the revoke. The session is no longer the one being deleted, so the ack reads cleanly.

Why it's not cosmetic

The false warning fired on every successful rotation. That trains an operator to ignore it — at which point a genuine revoke failure is indistinguishable noise and a real un-revoked full-admin token gets missed. With the fix, a warning means a real problem.

Verified

End-to-end against live HA on a throwaway token: revoke over the new session returns success=True and the old token is confirmed gone — exactly where the old flow got "connection closed". clippy -D warnings clean; 12 tests pass.

Test also exercised the real rotation

While finding this, the fixed binary (unique-name, from #320) rotated the live shelly-manager-llat token correctly: OpenBao updated, shelly-manager restarted onto the new token, 0 HA-auth errors, new token authenticates (state: RUNNING). So the full chain works; this PR only corrects the revoke session so the report matches reality.

🤖 Generated with Claude Code

Third and final correctness fix for `ha-token` mode, found by running the rotation **end-to-end against live HA** (the previous two — timeouts/floor/orphan in #318, unique-name in #320 — were also found by testing rather than review). ## Symptom The rotation ran correctly — new token minted, stored, old token revoked — but the job logged a **false** warning: ``` warning: ... the OLD token 'Shelly Manager' (d0159a29...) REMAINS VALID: connection closed by HA ``` Checked directly afterward: `d0159a29` was **already gone** from HA. The revoke had succeeded; only the report was wrong. ## Root cause HA binds a WebSocket session to the refresh token behind its access token. The rotator authenticated with the **old** token and revoked **that same token** over **that same session** — so HA tore the session down while processing the delete. The delete committed server-side, but the ack couldn't be read → "connection closed by HA". ## Fix After storing the new token, drop the old session and **reconnect with the new token** to do the revoke. The session is no longer the one being deleted, so the ack reads cleanly. ## Why it's not cosmetic The false warning fired on **every** successful rotation. That trains an operator to ignore it — at which point a *genuine* revoke failure is indistinguishable noise and a real un-revoked full-admin token gets missed. With the fix, a warning means a real problem. ## Verified End-to-end against live HA on a **throwaway** token: revoke over the new session returns `success=True` and the old token is confirmed gone — exactly where the old flow got "connection closed". `clippy -D warnings` clean; 12 tests pass. ## Test also exercised the real rotation While finding this, the fixed binary (unique-name, from #320) rotated the live `shelly-manager-llat` token correctly: OpenBao updated, shelly-manager restarted onto the new token, **0** HA-auth errors, new token authenticates (`state: RUNNING`). So the full chain works; this PR only corrects the revoke session so the report matches reality. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-07-21 10:14:57 +00:00
fix(secret-rotator): revoke old HA token over a new-token session
ci/woodpecker/push/build-ci-images Pipeline was successful
2e18d68a94
Found by running the ha-token rotation end-to-end against live HA. The rotation
was fully correct — new token minted, stored, and the old one revoked — but the
job logged a false "OLD token REMAINS VALID" warning.

Root cause: HA binds a WebSocket session to the refresh token behind its access
token. The rotator authenticated with the OLD token and revoked that same token
over that same session, so HA tore the session down as it processed the delete
— the delete succeeded server-side but the ack could not be read, surfacing as
"connection closed by HA". Verified: after the false-warning run, the old
token's refresh id was already gone from HA.

Fix: after storing the new token, drop the old session and reconnect with the
NEW token to perform the revoke. The session is then not the one being deleted,
so the ack reads cleanly and the report is accurate.

This matters beyond cosmetics: the false warning fired on every successful
rotation, which would train an operator to ignore it — and a genuine revoke
failure would then be indistinguishable noise. With the fix, a warning means a
real un-revoked token.

Verified end-to-end against live HA on a throwaway token: revoke over the new
session returns success=True and the old token is confirmed gone (where the old
flow got "connection closed"). clippy -D warnings clean; 12 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit 3f7aab0c56 into master 2026-07-21 10:16:11 +00:00
Sign in to join this conversation.