Skip to content

fix(experts): route to the repo's entire-api cell with an identity token#1588

Merged
Soph merged 5 commits into
mainfrom
codex/experts-cell-routing
Jul 1, 2026
Merged

fix(experts): route to the repo's entire-api cell with an identity token#1588
Soph merged 5 commits into
mainfrom
codex/experts-cell-routing

Conversation

@suhaanthayyil

@suhaanthayyil suhaanthayyil commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

https://entire.io/gh/entireio/cli/trails/715

Problem

entire experts --repo owner/repo fails with 401 Not authenticated, even while logged in and while entire activity / entire search work. Repo-scoped entire-api routes (GET /api/v1/repos, POST /api/v1/repos/{id}/experts) require a jurisdictional identity token (scope=openid, aud = the cell's jurisdiction host, signed by Core JWKS) — not the entire:api-access bearer minted for the entire.io BFF. The BFF also does not proxy these routes for bearer callers, so the CLI must dial the entire-api cell directly.

Fix

Mint the identity token (token-exchange from the login JWT → audience=<jurisdiction host>, scope=openid — the exact shape the entire.io BFF's mintJurisdictionToken uses) and dial the cell directly.

  • Route to the repo's own cell, not the caller's home cell. The cli layer resolves the repo's placement from the control plane (coreapi mirrors for owner/repo; GetRepo for a ULID) and maps it to a cell apiUrl + jurisdiction via ListClusters — mirroring how the BFF mints one token per target jurisdiction and fans out to the repo's cell. Resolution is best-effort with a 5s deadline: any failure/timeout/ambiguity falls back to home-jurisdiction routing, so the common same-region path can't regress.
  • Staging (partial.to) supported. Cell-vs-BFF detection is environment-agnostic (a direct cell is any host containing .api.); the audience/core follow the entire.io / partial.to family of the configured host.
  • Local dev fixed. A loopback discovered core is honored verbatim, so the exchange no longer goes to production.
  • Security. The login JWT / identity token targets (core + cell apiUrl) are affirmatively gated to https (loopback / --insecure-http-auth excepted); home_jurisdiction is validated as a DNS label before URL templating.
  • Clearer errors. A cross-region 404 and the repo-not-found path tell the user the repo may be homed in another region; a natural-language query against a cell without code search surfaces a clean message.

Verified live against production

Ran the branch binary with a real login (read-only):

Command Result
experts . --repo entireio/cli --json HTTP 200{"repo_full_name":"entireio/cli","source":"db","profiles":[]} — repo resolved + cell reached, 401 gone
experts cmd/entire/cli/ --repo entireio/cli exit 0, clean "no provenance" (backend has none indexed yet)
experts "…query…" --repo entireio/cli clean "code search not available" message (cell 503)
experts . --repo entireio/does-not-exist clear region/onboarding/access hint, no 401

Empty provenance + query-503 are backend-side (entire-api/entiredb work in progress), not this CLI change. The genuine cross-region path routes correctly in code and falls back safely, but wasn't exercised live (no repo homed in another region available).

Checks

  • go build ./... clean · mise run lint 0 issues · mise run test:ci green (unit + integration + e2e canary)
  • Three-pass adversarial audit (find → refute → synthesize) seeded with the entire-api cell + entire.io BFF ground truth; the final pass raised 0 medium+ findings.
  • Copilot review comments addressed (allowInsecure scoping; duplicate error prefix).

Entire trail: https://entire.io/gh/entireio/cli/trails/715

🤖 Generated with Claude Code

entire-api repo-scoped routes require a jurisdictional identity token
(scope=openid), not the narrowed api-access bearer minted for entire.io.
When the data host is the BFF, resolve the home cell from core clusters,
exchange the login JWT for the jurisdiction audience, and dial the cell
directly (COR-666).

Co-authored-by: Cursor <cursoragent@cursor.com>
@suhaanthayyil suhaanthayyil requested a review from a team as a code owner July 1, 2026 14:06
Copilot AI review requested due to automatic review settings July 1, 2026 14:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Routes the entire experts CLI command to a jurisdiction-specific entire-api cell by exchanging the login JWT for a jurisdictional identity token (openid scope) and dialing the cell directly, addressing prod 401s caused by using the narrower api-access bearer against repo-scoped entire-api routes.

Changes:

  • Switch entire experts to construct a cell-routed API client instead of the generic data API client.
  • Add auth.NewEntireAPICellClient to discover trusted login context, derive home_jurisdiction, resolve the correct cell base URL (when starting from the entire.io BFF), and perform the token exchange.
  • Add unit tests covering routing behavior and BFF detection.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
cmd/entire/cli/experts_cmd.go Uses the new cell-routed authenticated client for experts endpoints.
cmd/entire/cli/auth/cell_data_api.go Implements cell discovery + jurisdiction token exchange flow for entire-api access.
cmd/entire/cli/auth/cell_data_api_test.go Adds tests validating home-cell routing and direct-cell base URL behavior.
cmd/entire/cli/api_client.go Exposes a CLI-level constructor for the new entire-api cell client.

Comment thread cmd/entire/cli/auth/cell_data_api.go Outdated
Comment thread cmd/entire/cli/auth/cell_data_api.go
@suhaanthayyil suhaanthayyil marked this pull request as draft July 1, 2026 14:18
suhaanthayyil and others added 2 commits July 1, 2026 10:49
… auth

The experts cell client had several defects surfaced by an adversarial audit
against the entire-api cell + entire.io BFF ground truth:

- Cross-jurisdiction routing: it dialed the CALLER's home cell (login JWT
  home_jurisdiction), so a repo homed in another region always failed with a
  misleading "not onboarded/no access" error. Now the cli layer resolves the
  repo's OWN cell + jurisdiction from the control plane (coreapi mirrors for
  owner/repo, GetRepo for a ULID; mapped to a cell apiUrl via ListClusters) and
  passes it as an explicit CellTarget — mirroring how the BFF mints one token
  per target jurisdiction and fans out to the repo's cell. Resolution is
  best-effort: any failure falls back to home-jurisdiction routing, so the
  common same-region path can never regress.

- Staging (partial.to) was unsupported: isEntireIOBFF only matched *.entire.io
  and the audience/core templates were hardcoded to prod. Cell-vs-BFF detection
  is now environment-agnostic (a direct cell is any host with ".api."), and the
  audience/core follow the environment family (entire.io vs partial.to) derived
  from the configured data host, with the discovered core as fallback.

- Local dev sent the exchange to PRODUCTION core (the prod template ignored the
  discovered local core). A loopback discovered core is now honored verbatim so
  the local login JWT is exchanged at the local core.

- Security: the login JWT / identity token targets (core + cell apiUrl) are now
  gated to https (loopback/--insecure-http-auth excepted), and home_jurisdiction
  is validated as a DNS label before templating into URLs.

- Clearer errors: a cross-region 404 ("repo not in this region") and the
  repo-not-found path now tell the user the repo may be homed in another region
  instead of implying it isn't onboarded / accessible.

Adds unit tests for per-repo cell resolution, cross-jurisdiction routing,
environment-family templates, loopback core, and jurisdiction-label validation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Address the second adversarial audit pass over the cell-routing fix:

- Bound the best-effort control-plane resolution with a 5s deadline so a
  reachable-but-hung control plane degrades to home-jurisdiction routing
  instead of stalling the command (the "any failure falls back" contract must
  hold for slow cores, not just erroring ones).
- Skip failed/suspended mirror placements (not just archived) when choosing the
  repo's cell, so an unhealthy placement can't manufacture false cross-region
  ambiguity or become a sole "active" host.
- Apply ENTIRE_CORE_BASE_URL_TEMPLATE unconditionally (placeholder-less =
  single fixed core), matching the BFF and the audience handler.
- Lowercase the catalog jurisdiction before the auth-layer label check (DNS is
  case-insensitive) so a non-lowercase value routes instead of hard-failing.
- requireSafeExchangeURL now affirmatively requires https (not merely "not
  http"), rejecting ftp/ws/scheme-relative/empty targets for the login JWT.
- Introduce an injectable control-plane seam (expertsCoreClient +
  newExpertsCoreClient; listMirrorsForRepo takes a mirrorLister interface) so
  the resolver is unit-testable against a fake control plane.

Tests: requireSafeExchangeURL guard table; prod + fixed-template core URLs;
resolveExpertsCellTarget across ULID / owner-repo / multi-region-fallback /
no-match / lowercased-jurisdiction; a real cluster-host join; and a
non-tautological direct-cell test (asserts exchange happens, no cluster
resolution).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@suhaanthayyil suhaanthayyil changed the title fix(experts): route CLI to home entire-api cell fix(experts): route to the repo's entire-api cell with an identity token Jul 1, 2026
suhaanthayyil and others added 2 commits July 1, 2026 11:10
Live prod testing showed the entire-api cell returns a bare "Service
Unavailable" (no "code search" text) when a natural-language query hits a cell
without code search configured. The previous substring match missed that and
surfaced a raw "fetch experts: API error" wrap. Treat any 503 on a query as the
code-search-unavailable case (path scopes still fall through to the generic
error). Adds a test for the bare-503 query case.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- allowInsecure gates only on selected.CoreURL (+ --insecure-http-auth), not the
  data origin, matching the sibling ResolveDataAPIToken; a loopback data API no
  longer relaxes HTTPS for a non-loopback core.
- Drop the duplicate "refresh login token:" wrap; NewRefreshingLoginProvider
  already prefixes it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@suhaanthayyil suhaanthayyil marked this pull request as ready for review July 1, 2026 15:19
@Soph Soph merged commit 0ea0b0f into main Jul 1, 2026
9 checks passed
@Soph Soph deleted the codex/experts-cell-routing branch July 1, 2026 18:08
Soph added a commit that referenced this pull request Jul 1, 2026
Reconcile onto the cell-routing client that landed with the experts work
(#1588) instead of a parallel mechanism. `activity` and `recap` call the
/me/* endpoints entire-api serves, so they now go through
auth.NewEntireAPICellClient (home-jurisdiction routing, target=nil) — one
routing/token path across the CLI.

Cell routing is a best-effort upgrade: any failure building the cell
client (no cell for the region, not logged in, discovery/exchange error)
falls back to the data API, which also serves /me/* and yields the
canonical auth errors — so existing users are unaffected until their
region has a cell. recap keeps its render-through-401 behaviour via that
fallback.

recap's team column still needs the repo ULID for /me/recap?repo=;
currentRepoID resolves it best-effort from the mirror id (which entire-api
treats as the repo_id), empty → personal recap only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: dc4fb6be0484
Soph added a commit that referenced this pull request Jul 5, 2026
Reconcile onto the cell-routing client that landed with the experts work
(#1588) instead of a parallel mechanism. `activity` and `recap` call the
/me/* endpoints entire-api serves, so they now go through
auth.NewEntireAPICellClient (home-jurisdiction routing, target=nil) — one
routing/token path across the CLI.

Cell routing is a best-effort upgrade: any failure building the cell
client (no cell for the region, not logged in, discovery/exchange error)
falls back to the data API, which also serves /me/* and yields the
canonical auth errors — so existing users are unaffected until their
region has a cell. recap keeps its render-through-401 behaviour via that
fallback.

recap's team column still needs the repo ULID for /me/recap?repo=;
currentRepoID resolves it best-effort from the mirror id (which entire-api
treats as the repo_id), empty → personal recap only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: dc4fb6be0484
Soph added a commit that referenced this pull request Jul 5, 2026
Reconcile onto the cell-routing client that landed with the experts work
(#1588) instead of a parallel mechanism. `activity` and `recap` call the
/me/* endpoints entire-api serves, so they now go through
auth.NewEntireAPICellClient (home-jurisdiction routing, target=nil) — one
routing/token path across the CLI.

Cell routing is a best-effort upgrade: any failure building the cell
client (no cell for the region, not logged in, discovery/exchange error)
falls back to the data API, which also serves /me/* and yields the
canonical auth errors — so existing users are unaffected until their
region has a cell. recap keeps its render-through-401 behaviour via that
fallback.

recap's team column still needs the repo ULID for /me/recap?repo=;
currentRepoID resolves it best-effort from the mirror id (which entire-api
treats as the repo_id), empty → personal recap only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Entire-Checkpoint: dc4fb6be0484
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