// Copyright 2026 SZL Holdings // SPDX-License-Identifier: Apache-2.0 // // mesh-router — typed HTTP router from a11oy (the substrate) to its sibling // organs. a11oy is the skeleton: it routes reasoning to amaru (the brain), // policy verdicts to sentra (the immune organ), and operator I/O to rosie // (the operator console). This module is the substrate-side client for those // network calls. // // Source of this instillation: the mesh "wires that SHOULD exist but DON'T" // findings (MINING_REPORT.md §4 #2 and #3) — a11oy -> sentra delegation and // a11oy -> amaru memory query had no runtime client in a11oy. This package is // that client. Strategy: C (wire over HTTP using each producer's existing // server), per the instillation strategy preference order. // // Honesty note (Doctrine v7 §2): amaru exposes a FastAPI sidecar with the // routes called here (GET /state, GET /receipts, POST /chakra/{name}/evaluate // — verified in the cross-repo mining server index). Sentra ships // runtime/immune_server.py (sentra PR #96) exposing POST /v1/inspect — the // real inbound immune endpoint. The 2026-05-31 post-merge re-test wave found // Wire B was contract-mismatched (a11oy was calling /v1/verdict; sentra serves // /v1/inspect). This module now targets the deployed sentra contract: // path: POST /v1/inspect // body: {actionId, action, traceparent?} // resp: {actionId, decision: "allow"|"deny", gate, decidedBy, rationale, // lambdaScore, receiptHash, traceparent} // The exposed SentraVerdict shape ({decision, reason}) is preserved for // existing callers; `reason` is populated from sentra's `rationale` field on // allow + deny. // // No mocked data path: every function issues a real HTTP request via // node:http/https and parses the real response. The accompanying tests boot a // real loopback server and exercise these functions over real TCP. // // Authored for SZL Holdings. Signed-off per repository DCO. import * as http from "node:http"; import * as https from "node:https"; /** A resolved endpoint for one sibling organ. */ export interface OrganEndpoint { /** Base URL, e.g. "http://amaru.svc:8000". No trailing slash required. */ readonly baseUrl: string; /** Per-request timeout in milliseconds. */ readonly timeoutMs?: number; } /** The three organs a11oy routes to. */ export interface MeshConfig { /** amaru — reasoning / memory (the brain). */ readonly amaru?: OrganEndpoint; /** sentra — policy / threat verdicts (the immune organ). */ readonly sentra?: OrganEndpoint; /** rosie — operator I/O (the operator console). */ readonly rosie?: OrganEndpoint; } /** A structured transport result; never throws on a non-2xx status. */ export interface MeshResponse { readonly ok: boolean; readonly status: number; readonly body: T | null; /** Populated when the call could not complete (timeout, connection, parse). */ readonly error?: string; } const DEFAULT_TIMEOUT_MS = 5_000; function trimSlash(url: string): string { return url.endsWith("/") ? url.slice(0, -1) : url; } /** * Issue a single JSON HTTP request. Pure transport: it resolves with a * MeshResponse for any HTTP status, and only sets `error` for transport-level * failures (DNS, connect, timeout, body parse). It never throws. */ export function meshRequest( endpoint: OrganEndpoint, method: "GET" | "POST", path: string, payload?: unknown, ): Promise> { const url = new URL(trimSlash(endpoint.baseUrl) + path); const isHttps = url.protocol === "https:"; const lib = isHttps ? https : http; const data = payload === undefined ? undefined : JSON.stringify(payload); const timeoutMs = endpoint.timeoutMs ?? DEFAULT_TIMEOUT_MS; return new Promise>((resolve) => { const req = lib.request( { protocol: url.protocol, hostname: url.hostname, port: url.port || (isHttps ? 443 : 80), path: url.pathname + url.search, method, headers: { accept: "application/json", ...(data !== undefined ? { "content-type": "application/json", "content-length": Buffer.byteLength(data), } : {}), }, }, (res) => { const chunks: Buffer[] = []; res.on("data", (c: Buffer) => chunks.push(c)); res.on("end", () => { const raw = Buffer.concat(chunks).toString("utf8"); const status = res.statusCode ?? 0; let body: T | null = null; let parseError: string | undefined; if (raw.length > 0) { try { body = JSON.parse(raw) as T; } catch { parseError = "response body was not valid JSON"; } } resolve({ ok: status >= 200 && status < 300 && parseError === undefined, status, body, error: parseError, }); }); }, ); req.on("error", (err) => { resolve({ ok: false, status: 0, body: null, error: err.message }); }); req.setTimeout(timeoutMs, () => { req.destroy(); resolve({ ok: false, status: 0, body: null, error: `timeout after ${timeoutMs}ms` }); }); if (data !== undefined) req.write(data); req.end(); }); } // --- amaru: the brain (reasoning / memory) --------------------------------- export interface AmaruState { readonly [key: string]: unknown; } /** Query amaru's current memory/overwatch state (GET /state). */ export function queryAmaruState(cfg: MeshConfig): Promise> { if (!cfg.amaru) { return Promise.resolve({ ok: false, status: 0, body: null, error: "amaru endpoint not configured" }); } return meshRequest(cfg.amaru, "GET", "/state"); } /** Read amaru's published receipts (GET /receipts). */ export function queryAmaruReceipts(cfg: MeshConfig): Promise { if (!cfg.amaru) { return Promise.resolve({ ok: false, status: 0, body: null, error: "amaru endpoint not configured" }); } return meshRequest(cfg.amaru, "GET", "/receipts"); } /** Ask amaru to evaluate a named chakra (POST /chakra/{name}/evaluate). */ export function evaluateAmaruChakra( cfg: MeshConfig, chakra: string, input: unknown, ): Promise { if (!cfg.amaru) { return Promise.resolve({ ok: false, status: 0, body: null, error: "amaru endpoint not configured" }); } return meshRequest(cfg.amaru, "POST", `/chakra/${encodeURIComponent(chakra)}/evaluate`, input); } // --- sentra: the immune organ (policy / threat verdicts) ------------------- export interface SentraVerdictRequest { readonly actionId: string; readonly kind: "egress" | "threat" | "admission"; /** Arbitrary action payload sentra inspects (cmd, url, headers, etc.). */ readonly payload: unknown; /** Optional W3C traceparent so the immune call joins the parent trace. */ readonly traceparent?: string; } /** * Externally exposed verdict shape — kept small for callers. The full sentra * response is mapped down to {decision, reason} via inspectResponseToVerdict. */ export interface SentraVerdict { readonly decision: "allow" | "deny"; readonly reason?: string; } /** * Sentra's /v1/inspect response shape (anatomy-contracts PolicyDecision). We * declare it locally rather than importing to keep mesh-router dependency-free. */ interface SentraInspectResponse { readonly actionId: string; readonly decision: "allow" | "deny"; readonly gate?: string; readonly decidedBy?: string; readonly rationale?: string; readonly lambdaScore?: number; readonly receiptHash?: string; readonly traceparent?: string; } function inspectResponseToVerdict(r: SentraInspectResponse): SentraVerdict { return { decision: r.decision, reason: r.rationale, }; } /** * Translate the public SentraVerdictRequest to sentra's actual /v1/inspect * body. sentra accepts `{action, actionId, traceparent}` — the `kind` field on * our public request is folded into `action.kind` so sentra's signature-scan * can still see it. */ function verdictRequestToInspectBody(req: SentraVerdictRequest): { action: Record; actionId: string; traceparent?: string; } { const action: Record = req.payload && typeof req.payload === "object" && req.payload !== null ? { ...(req.payload as Record), kind: req.kind } : { payload: req.payload, kind: req.kind }; const body: { action: Record; actionId: string; traceparent?: string } = { action, actionId: req.actionId, }; if (req.traceparent) body.traceparent = req.traceparent; return body; } /** * Request an admission/egress verdict from sentra before a11oy admits an * action. Targets sentra's deployed POST /v1/inspect route. Fails closed * (deny) on transport error, missing config, or any non-2xx — so a missing or * crashed immune organ never silently allows. */ export async function requestSentraVerdict( cfg: MeshConfig, request: SentraVerdictRequest, ): Promise { if (!cfg.sentra) { return { decision: "deny", reason: "sentra endpoint not configured (fail closed)" }; } const body = verdictRequestToInspectBody(request); const res = await meshRequest( cfg.sentra, "POST", "/v1/inspect", body, ); if (!res.ok || res.body === null) { return { decision: "deny", reason: res.error ?? `sentra returned ${res.status} (fail closed)` }; } // Defensive: if sentra returned an unexpected shape, fail closed. if (res.body.decision !== "allow" && res.body.decision !== "deny") { return { decision: "deny", reason: `sentra returned unrecognised decision (fail closed)` }; } return inspectResponseToVerdict(res.body); } // --- rosie: the operator console (operator I/O) ---------------------------- /** Push an operator-facing event to rosie (POST /v1/events). */ export function notifyRosie(cfg: MeshConfig, event: unknown): Promise { if (!cfg.rosie) { return Promise.resolve({ ok: false, status: 0, body: null, error: "rosie endpoint not configured" }); } return meshRequest(cfg.rosie, "POST", "/v1/events", event); } /** Build a MeshConfig from environment variables (all optional). */ export function meshConfigFromEnv(env: NodeJS.ProcessEnv = process.env): MeshConfig { const opt = (v: string | undefined): OrganEndpoint | undefined => v && v.trim() ? { baseUrl: v.trim() } : undefined; return { amaru: opt(env.A11OY_AMARU_URL), sentra: opt(env.A11OY_SENTRA_URL), rosie: opt(env.A11OY_ROSIE_URL), }; }