Skip to content

Vastly simplify auth contexts#1318

Merged
Soph merged 12 commits into
mainfrom
20260602-auth-contexts
Jun 2, 2026
Merged

Vastly simplify auth contexts#1318
Soph merged 12 commits into
mainfrom
20260602-auth-contexts

Conversation

@toothbrush
Copy link
Copy Markdown
Contributor

@toothbrush toothbrush commented Jun 2, 2026

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

  • One account → exactly one eligible context → used automatically. No prompt, no extra step.
  • No added latency — /.well-known is cached; first op per cluster fetches once, as before.
  • No migration / re-login — existing contexts.json loads as-is; the obsolete cluster_contexts key is ignored and removed.
  • A new error fires only on genuine ambiguity (2+ accounts on the same core, none active) — previously a silent guess (e.g., admin@core, paul@core both match), now a clear "pick one."

Change

  • Cache only the objective cluster → [core_urls] fact (in cluster_cores.json, 24h TTL, stale-fallback). The matching account is recomputed locally every op, never persisted.
  • Selection: active context if eligible → else the sole eligible one → else (2+ eligible, none active) a one-line error listing them and pointing at entire auth use, instead of guessing.
  • Drops the now-defunct bind/unbind surface.

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 runs entire auth use or 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 at entire auth use (no silent multi-account guess).

Removed persisted cluster_contexts, BindCluster / File.Resolve, and entire auth unbind plus cluster-binding output from entire auth contexts. Logout / delete no longer auto-advance current_context to another stored identity.

Shared discovery cache I/O is refactored for the new cores file; git-remote-entire passes the discovery cache dir into resolution. .gitignore adds /git-remote-entire.

Reviewed by Cursor Bugbot for commit edb6bdb. Configure here.

toothbrush and others added 4 commits June 2, 2026 12:02
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
Copilot AI review requested due to automatic review settings June 2, 2026 04:27
@toothbrush toothbrush requested a review from a team as a code owner June 2, 2026 04:27
toothbrush and others added 4 commits June 2, 2026 13:58
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
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.json cache (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.

Comment thread internal/entireclient/contexts/contexts.go Outdated
@toothbrush toothbrush force-pushed the 20260602-auth-contexts branch from edb6bdb to 0f4b7fe Compare June 2, 2026 04:31
toothbrush and others added 2 commits June 2, 2026 14:17
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
Base automatically changed from silly-jingling-honey to main June 2, 2026 06:24
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
Soph and others added 2 commits June 2, 2026 09:01
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 Soph enabled auto-merge June 2, 2026 07:12
@Soph Soph merged commit e6c5a62 into main Jun 2, 2026
9 checks passed
@Soph Soph deleted the 20260602-auth-contexts branch June 2, 2026 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants