feat: Phase 1 hardening — Keychain token store + HTTP retry - #8
Merged
Merged
Conversation
- Token store is now host-keyed and macOS Keychain-backed (via the `security` CLI), falling back to the 0600 credentials file on non-macOS or when Keychain is unavailable. readToken precedence: CT_LOGINTOKEN env -> Keychain -> file. - New `src/api/http.ts`: bounded retry with exponential backoff + jitter, honouring `Retry-After`. Only idempotent GET/HEAD retry on 5xx/network; writes retry solely on 429 (server rejected before processing) — never blindly repeat a possibly-applied write. Wired into every CtClient call. - Defer the typed client: hand-written client stays (generated schema.d.ts is 8.2 MB; kept gitignored, generate on demand). - Tests: +7 (retry matrix, Retry-After, write-safety, env precedence) = 15. Refs #3
Address PR #8 review findings: - Token store: drop the file fallback and host-keying entirely. A single token now lives in the macOS Keychain (fixed account); CI/non-mac hosts use CT_LOGINTOKEN. Removes the bug where the non-host-keyed file fallback could return or overwrite another host's token. - http: validate and clamp Retry-After. Cap any single wait at 60s (an outsized value no longer hangs the CLI), reject negative/malformed values (they fell through to a 0ms immediate retry), and honour the HTTP-date form. - http: drain the body of discarded intermediate responses before retrying. - Tests: cover Retry-After cap + malformed fallback; update token-store test.
This was referenced Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finishes the Phase 1 foundation (#3): secure token storage and a resilient HTTP layer. Client stays hand-written (typed-client swap deferred — the generated schema is 8.2 MB).
What's in it
src/auth/tokenStore.ts): a single login token stored via thesecurityCLI (servicect-cli, accountlogin-token). No file fallback — on CI or non-macOS hosts, supply the token throughCT_LOGINTOKEN. Read precedence:CT_LOGINTOKENenv → Keychain.src/api/http.ts): exponential backoff + jitter, honoursRetry-After(delta-seconds and HTTP-date forms), with every wait clamped to ≤ 60 s so an outsized value can't hang the CLI. Write-safety: only idempotent GET/HEAD retry on 5xx/network errors; writes retry only on 429 (rejected before processing) so a possibly-applied write is never repeated. Discarded intermediate responses are drained before retrying. Wired into allCtClientcalls.Tests
19 passing: retry matrix (429, 5xx idempotent vs write, network error, budget exhaustion),
Retry-Afterduration + cap + malformed fallback, andCT_LOGINTOKENprecedence.Verification
npm run typecheck·npm run lint·npm test·npm run buildall green locally. Live login already verified against the instance (CT 3.134.0) in Phase 0.Review follow-up
Addressed the PR #8 review: removed the non-host-keyed file fallback (it could return/overwrite a different host's token) in favour of a single Keychain token; hardened
Retry-Afterparsing against unbounded/negative/date values.Deferred (tracked on #3)
openapi-fetch(8.2 MB generated schema — generate on demand for now).Refs #3