Correct cluster-resolution comments to match actual token flow · Entire

Correct cluster-resolution comments to match actual token flow

ea605a6→main·

toothbrush·1mo ago·2 files·+31 added/-20 removed

The resolver comments claimed not persisting a cluster binding prevented a "drive-by clone" from minting identity-bearing JWTs. That's false: the login JWT only ever goes to the resolved context's CoreURL, never to the cluster host, and the cluster host receives only a repo-scoped, audience-pinned token. Reframe not-persisting as the correctness choice it is (immediacy of auth use / context deletion, no host-controlled host->core mapping lingering), and align the auth-contexts audit comment.

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

ee378db97d3eView transcript

Changes

2

106 unmodified lines

// printClusterBindings lists any cluster_contexts bindings so users can
// audit which hosts auto-authenticate git operations with a stored
// context. A binding means future ops against that host mint
// identity-bearing JWTs without re-checking the host's /.well-known, so an
// unrecognised entry is worth revoking with `entire auth unbind`.
// audit which hosts skip /.well-known discovery and resolve straight to a
// stored context. A binding does not hand that host your login JWT — that
// only ever goes to the bound context's core, and the host receives a
// repo-scoped, audience-pinned token (see repocreds). What a binding pins
// is which core authenticates that host, so an entry you don't recognise
// is still worth revoking with `entire auth unbind`.
func printClusterBindings(w io.Writer) error {
    bindings, err := auth.ClusterBindings()
    if err != nil {

Mcmd/entire/cli/auth_context.go+6/-4

12 unmodified lines

// Resolution order:
//
//  1. Explicit `cluster_contexts[clusterHost]` binding in contexts.json
//     pointing at an existing context — used as-is. Bindings are only
//     created by deliberate action (`entire-core context bind`, or a
//     teammate's tooling); this CLI never writes one implicitly.
//     pointing at an existing context — used as-is. Bindings are
//     created only by deliberate action; this helper never writes one.
//
//  2. Discovery via /.well-known/entire-cluster.json on the cluster
//     itself, matched against existing local contexts by the
//     advertised core_urls. The first advertised URL with a local
//     context wins. This match is used for the current invocation only
//     — we deliberately do NOT persist a cluster→context binding.
//     Persisting it would let a single drive-by clone of an
//     attacker-controlled host (e.g. via a malicious submodule whose
//     /.well-known names the victim's real core) establish a durable,
//     silent channel that re-mints identity-bearing JWTs on every
//     future fetch. Re-evaluating the live /.well-known each time keeps
//     the trust decision fresh and revocable.
//     context wins. This match applies to the current invocation only:
//     this helper does not persist a cluster→context binding.
//
//     Resolving fresh on each call is a correctness choice, not a
//     credential-safety one. The login JWT is only ever sent to the
//     resolved context's CoreURL — a core the user already holds a local
//     context for — never to clusterHost; the token clusterHost receives
//     is repo-scoped and audience-pinned to clusterHost itself (see
//     repocreds.exchange). A hostile clusterHost can at most steer us
//     toward a core we already trust; it cannot introduce a new core or
//     capture an identity-bearing token. What resolving fresh buys is
//     immediacy: `entire auth use` and context deletion take effect on
//     the next fetch with no stale binding to unbind, and no
//     host→core mapping derived from a host-controlled /.well-known
//     lingers in contexts.json.
//
//  3. No local context matches any advertised URL — return a
//     fatal-ready error with the login hint listing the cluster's
//     advertised issuers.
//
// We deliberately do NOT fall back to current_context for an unknown
// cluster host. The old fallback would silently use a staging
// context against a prod (entire.io) cluster — which
// then 400s as "unknown cluster_host" because the cluster's own
// registry doesn't know the host. The cluster's /.well-known is the
// authoritative answer to "which env am I in", so we ask it.
// cluster host. current_context can point at a different environment
// than clusterHost (e.g. a staging context against a prod cluster);
// the cluster then rejects the exchanged token with "unknown cluster_host"
// because its own registry doesn't know that core. The cluster's
// /.well-known is the authoritative answer to "which env am I in", so we
// ask it rather than guessing from the active context.
//
// debugf is optional; nil suppresses debug output.
func ResolveContextForCluster(ctx context.Context, configDir, clusterHost string, httpClient *http.Client, debugf DebugFunc) (*contexts.Context, error) {