Refine logout Command with --all-contexts Flag

Claude Code·toothbrush·3mo ago·25hr 59min·5 Checkpoints·20 file changes·+1028/-84·123.4K tokens

We're working on https://github.com/entireio/cli/pull/1341 in this branch. Let's step through all the reviewer comments / nitpicks and consider what to do step by step.

3mo ago·2m

Done. Here's the summary.

What I did

  • 2 live fixes applied to cmd/entire/cli/logout.go:
    • Dropped the double-fmt.Errorf wraps in revokeAllSessionsListSessions/RevokeSession already wrap with their own prefix (incl. session id). No more "list sessions: list sessions: …".
    • Rewrote the stale revokeCurrentFunc doc comment — it claimed the impl "resolves its own data-API bearer"; the caller now resolves target.token and binds it into the closure.
  • Go lint clean, logout tests pass.

What I left alone

  • Wrong-core revocation (Cursor medium-severity + Copilot's matching comment): already fixed in f9b8c3e; Bugbot's re-review confirmed no issues.
  • "data-API bearer" comment on revokeAllSessions (Copilot): already stale-corrected in a prior commit.

Note

  • The mise run lint shellcheck failure is pre-existing in mise-tasks/release (SC2312), untouched by this branch — not something to fix here.

Want me to commit these two fixes?

commit separately.

3mo ago·2m

Done. Two separate commits:

  • 9901fbf9e6 — logout: stop double-wrapping session revoke errors
  • ddb0807af8 — logout: fix stale revokeCurrentFunc doc comment

Both go-lint clean; logout tests pass. Not pushed.

respond succinctly to all the feedback pointing to the fix-commits.

3mo ago·47.5s

Replied to all 7 threads:

  • Double-wrap errors (3 Copilot threads) → 9901fbf9e6
  • Stale revokeCurrentFunc comment → ddb0807af8
  • Wrong-core revoke (Cursor + Copilot) → already fixed in f9b8c3e
  • Stale data-API comment on revokeAllSessions → already corrected

wat why 16:45:36 (git) auth-context-co...▲ ↑2 $ entire checkpoint search "test" reading credentials: token exchange: status 400: invalid_request: subject_token is invalid

3mo ago·45.1s

Bottom line: your stored login JWT is dead — re-run entire login. This isn't a regression from this branch's code; it's an expired/revoked session, and checkpoint search just surfaces the raw STS error instead of a friendly hint.

What's happening:

  • entire checkpoint searchresolveSearchToken (search_cmd.go:215) → auth.TokenForResource, which does an RFC 8693 token exchange: it POSTs your stored login JWT as the subject_token to the data-API host's STS endpoint to mint a data-API-scoped bearer.
  • The STS rejected it: 400 invalid_request: subject_token is invalid. That means the login JWT you're holding is expired or revoked server-side — exactly what you'd expect after testing logout / logout --all on this branch and draining your sessions.
  • Fix: entire login.

The rough edge (worth noting, arguably out of scope here):

  • auth.go:108 has isKeychainTokenRejected, which explicitly matches "token exchange: status 4" and maps it to a "re-login" hint — this PR even hardened it. But checkpoint search doesn't route its credential error through that helper, so you get the raw subject_token is invalid dump instead of "not authenticated. Run 'entire login'".
  • The actual silent JWT refresh (COR-389) that would auto-recover from an expired-but-refreshable session is called out in the PR description as deliberately not in this PR.

Want me to make resolveSearchToken map STS-rejection through the same friendly re-login message? Small, in-scope-adjacent fix — but say the word, since this branch is about status/logout, not search.

what about this erorr? 16:45:38 (git) auth-context-co...▲ ↑2 $ entire login Device code: 5EKA-Q3UA Press Enter to open https://us.auth.entire.io/cli/auth?user_code=5EKA-Q3UA in your browser to approve this login...

Waiting for approval... Login complete. [paul@boulot] ~/.claude/worktrees/cli/wiggly-marinating-origami 16:50:58 (git) auth-context-co...▲ ↑2 $ entire checkpoint search "test" search failed: search service error (401): Invalid or expired GitHub token [paul@boulot] ~/.claude/worktrees/cli/wiggly-marinating-origami

3mo ago·1m

All defaults — you're on prod (us.auth.entire.io for login, prod data-API for search). That rules out a misconfigured-host theory.

Confirmed conclusion: the CLI authenticated fine; the search backend's stored GitHub token for your account is expired. Reconnect GitHub in the Entire dashboard, then retry. If it persists after reconnecting, it's a server-side issue for the core team — outside this CLI branch entirely.

what API endpoint does entire activity hit?

3mo ago·24.2s

entire activity hits two endpoints on the data-API host (api.BaseURL()):

  • GET /api/v1/me/activity?timezone=…&timeframe=…&limit=…activity_cmd.go:139
  • GET /api/v1/me/commits?timeframe=…&limit=…activity_cmd.go:159

Details:

  • Both go through NewAuthenticatedAPIClient (activity_cmd.go:61) → same STS-exchanged data-API bearer path as checkpoint search. So if search's token exchange fails, activity fails the same way.
  • Host = api.BaseURL() (overridable via ENTIRE_API_BASE_URL), not the auth/core host.

i want to point entire activity at partial.to endpoints. give me the env vars

3mo ago·28.3s

Two env vars control it — activity reads its data host from ENTIRE_API_BASE_URL and its auth/login host from ENTIRE_AUTH_BASE_URL.

Key points:

  • If partial.to fronts both API and auth on the same origin (single-host), set both to that one URL — or just set ENTIRE_API_BASE_URL and leave auth pointing at prod only if the token is cross-valid (it won't be). Simplest: set both the same.
  • If they're split (different auth vs data origins on partial.to), set each accordingly. When the two origins differ, activity auto-triggers an RFC 8693 STS exchange (IsSplitHost(), base_url.go:74).
  • You must re-login after setting these. The keyring bearer is keyed by the normalized AuthBaseURL (base_url.go:59), so your existing us.auth.entire.io token won't be found — run entire login with the new env vars set.
  • Plain http://? If the partial.to endpoint isn't https, you'll need --insecure-http-auth on the commands (and login), since auth refuses cleartext otherwise.

What's the actual partial.to host(s)? If you give me the URL(s) and whether auth/data are split, I'll hand you the exact exports.

so not this?

17:43:13 (git) auth-context-co...▲ $ ENTIRE_API_BASE_URL=https://partial.to ENTIRE_AUTH_BASE_URL=https://us.auth.partial.to entire activity

3mo ago

Failed to load activity: fetch activity: decode commits: decode JSON response: invalid character '<' looking for beginning of value

3mo ago·21.9s

Correct — that's not it. The < means the response was HTML, not JSONhttps://partial.to is the tunnel service's bare landing page, not your API. partial.to is an ngrok-style tunnel host; the real endpoint is a specific subdomain. Let me check what your actual cluster host serves.

what that's bullshit.

3mo ago

what that's bullshit.

3mo ago·32.2s

You're right, I overreached — apologies. The only hard fact here is narrow:

  • The error is decode commitsGET /api/v1/me/commits returned HTML (<…), not JSON.
  • activity and commits run concurrently (errgroup, activity_cmd.go:117-127), and Wait() surfaces whichever errored. So this does not prove the host is wrong — your ENTIRE_API_BASE_URL=https://partial.to may be perfectly correct and /me/activity may be returning fine.

What an HTML body on that one route actually points to:

  • /api/v1/me/commits doesn't exist on that backend (or hits a catch-all that serves an HTML page — SPA fallback, 404 page, or a reverse-proxy default), while /me/activity does exist.
  • i.e. likely a backend route mismatch, not your env vars.

I jumped to "partial.to is just a landing page" without evidence and shouldn't have. I won't probe your tunnel without asking.

Want me to confirm which it is? Cleanest is to see the raw status + body of just the commits call — either you run it, or tell me and I'll curl it:

If that returns 200+HTML, it's the backend route; if 404/302, same. Either way it's server-side, not your env config.

3mo ago
3mo ago
3mo ago

one last thing. add a entire logout --all flag which will iterate over the known contexts and issue logout against each of them.

3mo ago·1h 23m

API Error: Unable to connect to API (ECONNRESET)

3mo ago·1h 28m

Done. entire logout --all shipped in commit 394066405e.

Behavior:

  • entire logout --all → drains every saved login (context): revokes each context's current session server-side against its own core, removes each locally, then clears the legacy keyring entry. Ends fully logged out.
  • --all --everywhere → for each context, revokes every session on that core (all devices, every login).
  • Per-context failures (revoke error, unreadable token, non-TLS core without --insecure-http-auth) warn but never abort — local removal always proceeds.

What I added:

  • auth.RemoveContext(name) — named sibling of RemoveCurrentContext, sharing a new deleteContextKeychain helper (kept dupl-clean).
  • runLogoutAll — fully injected, 6 unit tests covering revoke-each, empty, revoke-failure-continues, silent-401, unreadable-token, insecure-core-skip.
  • TestRemoveContext in the auth package.
  • Fixed the stale --all reference in the logout help your rename left behind.

Verified: build, go test (cli + auth + api), mise run lint:go (0 issues), --help renders correctly. Not pushed.

One note for the PR description: --all and --everywhere are different axes (all logins vs all sessions on one core) and compose — worth a line so reviewers don't read them as redundant.

i feel like --all and --everywhere should interact reasonably:

logout # just this context, just this session. logout --everywhere # this context, all sessions logout --all # iterate over contexts, kill the current session of each logout --all --everywhere # iterate over contexts and kill all sessions in each

make sense?

3mo ago·23.5s

Yes — that's exactly the matrix I built. It already behaves that way. The wiring computes the per-target revoke once from --everywhere, then applies it through whichever path --all selects:

So the 2×2 maps to:

CommandPathPer-core revokeResult
logoutrunLogoutcurrent sessionthis context, this session
logout --everywhererunLogoutall sessionsthis context, all sessions
logout --allrunLogoutAllcurrent sessionevery context, current session each
logout --all --everywhererunLogoutAllall sessionsevery context, all sessions each

One gap: the command-layer mapping (--everywhererevokeAllSessions for the --all path) isn't directly unit-tested — runLogoutAll's tests inject the revoke func, and only the single-context --everywhere selection has a test (TestRunLogout_AllRevokesAllSessions). The logic is 3 shared lines, but it's the crux of your matrix. Want me to add a test that exercises the cobra command end-to-end (with stubbed contexts/keyring) to pin all four quadrants? Otherwise it's done as specified.

yep definitely add such a test. then commit. then rename --all to --all-contexts. then commit.

3mo ago·8h 6m

API Error: The socket connection was closed unexpectedly. For more information, pass verbose: true in the second argument to fetch()

try again

3mo ago·3m

All four steps done.

Commits added:

  • 51486c980a — end-to-end matrix test (cobra command vs two fake cores, all four quadrants)
  • df83448d98 — rename --all--all-contexts

Final flag surface:

CommandResult
logoutactive context, current session
logout --everywhereactive context, all sessions
logout --all-contextsevery context, current session each
logout --all-contexts --everywhereevery context, all sessions each

Verified: mise run lint:go clean, cli + auth tests green, --help renders --all-contexts. Not pushed.

Note: I left the test function TestRunLogout_AllRevokesAllSessions named as-is (it predates this work and tests the --everywhere param) — only corrected its misleading --all message strings. Rename the func too if you want it fully consistent.