import { type EnvReader, experimentalOptionsFromEnv, Runtime, type RuntimeOptions, runtimePresets, } from "@commonfabric/runner"; import { publishCfcPosture } from "@/lib/cfc-posture.ts"; import { publishExperimentalPosture } from "@/lib/experimental-posture.ts"; import { selfDirectedFetch } from "@/lib/internal-api-origin.ts"; import type { env as ToolshedEnv } from "@/env.ts"; type UrlEnv = Pick; /** * Assemble this toolshed's `RuntimeOptions` (CT-1814), extracted pure from * the server startup path so the wiring decisions are unit-testable: * `apiUrl` is the storage/memory base (MEMORY_URL), while patterns fetch * against the public API base (API_URL) — the builder/env.ts fallback is a * hardcoded `localhost:`, wrong for any non-default port. * With API_INTERNAL_URL set, the runtime's fetch sends requests addressed to * API_URL to that origin instead (`selfDirectedFetch`); the URLs themselves * are unchanged. EXPERIMENTAL_* flags come from the injected env reader via * the canonical mapping. */ export function toolshedRuntimeOptions( config: UrlEnv, storageManager: RuntimeOptions["storageManager"], envGet: EnvReader = Deno.env.get, ): RuntimeOptions { const fetch = selfDirectedFetch(config); return runtimePresets.productionServer({ apiUrl: new URL(config.MEMORY_URL), patternApiUrl: new URL(config.API_URL), ...(fetch !== undefined ? { fetch } : {}), storageManager, experimental: experimentalOptionsFromEnv(envGet), }); } type OtelEnv = Pick; /** * Construct this toolshed's Runtime and, when OTel is enabled, bridge its * telemetry bus to OpenTelemetry as a second consumer of the same marker * stream the debug tooling uses. Toolshed's Runtime only executes patterns * for webhook deliveries (interactive patterns run in browser runtimes), so * the bridge is low-volume — but without it those runs emit markers into the * void. * * The attach is fire-and-forget off the startup path (dynamic imports defer * OTel module load); the promise is returned so tests can await it. Failures * are logged, never fatal. */ export function createToolshedRuntime( config: UrlEnv & OtelEnv, storageManager: RuntimeOptions["storageManager"], envGet: EnvReader = Deno.env.get, ): Runtime { const options = toolshedRuntimeOptions(config, storageManager, envGet); // Webhook runs persist their session state even when browser clients keep // theirs local. The deployment's client policy is published separately. const runtime = new Runtime({ ...options, experimental: { ...options.experimental, localSessionDocuments: false }, }); // The Runtime just declared the deployment's `sharedMemoryConnection` on // this storage manager. This process's own sessions do not follow it. // Under routed Mode A, MEMORY_URL names a route that picks the toolshed by // the space in the address (a loopback proxy keyed on `?space=`), and // a shared connection names no space: it lands on one toolshed, which // refuses every space it does not own. A dedicated connection names its // space and reaches the owner; where MEMORY_URL is this toolshed itself, // the default, either choice reaches it. Only this manager is affected: the // posture published below still carries the deployment's flag, which // clients adopt for the connections they open on the router. The cost is // one socket and one authentication per space this process opens, the same // as with the flag off. Declared here, before any session opens, because // the manager refuses to change the choice once one has; a manager without // the method (the emulated one in tests) has no connections to share. The // override goes once MEMORY_URL names the router (infra#244), which serves // either topology. storageManager.setSharedMemoryConnection?.(false); // `/api/meta` publishes resolved defaults and the deployment's client // policy even though the webhook runtime synchronizes its own sessions. publishExperimentalPosture({ ...runtime.experimental, localSessionDocuments: options.experimental?.localSessionDocuments ?? false, }); publishCfcPosture(runtime); // Fire-and-forget; the attach itself is exported and unit-tested. void attachRuntimeOtelBridge(runtime, config); return runtime; } // The one live bridge detach (toolshed runs a single Runtime); invoked from // shutdownOpenTelemetry so in-flight storage spans are ended before the final // flush instead of being dropped with the process. let activeOtelBridgeDetach: (() => void) | undefined; /** Idempotent; returns whether a bridge was attached. */ export function detachRuntimeOtelBridgeIfAttached(): boolean { const detach = activeOtelBridgeDetach; activeOtelBridgeDetach = undefined; detach?.(); return detach !== undefined; } /** * Exported for tests; production reaches it through createToolshedRuntime. * Structural param so failure paths can be exercised with a stub. * * Metrics caveat: toolshed's own OTel setup (lib/otel.ts) registers a tracer * provider only, so with OTEL_ENABLED alone the bridge's ct.* instruments are * API no-ops. On the VMs the process also runs under Deno native OTel * (OTEL_DENO + --unstable-otel), whose global MeterProvider makes them real — * spans work either way. Deliberate: a second SDK MeterProvider here would * duplicate what Deno native already exports. */ export async function attachRuntimeOtelBridge( runtime: Pick, config: OtelEnv, ): Promise { if (!config.OTEL_ENABLED) return false; let detach: (() => void) | undefined; try { const [{ attachRuntimeTelemetryOtelBridge }, { metrics, trace }] = await Promise.all([ // The OpenTelemetry bridge loads only for a run that reports. // deno-lint-ignore cf-imports/no-inline-module-import import("@commonfabric/runner/telemetry-otel-bridge"), // deno-lint-ignore cf-imports/no-inline-module-import import("@opentelemetry/api"), ]); detach = attachRuntimeTelemetryOtelBridge(runtime.telemetry, { tracer: trace.getTracer("ct-runner-bridge"), meter: metrics.getMeter("ct-runner-bridge"), attributes: { "ct.runtime": "server" }, metricAttributes: { "service.name": config.OTEL_SERVICE_NAME, "deployment.environment": config.ENV, }, }); // Preflight markers are gated; without the flip the event-admission // spans/histograms never fire. runtime.scheduler.setEventPreflightTelemetryEnabled(true); // Registered only after full success so a failed attach never leaves a // half-wired bridge behind for shutdown to detach. activeOtelBridgeDetach = detach; return true; } catch (error) { detach?.(); console.warn("Runtime OTel bridge attach failed:", error); return false; } }