Skip to content

feat(eve): take the dev Gateway credential from a launcher-served URL - #4997

Open
ruiconti wants to merge 2 commits into
mainfrom
rui/model-credential-url
Open

ruiconti wants to merge 2 commits into
mainfrom
rui/model-credential-url

Conversation

@ruiconti

Copy link
Copy Markdown
Contributor

A program that launches eve dev for someone (for example a hub that runs that person's agents on their laptop) already holds their AI Gateway sign-in, but had no way to hand it to eve. eve only looked at its own /login store, the Vercel CLI, and environment keys, so the laptop needed its own sign-in.

Change

  • EVE_MODEL_CREDENTIAL_URL + EVE_MODEL_CREDENTIAL_TOKEN: when set, resolveGatewayModelCredential GETs the credential from that URL with Authorization: Bearer <token>, ahead of every other source. This covers both the dev host's broker and direct callers, so steps and compaction use it alike.
  • When Gateway returns 401 for an oauth token, the existing retry asks the URL again with x-eve-model-rejected-token-sha256 set to the refused token's hash, so the server can refresh.
  • The request/validation code is shared with the loopback broker client (fetchModelCredential). A server { error } message is surfaced instead of the generic "/login" hint.
  • The local Gateway is enabled when only the URL is set.

Response shape: { kind: "oauth" | "api-key" | "oidc", token, teamId? } or { error } with a failure status. Documented in docs/guides/dev-tui.md.

Tests

src/internal/model-auth/gateway-credential.test.ts: URL wins over a machine key, rejected token is hashed into the header, server error is surfaced, URL without token is refused, the local Gateway is enabled by the URL alone.

A program that launches `eve dev` on someone's behalf (a hub running
bots on a laptop, say) already holds that person's Gateway sign-in, but
had no way to hand it to eve: eve only looked at its own /login store,
the Vercel CLI, and environment keys.

With EVE_MODEL_CREDENTIAL_URL and EVE_MODEL_CREDENTIAL_TOKEN set, every
Gateway model call, compaction included, GETs its credential from that
URL with the token as a bearer, ahead of every other source. When
Gateway refuses an oauth token, eve asks again with the refused token's
SHA-256 in x-eve-model-rejected-token-sha256 so the server can refresh
it. The broker fetch is shared with the existing loopback broker client.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
@vercel

vercel Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
eve-docs Ready Ready Preview, v0 Oct 17, 2026 9:03pm UTC
eve-pkg Ready Ready Preview, v0 Oct 17, 2026 9:03pm UTC

Comment on lines +60 to +64
throw new Error(
typeof value.error === "string"
? value.error
: "The model connection is unavailable. Run /login to reconnect.",
);

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.

The served URL's { error } won't reach the user in a normal eve dev session. Workers have EVE_DEV_CONTROL_URL set, so createLocalGateway calls readDevelopmentModelCredential("gateway") first. That request goes to handleDevelopmentModelCredentialRequest on the host, and the host calls resolveGatewayModelCredential() there. Its catch {} (development-broker-server.ts:54-59) swallows every error and returns the fixed "The model connection is unavailable. Run /login to reconnect." with a 503. So the hub message, and the EVE_MODEL_CREDENTIAL_URL is set without EVE_MODEL_CREDENTIAL_TOKEN error, both turn into the /login hint this PR is trying to avoid, and /login can't fix either one.

The new test calls resolveGatewayModelCredential directly, so it never goes through the broker. Suggest forwarding the error message from the gateway branch in the broker server, at least for served-URL failures, and adding a test that goes through handleDevelopmentModelCredentialRequest with the URL env set.

const token = process.env[MODEL_CREDENTIAL_TOKEN_ENV];
if (!token)
throw new Error(`${MODEL_CREDENTIAL_URL_ENV} is set without ${MODEL_CREDENTIAL_TOKEN_ENV}.`);
return fetchModelCredential(served, { authorization: `Bearer ${token}` }, rejectedToken);

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.

EVE_MODEL_CREDENTIAL_TOKEN goes out as a bearer header to whatever EVE_MODEL_CREDENTIAL_URL holds, with no URL checks. A typo or a stale http:// value would send the launcher token in cleartext to a non-local host, and the response it returns becomes the Gateway credential. The sibling loopback client checks this case (readDevelopmentModelCredential rejects anything that isn't loopback http: or that has userinfo). Suggest parsing the URL once and allowing only https: or loopback http: (reuse isLoopbackHostname), rejecting username/password, and naming the env var in the error.

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Bundle + Package Summary: apps/fixtures/weather-agent

Key takeaways

  • No notable deltas vs main (f4e8ef1).

Delta vs main (f4e8ef1)

Area Metric Baseline Current Delta
Package Packed tarball 7.66 MB 7.66 MB +607 B ⚠️
Package Unpacked publish size 26.97 MB 26.98 MB +2.3 kB ⚠️
Package Installed footprint 75.11 MB 75.12 MB +2.3 kB ⚠️
Package Published files 4171 4171 0
Package Installed files 8182 8182 0
Package Installed package instances 34 34 0
Package Distinct installed package names 32 32 0
Package Installed dependency edges 51 51 0
Package Installed optional peer edges 9 9 0
Runtime Unique function payloads 2 2 0
Runtime Total function bytes 20.77 MB 20.77 MB +1.3 kB ⚠️
Runtime Public routes 18 18 0
Changed function payloads vs main (f4e8ef1) (2)
Function Status Baseline Current Delta Route changes
functions/__server.func changed 10.38 MB 10.39 MB +630 B ⚠️ none
functions/.well-known/workflow/v1/flow.func changed 10.38 MB 10.39 MB +630 B ⚠️ none

eve init install

Metric Baseline Current Delta
Installed footprint 113.50 MB 113.50 MB +2.3 kB ⚠️
Installed packages 97 97 0
dependencies 4 4 0
devDependencies 2 2 0
Dependency package bytes 45.25 MB 45.25 MB +2.3 kB ⚠️
devDependency package bytes 5.11 MB 5.11 MB 0 B ➖
Build Metadata
  • Preset: vercel
  • Nitro: nitro@3.0.260903-beta
  • Output directory: apps/fixtures/weather-agent/.vercel/output
  • Build metadata timestamp: 2026-10-17T21:03:33.465Z
  • Route aliases: 18 public, 1 internal (19 total aliases)
  • Vercel routes in config: 21
  • Severity legend: 🔴 dominant/large, 🟠 notable, 🟡 watch, ⚪ small
Package Drill-Down

Package Details

  • Package: eve@0.80.1
  • Package directory: packages/eve
  • Tarball: 7.66 MB (eve-0.80.1.tgz)
  • Unpacked payload: 26.98 MB across 4171 published files
  • Installed footprint: 75.12 MB across 8182 installed files
  • Installed root package: 26.98 MB
  • Installed dependencies: 48.14 MB
  • Installed package instances: 34
  • Distinct installed package names: 32
  • Installed dependency edges: 51
  • Installed optional peer edges: 9
  • Runtime dependencies: 2
  • Peer dependencies: 7 (6 optional)

Installed footprint is measured from an isolated temporary npm install of the packed tarball.
Graph metrics read only package.json files in package directories directly beneath a node_modules boundary, including nested boundaries. Each directory is one package instance; distinct names come from those manifests. Dependency edges count each unique name in dependencies or optionalDependencies per instance; optional peer edges count peerDependencies marked optional.

Heavy installed dependencies

  • eve: 26.98 MB (35.9%)
  • @rolldown/binding-linux-x64-gnu: 19.81 MB (26.4%)
  • ai: 8.24 MB (11.0%)
  • zod: 6.15 MB (8.2%)
  • undici: 3.58 MB (4.8%)
Publish payload breakdown
Published file size
🟠 dist/src/compiled/shadcn-registry/index.js       [#####...................] 3.85 MB 14.3%
🟠 dist/src/compiled/@photon-ai/chat-adapter-ime... [###.....................] 2.29 MB 8.5%
🟠 dist/src/compiled/@ai-sdk/code-mode/index.js     [#.......................] 1.03 MB 3.8%
🟡 dist/src/compiled/@vercel/blob/index.js          [#.......................] 604.9 kB 2.2%
🟡 dist/src/compiled/_chunks/workflow/undici-D-I... [#.......................] 521.9 kB 1.9%
🔴 Other published files                            [########################] 18.69 MB 69.3%
Installed footprint breakdown
Installed package size
🔴 eve                             [########################] 26.98 MB 35.9%
🔴 @rolldown/binding-linux-x64-gnu [##################......] 19.81 MB 26.4%
🔴 ai                              [#######.................] 8.24 MB 11.0%
🔴 zod                             [#####...................] 6.15 MB 8.2%
🟠 undici                          [###.....................] 3.58 MB 4.8%
🟠 nitro                           [##......................] 1.89 MB 2.5%
🔴 Other installed packages        [########................] 8.46 MB 11.3%
Runtime dependencies (2)
Package Range Notes
nitro 3.0.260903-beta
undici 8.10.2
Peer dependencies (7)
Package Range Notes
@opentelemetry/api ^1.0.0 optional peer
ai catalog:
braintrust ^3.0.0 optional peer
chat ^4.42.0 optional peer
dd-trace ^6.13.0 optional peer
just-bash ^3.1.0 optional peer
microsandbox ^0.5.0 optional peer
eve init install drill-down

eve init install details

  • Command: eve init my-agent
  • Package manager: npm
  • Installed footprint: 113.50 MB across 10099 installed files
  • Installed packages: 97 total (91 transitive-only)
  • dependencies: 4 direct packages totaling 45.25 MB
  • devDependencies: 2 direct packages totaling 5.11 MB
  • Other transitive package files: 63.14 MB

Installed footprint is measured from an isolated temporary eve init my-agent using the current packed eve tarball.

Heavy installed dependencies

  • @typescript/typescript-linux-x64: 27.95 MB (24.6%)
  • eve: 26.98 MB (23.8%)
  • @rolldown/binding-linux-x64-gnu: 19.81 MB (17.5%)
  • zod: 9.76 MB (8.6%)
  • ai: 8.24 MB (7.3%)
Installed footprint breakdown
Installed package size
🔴 @typescript/typescript-linux-x64 [########################] 27.95 MB 24.6%
🔴 eve                              [#######################.] 26.98 MB 23.8%
🔴 @rolldown/binding-linux-x64-gnu  [#################.......] 19.81 MB 17.5%
🔴 zod                              [########................] 9.76 MB 8.6%
🔴 ai                               [#######.................] 8.24 MB 7.3%
🟠 undici                           [###.....................] 3.58 MB 3.1%
🔴 Other installed packages         [###############.........] 17.19 MB 15.1%
dependencies (4)
Package Range Installed size Share
@vercel/connect 2.5.2 276.3 kB 0.2%
ai ^7.0.137 8.24 MB 7.3%
eve file:eve-0.80.1.tgz 26.98 MB 23.8%
zod 4.5.4 9.76 MB 8.6%
devDependencies (2)
Package Range Installed size Share
@types/node 24.x 2.61 MB 2.3%
typescript 7.0.2 2.50 MB 2.2%
Function Drill-Down

Payload Size Graph

Unique function payload size and share of total
🔴 functions/.well-known/workflow/v1/flow.func     [########################] 10.39 MB 50.0%
🔴 functions/__server.func                         [########################] 10.39 MB 50.0%

Top Function Payloads

🟠 functions/.well-known/workflow/v1/flow.func • 1 public route • 10.39 MB
Metric Value
Public routes /.well-known/workflow/v1/flow
Runtime nodejs24.x
Handler index.mjs
Payload 10.39 MB
Function files 10.39 MB across 122 files
Traced dependencies 0 B
Signal 🟠 Bundled file _chunks/vercel.web.mjs is 2.38 MB (22.9%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 _chunks/vercel.web.mjs                         [############............] 2.38 MB 22.9%
🟠 _libs/undici.mjs                               [#####...................] 1.01 MB 9.7%
🟡 _chunks/sandbox3.mjs                           [####....................] 815.4 kB 7.9%
🟡 _chunks/compiled-artifacts-instrumentation.mjs [####....................] 734.8 kB 7.1%
🟡 _chunks/token-util-Bla5Z0G9.mjs                [####....................] 704.7 kB 6.8%
🔴 Other bundled files                            [########################] 4.74 MB 45.7%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x",
  "maxDuration": "max",
  "experimentalTriggers": [
    {
      "type": "queue/v2beta",
      "topic": "__eve776561746865722d6167656e74_wkf_workflow_*",
      "consumer": "default",
      "retryAfterSeconds": 5,
      "initialDelaySeconds": 0
    }
  ],
  "environment": {
    "WORKFLOW_PRECONDITION_GUARD": "1"
  }
}

🟠 functions/__server.func • 17 public routes, 1 internal alias • 10.39 MB
Metric Value
Public routes /
/.well-known/workflow/v1/webhook/[token]
/eve/v1/callback/[token]
/eve/v1/connections/[name]/callback/[attemptId]/[token]
/eve/v1/connections/[name]/callback/[token]
/eve/v1/health
/eve/v1/info
/eve/v1/session
/eve/v1/session/[parentSessionId]/subagents/[callId]/[childSessionId]/stream
/eve/v1/session/[sessionId]
/eve/v1/session/[sessionId]/cancel
/eve/v1/session/[sessionId]/clear
/eve/v1/session/[sessionId]/compact
/eve/v1/session/[sessionId]/reset
/eve/v1/session/[sessionId]/stream
/eve/v1/session/[sessionId]/stubs
/eve/v1/task-input/[token]
Internal aliases /__server
Runtime nodejs24.x
Handler index.mjs
Payload 10.39 MB
Function files 10.39 MB across 122 files
Traced dependencies 0 B
Signal 🟠 Bundled file _chunks/vercel.web.mjs is 2.38 MB (22.9%)

🟠 🔎 Dependency Analysis

📦 Bundled files:

Bundled file size
🟠 _chunks/vercel.web.mjs                         [############............] 2.38 MB 22.9%
🟠 _libs/undici.mjs                               [#####...................] 1.01 MB 9.7%
🟡 _chunks/sandbox3.mjs                           [####....................] 815.4 kB 7.9%
🟡 _chunks/compiled-artifacts-instrumentation.mjs [####....................] 734.8 kB 7.1%
🟡 _chunks/token-util-Bla5Z0G9.mjs                [####....................] 704.7 kB 6.8%
🔴 Other bundled files                            [########################] 4.74 MB 45.7%

🧾 Vercel Config

{
  "handler": "index.mjs",
  "launcherType": "Nodejs",
  "shouldAddHelpers": false,
  "supportsResponseStreaming": true,
  "runtime": "nodejs24.x"
}

Build Timing: e2e/fixtures/agent-tools-sandbox

This is an informational timing measurement inside eve build, from preflight through publication. Output-size measurement and profile writing are excluded.

Build mode: deployable Vercel build with sandbox template prewarm included.

  • Build pipeline: 2.51 s -> 2.42 s (-85.1 ms) vs main (f4e8ef1).
  • Timing is informational: shared GitHub runners are too variable for a hard timing budget.
Detailed phase timings vs `main (f4e8ef1)`
Phase Baseline Current Delta
extension.check 15.0 ms 0.3 ms -14.7 ms
project.resolve 0.3 ms 0.2 ms -0.1 ms
workspace.create 0.4 ms 0.3 ms -0.1 ms
host.prepare 523.4 ms 444.4 ms -79.0 ms
vercel.service-prefix.resolve 0.9 ms 0.9 ms 0.0 ms
nitro.create 410.8 ms 390.8 ms -20.0 ms
sandbox.prewarm 327.9 ms 334.3 ms +6.4 ms
nitro.cache.prepare 0.2 ms 0.2 ms 0.0 ms
nitro.prepare 0.6 ms 0.5 ms -0.1 ms
nitro.public-assets 0.6 ms 0.6 ms 0.0 ms
nitro.prerender 0.5 ms 0.3 ms -0.2 ms
nitro.bundle 1.12 s 1.05 s -61.9 ms
nitro.cache.write 0.2 ms 0.2 ms 0.0 ms
vercel.workflow-function.materialize 59.7 ms 149.9 ms +90.2 ms
agent-summary.emit 0.5 ms 0.5 ms 0.0 ms
connect-manifest.emit 0.2 ms 0.2 ms 0.0 ms
nitro.close 0.1 ms 0.1 ms 0.0 ms
output.publish 2.3 ms 2.4 ms +0.1 ms
workspace.remove 1.5 ms 1.5 ms 0.0 ms

eve dev's credential relay replaced every failure with "Run /login to
reconnect", so a turn never saw the served URL's own message (say, that
the sign-in needs renewing in the app that launched eve), and got advice
that doesn't apply when the launcher holds the sign-in. The relay now
passes that message through; other resolver errors stay generic.

Tested on the path a dev turn takes, worker model to relay to served
URL: the URL's error reaches the turn, and a token Gateway refused is
refreshed by asking the URL again with the refused token's hash.

Signed-off-by: Rui Conti <ruiconti@gmail.com>
const token = process.env[MODEL_CREDENTIAL_TOKEN_ENV];
if (!token)
throw new Error(`${MODEL_CREDENTIAL_URL_ENV} is set without ${MODEL_CREDENTIAL_TOKEN_ENV}.`);
return fetchModelCredential(served, { authorization: `Bearer ${token}` }, rejectedToken);

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.

The served-credential branch of resolveGatewayModelCredential sends the launcher's secret EVE_MODEL_CREDENTIAL_TOKEN as a Bearer header to the raw EVE_MODEL_CREDENTIAL_URL without validating the URL, allowing cleartext secret leaks / SSRF to an arbitrary host.

Fix on Vercel

@ruiconti
ruiconti marked this pull request as ready for review October 17, 2026 21:09
@ruiconti
ruiconti requested a review from a team as a code owner October 17, 2026 21:09

This branch was successfully deployed

2 active deployments
Preview – eve-docs — 5fe93212 Deployed Oct 17, 2026 by vercel[bot]
Preview – eve-pkg — 5fe93212 Deployed Oct 17, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant