From 03bf13e9e3c9ce585bada6ce2007bfed3abd764c Mon Sep 17 00:00:00 2001 From: Anton Evseev <78427278+slayoffer@users.noreply.github.com> Date: Fri, 30 Jan 2026 18:06:00 +1000 Subject: [PATCH] fix(control-plane): pass API key to dataplane for tenant auth (#243) The control plane proxy routes never sent an Authorization header to the dataplane API. With the tenant extension active, all GUI requests failed with "Invalid API key". Add HINDSIGHT_CP_DATAPLANE_API_KEY env var support to hindsight-client.ts and propagate auth headers to both SDK clients and all direct fetch routes. --- .../directives/[directiveId]/route.ts | 9 +++--- .../api/banks/[bankId]/directives/route.ts | 7 ++--- .../[mentalModelId]/refresh/route.ts | 5 ++-- .../mental-models/[mentalModelId]/route.ts | 9 +++--- .../api/banks/[bankId]/mental-models/route.ts | 7 ++--- .../[bankId]/observations/[modelId]/route.ts | 5 ++-- .../src/app/api/health/route.ts | 2 ++ .../src/app/api/memories/[memoryId]/route.ts | 7 ++--- .../src/lib/hindsight-client.ts | 28 +++++++++++++++++-- 9 files changed, 47 insertions(+), 32 deletions(-) diff --git a/hindsight-control-plane/src/app/api/banks/[bankId]/directives/[directiveId]/route.ts b/hindsight-control-plane/src/app/api/banks/[bankId]/directives/[directiveId]/route.ts index ece22087..59549d84 100644 --- a/hindsight-control-plane/src/app/api/banks/[bankId]/directives/[directiveId]/route.ts +++ b/hindsight-control-plane/src/app/api/banks/[bankId]/directives/[directiveId]/route.ts @@ -1,6 +1,5 @@ import { NextResponse } from "next/server"; - -const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; export async function GET( request: Request, @@ -15,7 +14,7 @@ export async function GET( const response = await fetch( `${DATAPLANE_URL}/v1/default/banks/${bankId}/directives/${directiveId}`, - { method: "GET" } + { method: "GET", headers: getDataplaneHeaders() } ); if (!response.ok) { @@ -49,7 +48,7 @@ export async function PATCH( `${DATAPLANE_URL}/v1/default/banks/${bankId}/directives/${directiveId}`, { method: "PATCH", - headers: { "Content-Type": "application/json" }, + headers: getDataplaneHeaders({ "Content-Type": "application/json" }), body: JSON.stringify(body), } ); @@ -84,7 +83,7 @@ export async function DELETE( const response = await fetch( `${DATAPLANE_URL}/v1/default/banks/${bankId}/directives/${directiveId}`, - { method: "DELETE" } + { method: "DELETE", headers: getDataplaneHeaders() } ); if (!response.ok) { diff --git a/hindsight-control-plane/src/app/api/banks/[bankId]/directives/route.ts b/hindsight-control-plane/src/app/api/banks/[bankId]/directives/route.ts index aa884c94..89f52bac 100644 --- a/hindsight-control-plane/src/app/api/banks/[bankId]/directives/route.ts +++ b/hindsight-control-plane/src/app/api/banks/[bankId]/directives/route.ts @@ -1,6 +1,5 @@ import { NextResponse } from "next/server"; - -const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; export async function GET(request: Request, { params }: { params: Promise<{ bankId: string }> }) { try { @@ -22,7 +21,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ bank } const url = `${DATAPLANE_URL}/v1/default/banks/${bankId}/directives${queryParams.toString() ? `?${queryParams}` : ""}`; - const response = await fetch(url, { method: "GET" }); + const response = await fetch(url, { method: "GET", headers: getDataplaneHeaders() }); if (!response.ok) { const errorText = await response.text(); @@ -50,7 +49,7 @@ export async function POST(request: Request, { params }: { params: Promise<{ ban const response = await fetch(`${DATAPLANE_URL}/v1/default/banks/${bankId}/directives`, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: getDataplaneHeaders({ "Content-Type": "application/json" }), body: JSON.stringify(body), }); diff --git a/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/[mentalModelId]/refresh/route.ts b/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/[mentalModelId]/refresh/route.ts index 4ea6148c..0ffab31a 100644 --- a/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/[mentalModelId]/refresh/route.ts +++ b/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/[mentalModelId]/refresh/route.ts @@ -1,6 +1,5 @@ import { NextResponse } from "next/server"; - -const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; export async function POST( request: Request, @@ -18,7 +17,7 @@ export async function POST( const response = await fetch( `${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${mentalModelId}/refresh`, - { method: "POST" } + { method: "POST", headers: getDataplaneHeaders() } ); if (!response.ok) { diff --git a/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/[mentalModelId]/route.ts b/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/[mentalModelId]/route.ts index 8c48f0e5..565c66bd 100644 --- a/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/[mentalModelId]/route.ts +++ b/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/[mentalModelId]/route.ts @@ -1,6 +1,5 @@ import { NextResponse } from "next/server"; - -const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; export async function GET( request: Request, @@ -18,7 +17,7 @@ export async function GET( const response = await fetch( `${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${mentalModelId}`, - { method: "GET" } + { method: "GET", headers: getDataplaneHeaders() } ); if (!response.ok) { @@ -58,7 +57,7 @@ export async function PATCH( `${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${mentalModelId}`, { method: "PATCH", - headers: { "Content-Type": "application/json" }, + headers: getDataplaneHeaders({ "Content-Type": "application/json" }), body: JSON.stringify(body), } ); @@ -96,7 +95,7 @@ export async function DELETE( const response = await fetch( `${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${mentalModelId}`, - { method: "DELETE" } + { method: "DELETE", headers: getDataplaneHeaders() } ); if (!response.ok) { diff --git a/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/route.ts b/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/route.ts index 57702d00..2b0368c6 100644 --- a/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/route.ts +++ b/hindsight-control-plane/src/app/api/banks/[bankId]/mental-models/route.ts @@ -1,6 +1,5 @@ import { NextResponse } from "next/server"; - -const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; export async function GET(request: Request, { params }: { params: Promise<{ bankId: string }> }) { try { @@ -22,7 +21,7 @@ export async function GET(request: Request, { params }: { params: Promise<{ bank } const url = `${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models${queryParams.toString() ? `?${queryParams}` : ""}`; - const response = await fetch(url, { method: "GET" }); + const response = await fetch(url, { method: "GET", headers: getDataplaneHeaders() }); if (!response.ok) { const errorText = await response.text(); @@ -53,7 +52,7 @@ export async function POST(request: Request, { params }: { params: Promise<{ ban const response = await fetch(`${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models`, { method: "POST", - headers: { "Content-Type": "application/json" }, + headers: getDataplaneHeaders({ "Content-Type": "application/json" }), body: JSON.stringify(body), }); diff --git a/hindsight-control-plane/src/app/api/banks/[bankId]/observations/[modelId]/route.ts b/hindsight-control-plane/src/app/api/banks/[bankId]/observations/[modelId]/route.ts index 97333009..ef9d8cfb 100644 --- a/hindsight-control-plane/src/app/api/banks/[bankId]/observations/[modelId]/route.ts +++ b/hindsight-control-plane/src/app/api/banks/[bankId]/observations/[modelId]/route.ts @@ -1,6 +1,5 @@ import { NextResponse } from "next/server"; - -const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; export async function GET( request: Request, @@ -15,7 +14,7 @@ export async function GET( const response = await fetch( `${DATAPLANE_URL}/v1/default/banks/${bankId}/mental-models/${modelId}`, - { method: "GET" } + { method: "GET", headers: getDataplaneHeaders() } ); if (!response.ok) { diff --git a/hindsight-control-plane/src/app/api/health/route.ts b/hindsight-control-plane/src/app/api/health/route.ts index f7c47a22..fa98cd0a 100644 --- a/hindsight-control-plane/src/app/api/health/route.ts +++ b/hindsight-control-plane/src/app/api/health/route.ts @@ -1,5 +1,6 @@ import { NextResponse } from "next/server"; import { createClient, createConfig, sdk } from "@vectorize-io/hindsight-client"; +import { getDataplaneHeaders } from "@/lib/hindsight-client"; const HEALTH_CHECK_TIMEOUT_MS = 3000; @@ -27,6 +28,7 @@ export async function GET() { createConfig({ baseUrl: dataplaneUrl, signal: controller.signal, + headers: getDataplaneHeaders(), }) ); diff --git a/hindsight-control-plane/src/app/api/memories/[memoryId]/route.ts b/hindsight-control-plane/src/app/api/memories/[memoryId]/route.ts index 67cbec28..ea0be6a6 100644 --- a/hindsight-control-plane/src/app/api/memories/[memoryId]/route.ts +++ b/hindsight-control-plane/src/app/api/memories/[memoryId]/route.ts @@ -1,6 +1,5 @@ import { NextRequest, NextResponse } from "next/server"; - -const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +import { DATAPLANE_URL, getDataplaneHeaders } from "@/lib/hindsight-client"; export async function GET( request: NextRequest, @@ -19,9 +18,7 @@ export async function GET( `${DATAPLANE_URL}/v1/default/banks/${bankId}/memories/${memoryId}`, { method: "GET", - headers: { - "Content-Type": "application/json", - }, + headers: getDataplaneHeaders({ "Content-Type": "application/json" }), } ); diff --git a/hindsight-control-plane/src/lib/hindsight-client.ts b/hindsight-control-plane/src/lib/hindsight-client.ts index fb00b21b..038865e4 100644 --- a/hindsight-control-plane/src/lib/hindsight-client.ts +++ b/hindsight-control-plane/src/lib/hindsight-client.ts @@ -5,17 +5,39 @@ import { HindsightClient, createClient, createConfig, sdk } from "@vectorize-io/hindsight-client"; -const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +export const DATAPLANE_URL = process.env.HINDSIGHT_CP_DATAPLANE_API_URL || "http://localhost:8888"; +const DATAPLANE_API_KEY = process.env.HINDSIGHT_CP_DATAPLANE_API_KEY || ""; + +/** + * Auth headers for direct fetch calls to the dataplane API. + */ +export function getDataplaneHeaders(extra?: Record): Record { + const headers: Record = { ...extra }; + if (DATAPLANE_API_KEY) { + headers["Authorization"] = `Bearer ${DATAPLANE_API_KEY}`; + } + return headers; +} /** * High-level client with convenience methods */ -export const hindsightClient = new HindsightClient({ baseUrl: DATAPLANE_URL }); +export const hindsightClient = new HindsightClient({ + baseUrl: DATAPLANE_URL, + apiKey: DATAPLANE_API_KEY || undefined, +}); /** * Low-level client for direct SDK access */ -export const lowLevelClient = createClient(createConfig({ baseUrl: DATAPLANE_URL })); +export const lowLevelClient = createClient( + createConfig({ + baseUrl: DATAPLANE_URL, + headers: DATAPLANE_API_KEY + ? { Authorization: `Bearer ${DATAPLANE_API_KEY}` } + : undefined, + }), +); /** * Export SDK functions for direct API access