Skip to content

feat: Phase 1 hardening — Keychain token store + HTTP retry - #8

Merged
2000game merged 2 commits into
mainfrom
feat/phase-1-hardening
Jul 7, 2026
Merged

2000game merged 2 commits into
mainfrom
feat/phase-1-hardening

Conversation

@2000game

@2000game 2000game commented Jul 7, 2026 •

Copy link
Copy Markdown
Member

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

  • macOS Keychain token store (src/auth/tokenStore.ts): a single login token stored via the security CLI (service ct-cli, account login-token). No file fallback — on CI or non-macOS hosts, supply the token through CT_LOGINTOKEN. Read precedence: CT_LOGINTOKEN env → Keychain.
  • HTTP retry / rate-limit (src/api/http.ts): exponential backoff + jitter, honours Retry-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 all CtClient calls.

Tests

19 passing: retry matrix (429, 5xx idempotent vs write, network error, budget exhaustion), Retry-After duration + cap + malformed fallback, and CT_LOGINTOKEN precedence.

Verification

npm run typecheck · npm run lint · npm test · npm run build all 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-After parsing against unbounded/negative/date values.

Deferred (tracked on #3)

  • Typed client swap to openapi-fetch (8.2 MB generated schema — generate on demand for now).

Refs #3

2000game added 2 commits July 7, 2026 12:48
- 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.
@2000game
2000game merged commit 429a61e into main Jul 7, 2026
1 check passed
@2000game
2000game deleted the feat/phase-1-hardening branch July 7, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant