git-remote-entire: ship the remote helper in the CLI, on a shared contexts.json login#1306
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 5 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 1476f90. Configure here.
There was a problem hiding this comment.
Pull request overview
This PR folds the git-remote-entire remote helper into the CLI so git clone entire://… works out of the box, and migrates authentication toward a shared contexts.json + keychain credential model to enable single-login reuse across control-plane commands and git operations.
Changes:
- Add a dedicated
cmd/git-remote-entirebinary plus remote-helper protocol/transport internals (smart-HTTP proxying, failover, replica caching, debug tooling). - Introduce shared auth/storage building blocks under
internal/entireclient/*(token store, contexts, cluster discovery, STS exchange caching). - Update the CLI auth flows to dual-write contexts on login, prefer contexts on reads, and add
entire auth contexts/entire auth use.
Reviewed changes
Copilot reviewed 74 out of 75 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/install.sh | Installs git-remote-entire alongside entire from release archives. |
| scripts/entire-dev | Runs go run on the whole cmd/entire package (not just main.go). |
| internal/remotehelper/transport/inforefs.go | Implements info/refs cold/warm paths, redirect handling, and shared HTTP error shaping. |
| internal/remotehelper/transport/fault_test.go | Adds failure-mode tests (truncation, ctx cancel, connection reset failover). |
| internal/remotehelper/replicas/replicas.go | Adds persistent replica-cache adapter and per-invocation node config resolution. |
| internal/remotehelper/replicas/replicas_test.go | Tests repo-path extraction and replica cache persist/load/invalidate behavior. |
| internal/remotehelper/name.go | Defines shared helper binary name constant. |
| internal/remotehelper/httpdebug/roundtripper.go | Adds debug-only HTTP request/response logging wrapper with redaction hooks. |
| internal/remotehelper/httpdebug/redact.go | Implements header/URL/JSON token redaction and safe body previewing. |
| internal/remotehelper/gitproto/gitproto.go | Adds pkt-line helpers for v0/v1/v2 smart-HTTP framing and request parsing. |
| internal/remotehelper/gitproto/fuzz_test.go | Fuzz coverage for git protocol helpers to prevent panics/OOM. |
| internal/remotehelper/gitproto/consts_test.go | Shared test constants for gitproto tests. |
| internal/remotehelper/gitproto/agent.go | Logic to append helper agent identifiers to v0/v1/v2 requests. |
| internal/remotehelper/githelper/transport.go | Defines the transport interface boundary for the helper loop. |
| internal/remotehelper/githelper/stateless.go | Implements stateless-connect handling for protocol v2 upload-pack. |
| internal/remotehelper/githelper/stateless_test.go | Tests stateless-connect behavior, agent amendment, and bundle-uri edge case. |
| internal/remotehelper/githelper/signal_test.go | Pins cancellation/EOF behavior for the helper main loop. |
| internal/remotehelper/githelper/run.go | Implements the git-remote-helper command loop (capabilities/list/connect/push). |
| internal/remotehelper/githelper/run_test.go | Tests capabilities/options and connect-mode upload-pack flow. |
| internal/remotehelper/githelper/push.go | Bridges git send-pack --stateless-rpc to receive-pack HTTP POSTs with auth/failover. |
| internal/remotehelper/githelper/options.go | Tracks helper options and translates them into send-pack flags. |
| internal/remotehelper/githelper/options_test.go | Tests option parsing and send-pack flag emission. |
| internal/remotehelper/githelper/list.go | Implements list/list for-push parsing and symref/object-format emission. |
| internal/remotehelper/githelper/list_test.go | Tests list output for symrefs, detached HEAD, unborn HEAD, and for-push. |
| internal/remotehelper/githelper/consts.go | Centralizes service-name and option constants. |
| internal/remotehelper/githelper/consts_test.go | Shared test constants for githelper tests. |
| internal/remotehelper/githelper/connect.go | Implements connect-mode upload-pack negotiation and receive-pack relaying. |
| internal/remotehelper/githelper/connect_test.go | Tests receive-pack connect mode, including push-size header behavior. |
| internal/remotehelper/githelper/agent.go | Defines the helper’s default agent string (overridden at startup). |
| internal/remotehelper/debuglog/debuglog.go | Adds ENTIRE_DEBUG-gated debug logger for the helper. |
| internal/remotehelper/debuglog/debuglog_test.go | Tests debug logging gating and prefix behavior. |
| internal/entireclient/tokenstore/tokenstore.go | Adds shared token store abstraction (keyring by default, file backend optional). |
| internal/entireclient/tokenstore/testing.go | Test helper to force a file backend safely under package-level locking. |
| internal/entireclient/tokenstore/file.go | Implements file-backed token store with flock + atomic writes. |
| internal/entireclient/tokenstore/file_test.go | Tests file token store semantics and permissions. |
| internal/entireclient/tokenstore/expiry.go | Adds `token |
| internal/entireclient/tokenstore/expiry_test.go | Tests encoding/decoding and expiring-buffer logic. |
| internal/entireclient/repocreds/repocreds.go | Adds cached RFC 8693 token exchange for repo-scoped JWTs. |
| internal/entireclient/repocreds/repocreds_test.go | Tests caching/expiry/invalidation/concurrency behavior of repo-scoped tokens. |
| internal/entireclient/httputil/oauth.go | Adds OAuth form-post helper with Basic-auth client credential lifting. |
| internal/entireclient/httputil/oauth_test.go | Tests Basic-auth lifting + percent-encoding behavior for client creds. |
| internal/entireclient/httpclient/transport.go | Centralizes HTTP transport construction and dial timeout config. |
| internal/entireclient/httpclient/transport_test.go | Tests dial timeout env parsing behavior. |
| internal/entireclient/discovery/parse_replicas.go | Adds parsing for X-Entire-Replicas and in-cluster host checks. |
| internal/entireclient/discovery/parse_replicas_test.go | Tests replica parsing and HostInCluster behavior. |
| internal/entireclient/discovery/cache.go | Adds on-disk replica/node cache with TTL and file locking. |
| internal/entireclient/discovery/cache_test.go | Tests cache round-trip, expiry, misses, and invalidation. |
| internal/entireclient/clusterdiscovery/resolve.go | Resolves auth contexts for a cluster via binding or /.well-known discovery. |
| internal/entireclient/clusterdiscovery/resolve_test.go | Tests binding short-circuit, discovery+autobind, stale bindings, and error modes. |
| internal/entireclient/clusterdiscovery/discovery.go | Implements /.well-known/entire-cluster.json fetch/parse and user hints. |
| internal/entireclient/clusterdiscovery/discovery_test.go | Tests discovery behavior across status codes, transport errors, and nil debugf. |
| go.sum | Adds/removes module sums related to new deps (e.g., flock). |
| go.mod | Adds new dependency on github.com/gofrs/flock. |
| cmd/git-remote-entire/main.go | Introduces the standalone remote helper binary wiring auth + transport + protocol loop. |
| cmd/git-remote-entire/main_test.go | Tests request→git action classification for token scoping. |
| cmd/entire/cli/logout.go | Extends logout to clear the active contexts.json context as well as legacy token. |
| cmd/entire/cli/logout_test.go | Updates logout tests for the injected context-clear hook. |
| cmd/entire/cli/login.go | Dual-writes contexts.json context on successful login (best-effort). |
| cmd/entire/cli/auth/store.go | Switches “current token” lookup to prefer contexts.json via ContextStore. |
| cmd/entire/cli/auth/exchange.go | Wires tokenmanager to use ContextStore for context-preferring reads. |
| cmd/entire/cli/auth/contexts.go | Adds context recording, legacy migration, and token lookup by context. |
| cmd/entire/cli/auth/contexts_test.go | Tests context record/migrate/token lookup + store preference + context removal/switching. |
| cmd/entire/cli/auth/context_store.go | Implements context-preferring Store wrapper + context list/switch/remove helpers. |
| cmd/entire/cli/auth.go | Adds new auth contexts / auth use commands and routes auth status/list through ContextStore. |
| cmd/entire/cli/auth_context.go | Implements entire auth contexts and entire auth use. |
| cmd/entire/cli/auth_context_test.go | Tests local listing output for contexts. |
| .goreleaser.yaml | Adds a second build for git-remote-entire and ships it in Homebrew casks. |
10b2341 to
213245b
Compare
Move the git remote-helper that powers `entire://` clones out of
entiredb and into the CLI so a single binary can serve both roles:
when invoked as `git-remote-entire` (via a shipped symlink), `main()`
branches into the helper protocol loop before any cobra/stdout
machinery runs, since git parses our stdout as a strict pkt-line
stream.
Copied verbatim (import paths rewritten):
- internal/remotehelper/{githelper,gitproto,transport,replicas,
debuglog,httpdebug} — the protocol loop, failover transport, and
replica cache.
- internal/entireclient/{discovery,httpclient} — the two
self-contained client packages transport depends on.
Adaptations:
- dropped the antithesis assertion from transport/proxy.go (no
fuzzer in this repo).
- replaced entire.io/version.GitRemoteHelperAgent() with a
githelper.Agent var the entrypoint stamps from versioninfo.
- wired auth to the CLI's existing single-context
auth.RepoScopedToken. Full multi-context resolution (contexts.json
+ cluster discovery) is a follow-up; this stage gets a working
`git clone entire://` for the current login.
Adds github.com/gofrs/flock (used by discovery's on-disk cache).
scripts/entire-dev now builds/runs the ./cmd/entire package instead of
cmd/entire/main.go alone — package main is now split across main.go and
remotehelper.go, and naming a single file left the dispatch out and
broke the local-dev git hooks.
This is the first stage of consolidating the helper into the CLI; the
shared credential packages will later be promoted to auth-go and
entiredb's cmd/git-remote-entire retired.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 647feebd712d
The CLI's golangci config enables goconst (entiredb's did not), so the
vendored protocol code tripped it on repeated string literals. Extract
package-level constants:
- githelper: serviceUploadPack/serviceReceivePack for the git
smart-HTTP service names, optionValueTrue for git's boolean option
literal, and test fixtures (testRefMain, testRefFeatureBranch,
testHeadSHA).
- gitproto: test fixtures (testRefMain, testRefFeatureBranch,
testHeadSHA, testFakePackData).
No behaviour change; golangci-lint run (repo config, no --fix) reports
0 issues for the ported packages.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: e0c9102fc8fb
Bring in the kubectl-style context store and its supporting packages so
the CLI can adopt entiredb's shared credential model (Stage 3 of folding
the git remote helper into the CLI):
- internal/entireclient/tokenstore — OS-keyring storage under the
entire-core:<issuer> / entire:<cluster> service scheme, keyed by
handle, with the "token|expires_at" value encoding and sibling
:refresh slots. This is the layout entiredb's CLIs already use, so
once login is repointed at it a login from either CLI is visible to
the other.
- internal/entireclient/contexts — contexts.json (named contexts,
cluster_contexts bindings, current_context) with atomic flock'd I/O.
- internal/entireclient/clusterdiscovery — resolve a cluster host to a
context via cluster_contexts or /.well-known/entire-cluster.json.
- internal/entireclient/repocreds — RFC 8693 repo-scoped token cache.
- internal/entireclient/httputil — just PostOAuthToken (the /oauth/token
POST helper); the rest of entiredb's httputil (otel, middleware) is
intentionally left out.
Adaptations for the CLI's stricter lint: wrap the PostOAuthToken error
(preserving *OAuthError via %w) and annotate the tokenstore backend
seam's interface returns. Copied verbatim otherwise; package tests pass
and golangci-lint reports 0 issues for the new packages.
Not yet wired into login/coreapi/the helper — that follows.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 6190bcfc6e80
…per cluster Make `git clone entire://<any-cluster>/…` work from a single login by moving the helper onto the shared contexts.json credential model, while leaving the control-plane readers on the legacy entry untouched. Login (cmd/entire/cli/auth.RecordLoginContext): after the device flow, derive the issuer (core URL), handle, and expiry from the token's own claims, store the token in the keyring under entire-core:<issuer>/handle with the token|expiry encoding, and record/activate the matching context. login.go calls this as a best-effort dual-write — the legacy entire-cli/<authBaseURL> entry is still written, so the control plane is unaffected and a failure here only warns. Read-time migration (MigrateLegacyLoginContext): users who logged in before this change get a context synthesized from their legacy keyring token on first helper use, so no re-login is required. Idempotent. Helper (cmd/entire/remotehelper.go): replace Stage 1's single-context auth.RepoScopedToken with clusterdiscovery.ResolveContextForCluster (an explicit cluster_contexts binding, else /.well-known discovery matched against local contexts) feeding a repocreds cache that exchanges the resolved context's login JWT for repo-scoped tokens per (repo, action). Not done here (intentionally deferred): repointing the coreapi / control-plane token readers at contexts.json. The legacy store keeps them working; converging them is a low-risk follow-up, ideally with the auth-go extraction. Tests cover RecordLoginContext (happy path + missing issuer), the legacy migration (synthesize + idempotent no-op), and LoginTokenForContext. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 932f4d26a055
Complete the contexts.json migration so a single login authenticates
every CLI command, not just clone, and let users manage which login is
active.
Readers (ContextStore): a context-preferring view over the legacy keyring
store — token reads resolve the active contexts.json context (its
entire-core:<issuer>/handle slot), falling back to the legacy
entire-cli/<authBaseURL> entry for pre-contexts logins. Wired into the
three control-plane readers:
- LookupCurrentToken
- the tokenmanager built in defaultManager (so coreapi's bearer +
RepoScopedToken resolve via contexts)
- auth status / list's "logged in?" check
This closes the asymmetry from the previous commit: a login created by
entiredb's CLIs (or `entire login`) now authenticates `entire repo
create`, org/project/grant, etc. — not only `git clone entire://`.
Logout now clears the active context (RemoveCurrentContext: drop its
keyring token + remove it from contexts.json, advancing current_context),
so the context-preferring readers no longer report a stale login. `auth
revoke --current` reuses the same path.
Context switching:
- `entire auth contexts` — list stored contexts, marking the current.
- `entire auth use <context>` — switch the active context.
cluster_contexts bindings remain auto-managed by clusterdiscovery on
clone, so no manual bind command is needed yet.
Tests cover ContextStore's read preference + fallback, RemoveCurrentContext
(clear + idempotent), SetCurrentContext (switch + unknown-context error),
and the contexts listing. Full unit suite green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 0e31e56b21a4
RemoveCurrentContext: collapse the separate Load + Modify into a single locked Modify. The old form read contexts.json unlocked to grab the keychain slot, then deleted under a second lock — a TOCTOU window where a concurrent `auth use` could orphan the wrong keychain entry — plus a redundant read and a re-find guard. Now it captures the slot and deletes the context in one closure, then does the best-effort keychain delete sequenced off what was actually removed. Contexts: drop the unused named returns and the loadErr shadow of the named err return. No behavior change; auth package tests and golangci-lint (no --fix) green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 1b6464f1632d
`git clone entire://…` needs a git-remote-entire helper on PATH. Rather
than the single entire binary dispatching on argv[0] (which forces a
symlink on Unix and a copy/shim on Windows, where shims can rewrite
argv[0] and break the dispatch), ship a small dedicated binary. A real
git-remote-entire[.exe] that git execs directly removes the argv[0]
dependency entirely, so symlink elevation, scoop shims, and per-package-
manager alias hacks all stop mattering.
- cmd/git-remote-entire: lean main that runs the remote-helper loop
directly, importing only the protocol + auth packages (no cobra
tree). ~28MB stripped vs the full CLI's ~45MB — much cheaper than
shipping entire twice. (Logic relocated from the earlier argv[0]
path; behavior unchanged.)
- cmd/entire: drop the argv[0] dispatch — each binary now has its own
identity.
- internal/remotehelper.BinaryName: the helper name, used by the lean
main for its git agent string and usage text.
- .goreleaser.yaml: second build for cmd/git-remote-entire (versioninfo
stamp only, no telemetry) listed in both homebrew casks' binaries;
scoop ships archive binaries too.
- install.sh: install both binaries from the archive.
Full unit (5939), integration (352), e2e canary (59+4) green; lint clean
(no --fix) on changed packages; shellcheck clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 6ded54a6ff1d
…text Address three review findings in the multi-context auth flow: 1. Same-core accounts no longer collapse. RecordLoginContext keyed every context name on the core host, so a second login to the same core (a different handle) overwrote the first via Upsert. Contexts are now keyed by identity (core URL + handle): the same identity updates in place, a second identity on the same core gets its own context (handle@host). 2. `entire auth revoke` honors context-backed logins. The command used auth.NewStore() instead of NewContextStore(), so revoke --current missed a context-only login, and revoke-by-id's self-revoke cleanup deleted only the legacy keyring entry. It now reads through the context store and, when it detects it revoked this CLI's own token, clears the active context too (threaded as a clearContext func, matching logout). 3. Migration no longer hijacks the active context. RecordLoginContext took an `activate` flag: login passes true (use-context semantics), but the git helper's read-time MigrateLegacyLoginContext passes false, so a first `git clone entire://…` after upgrade records the legacy context without silently switching current_context. It still sets current when none exists. Tests: same-core coexistence + re-login-in-place, migration preserves an existing current context. Full unit/integration/canary green; lint clean (no --fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 50995fd5dc3f
Second round of review fixes for the multi-context flow: 1. New-cluster resolution prefers the active context. ResolveContextForCluster used matches[0] among the issuer's contexts, so a core with several accounts (alice@core, bob@core) auto-bound whichever was saved first — silently authenticating as the wrong user. It now prefers current_context when it's one of the eligible matches, falling back to the first otherwise. 2. Legacy migration keys on (issuer, handle), not issuer alone. MigrateLegacyLoginContext skipped when *any* context for the issuer existed, so a legacy bob@core was never migrated if alice@core was already present. It now skips only when this exact identity is represented. 3. Auth recovery hints name this CLI. The cluster login hint and the info/refs 401 message told users to run `entire-core auth login --base-url`, but this repo ships `entire` and login isn't a --base-url command. They now point at `entire login` / `entire auth use`. 4. Replica cache updates are atomic. discovery.ModifyCache does load-mutate-save under a single flock (mirroring contexts.Modify); replicas.Persist/Invalidate use it instead of the racy LoadCache + mutate + SaveCache, so concurrent clone/fetch/push no longer clobber nodes.json. Tests: prefer-current resolution, same-core different-handle migration, ModifyCache atomicity + abort-on-fn-error, updated hint assertions. Full unit/integration/canary green; lint clean (no --fix). The clusterdiscovery/discovery/replicas changes are in vendored packages and should be reconciled upstream in the planned auth-go extraction. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 74e46943f957
Third round of review fixes for the multi-context flow: 1. `auth use` no longer over-claims cross-core control-plane switching. The active context only retargets which login `git clone entire://…` prefers; control-plane commands (auth status/list/revoke, org/project/repo/grant) still target the configured auth host (ENTIRE_AUTH_BASE_URL / default) because the token manager and coreapi client are built against api.AuthBaseURL(), not the context's core. So `auth use` now warns when the target context authenticates against a different core, and the docs say so plainly. (Full per-context host resolution is the deferred multi-core control-plane work.) 2. Logout truly logs out. RemoveCurrentContext let File.Delete advance current_context to a surviving context, so `entire logout` (and `auth revoke --current`) silently re-authenticated as another account while printing "Logged out". It now clears current_context — logged out means logged out; other contexts survive and can be re-selected with `entire auth use`. 3. Cluster login hint is actionable for non-default cores. `entire login` only targets the configured host, so RenderLoginHint now suggests `ENTIRE_AUTH_BASE_URL=<url> entire login` (or `entire auth use` for an existing login) so users can authenticate against a cluster's non-default accepted core. Tests: cross-core warning (warns vs silent same-core), logout clears current without switching, hint mentions ENTIRE_AUTH_BASE_URL. Full unit/integration/canary green; lint clean (no --fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 191830b5fec0
Two follow-up review fixes about the gap between messaging and the cluster_contexts bindings reality: 1. `auth use` no longer claims clone "uses this context regardless". ResolveContextForCluster honors an existing cluster→context binding first, so for an already-bound cluster the active context is irrelevant to clone/push. The warning and help now say so: a bound cluster keeps its binding; the active context applies only to clusters not yet bound. 2. Logout is honest about surviving logins, plus `entire logout --all`. A default `entire logout` removes only the active context, but other saved contexts (and their cluster bindings) can still authenticate clone/push — so "Logged out." overstated it. It now appends a note listing the remaining logins and pointing at --all / `auth use`. New `entire logout --all` removes every context, its keyring token, and all cluster bindings (auth.RemoveAllContexts) for a true full logout. Tests: RemoveAllContexts (clears contexts + bindings + current, idempotent), noteRemainingLogins (silent when none, names survivors + --all otherwise). Full unit/integration/canary green; lint clean (no --fix). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 18eda08cf5f1
The cross-core warning only fires for different-core switches, so a same-core `auth use` gave no hint that an already-bound cluster keeps its existing binding for clone/push. Add a Long help that states the binding precedence (active context is a fallback; bound clusters keep their binding) and the control-plane-targeting caveat, so it's discoverable via `entire auth use --help` for all cases, not only cross-core. Help-text only; no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 7d4609679179
The package/const comments still described the old argv[0]-dispatch + installer-symlink model. There's now a dedicated cmd/git-remote-entire binary and BinaryName is used only by it (usage text + git agent string). Update the docs to match. Comment-only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 519fe02a690f
Re-vendor githelper/connect.go from entiredb origin/main, which gained v0/v1 shallow-fetch (`--depth`) support in #1821 after our initial vendoring. Without it, `git clone --depth=N entire://…` hangs: canonical git in connect mode parks on a `deepen…` wants-flush waiting for the server's shallow/unshallow update, and the old relay never fired the probe POST. The port adds the deepen-triggered probe round and strips the duplicated shallow prefix the stateless-RPC server emits on each round. Client-side only and self-contained (upstream go-git pktline/packp + our gitproto/debuglog); the server half of #1821 is already deployed. Re-applied our local adaptations (githelper.Agent, serviceUploadPack/ serviceReceivePack consts). The upstream regression test is an e2e that needs entiredb's server/storage/gittest, so it isn't portable here; the logic is a verbatim port and githelper unit tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Entire-Checkpoint: a5960da6a540
The helper attaches a core-minted bearer JWT to every data-plane request,
but the replica set it dials was populated from attacker-influenceable
sources with no host validation:
- the X-Entire-Replicas response header (warm + cold paths),
- the redirect Location salvage target, and
- the on-disk node cache (~/.cache/entire/nodes.json).
A malicious entry domain (reachable via a submodule pointing at an
attacker entire:// URL, or a .well-known/entire-cluster.json naming the
victim's real core) could thus advertise replicas on arbitrary hosts and
have the helper POST git-upload-pack to them with the token attached.
Likewise anything running as the user that amends nodes.json could
redirect credentialed requests off-cluster.
checkRedirect already enforces a HostInCluster trust boundary, but only
for HTTP-level redirects, never for the node base URLs the proxy dials
directly. Extend that same boundary to the replica set:
- filterReplicas drops off-cluster URLs at every ingress (cache load,
X-Entire-Replicas header, response-URL fallback) so they never enter
the node set or get persisted;
- setAuthOrError refuses to stamp a token onto any request bound for an
out-of-cluster host (defense-in-depth chokepoint);
- coldInfoRefs refuses an off-cluster Location salvage target.
Scoping is keyed on clusterHost (the host in the user-typed entire:// URL)
and is a no-op when unset, matching checkRedirect. This closes the
token-forwarding leak; it does not change the upstream decision to mint an
aud-bound token for the addressed cluster.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 1eca60eefa33

https://entire.io/gh/entireio/cli/trails/460
Stacked on #1299 — review/merge that first; this PR's base is
soph/entire-control-plane-client, so the diff here is only the helper + multi-context auth work.What this does
Makes
git clone entire://…work from the CLI by folding entiredb'sgit-remote-entireinto this repo, and re-platforms the CLI's auth onto the shared kubectl-stylecontexts.jsoncredential model so a single login authenticates both control-plane commands and clones — across any entitled cluster.Shape of the change
1. Vendor the remote-helper protocol (
internal/remotehelper/*+internal/entireclient/{discovery,httpclient}) — copied from entiredb with import rewrites; antithesis assertion dropped,version.GitRemoteHelperAgentswapped for a local stamp. Kept verbatim so the planned auth-go extraction stays a clean move.2. Shared credential model (
internal/entireclient/{tokenstore,contexts,clusterdiscovery,repocreds,httputil}) — adopts entiredb's keychain layout (entire-core:<issuer>/handle,token|expiryencoding) so a login in either CLI is visible to the other.3. Multi-context auth
entire logindual-writes acontexts.jsoncontext (derived from the token's own claims) alongside the legacy entry.ContextStorewires the control-plane readers (tokenmanager,LookupCurrentToken,auth status/list) atcontexts.json, with legacy fallback + read-time migration so existing users aren't logged out.entire auth contexts/entire auth use <ctx>to list/switch;logoutclears the active context.4. Dedicated
git-remote-entirebinary (cmd/git-remote-entire) — a lean main (~28 MB stripped vs the CLI's ~45 MB) that runs the helper loop directly. A real binary on PATH means no argv[0]/symlink/shim fragility, so Windows + Homebrew + scoop all work. Shipped via goreleaser (second build + caskbinaries) andinstall.sh.Notes / follow-ups (not in this PR)
internal/entireclient/*packages are vendored copies of entiredb's; the intent is to promote them intoauth-goand have both CLIs consume from there.cmd/git-remote-entireonce this ships.goreleaser check/ snapshot before the next release.Verification
Full unit (5884), integration, and e2e canary green;
golangci-lintclean on the changed packages.Note
High Risk
Touches authentication, credential storage, logout/revoke paths, and a new git transport surface; cluster discovery changes which login applies per host, so mis-binding or migration bugs could cause wrong-cluster auth or failed clones.
Overview
This PR makes
entire://remotes work from the Entire CLI by shipping a dedicatedgit-remote-entirebinary (Goreleaser + Homebrew casks) and moving login onto a sharedcontexts.json+ keychain model aligned with entiredb.Auth: Login dual-writes a named context from JWT claims while keeping the legacy keyring entry. A
ContextStoreprefers the active context for token reads, STS, andauth status/list; logout/revoke-current also clear the active context.entire auth contextsandentire auth uselist and switch contexts. Legacy logins can be migrated at read time for the helper.Git remote: The helper resolves cluster auth via explicit bindings or
/.well-known/entire-cluster.json(no silent reuse ofcurrent_contextagainst the wrong cluster), exchanges login JWTs for repo-scoped pull/push tokens, and runs the smart-HTTP remote-helper protocol (connect/stateless fetch bridge, list, push options, replica discovery/cache).Shared client libraries under
internal/entireclient/(contexts file locking, tokenstore, repocreds, cluster discovery, HTTP transport) andinternal/remotehelper/support the helper and future auth-go consolidation.Reviewed by Cursor Bugbot for commit 1476f90. Configure here.