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.
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)
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>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Third and final correctness fix for
ha-tokenmode, 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:
Checked directly afterward:
d0159a29was 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=Trueand the old token is confirmed gone — exactly where the old flow got "connection closed".clippy -D warningsclean; 12 tests pass.Test also exercised the real rotation
While finding this, the fixed binary (unique-name, from #320) rotated the live
shelly-manager-llattoken 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