providers: never pair a caller-supplied base URL with the server's API key - #3
Open
egeboy35 wants to merge 1 commit into
Open
providers: never pair a caller-supplied base URL with the server's API key#3egeboy35 wants to merge 1 commit into
egeboy35 wants to merge 1 commit into
Conversation
egeboy35
force-pushed
the
fix/server-key-not-paired-with-caller-url
branch
from
September 1, 2026 12:28
bafcfa9 to
78460e6
Compare
…I key
`resolveBackend` takes each half of the pair from a different place:
const baseUrl =
(options.baseUrl && String(options.baseUrl).trim()) || // the request
process.env[def.baseUrlEnv] || def.baseUrlDefault || "";
let apiKey = options.apiKey && String(options.apiKey);
if (!apiKey && def.keyEnv) apiKey = process.env[def.keyEnv] || ""; // the server
The request wins on the URL. The server fills in the key whenever the request
omits it. A request that supplies base_url and omits api_key therefore gets
the server's key aimed at the caller's host, and `/api/agent` forwards both to
the sidecar, which builds `OpenAI(base_url, api_key)` and puts
`Authorization: Bearer <the server's key>` on the wire.
Measured by calling the real resolver with a scratch HOME holding a marker
key, nothing stubbed:
normal use, no override
baseUrl -> https://integrate.api.nvidia.com/v1
apiKey -> nvapi-SERVER-HELD-KEY
base_url supplied, api_key omitted
baseUrl -> http://somewhere-else.example/v1
apiKey -> nvapi-SERVER-HELD-KEY <-- to a host the caller named
app/api/chat/route.js:2 states the intent this violates:
// The NVIDIA_API_KEY stays on the server — the browser never sees it.
Which holds literally. The browser does not see the key; it chooses where the
key is sent, and reads the reply.
A key the server holds is now only applied when the request either does not
override the base URL or names the same endpoint the server configured.
Otherwise it is withheld, `keyWithheld` is set, and the route answers 400
naming both URLs and the variable, rather than the misleading "NVIDIA_API_KEY
is not set" 500 the existing EMPTY check would produce.
Every path the Agent Lab actually uses is unchanged. AgentLab.js sends
base_url for exactly two backends: `custom`, where the user types their own
key into the UI beside the URL, and `llama`, whose keyEnv is null. The one
behaviour change is that CUSTOM_API_KEY now requires CUSTOM_BASE_URL to be set
to be used at all -- documented in .env.local.example, which did not mention
either variable before.
Adds lib/providers.test.mjs -- 15 tests on Node's built-in runner, so the app
gains no dependency:
node --test edgeLLM/nextjs-nemotron-app/lib/providers.test.mjs
Against this branch: 15 pass. Against lib/providers.js as it stands on main:
9 pass, 6 fail. Four of the six are behaviour --
a redirected base URL does not receive the server key
whitespace around a redirected URL does not slip the key through
a redirected URL under an env-configured endpoint is still a redirect
the custom backend withholds its key when pointed somewhere else
-- and two assert on `keyWithheld`/`serverBaseUrl`, which this change adds, so
they could not pass before it. The behavioural tests deliberately assert
nothing about those fields, so the four above fail on what the resolver does
rather than on a field that is missing. The 9 that pass either way are the
ordinary paths.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
egeboy35
force-pushed
the
fix/server-key-not-paired-with-caller-url
branch
from
September 1, 2026 12:43
78460e6 to
f0e4ada
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
resolveBackendtakes each half of the pair from a different place:The request wins on the URL. The server fills in the key whenever the request omits it. So a request that supplies
base_urland omitsapi_keygets the server's key aimed at the caller's host — and/api/agentforwards both to the sidecar, which buildsOpenAI(base_url, api_key)and putsAuthorization: Bearer <the server's key>on the wire.Measured
The real resolver, nothing stubbed, with a scratch
HOMEholding a marker key:app/api/chat/route.js:2states the intent this violates:// The NVIDIA_API_KEY stays on the server — the browser never sees it.Which holds literally. The browser does not see the key; it chooses where the key is sent, and reads the reply.
The change
A key the server holds is applied only when the request either does not override the base URL, or names the same endpoint the server configured. Otherwise it is withheld,
keyWithheldis set, and the route answers 400 naming both URLs and the variable — rather than the misleading "NVIDIA_API_KEY is not set" 500 the existingEMPTYcheck would produce.Every path the Agent Lab actually uses is unchanged.
AgentLab.jssendsbase_urlfor exactly two backends:custom, where the user types their own key into the UI beside the URL, andllama, whosekeyEnvisnull. The one behaviour change is thatCUSTOM_API_KEYnow requiresCUSTOM_BASE_URLto be set to be used at all — documented in.env.local.example, which did not mention either variable before.Tests
Adds
lib/providers.test.mjs— 15 tests on Node's built-in runner, so the app gains no dependency.Against this branch: 15 pass. Against
lib/providers.jsas it stands onmain: 9 pass, 6 fail. Four of the six are behaviour:The other two assert on
keyWithheld/serverBaseUrl, which this change adds, so they could not pass before it. The behavioural tests deliberately assert nothing about those fields, so the four above fail on what the resolver does rather than on a field that is missing. The 9 that pass either way are the ordinary paths.