Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/proxy/edge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,17 @@ export function makeFetchApiSecrets({
authToken: string,
model: string | null,
org_name?: string,
project_id?: string,
): Promise<APISecret[]> => {
// Project-level secrets are not supported on the edge proxy since they are
// stored in the data plane
if (project_id) {
throw new Error(
"Project-level AI provider secrets are not supported on the edge proxy. " +
"Please use the hosted API proxy or remove the x-bt-project-id header.",
);
}

// First try to decode & verify as JWT. We gate this on Braintrust JWT
// format, not just any JWT, in case a future model provider uses JWT as
// the auth token.
Expand Down
5 changes: 5 additions & 0 deletions packages/proxy/src/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const CACHE_HEADER = "x-bt-use-cache";
export const CACHE_TTL_HEADER = "x-bt-cache-ttl";
export const CREDS_CACHE_HEADER = "x-bt-use-creds-cache";
export const ORG_NAME_HEADER = "x-bt-org-name";
export const PROJECT_ID_HEADER = "x-bt-project-id";
export const ENDPOINT_NAME_HEADER = "x-bt-endpoint-name";
export const FORMAT_HEADER = "x-bt-stream-fmt";

Expand Down Expand Up @@ -219,6 +220,7 @@ export async function proxyV1({
authToken: string,
model: string | null,
org_name?: string,
project_id?: string,
) => Promise<APISecret[]>;
cacheGet: (encryptionKey: string, key: string) => Promise<string | null>;
cachePut: (
Expand Down Expand Up @@ -293,6 +295,8 @@ export async function proxyV1({
);

let orgName: string | undefined = proxyHeaders[ORG_NAME_HEADER] ?? undefined;
const projectId: string | undefined =
proxyHeaders[PROJECT_ID_HEADER] ?? undefined;

const pieces = url
.split("/")
Expand Down Expand Up @@ -614,6 +618,7 @@ export async function proxyV1({
cachedAuthToken || authToken,
model,
orgName,
projectId,
Comment on lines 614 to +621

Choose a reason for hiding this comment

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

P1 Badge Pass project IDs to secrets lookup

The new PROJECT_ID_HEADER is parsed and passed to getApiSecrets here, but none of the existing implementations (e.g., lookupApiSecret in apis/node/src/login.ts) use the fifth argument or include project_id in the /api/secret POST. When a caller sends x-bt-project-id expecting project-scoped credentials, the proxy still fetches and caches org-level secrets keyed only by token/model, so a secret from one project can be reused for another and the project-level secret selection never takes effect.

Useful? React with 👍 / 👎.

);
logHistogram?.({
name: "aiproxy.secrets_fetch_time_ms",
Expand Down