Vastly simplify auth contexts#1318
Merged
Merged
Conversation
Previously every git op against an entire:// cluster re-ran /.well-known discovery, and successful lookups were never persisted. Bind the clusterHost->context mapping into contexts.json on the first successful scoped-token exchange, so subsequent invocations short-circuit on the stored binding and skip discovery. Conservative (post-success) variant: the bind fires only after the cluster has provably authenticated the resolved context, so a host whose /.well-known matches a local context but then fails to authenticate leaves no stale binding. Resolution itself stays non-persisting; makeBindHook (guarded by sync.Once on a freshly-constructed token cache) owns the write. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: ff5ccce200c0
Covers the three paths: nil hook for an empty context name, a binding persisted after the first call for an existing context, and no binding written when the named context is missing (BindCluster fails, logged not fatal). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 9e787e814af7
Adds cluster_cores.json (alongside nodes.json in the cache dir): a
host -> {core_urls, fetched_at} map memoizing /.well-known discovery,
with a 7d TTL. Get() reports both freshness and existence so callers can
re-fetch on expiry but fall back to a stale entry if the cluster is
briefly unreachable.
Unlike a context binding this stores only the objective cluster->core
fact, never which local account to use, so a multi-account user is never
silently pinned to one identity. Safe to delete by hand to force
re-discovery.
Factors the shared lock/atomic-write/read primitives out of the existing
node cache so both files reuse them with no duplication; existing cache
tests guard the refactor.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: efec36727758
Reworks ResolveContextForCluster around the cluster_cores cache and a fresh per-operation account selection, replacing the cluster->context binding model: - Cluster cores come from cluster_cores.json (TTL'd, stale-fallback), not a persisted account choice. - Account selection: active context wins if its core fronts the cluster; else the sole eligible context; else (>1 eligible, none active) a hard error listing the candidates and pointing at `entire auth use`. No more silent on-disk-order pick, which could authenticate as the wrong account and then freeze that choice. Drops the post-success bind hook from git-remote-entire (makeBindHook) and adds a cacheDir argument to ResolveContextForCluster. The binding APIs in contexts/ and the auth command surface are removed in a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 32bbd6549652
Removes the cluster→context binding now that account selection is recomputed per operation: - contexts: drop File.Resolve and BindCluster. The ClusterContexts field is kept but inert (deprecated) so an existing contexts.json round-trips without data loss during the transition; Delete still prunes it and a full logout still clears it. - auth: drop ClusterBindings/UnbindCluster, the `entire auth unbind` command, and the "Cluster bindings" section of `entire auth contexts`. Tests updated accordingly; RemoveAllContexts still clears the legacy field (seeded directly now that BindCluster is gone). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 1a18bfd708fc
json.Unmarshal ignores unknown keys, so an existing contexts.json with a "cluster_contexts" entry still loads cleanly — the obsolete key is simply dropped on the next write. With nothing reading or writing it, removing the field (and the last references in Delete and RemoveAllContexts) is safe and leaves no dead weight. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 4f015a69dc6b
- Delete now always clears current_context when the deleted context was active, instead of advancing to the first remaining context. Deleting your active login must never silently switch you to another identity. - Remove the leftover "legacy cluster_contexts used to live here" note; comments describe today's behavior only. RemoveCurrentContext relies on Delete's clear and drops its redundant reassignment. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 49c99a4a7077
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors entire:// auth resolution to cleanly separate infrastructure discovery (which cores front a cluster) from identity selection (which local login context to use), removing the previously persisted cluster→context bindings and eliminating silent “best guess” selection when multiple accounts match.
Changes:
- Introduces a new
cluster_cores.jsoncache (24h TTL + stale fallback) for cluster→core URL mappings, and refactors cache file I/O to be reusable across cache files. - Updates cluster auth resolution to recompute eligible contexts on each operation: prefer active context if eligible → else use the sole eligible context → else error on ambiguity (no persisted binding).
- Removes the cluster binding surface (
cluster_contexts, bind/unbind functionality, and related CLI output/tests), and changes logout/delete behavior to never auto-switch identities.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| internal/entireclient/discovery/cluster_cores.go | Adds the new cluster→core URL cache type and helpers (load/modify/get/set). |
| internal/entireclient/discovery/cluster_cores_test.go | Tests for cache round-trip, freshness/staleness behavior, and copy semantics. |
| internal/entireclient/discovery/cache.go | Refactors cache locking + JSON read/write into shared primitives used by multiple cache files. |
| internal/entireclient/contexts/contexts.go | Removes cluster_contexts state + APIs and changes deletion semantics to clear current_context rather than auto-advance. |
| internal/entireclient/contexts/contexts_test.go | Updates tests to reflect removal of bindings and new delete/current semantics. |
| internal/entireclient/clusterdiscovery/resolve.go | Switches resolution to use cluster cores cache + deterministic context selection, erroring on ambiguity. |
| internal/entireclient/clusterdiscovery/resolve_test.go | Adds coverage for cores caching, stale fallback, ambiguity errors, and updated selection rules. |
| cmd/git-remote-entire/main.go | Passes cache dir into resolution and updates description of the new resolution flow. |
| cmd/entire/cli/auth/contexts_test.go | Updates logout/remove-all behavior tests to reflect removal of bindings. |
| cmd/entire/cli/auth/context_store.go | Removes binding cleanup paths; ensures logout clears current context without switching identities. |
| cmd/entire/cli/auth.go | Removes entire auth unbind command registration. |
| cmd/entire/cli/auth_context.go | Removes cluster bindings listing and unbind command implementation from entire auth contexts. |
| cmd/entire/cli/auth_context_test.go | Removes tests that asserted binding visibility and unbind behavior. |
| .gitignore | Ignores the git-remote-entire binary at repo root. |
edb6bdb to
0f4b7fe
Compare
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
CI's golangci-lint flagged ireturn on the generic readCacheFile, which returned its type parameter T. (Locally the nolint directive that silenced it kept getting stripped by `golangci-lint run --fix`'s nolintlint autofix, so it only surfaced in CI.) Replace it with loadCacheFile(path, *T, newEmpty) error: it fills a destination pointer and returns error, which ireturn allows, while still sharing the read/unmarshal/corrupt-reset logic across both caches. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 6d6f3baefcd9
Soph
added a commit
that referenced
this pull request
Jun 2, 2026
PR #1318 removed the cluster→context binding model but left several user-facing strings and package docs describing bindings that no longer exist, and used the internal term "core" for the JWT issuer. Update them: - logout --help / --all flag: no longer claims to remove "cluster bindings" - auth use --help, doc comment, and the cross-core warning: the active context is the preferred identity for any cluster its login server fronts; the switch takes effect on the next op (no "bound/not yet bound" set) - git-remote-entire package doc: cores-cache-or-/.well-known resolution - contexts package doc: drop the removed cluster_contexts field - RenderLoginHint and warning text: "core(s)" -> "login server(s)" for users Docs/help text only; no logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 3af886a3214e
PR #1318 removed the cluster→context binding model but left several user-facing strings and package docs describing bindings that no longer exist, and used the internal term "core" for the JWT issuer. Update them: - logout --help / --all flag: no longer claims to remove "cluster bindings" - auth use --help, doc comment, and the cross-core warning: the active context is the preferred identity for any cluster its login server fronts; the switch takes effect on the next op (no "bound/not yet bound" set) - git-remote-entire package doc: cores-cache-or-/.well-known resolution - contexts package doc: drop the removed cluster_contexts field - RenderLoginHint and warning text: "core(s)" -> "login server(s)" for users Docs/help text only; no logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 3af886a3214e
Auth contexts: finish binding-removal cleanup, use "login server" wording
Soph
approved these changes
Jun 2, 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.
https://entire.io/gh/entireio/cli/trails/471
TLDR: Cache entiredb-cluster→entire-core mappings
Problem
Resolving which login context to use for an
entire://cluster mixed two concerns: which control plane fronts a cluster (stable infra) and which of your accounts to use (per-user). When several contexts were eligible and none was active, it picked one by last-save order and persisted that guess as a binding — so the choice was somewhat arbitrary, and the stored state ended up asymmetric and confusing.99% happy path: unchanged
/.well-knownis cached; first op per cluster fetches once, as before.contexts.jsonloads as-is; the obsoletecluster_contextskey is ignored and removed.admin@core,paul@coreboth match), now a clear "pick one."Change
cluster → [core_urls]fact (incluster_cores.json, 24h TTL, stale-fallback). The matching account is recomputed locally every op, never persisted.entire auth use, instead of guessing.Note
High Risk
Changes how every
entire://operation picks credentials (auth path); wrong selection or stale core cache could block git or use the wrong account until the user runsentire auth useor clears cache.Overview
This PR splits cluster auth into infra vs identity: only which cores front a cluster is cached (
cluster_cores.json, 24h TTL, stale fallback on discovery failure); which login context to use is recomputed every git op and never written to disk.entire://resolution loads cores from cache or/.well-known, then picks a context: active context if its core is eligible → else the only eligible context → else an error listing candidates and pointing atentire auth use(no silent multi-account guess).Removed persisted
cluster_contexts,BindCluster/File.Resolve, andentire auth unbindplus cluster-binding output fromentire auth contexts. Logout / delete no longer auto-advancecurrent_contextto another stored identity.Shared discovery cache I/O is refactored for the new cores file;
git-remote-entirepasses the discovery cache dir into resolution..gitignoreadds/git-remote-entire.Reviewed by Cursor Bugbot for commit edb6bdb. Configure here.