# auth: active context wins over ENTIRE_AUTH_BASE_URL for control plane

`81e341d`→[main](/content/gh/entireio/cli/commits/main/index.html)·

toothbrush·1mo ago·4 files·+67 added/-57 removed

Make ResolveControlPlaneTarget match auth status: the active context
always drives host+token; ENTIRE_AUTH_BASE_URL is only the fallback when
no context is active, not an override. A token minted by the context's
core can't authenticate against a different host, so "override host +
context identity" can't succeed — the active context is authoritative.

Drops the now-unused api.AuthBaseURLOverridden() helper.

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

## Sessions

dbdac822b936View transcript

## Changes

4

- cmd/entire/cli
  
  - api
  
    - Mbase_url.go-9

- auth
  
    - Mcontrol_plane.go+14/-18
    - Mcontrol_plane_test.go+38/-20

- docs/architecture
  
  - Mupstream-host-resolution.md+15/-10

```go
63 unmodified lines

return NormalizeOriginURL(raw)
}

// AuthBaseURLOverridden reports whether ENTIRE_AUTH_BASE_URL is explicitly
// set (non-empty after trimming). Control-plane host resolution treats an
// explicit override as unconditional: it pins the core to that origin and
// skips the active-context lookup, so split-host / local-dev invocations that
// already set this var keep their exact behaviour.
func AuthBaseURLOverridden() bool {
    return strings.TrimSpace(os.Getenv(AuthBaseURLEnvVar)) != ""
}

// IsSplitHost reports whether the CLI is configured for split-host —
// i.e. ENTIRE_AUTH_BASE_URL points at a different origin than the data
// API. Both sides are canonicalised via NormalizeOriginURL before
```

Mcmd/entire/cli/api/base_url.go-9

```go
22 unmodified lines

// ResolveControlPlaneTarget chooses which core the control-plane commands talk
// to and how their bearer is obtained. The control-plane host *is* a core, so
// there is no /.well-known discovery here — the active context already names
// the core. Precedence:
//
// The core. Precedence (matching `auth status`):
//
// 1. ENTIRE_AUTH_BASE_URL explicitly set -> that origin verbatim, bearer via
//    the singleton manager (TokenForResource), exactly as before. The env
//    var stays an unconditional override so split-host / local-dev
//    invocations are untouched.
// 2. otherwise the active contexts.json login -> its CoreURL, with a
//    per-context refreshing bearer (silent JWT re-mint). This is what makes
// 1. the active contexts.json login -> its CoreURL, with a per-context
//    refreshing bearer (silent JWT re-mint). This is what makes
//    `entire auth use <ctx>` retarget the control plane onto that core.
// 3. no active context -> the configured default origin + TokenForResource,
//    the pre-contexts behaviour for users who never ran `entire auth use`.
// 2. no active context -> the configured auth origin (ENTIRE_AUTH_BASE_URL or
//    the default) + TokenForResource, the pre-contexts fallback.
//
// ENTIRE_AUTH_BASE_URL is the fallback host, not an override: a token minted
// by the active context's core can't authenticate against a different host, so
// "use the override host but the context's identity" can't succeed. The active
// context always wins when present.
func ResolveControlPlaneTarget() (ControlPlaneTarget, error) {
    if api.AuthBaseURLOverridden() {
        return staticControlPlaneTarget(), nil
    }

c, ok, err := activeContext()
    if err != nil {
        return ControlPlaneTarget{}, err
    }
    return ControlPlaneTarget{CoreURL: strings.TrimRight(c.CoreURL, "/"), TokenSource: src}, nil
}

// staticControlPlaneTarget is the pre-contexts path: dial the configured auth
// origin and resolve the bearer through the singleton manager, which performs
// an RFC 8693 exchange when the stored token's audience doesn't cover the
// core. Used for an explicit ENTIRE_AUTH_BASE_URL override and as the
// no-active-context fallback.
// staticControlPlaneTarget is the no-active-context fallback: dial the
// configured auth origin (ENTIRE_AUTH_BASE_URL or the default) and resolve the
// bearer through the singleton manager, which performs an RFC 8693 exchange
// when the stored token's audience doesn't cover the core.
func staticControlPlaneTarget() ControlPlaneTarget {
    base := strings.TrimRight(api.AuthBaseURL(), "/")
    // The exchange's resource must be the bare origin; OriginOnly strips any
```

Mcmd/entire/cli/auth/control_plane.go+14/-18

```go
24 unmodified lines

// An explicit ENTIRE_AUTH_BASE_URL pins the core to that origin and skips the
// active-context lookup entirely — the override is unconditional.
func TestResolveControlPlaneTarget_EnvOverrideWins(t *testing.T) {
// The active context wins even when ENTIRE_AUTH_BASE_URL is set to a different
// origin: the env var is a fallback host, not an override (a token from the
// context's core can't authenticate against the env host anyway).
func TestResolveControlPlaneTarget_ActiveContextBeatsEnv(t *testing.T) {
    configDir := t.TempDir()
    t.Setenv("ENTIRE_CONFIG_DIR", configDir)
    t.Setenv(api.AuthBaseURLEnvVar, "https://override.example")

// An active context on a *different* core must be ignored under override.
    writeActiveContext(t, configDir, "ctx", "https://other-core.example", "alice", "svc")
    const coreURL = "https://other-core.example"
    writeActiveContext(t, configDir, "ctx", coreURL, "alice", "svc")

target, err := ResolveControlPlaneTarget()
    if err != nil {
        t.Fatalf("ResolveControlPlaneTarget: %v", err)
    }
    if want := api.AuthBaseURL(); target.CoreURL != want {
        t.Fatalf("CoreURL = %q, want the override origin %q (not the context core)", target.CoreURL, want)
    }
    if target.CoreURL != coreURL {
        t.Fatalf("CoreURL = %q, want the active context's core %q (env is only a fallback)", target.CoreURL, coreURL)
    }
}

// With no override and no active context, the target falls back to the
// configured default auth origin (pre-contexts behaviour).
func TestResolveControlPlaneTarget_NoContextFallsBackToDefault(t *testing.T) {
    configDir := t.TempDir() // empty: no contexts.json
    t.Setenv("ENTIRE_CONFIG_DIR", configDir)
    t.Setenv(api.AuthBaseURLEnvVar, "")
// With no active context, the target falls back to the configured auth origin
// — the default when ENTIRE_AUTH_BASE_URL is unset, or the env value when set
// (the env var is the fallback host).
func TestResolveControlPlaneTarget_NoContextFallsBackToAuthBaseURL(t *testing.T) {
    t.Run("default when env unset", func(t *testing.T) {
        configDir := t.TempDir() // empty: no contexts.json
        t.Setenv("ENTIRE_CONFIG_DIR", configDir)
        t.Setenv(api.AuthBaseURLEnvVar, "")

target, err := ResolveControlPlaneTarget()
        if err != nil {
            t.Fatalf("ResolveControlPlaneTarget: %v", err)
        }
        if want := api.AuthBaseURL(); target.CoreURL != want {
            t.Fatalf("CoreURL = %q, want the default auth origin %q", target.CoreURL, want)
        }
        target, err := ResolveControlPlaneTarget()
        if err != nil {
            t.Fatalf("ResolveControlPlaneTarget: %v", err)
        }
        if want := api.AuthBaseURL(); target.CoreURL != want {
            t.Fatalf("CoreURL = %q, want the default auth origin %q", target.CoreURL, want)
        }
    })

t.Run("env value when set", func(t *testing.T) {
        configDir := t.TempDir()
        t.Setenv("ENTIRE_CONFIG_DIR", configDir)
        t.Setenv(api.AuthBaseURLEnvVar, "https://fallback.example")

target, err := ResolveControlPlaneTarget()
        if err != nil {
            t.Fatalf("ResolveControlPlaneTarget: %v", err)
        }
        if want := api.AuthBaseURL(); target.CoreURL != want {
            t.Fatalf("CoreURL = %q, want the env fallback origin %q", target.CoreURL, want)
        }
    })
}
```

Mcmd/entire/cli/auth/control_plane_test.go+38/-20

### Control plane (done — this slice)

The host *is* a core, so there is no discovery. `coreapi.New()` consults
`auth.ResolveControlPlaneTarget()`, which mirrors `auth status`:

1. **`ENTIRE_AUTH_BASE_URL` set** → that origin, verbatim; bearer via the
   singleton token manager (`TokenForResource`). The env var is an
   unconditional override, so split-host / local-dev invocations are
   untouched.
2. **else active context** → its `CoreURL`, with a **per-context refreshing** bearer (`auth.NewRefreshingLoginProvider`): the token manager is keyed on
   `c.CoreURL` as issuer, so store reads and refresh/STS hit the right core,
   and an expired access token is silently re-minted from the stored refresh
   token. This is what makes `entire auth use <ctx>` actually retarget
   `org`/`repo`/`project`/`grant`.
3. **else** (no active context) → the configured default origin +
   `TokenForResource` — the pre-contexts behaviour.
2. **else** (no active context) → the configured auth origin
   (`ENTIRE_AUTH_BASE_URL` or the default) + `TokenForResource` — the
   pre-contexts fallback.

`ENTIRE_AUTH_BASE_URL` is the fallback host, **not** an override: a token
minted by the active context's core can't authenticate against a different
host, so the active context always wins when present. (At login time the env
var still chooses where to authenticate, and the resulting context's `CoreURL`
*is* that host — so local-dev / split-host setups keep working.)

Key files: `cmd/entire/cli/auth/control_plane.go` (resolver),
`cmd/entire/cli/auth/refresh.go` (per-context refreshing provider),
   the `activity` / `search` / `trail` / `dispatch` constructors
   (`NewAuthenticatedAPIClient`, `dispatch.NewCloudClient`, `search.Search`).

`ENTIRE_AUTH_BASE_URL` / `ENTIRE_API_BASE_URL` remain explicit overrides: when
set they short-circuit discovery; discovery only runs when they are unset.
`ENTIRE_API_BASE_URL` names the resource host to dial (the discovery target);
the context is then chosen from the issuers that host advertises. It does not
need to be paired with `ENTIRE_AUTH_BASE_URL` once discovery exists — that is
the whole point of the well-known.
