Commit Graph
8 Commits
Author SHA1 Message Date
justinandClaude Opus 5 8ac89c7f82 fix(client): serialise Keycloak token acquisition
Two concurrent tool calls each found the token stale, each POSTed the
password grant, and the appliance rejected one of the simultaneous
grants. That call died with "Keycloak token failed HTTP 401" -- nothing
to do with the operation it was performing. Hit while testing the guard:
two concurrent zerto_guard_before_mutate calls, one came back 401.

ensure_token now double-checks the cache under an asyncio.Lock, so N
concurrent callers produce exactly one token request and the rest reuse
the result. Measured against the lab ZVM with a cold client: 8 concurrent
reads made 8 token POSTs before, 1 after.

The 401-retry path had the same shape and was worse: every in-flight
request that got a 401 set _token = None and re-authed independently, so
one expiry became a thundering herd. Requests now capture a token
generation, and _reauth re-fetches only if nothing else has already
moved past it.

Token fetch also gets a bounded retry for transient failures (5xx,
network) with linear backoff. 401 and 403 are NOT retried: those are the
credentials themselves, and hammering Keycloak can trip its brute-force
lockout on a real service account.

Per the Zerto API lessons, auth failures now name the cause Keycloak
reported instead of a generic hint -- invalid_client means the client_id
is wrong for this appliance (10.x zerto-client, 9.x may be zerto-api),
invalid_grant means the username or password is. That is the first thing
to check and it was previously guesswork.

Also clears the two long-standing lint findings in this file (PIE810,
E501) while it is open. ruff check now passes across src/ and tests/.

pytest 48 passed (7 new, covering single-request concurrency, cache
reuse, both generation branches, no-retry-on-bad-credentials, the
invalid_client message, and transient retry).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_016yVfC5nvZowoLFnEGWhLGn
2026-09-21 15:12:28 -04:00
claude 1d53029038 feat(guard): read the Zerto task, and ask before guarding unknown tools (#6) 2026-09-21 15:07:43 -04:00
claude 5039f7378d feat(catalog): cover Windows guest-mutating tools in the starter list (#5) 2026-09-21 15:06:41 -04:00
claude 90768f17e1 feat(flr): windows paths, recovery-site gate, stable partition reads (#4) 2026-09-21 14:11:09 -04:00
claude 59d1f71617 feat(flr): make FLR session lifecycle visible and reapable (#3) 2026-09-21 13:43:05 -04:00
claude 108e919dcb fix(flr): partition-rooted FLR paths + agent/intent in checkpoint names (#2) 2026-09-21 13:42:36 -04:00
claude 2473d22d2e fix(flr): 10.9 session list + download path (#1)
Co-authored-by: claude <[email protected]>
2026-09-21 12:18:13 -04:00
claude 38ba9c1b50 feat(poc): rewind MCP, skill, and recover-ladder docs
Initial PoC: find_protection, tagged checkpoints, FLR, mutating catalog.
Lab 10.9 status enums (0=Initializing, 1=MeetingSLA). Credentials stay in gitignored config.json.
2026-09-21 12:09:11 -04:00