/** * HTTP (streamable-HTTP) transport + Bearer auth — port of the HTTP half of * `core/l6_server.py` (`_build_auth_middleware`, `_build_operator_identity_middleware`, * the `run_l6_server` http branch, `_is_loopback_host`, `_normalized_legacy_token`). * * Security invariants preserved IN CODE (a miss here leaks PRIVATE memory or * escalates a quarantined caller to OPERATOR across machines): * - Fail CLOSED: with no auth configured and not `--allow-unauthenticated`, * every request gets 503. * - Empty Bearer can NEVER authenticate as OPERATOR — the legacy compare * requires BOTH a configured legacy token AND a non-empty presented token * before the constant-time compare, and `normalizedLegacyToken()` maps a * set-but-empty/whitespace `BOURDON_PEER_TOKEN_SERVER` to null (3-Star P1-1). * - Per-agent `bdn_` tokens resolve via the registry (sha256-at-rest, * constant-time; re-reads on mtime so `bourdon revoke` takes effect live). * - A 401 does NOT distinguish invalid vs revoked and never echoes the token. * - Default bind 127.0.0.1; a NON-LOOPBACK bind REFUSES TO START (throws, the * CLI exits non-zero) with `--allow-unauthenticated` (anonymous = loopback * only) OR with no auth configured. * - On success the identity is bound for the request subtree via * AsyncLocalStorage (`runWithCaller`), so the tool handlers observe the * authenticated caller deterministically; an HTTP request that bypassed this * middleware would resolve to OPERATOR only under stdio, never here. */ import { type IncomingMessage, type ServerResponse } from "node:http"; import { AgentIdentity, FederationRegistry, HandoffGovernance } from "@getbourdon/federation"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; export declare const DEFAULT_PORT = 7500; export declare const DEFAULT_HOST = "127.0.0.1"; /** * The legacy shared peer token, with empty/whitespace normalized to null. A * set-but-empty `BOURDON_PEER_TOKEN_SERVER` (a routine .env slip) is treated as * "no token" everywhere — both the startup gate and per-request auth (P1-1). */ export declare function normalizedLegacyToken(): string | null; /** Whether a bind host is loopback-only. */ export declare function isLoopbackHost(host: string): boolean; export type AuthResult = { kind: "unconfigured"; } | { kind: "unauthorized"; message: string; } | { kind: "ok"; identity: AgentIdentity; }; /** * Resolve an `Authorization` header to an identity, reproducing the * `_BearerAuth.dispatch` decision exactly. */ export declare function authenticateBearer(registry: FederationRegistry, legacy: string | null, authHeader: string | null | undefined): AuthResult; /** Error thrown when a non-loopback bind is refused. The CLI catches it and * exits non-zero (mirrors Python's `raise SystemExit`). */ export declare class BindRefusedError extends Error { constructor(message: string); } export interface RunHttpServerOptions { port?: number; host?: string; allowUnauthenticated?: boolean; registry?: FederationRegistry; governance?: HandoffGovernance; } /** Internal transport boundary; not exported by the package facade. */ export declare function readBody(req: IncomingMessage): Promise; /** * Serve the L6 MCP server over STATELESS streamable-HTTP, always via our own * `node:http` server so the bind host is ours. Each request runs in its own * task with a fresh transport, so the bound caller identity deterministically * reaches the tool handlers. * * `createServer` is a factory so each request gets a fresh `McpServer`/transport * pair (stateless = no cross-request session state). The returned handler is * also used by `runHttpServer`; this module-only seam does not bypass startup gates. * * v0.9.0 bind/auth contract: default bind 127.0.0.1; a non-loopback bind with * `--allow-unauthenticated` (anonymous is loopback-only) OR with no auth * configured throws `BindRefusedError` BEFORE listening (never serves-then-503). */ export declare function createHttpRequestHandler(createServer: () => McpServer, options?: RunHttpServerOptions): (req: IncomingMessage, res: ServerResponse) => Promise; /** Start the same authenticated request handler exposed for in-process integration. */ export declare function runHttpServer(createServer: () => McpServer, options?: RunHttpServerOptions): import("node:http").Server;