Refine logout Command with --all-contexts Flag

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.
Done. Here's the summary.
What I did
- 2 live fixes applied to
cmd/entire/cli/logout.go:- Dropped the double-
fmt.Errorfwraps inrevokeAllSessions—ListSessions/RevokeSessionalready wrap with their own prefix (incl. session id). No more"list sessions: list sessions: …". - Rewrote the stale
revokeCurrentFuncdoc comment — it claimed the impl "resolves its own data-API bearer"; the caller now resolvestarget.tokenand binds it into the closure.
- Dropped the double-
- 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 lintshellcheck failure is pre-existing inmise-tasks/release(SC2312), untouched by this branch — not something to fix here.
Want me to commit these two fixes?
Done. Two separate commits:
9901fbf9e6— logout: stop double-wrapping session revoke errorsddb0807af8— logout: fix stale revokeCurrentFunc doc comment
Both go-lint clean; logout tests pass. Not pushed.
Replied to all 7 threads:
- Double-wrap errors (3 Copilot threads) → 9901fbf9e6
- Stale
revokeCurrentFunccomment → 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
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 search→resolveSearchToken(search_cmd.go:215) →auth.TokenForResource, which does an RFC 8693 token exchange: it POSTs your stored login JWT as thesubject_tokento 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 testinglogout/logout --allon this branch and draining your sessions. - Fix:
entire login.
The rough edge (worth noting, arguably out of scope here):
auth.go:108hasisKeychainTokenRejected, which explicitly matches"token exchange: status 4"and maps it to a "re-login" hint — this PR even hardened it. Butcheckpoint searchdoesn't route its credential error through that helper, so you get the rawsubject_token is invaliddump 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
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.
entire activity hits two endpoints on the data-API host (api.BaseURL()):
GET /api/v1/me/activity?timezone=…&timeframe=…&limit=…—activity_cmd.go:139GET /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 ascheckpoint search. So if search's token exchange fails, activity fails the same way. - Host =
api.BaseURL()(overridable viaENTIRE_API_BASE_URL), not the auth/core host.
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_URLand 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,
activityauto-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 existingus.auth.entire.iotoken won't be found — runentire loginwith the new env vars set. - Plain
http://? If the partial.to endpoint isn'thttps, you'll need--insecure-http-authon 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
Failed to load activity: fetch activity: decode commits: decode JSON response: invalid character '<' looking for beginning of value
Correct — that's not it. The < means the response was HTML, not JSON — https://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.
You're right, I overreached — apologies. The only hard fact here is narrow:
- The error is
decode commits→GET /api/v1/me/commitsreturned HTML (<…), not JSON. activityandcommitsrun concurrently (errgroup,activity_cmd.go:117-127), andWait()surfaces whichever errored. So this does not prove the host is wrong — yourENTIRE_API_BASE_URL=https://partial.tomay be perfectly correct and/me/activitymay be returning fine.
What an HTML body on that one route actually points to:
/api/v1/me/commitsdoesn'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/activitydoes 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.
one last thing. add a entire logout --all flag which will iterate over the known contexts and issue logout against each of them.
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 ofRemoveCurrentContext, sharing a newdeleteContextKeychainhelper (kept dupl-clean).runLogoutAll— fully injected, 6 unit tests covering revoke-each, empty, revoke-failure-continues, silent-401, unreadable-token, insecure-core-skip.TestRemoveContextin the auth package.- Fixed the stale
--allreference 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?
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:
| Command | Path | Per-core revoke | Result |
|---|---|---|---|
logout | runLogout | current session | this context, this session |
logout --everywhere | runLogout | all sessions | this context, all sessions |
logout --all | runLogoutAll | current session | every context, current session each |
logout --all --everywhere | runLogoutAll | all sessions | every context, all sessions each |
One gap: the command-layer mapping (--everywhere → revokeAllSessions 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.
API Error: The socket connection was closed unexpectedly. For more information, pass verbose: true in the second argument to fetch()
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:
| Command | Result |
|---|---|
logout | active context, current session |
logout --everywhere | active context, all sessions |
logout --all-contexts | every context, current session each |
logout --all-contexts --everywhere | every 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.