Skip to content

fix(cli): persist login token durably on detached/headless boxes#38

Merged
mastermanas805 merged 2 commits into
masterfrom
fix/cli-headless-login-fresh
Jul 4, 2026
Merged

fix(cli): persist login token durably on detached/headless boxes#38
mastermanas805 merged 2 commits into
masterfrom
fix/cli-headless-login-fresh

Conversation

@mastermanas805

Copy link
Copy Markdown
Member

Summary

  • New persistSecret() in cliconfig does write-then-readback: after Set, immediately Get and confirm the value round-trips; mismatch → fall back to 0600 file at ~/.instant-config
  • runLogin no longer prints the success banner if Save() fails both keychain and file; returns an error with the raw token + export INSTANT_TOKEN=… escape hatch so headless agents can proceed in-flow
  • 100% patch coverage: 6 validation tests for all persistSecret outcomes + Save-failure path assertions

Why

Wave-3 B5: instant login from a headless/detached box (nohup, CI runner, container) printed "✓ Logged in" while persisting nothing the next command could read — the keychain backend's Available() probe is a false positive on detached boxes (a probing Get returning ErrNotFound reads as "available, just empty"). The token landed in an ephemeral session keyring that the next process couldn't see.

Test plan

  • go test ./cmd/ ./internal/cliconfig/ -run TestLogin — all pass
  • go build ./... — clean build

🤖 Generated with Claude Code

mastermanas805 and others added 2 commits June 26, 2026 14:30
`instant login` from a detached/headless context (e.g. `nohup instant
login &` on an agent box) printed "✓ Logged in" while persisting NOTHING
the next `instant whoami` could read — so the very next command said
"Not logged in". This undercuts the "frictionless for agents" promise.

Root cause: cliconfig.Save() trusted secretstore.Set()'s nil return as
proof of durable persistence. On a detached box the keychain backend's
Available() probe is a false positive (a probing Get returning ErrNotFound
reads as "available, just empty"), and the subsequent Set returns nil even
though the write lands in an ephemeral/locked session keyring the NEXT
process can't read.

Fix: new persistSecret() in cliconfig does write-then-readback validation.
If Set returns success but the immediate Get either errors or returns a
mismatched value, persistSecret falls through to the 0600 file fallback
so the token lands in ~/.instant-config. If both keychain and file fail,
runLogin returns an error with the raw token + an `export INSTANT_TOKEN=…`
escape hatch — no more false-success banner on a totally-failed persist.
100% patch coverage (diff-cover --fail-under=100):

- cliconfig/headless_persist_test.go: table-driven tests for all four
  persistSecret outcomes — silent-keychain-write (Set ok, Get mismatch)
  falls back to 0600 file + survives Load(); Set-error fallback; durable
  round-trip skips the file write (no plaintext leak on working keychain);
  both-fail propagates the error with the token payload.
- cmd/login_persist_test.go: asserts that the Save-failure path returns NO
  success banner, surfaces the INSTANT_TOKEN escape hatch, and embeds the
  raw token in the error — verifying the agent-recoverable path end-to-end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mastermanas805 mastermanas805 merged commit 4090e32 into master Jul 4, 2026
10 checks passed
@mastermanas805 mastermanas805 deleted the fix/cli-headless-login-fresh branch July 4, 2026 09:26
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