src/dashboard.ts

import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { Hono } from "hono"; import { serveStatic } from "hono/bun"; import type { Store } from "./store/store.ts"; import { normalizeDashboardHost } from "./config.ts"; import { classifyApiAccessPath, isAllowedPublicApiPath, isPublicTunnelRequestHost } from "./services/publicIngressAccess.ts"; import { attachAuthRoutes, hasValidDashboardSessionCookie, isDashboardAuthSessionApiPath } from "./dashboard/routesAuth.ts"; import { attachSettingsRoutes } from "./dashboard/routesSettings.ts"; import { attachOAuthRoutes } from "./dashboard/routesOAuth.ts"; import { attachMetricsRoutes } from "./dashboard/routesMetrics.ts"; import { attachVoiceRoutes } from "./dashboard/routesVoice.ts"; import { BonjourAdvertiser } from "./services/bonjourAdvertiser.ts"; import { createDashboardServerHandle, DashboardHttpError, getRequestIp, isApiPath, type DashboardApp, type DashboardEnv, type DashboardServerHandle, type DashboardSseClient, stripApiPrefix, STREAM_INGEST_API_PATH } from "./dashboard/shared.ts";

const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(_filename); const PUBLIC_FRAME_REQUEST_WINDOW_MS = 60_000; const PUBLIC_FRAME_REQUEST_MAX_PER_WINDOW = 1200; const PUBLIC_FRAME_DECLARED_BYTES_MAX = 6_000_000; const PUBLIC_SHARE_FRAME_PATH_RE = /^/api/voice/share-session/[a-z0-9-]{16,}/frame/?$/i;

function isLocalDashboardHost(value: string) { const normalized = String(value || "").trim().toLowerCase(); return ( normalized === "127.0.0.1" || normalized === "localhost" || normalized === "::1" || normalized === "[::1]" ); }

export interface DashboardAppConfig { dashboardPort: number; dashboardHost: string; dashboardToken: string; dashboardSettingsSaveDebug?: boolean | null; publicApiToken: string; elevenLabsApiKey?: string | null; anthropicApiKey?: string | null; openaiApiKey?: string | null; claudeOAuthRefreshToken?: string | null; openaiOAuthRefreshToken?: string | null; xaiApiKey?: string | null; }

export interface DashboardBot { applyRuntimeSettings(settings: unknown): Promise; reloadOAuthProviders?(): Promise<{ claudeOAuth: boolean; codexOAuth: boolean }>; getRuntimeState(): Record<string, unknown> & { voice?: { activeCount?: unknown; sessions?: Array<Record<string, unknown>>; }; }; getGuilds(): Array<{ id: string; name: string }>; getGuildChannels(guildId: string): unknown; purgeGuildMemoryRuntime?(guildId: string): Promise | unknown; requestVoiceJoinFromDashboard?(payload: { guildId: string | null; requesterUserId: string | null; textChannelId: string | null; source: string; }): Promise; ingestVoiceStreamFrame(payload: { guildId: string; streamerUserId: string | null; mimeType: string; dataBase64: string; source: string; }): Promise; }

export interface DashboardMemory { readMemoryMarkdown(opts?: { guildId?: string | null; }): Promise; refreshMemoryMarkdown(): Promise; purgeGuildMemory?(opts?: { guildId?: string | null; }): Promise<{ ok: boolean; reason?: string; guildId?: string | null; durableFactsDeleted?: number; durableFactVectorsDeleted?: number; conversationMessagesDeleted?: number; conversationVectorsDeleted?: number; reflectionEventsDeleted?: number; sessionSummariesDeleted?: number; journalEntriesDeleted?: number; journalFilesTouched?: number; summaryRefreshed?: boolean; }>; loadFactProfile?(payload: { userId?: string | null; guildId?: string | null; participantIds?: string[]; participantNames?: Record<string, string>; includeOwner?: boolean; }): { participantProfiles?: unknown[]; selfFacts?: unknown[]; loreFacts?: unknown[]; ownerFacts?: unknown[]; userFacts?: unknown[]; relevantFacts?: unknown[]; guidanceFacts?: unknown[]; }; loadUserFactProfile?(payload: { userId?: string | null; guildId?: string | null; }): { userFacts?: unknown[]; }; loadGuildFactProfile?(payload: { guildId?: string | null; }): { selfFacts?: unknown[]; loreFacts?: unknown[]; }; loadOwnerFactProfile?(): { ownerFacts?: unknown[]; guidanceFacts?: unknown[]; }; loadBehavioralFactsForPrompt?(payload: { guildId: string; channelId?: string | null; queryText: string; participantIds?: string[]; settings?: unknown; trace?: Record<string, unknown>; limit?: number; }): Promise<unknown[]>; searchDurableFacts(payload: { guildId?: string | null; scope?: "user" | "guild" | "owner" | "owner_private" | "all"; queryText: string; settings?: Record<string, unknown>; channelId?: string | null; subjectIds?: string[] | null; factTypes?: string[] | null; trace?: Record<string, unknown>; limit?: number; }): Promise<unknown[]>; searchConversationHistory?(payload: { guildId?: string | null; channelId?: string | null; queryText: string; settings?: Record<string, unknown>; trace?: Record<string, unknown>; limit?: number; maxAgeHours?: number; before?: number; after?: number; }): Promise<unknown[]>; getRecentVoiceSessionSummariesForPrompt?(payload: { guildId: string; channelId?: string | null; referenceAtMs?: number | null; limit?: number; windowMinutes?: number; }): Array<Record<string, unknown>>; }

export interface DashboardPublicHttpsState { enabled?: boolean; publicUrl?: string; [key: string]: unknown; }

export interface DashboardPublicHttpsEntrypoint { getState?(): DashboardPublicHttpsState | null; }

export interface DashboardScreenShareSessionManager { getRuntimeState?(): unknown; renderSharePage(token: string): { statusCode?: number | null; html?: string | null; }; createSession(payload: { guildId: string; channelId: string; requesterUserId: string; requesterDisplayName?: string; targetUserId?: string | null; source?: string; }): Promise<Record<string, unknown>>; ingestFrameByToken(payload: { token: string; mimeType: string; dataBase64: string; source?: string; }): Promise<Record<string, unknown>>; stopSessionByToken(payload: { token: string; reason: string; }): Promise | unknown; }

interface DashboardDeps { appConfig: DashboardAppConfig; store: Store; bot: DashboardBot; memory: DashboardMemory; publicHttpsEntrypoint?: DashboardPublicHttpsEntrypoint | null; screenShareSessionManager?: DashboardScreenShareSessionManager | null; }

export function createDashboardServer({ appConfig, store, bot, memory, publicHttpsEntrypoint = null, screenShareSessionManager = null }: DashboardDeps): { app: DashboardApp; server: DashboardServerHandle; } { const dashboardHost = normalizeDashboardHost(appConfig.dashboardHost); const dashboardToken = String(appConfig.dashboardToken || "").trim(); if (!dashboardToken && !isLocalDashboardHost(dashboardHost)) { throw new Error("DASHBOARD_TOKEN is required when DASHBOARD_HOST is not loopback-only."); }

const app = new Hono(); const publicFrameIngressRateLimit = new Map<string, FixedWindowBucket>(); const getStatsPayload = (guildId: string | null = null) => { const botRuntime = bot.getRuntimeState(); return { stats: store.getStats({ guildId }), runtime: { ...botRuntime, publicHttps: publicHttpsEntrypoint?.getState?.() || null, screenShare: screenShareSessionManager?.getRuntimeState?.() || null } }; };

app.onError((error, c) => { if (error instanceof DashboardHttpError) { if (error.responseKind === "text") { return new Response(String(error.responseBody), { status: error.status, headers: { "content-type": "text/plain; charset=UTF-8" } }); } return Response.json(error.responseBody, { status: error.status }); }

console.error("Dashboard request failed:", error);
if (isApiPath(c.req.path)) {
  return c.json(
    {
      error: String(error instanceof Error ? error.message : error)
    },
    500
  );
}
return c.text("Internal Server Error", 500);

});

app.notFound((c) => { if (isApiPath(c.req.path)) { return c.json({ error: "Not found." }, 404); } return c.text("Not found.", 404); });

app.use("*", async (c, next) => { if (!isPublicFrameIngressPath(c.req.path)) { await next(); return; }

const contentLengthHeader = String(c.req.header("content-length") || "").trim();
if (contentLengthHeader) {
  const declaredBytes = Number(contentLengthHeader);
  if (Number.isFinite(declaredBytes) && declaredBytes > PUBLIC_FRAME_DECLARED_BYTES_MAX) {
    return c.json(
      {
        accepted: false,
        reason: "payload_too_large"
      },
      413
    );
  }
}

const callerIp = getRequestIp(c);
const rateKey = `${callerIp}|${c.req.path}`;
const allowed = consumeFixedWindowRateLimit({
  buckets: publicFrameIngressRateLimit,
  key: rateKey,
  nowMs: Date.now(),
  windowMs: PUBLIC_FRAME_REQUEST_WINDOW_MS,
  maxRequests: PUBLIC_FRAME_REQUEST_MAX_PER_WINDOW
});
if (!allowed) {
  return c.json(
    {
      accepted: false,
      reason: "ingest_rate_limited"
    },
    429
  );
}

await next();

});

app.use("*", async (c, next) => { if (!isApiPath(c.req.path)) { await next(); return; }

const apiPath = stripApiPrefix(c.req.path);
if (isDashboardAuthSessionApiPath(apiPath)) {
  await next();
  return;
}
const apiAccessKind = classifyApiAccessPath(apiPath);
const isPublicApiRoute = isAllowedPublicApiPath(apiPath);
const dashboardToken = String(appConfig.dashboardToken || "").trim();
const publicApiToken = String(appConfig.publicApiToken || "").trim();
const presentedDashboardToken = c.req.header("x-dashboard-token") || "";
const presentedPublicToken = c.req.header("x-public-api-token") || "";
const isDashboardSessionAuthorized = await hasValidDashboardSessionCookie(c, dashboardToken);
const isDashboardAuthorized =
  (Boolean(dashboardToken) && presentedDashboardToken === dashboardToken) || isDashboardSessionAuthorized;
const isPublicApiAuthorized = Boolean(publicApiToken) && presentedPublicToken === publicApiToken;
const isPublicTunnelRequest = isRequestFromPublicTunnel(c, publicHttpsEntrypoint);
const publicHttpsEnabled = Boolean(publicHttpsEntrypoint?.getState?.()?.enabled);

if (isDashboardAuthorized) {
  await next();
  return;
}
if (apiAccessKind === "public_session_token") {
  await next();
  return;
}
if (apiAccessKind === "public_header_token" && isPublicApiAuthorized) {
  await next();
  return;
}

if (isPublicTunnelRequest && !isPublicApiRoute) {
  return c.json({ error: "Not found." }, 404);
}

if (apiAccessKind === "public_header_token") {
  if (!dashboardToken && !publicApiToken) {
    return c.json(
      {
        accepted: false,
        reason: "dashboard_or_public_api_token_required"
      },
      503
    );
  }
  if (publicApiToken && !isPublicApiAuthorized) {
    return c.json(
      {
        accepted: false,
        reason: "unauthorized_public_api_token"
      },
      401
    );
  }
  return c.json(
    {
      accepted: false,
      reason: "unauthorized_dashboard_token"
    },
    401
  );
}

if (!dashboardToken) {
  if (publicHttpsEnabled) {
    return c.json(
      {
        error: "dashboard_token_required_when_public_https_enabled"
      },
      503
    );
  }
  await next();
  return;
}

return c.json({ error: "Unauthorized. Provide x-dashboard-token." }, 401);

});

const voiceSseClients = new Set(); const activitySseClients = new Set(); const writeSseEvent = async (client: DashboardSseClient, eventName: string, payload: unknown) => { const wirePayload = `event: ${String(eventName || "message")} data: ${JSON.stringify(payload)}

`; await client.write(wirePayload); }; const broadcastSseEvent = ( clients: Set, eventName: string, payload: unknown ) => { if (clients.size === 0) return; for (const client of clients) { void writeSseEvent(client, eventName, payload).catch(() => { clients.delete(client); }); } };

attachAuthRoutes(app, { appConfig, publicHttpsEntrypoint }); attachSettingsRoutes(app, { store, bot, appConfig }); attachOAuthRoutes(app, { store, appConfig, bot }); attachMetricsRoutes(app, { store, publicHttpsEntrypoint, getStatsPayload, activitySseClients, writeSseEvent }); attachVoiceRoutes(app, { store, bot, memory, screenShareSessionManager, voiceSseClients });

const previousActionListener = typeof store.onActionLogged === "function" ? store.onActionLogged : null; store.onActionLogged = (action) => { if (previousActionListener) { try { previousActionListener(action); } catch { // keep dashboard listener resilient } }

if (activitySseClients.size > 0) {
  broadcastSseEvent(activitySseClients, "action_event", action);
  broadcastSseEvent(activitySseClients, "stats_update", getStatsPayload());
}
if (action?.kind?.startsWith("voice_") && voiceSseClients.size > 0) {
  broadcastSseEvent(voiceSseClients, "voice_event", action);
}

};

const staticDir = path.resolve(__dirname, "../dashboard/dist"); const staticRoot = path.relative(process.cwd(), staticDir) || "."; const indexPath = path.join(staticDir, "index.html");

if (!fs.existsSync(indexPath)) { throw new Error("React dashboard build missing at dashboard/dist. Run bun run build:ui."); }

const indexHtml = fs.readFileSync(indexPath, "utf8"); const serveDashboardStatic = serveStatic({ root: staticRoot });

app.use("*", async (c, next) => { if (isRequestFromPublicTunnel(c, publicHttpsEntrypoint) && !c.req.path.startsWith("/share/")) { return c.text("Not found.", 404); }

await next();

});

app.get("/share/:token", (c) => { if (!screenShareSessionManager) { return c.text("Screen share link unavailable.", 503); } const rendered = screenShareSessionManager.renderSharePage(String(c.req.param("token") || "").trim()); return new Response(String(rendered.html || ""), { status: rendered.statusCode || 200, headers: { "content-type": "text/html; charset=UTF-8" } }); });

app.use("/assets/*", serveDashboardStatic);

app.get("*", (c) => { if (isApiPath(c.req.path) || c.req.path.startsWith("/share/")) { return c.text("Not found.", 404); } c.header("Cache-Control", "no-store"); return c.html(indexHtml); });

app.on("HEAD", "*", (c) => { if (isApiPath(c.req.path) || c.req.path.startsWith("/share/")) { return c.body(null, 404); } return c.body(null, 200, { "Cache-Control": "no-store", "content-type": "text/html; charset=UTF-8" }); });

const bunServer = Bun.serve({ hostname: dashboardHost, port: appConfig.dashboardPort, fetch(request, server) { return app.fetch(request, { server }); } }); const server = createDashboardServerHandle(bunServer, dashboardHost);

console.log(Dashboard running on http://${dashboardHost}:${bunServer.port});

// Bonjour: advertise dashboard on local network for iOS app discovery const bonjour = new BonjourAdvertiser(bunServer.port); const tunnelUrl = publicHttpsEntrypoint?.getState?.()?.publicUrl || ""; bonjour.start(tunnelUrl); if (!tunnelUrl && publicHttpsEntrypoint) { console.log("Bonjour: waiting for tunnel URL before advertising"); }

// Re-advertise quickly once the tunnel becomes ready so discovery clients // can catch the new TXT record during initial setup. if (publicHttpsEntrypoint) { let lastTunnelUrl = tunnelUrl; setInterval(() => { const currentUrl = publicHttpsEntrypoint.getState?.()?.publicUrl || ""; if (currentUrl !== lastTunnelUrl) { lastTunnelUrl = currentUrl; bonjour.updateTunnelUrl(currentUrl); if (currentUrl) { console.log(Bonjour: updated tunnel URL → ${currentUrl}); } else { console.log("Bonjour: cleared tunnel URL advertisement"); } } }, 1_000); }

return { app, server }; }

function isRequestFromPublicTunnel( c: { req: { header(name: string): string | undefined } }, publicHttpsEntrypoint: DashboardPublicHttpsEntrypoint | null | undefined ) { const requestHost = String(c.req.header("x-forwarded-host") || c.req.header("host") || "").trim(); if (!requestHost) return false; const publicState = publicHttpsEntrypoint?.getState?.() || null; return isPublicTunnelRequestHost(requestHost, publicState); }

function isPublicFrameIngressPath(rawPath: string) { const normalizedPath = String(rawPath || "").trim(); if (!normalizedPath) return false; if (normalizedPath === /api${STREAM_INGEST_API_PATH} || normalizedPath === /api${STREAM_INGEST_API_PATH}/) { return true; } return PUBLIC_SHARE_FRAME_PATH_RE.test(normalizedPath); }

interface FixedWindowBucket { windowStartedAt: number; count: number; lastSeenAt: number; }

function consumeFixedWindowRateLimit({ buckets, key, nowMs, windowMs, maxRequests }: { buckets: Map<string, FixedWindowBucket>; key: string; nowMs: number; windowMs: number; maxRequests: number; }) { if (!key) return false; const now = Number.isFinite(Number(nowMs)) ? Number(nowMs) : Date.now(); const windowSpan = Math.max(1, Number(windowMs) || 1); const maxInWindow = Math.max(1, Number(maxRequests) || 1);

let bucket = buckets.get(key) || null; if (!bucket || now - Number(bucket.windowStartedAt || 0) >= windowSpan) { bucket = { windowStartedAt: now, count: 0, lastSeenAt: now }; buckets.set(key, bucket); }

if (Number(bucket.count || 0) >= maxInWindow) { bucket.lastSeenAt = now; pruneRateLimitBuckets(buckets, now, windowSpan); return false; }

bucket.count = Number(bucket.count || 0) + 1; bucket.lastSeenAt = now; pruneRateLimitBuckets(buckets, now, windowSpan); return true; }

function pruneRateLimitBuckets(buckets: Map<string, FixedWindowBucket>, nowMs: number, windowMs: number) { if (buckets.size <= 2500) return; const staleBefore = nowMs - windowMs * 3; for (const [key, bucket] of buckets.entries()) { if (Number(bucket.lastSeenAt || 0) < staleBefore) { buckets.delete(key); } if (buckets.size <= 1500) break; } }