@powerhousedao/switchboard 6.2.3-dev.50 → 6.2.3-dev.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tsbuild/test/boot-workflow-singleton-conflict.test.d.ts +2 -0
- package/.tsbuild/test/boot-workflow-singleton-conflict.test.d.ts.map +1 -0
- package/.tsbuild/test/boot-workflow-singleton-retry.test.d.ts +2 -0
- package/.tsbuild/test/boot-workflow-singleton-retry.test.d.ts.map +1 -0
- package/.tsbuild/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +32 -0
- package/dist/index.mjs +1 -1
- package/dist/{server-Zz7SqTwO.mjs → server-J9fpd42v.mjs} +262 -48
- package/dist/server-J9fpd42v.mjs.map +1 -0
- package/dist/server.d.mts +3 -2
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +3 -3
- package/package.json +15 -15
- package/test/boot-workflow-singleton-conflict.test.ts +107 -0
- package/test/boot-workflow-singleton-retry.test.ts +133 -0
- package/test/pglite-dialect.test.ts +58 -0
- package/test/workflow/resolvers.test.ts +35 -0
- package/test/workflow-reactor-enforcement.test.ts +3 -0
- package/test/workflow-read-gate.test.ts +3 -0
- package/test/workflow-runtime.test.ts +502 -7
- package/dist/server-Zz7SqTwO.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-J9fpd42v.mjs","sources":["../src/pglite-version.ts","../src/profiler.ts","../src/attachments/auth.ts","../src/attachments/mount-auth.ts","../src/attachments/routes.ts","../src/attachments/url-signer.ts","../src/attachments/index.ts","../src/attachment-reference-read-model.mts","../src/fatal-shutdown.mts","../src/privacy.mts","../src/builder-defaults.mts","../src/worker-pool.mts","../src/projection-worker.mts","../src/feature-flags.ts","../src/mcp-flag.mts","../src/workflow/oauth-callback.ts","../src/workflow/resolvers.ts","../src/workflow/schema.ts","../src/workflow/subgraph.ts","../src/workflow-runtime.mts","../src/pglite-dialect.ts","../src/pglite-migration.ts","../src/pglite-preflight.ts","../src/reactor-feature-flags.mts","../src/create-signature-policy.mts","../src/renown.ts","../src/unsupported-stored-documents.mts","../src/server.mts"],"sourcesContent":["import type * as CurrentPGliteModuleNs from \"@electric-sql/pglite\";\nimport type {\n NodeFsClass,\n VerifyHandle,\n} from \"@powerhousedao/reactor-api/pglite-node\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\n\nexport const CURRENT_PG_MAJOR = 17;\nexport const SUPPORTED_PG_MAJORS = [16, 17] as const;\nexport type SupportedPgMajor = (typeof SUPPORTED_PG_MAJORS)[number];\n\ntype CurrentPGliteModule = typeof CurrentPGliteModuleNs;\n\nexport async function readPgVersionFile(\n dataDir: string,\n): Promise<number | null> {\n try {\n const raw = await fs.readFile(path.join(dataDir, \"PG_VERSION\"), \"utf8\");\n const major = parseInt(raw.trim(), 10);\n return Number.isFinite(major) ? major : null;\n } catch {\n return null;\n }\n}\n\nexport function isSupportedMajor(major: number): major is SupportedPgMajor {\n return (SUPPORTED_PG_MAJORS as readonly number[]).includes(major);\n}\n\n/**\n * Parses the `PH_FORCE_PG_VERSION` env var. Returns the validated major, or\n * `null` when the var is unset/empty. Throws on any value that is not a\n * supported major — invalid configuration must fail before the server starts\n * touching disk.\n */\nexport function parseForcePgVersion(\n raw: string | undefined,\n): SupportedPgMajor | null {\n if (raw === undefined || raw.trim() === \"\") return null;\n const parsed = Number(raw);\n if (Number.isInteger(parsed) && isSupportedMajor(parsed)) return parsed;\n throw new Error(\n `PH_FORCE_PG_VERSION must be one of: ${SUPPORTED_PG_MAJORS.join(\", \")} (got: ${raw})`,\n );\n}\n\nexport async function loadPGliteModule(\n major: SupportedPgMajor,\n): Promise<CurrentPGliteModule> {\n if (major === 16) {\n return (await import(\"pglite-legacy-02\")) as unknown as CurrentPGliteModule;\n }\n return import(\"@electric-sql/pglite\");\n}\n\n/** The stock NodeFS class of the PGlite module that opens `major` dirs. */\nexport async function loadNodeFsClass(\n major: SupportedPgMajor,\n): Promise<NodeFsClass> {\n if (major === 16) {\n const mod = await import(\"pglite-legacy-02/nodefs\");\n return mod.NodeFS as unknown as NodeFsClass;\n }\n const mod = await import(\"@electric-sql/pglite/nodefs\");\n return mod.NodeFS;\n}\n\n/** Opens a converted dir over stock NodeFS so the conversion can check it. */\nexport async function openForVerify(\n major: number,\n dataDir: string,\n): Promise<VerifyHandle> {\n if (!isSupportedMajor(major)) {\n throw new Error(\n `Cannot verify PGlite data dir ${dataDir}: PG_VERSION=${major} is not supported (expected one of ${SUPPORTED_PG_MAJORS.join(\", \")})`,\n );\n }\n const [{ PGlite }, NodeFS] = await Promise.all([\n loadPGliteModule(major),\n loadNodeFsClass(major),\n ]);\n const pg = new PGlite({ fs: new NodeFS(dataDir) });\n await pg.waitReady;\n return pg;\n}\n\ntype PgDumpFn = (options: {\n pg: unknown;\n}) => Promise<{ text(): Promise<string> }>;\n\nexport async function loadPgDump(major: SupportedPgMajor): Promise<PgDumpFn> {\n if (major === 16) {\n const mod = (await import(\"pglite-tools-legacy-02/pg_dump\")) as {\n pgDump: PgDumpFn;\n };\n return mod.pgDump;\n }\n const mod = (await import(\"@electric-sql/pglite-tools/pg_dump\")) as {\n pgDump: PgDumpFn;\n };\n return mod.pgDump;\n}\n","import type { PyroscopeConfig } from \"@pyroscope/nodejs\";\n\nlet started = false;\n\n// Once per process: the standalone entry and an embedding host (ph vetra) both\n// ask, and Pyroscope must only be initialised once.\nexport async function initProfilerFromEnv(env: typeof process.env) {\n if (started || !env.PYROSCOPE_SERVER_ADDRESS) return;\n started = true;\n const {\n PYROSCOPE_SERVER_ADDRESS: serverAddress,\n PYROSCOPE_APPLICATION_NAME: appName,\n PYROSCOPE_USER: basicAuthUser,\n PYROSCOPE_PASSWORD: basicAuthPassword,\n PYROSCOPE_WALL_ENABLED: wallEnabled,\n PYROSCOPE_HEAP_ENABLED: heapEnabled,\n } = env;\n\n const options: PyroscopeConfig = {\n serverAddress,\n appName,\n basicAuthUser,\n basicAuthPassword,\n // Wall profiling captures wall-clock time (includes async I/O waits)\n // This shows GraphQL resolvers even when waiting for database\n wall: {\n samplingDurationMs: 10000, // 10 second sampling windows\n samplingIntervalMicros: 10000, // 10ms sampling interval (100 samples/sec)\n collectCpuTime: true, // Also collect CPU time alongside wall time\n },\n // Heap profiling for memory allocation tracking\n heap: {\n samplingIntervalBytes: 512 * 1024, // Sample every 512KB allocated\n stackDepth: 64, // Capture deeper stacks for better context\n },\n };\n return initProfiler(options, {\n wallEnabled: wallEnabled !== \"false\",\n heapEnabled: heapEnabled === \"true\",\n });\n}\n\ninterface ProfilerFlags {\n wallEnabled?: boolean;\n heapEnabled?: boolean;\n}\n\nexport async function initProfiler(\n options?: PyroscopeConfig,\n flags: ProfilerFlags = { wallEnabled: true, heapEnabled: false },\n) {\n console.log(\"Initializing Pyroscope profiler at:\", options?.serverAddress);\n console.log(\" Wall profiling:\", flags.wallEnabled ? \"enabled\" : \"disabled\");\n console.log(\" Heap profiling:\", flags.heapEnabled ? \"enabled\" : \"disabled\");\n\n const { default: Pyroscope } = await import(\"@pyroscope/nodejs\");\n Pyroscope.init(options);\n\n // Start wall profiling (captures async I/O time - shows resolvers)\n if (flags.wallEnabled) {\n Pyroscope.startWallProfiling();\n }\n\n // Start CPU profiling (captures CPU-bound work)\n Pyroscope.startCpuProfiling();\n\n // Optionally start heap profiling (memory allocations)\n if (flags.heapEnabled) {\n Pyroscope.startHeapProfiling();\n }\n}\n","import type { AuthContext, AuthService } from \"@powerhousedao/reactor-api\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\n\n/**\n * Verified actor context for authenticated attachment handlers. The user comes\n * exclusively from bearer verification: when auth is disabled (OPEN mode)\n * `user` is undefined and `authEnabled` is false, and handlers must treat the\n * caller as anonymous. Caller-supplied identity headers are never consulted.\n */\nexport type AttachmentActorContext = {\n user: AuthContext[\"user\"];\n authEnabled: boolean;\n};\n\nconst ANONYMOUS_ACTOR: AttachmentActorContext = {\n user: undefined,\n authEnabled: false,\n};\n\nexport type NodeHandler = (\n req: IncomingMessage,\n res: ServerResponse,\n body?: unknown,\n actor?: AttachmentActorContext,\n) => Promise<void> | void;\n\nexport type RequireAuthOptions = {\n /**\n * Let requests without a bearer identity through as anonymous actors\n * instead of answering 401, so the handler's own document authorization\n * decides. A bearer that IS supplied must still verify — an invalid token\n * is rejected, never downgraded to anonymous.\n */\n allowAnonymous?: boolean;\n /**\n * Whether the deployment has said it accepts no anonymous callers at all\n * (`REQUIRE_AUTHENTICATED_CALLER`). It overrides `allowAnonymous`, because\n * the two answer different questions: `allowAnonymous` says \"this handler\n * authorizes per document, so it does not need identity to decide\", while\n * this says \"this server does not serve callers it cannot name\". A route can\n * be happy to decide without identity and still not be allowed to.\n *\n * These routes are mounted straight on the HTTP adapter, so the fetch chain's\n * own require-auth middleware never sees them — this is how the same floor\n * reaches them.\n */\n requireAuthenticatedCaller?: boolean;\n};\n\n/**\n * Wrap a Node-style handler so that, when `authService` is provided and auth is\n * enabled, the request must carry a verifiable Bearer token. The handler always\n * receives an actor context: the verified bearer user when auth is enabled, or\n * the anonymous context when it is disabled. With `allowAnonymous`, a missing\n * bearer yields an anonymous actor with `authEnabled: true` instead of a 401 —\n * unless `requireAuthenticatedCaller` says the deployment refuses anonymous\n * callers outright, which overrides it.\n *\n * With no `authService` there is no bearer to read and every caller is\n * anonymous, so nothing is refused here. That combination cannot carry the\n * floor: `assertRequireAuthenticatedCallerAllowed` refuses to boot with\n * `REQUIRE_AUTHENTICATED_CALLER` set and no identity resolution, and identity\n * resolution is what builds the service.\n */\nexport function requireAuth(\n authService: AuthService | undefined,\n handler: NodeHandler,\n options?: RequireAuthOptions,\n): NodeHandler {\n if (!authService) {\n return (req, res, body) => handler(req, res, body, ANONYMOUS_ACTOR);\n }\n\n return async (req, res, body) => {\n let result;\n try {\n result = await authService.verifyBearer(req.headers.authorization);\n } catch {\n res.statusCode = 500;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify({ error: \"Internal authentication error\" }));\n return;\n }\n\n if (result instanceof Response) {\n const body = await result.text();\n res.statusCode = result.status;\n const contentType = result.headers.get(\"content-type\");\n if (contentType) res.setHeader(\"Content-Type\", contentType);\n res.end(body);\n return;\n }\n\n if (!result.user) {\n const refuse =\n options?.requireAuthenticatedCaller === true ||\n (result.auth_enabled && !options?.allowAnonymous);\n if (refuse) {\n res.statusCode = 401;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify({ error: \"Authentication required\" }));\n return;\n }\n }\n\n await handler(req, res, body, {\n user: result.user,\n authEnabled: result.auth_enabled,\n });\n };\n}\n","import type { API } from \"@powerhousedao/reactor-api\";\nimport {\n requireAuth,\n type NodeHandler,\n type RequireAuthOptions,\n} from \"./auth.js\";\n\nexport type HttpMethod = \"DELETE\" | \"GET\" | \"HEAD\" | \"POST\" | \"PUT\";\n\n/**\n * Mount a Node-style attachment route with `requireAuth` applied unconditionally.\n * When `api.authService` is undefined (auth disabled), the handler still runs\n * through `requireAuth` and receives the anonymous actor context — that is the\n * only way to opt out of bearer verification. To register a route without auth\n * wrapping you must call `api.httpAdapter.mountNodeRoute` directly.\n *\n * `allowAnonymous` is reserved for routes whose handlers make a per-document\n * authorization decision themselves; identity-only routes must not use it. It\n * is also not the last word: a deployment that refuses anonymous callers\n * (`REQUIRE_AUTHENTICATED_CALLER`) refuses them here too, because these routes\n * are mounted on the HTTP adapter and never reach the fetch chain that carries\n * that floor for GraphQL. The flag is read from the API, so every route\n * mounted through here inherits it without each one remembering to.\n */\nexport function mountAuthenticatedNodeRoute(\n api: Pick<API, \"httpAdapter\" | \"authService\" | \"requireAuthenticatedCaller\">,\n method: HttpMethod,\n path: string,\n handler: NodeHandler,\n options?: RequireAuthOptions,\n): void {\n api.httpAdapter.mountNodeRoute(\n method,\n path,\n requireAuth(api.authService, handler, {\n ...options,\n requireAuthenticatedCaller: api.requireAuthenticatedCaller,\n }),\n );\n}\n","import {\n AttachmentAlreadyExists,\n AttachmentNotFound,\n AttachmentPending,\n HashMismatch,\n InvalidAttachmentMetadata,\n InvalidAttachmentRef,\n ReservationNotFound,\n SizeMismatch,\n UploadTooLarge,\n createRef,\n parseAttachmentDownloadTarget,\n validateReserveMetadata,\n type AttachmentBuildResult,\n type AttachmentDownloadTarget,\n type ReserveAttachmentOptions,\n type ReserveMetadata,\n} from \"@powerhousedao/reactor-attachments\";\nimport type { IAttachmentAccessService } from \"@powerhousedao/reactor-api\";\nimport type { AttachmentHash } from \"@powerhousedao/reactor\";\nimport { childLogger } from \"document-model\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { Readable } from \"node:stream\";\nimport type { ReadableStream as NodeReadableStream } from \"node:stream/web\";\nimport type { AttachmentActorContext } from \"./auth.js\";\nimport type { AttachmentUrlSigner } from \"./url-signer.js\";\n\nconst logger = childLogger([\"switchboard\", \"attachments\"]);\n\nconst RETRY_AFTER_SECONDS = 5;\n\n// Canonical form is lowercase hex (the SHA-256 hasher emits lowercase), but\n// accept either case from the wire and normalise before lookup. This keeps\n// the API forgiving for hand-typed URLs without changing storage semantics.\nconst HASH_PATTERN = /^[a-f0-9]{64}$/i;\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n res.statusCode = status;\n res.setHeader(\"Content-Type\", \"application/json\");\n res.end(JSON.stringify(body));\n}\n\nfunction sendError(res: ServerResponse, status: number, message: string): void {\n sendJson(res, status, { error: message });\n}\n\nfunction statusForError(err: unknown): number {\n if (err instanceof AttachmentNotFound) return 404;\n if (err instanceof ReservationNotFound) return 404;\n if (err instanceof InvalidAttachmentRef) return 400;\n if (err instanceof UploadTooLarge) return 413;\n return 500;\n}\n\nfunction sendErrorFromException(res: ServerResponse, err: unknown): void {\n const status = statusForError(err);\n if (status >= 500) {\n logger.error(\"Attachment route error: @error\", err);\n sendError(res, status, \"Internal error\");\n return;\n }\n sendError(res, status, err instanceof Error ? err.message : String(err));\n}\n\nasync function readJsonBody(\n req: IncomingMessage,\n body: unknown,\n): Promise<unknown> {\n // The Express body-parser may have already populated `body`. When that\n // happens we trust it; otherwise read the raw stream ourselves so this\n // module is independent of upstream middleware ordering.\n if (body !== undefined && body !== null && typeof body === \"object\") {\n return body;\n }\n const chunks: Buffer[] = [];\n for await (const chunk of req) {\n chunks.push(chunk as Buffer);\n }\n if (chunks.length === 0) return undefined;\n const text = Buffer.concat(chunks).toString(\"utf8\");\n if (text.length === 0) return undefined;\n return JSON.parse(text);\n}\n\nexport function parseReserveOptions(\n input: unknown,\n): ReserveAttachmentOptions | null {\n if (input === null || typeof input !== \"object\") return null;\n const obj = input as Record<string, unknown>;\n const { mimeType, fileName } = obj;\n const extension = obj.extension ?? null;\n try {\n validateReserveMetadata({\n mimeType,\n fileName,\n extension,\n } as ReserveMetadata);\n } catch (err) {\n if (err instanceof InvalidAttachmentMetadata) return null;\n throw err;\n }\n if (typeof mimeType !== \"string\" || typeof fileName !== \"string\") return null;\n if (extension !== null && typeof extension !== \"string\") return null;\n\n // Hash-first mode: clientHash triggers this path; sizeBytes is required alongside it.\n // A body with sizeBytes but no clientHash falls through to the legacy path unchanged.\n if (obj.clientHash !== undefined) {\n if (\n typeof obj.clientHash !== \"string\" ||\n !HASH_PATTERN.test(obj.clientHash)\n ) {\n return null;\n }\n if (\n typeof obj.sizeBytes !== \"number\" ||\n !Number.isInteger(obj.sizeBytes) ||\n obj.sizeBytes <= 0 ||\n !Number.isSafeInteger(obj.sizeBytes)\n ) {\n return null;\n }\n return {\n mimeType,\n fileName,\n extension,\n clientHash: obj.clientHash.toLowerCase() as AttachmentHash,\n sizeBytes: obj.sizeBytes,\n };\n }\n\n return {\n mimeType,\n fileName,\n extension,\n };\n}\n\nexport function quoteFilename(name: string): string {\n // RFC 6266: quoted-string with internal \" and \\ escaped.\n return `\"${name.replace(/[\\\\\"]/g, \"\\\\$&\")}\"`;\n}\n\nexport function buildContentDisposition(fileName: string): string {\n // ASCII fallback: replace any byte outside printable ASCII (0x20-0x7e),\n // plus `\"` and `\\`, with `_`. Browsers fall back to this when they don't\n // grok `filename*=`; the modern parameter carries the real name.\n const ascii = fileName.replace(/[^\\x20-\\x21\\x23-\\x5b\\x5d-\\x7e]/g, \"_\");\n // RFC 5987: percent-encode UTF-8 bytes. encodeURIComponent leaves a few\n // chars that 5987 disallows in token; re-encode them.\n const encoded = encodeURIComponent(fileName).replace(\n /['()*!]/g,\n (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n return `attachment; filename=${quoteFilename(ascii)}; filename*=UTF-8''${encoded}`;\n}\n\nexport function makeReserveHandler(attachments: AttachmentBuildResult) {\n return async (\n req: IncomingMessage,\n res: ServerResponse,\n body?: unknown,\n ): Promise<void> => {\n let parsed: unknown;\n try {\n parsed = await readJsonBody(req, body);\n } catch {\n sendError(res, 400, \"Invalid JSON body\");\n return;\n }\n const opts = parseReserveOptions(parsed);\n if (!opts) {\n sendError(\n res,\n 400,\n \"Body must be { mimeType: string (type/subtype), fileName: string (no control characters, max 255 chars), extension?: string|null, clientHash?: string (64 hex chars), sizeBytes?: number (required with clientHash) }\",\n );\n return;\n }\n\n let upload;\n try {\n upload = await attachments.service.reserve(opts);\n } catch (err) {\n if (err instanceof AttachmentAlreadyExists) {\n sendJson(res, 409, { error: \"already_exists\", ref: err.ref });\n return;\n }\n sendErrorFromException(res, err);\n return;\n }\n\n sendJson(res, 201, {\n reservationId: upload.reservationId,\n ref: upload.ref,\n expiresAtUtc: upload.expiresAtUtc,\n ...(upload.uploadTarget ? { uploadTarget: upload.uploadTarget } : {}),\n });\n };\n}\n\nexport function makeUploadHandler(attachments: AttachmentBuildResult) {\n return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const reservationId = extractParam(req, \"reservationId\");\n if (!reservationId) {\n sendError(res, 400, \"Missing reservationId\");\n return;\n }\n if (attachments.backend?.kind === \"s3\") {\n sendError(res, 405, \"Use the reservation uploadTarget for S3 uploads\");\n return;\n }\n\n let reservation;\n try {\n reservation = await attachments.reservations.get(reservationId);\n } catch (err) {\n sendErrorFromException(res, err);\n return;\n }\n\n const upload = attachments.uploadFactory.createUpload(reservation);\n\n const webStream = Readable.toWeb(\n req as Readable,\n ) as ReadableStream<Uint8Array>;\n\n try {\n const result = await upload.send(webStream);\n sendJson(res, 200, result);\n } catch (err) {\n if (err instanceof HashMismatch) {\n sendJson(res, 422, {\n error: \"hash_mismatch\",\n claimed: err.claimed,\n actual: err.actual,\n });\n return;\n }\n if (err instanceof SizeMismatch) {\n sendJson(res, 422, {\n error: \"size_mismatch\",\n declared: err.declared,\n actual: err.actual,\n });\n return;\n }\n sendErrorFromException(res, err);\n }\n };\n}\n\nexport function makeDownloadHandler(\n attachments: AttachmentBuildResult,\n attachmentAccess: IAttachmentAccessService,\n urlSigner: AttachmentUrlSigner | null,\n) {\n return async (\n req: IncomingMessage,\n res: ServerResponse,\n _body?: unknown,\n actor?: AttachmentActorContext,\n ): Promise<void> => {\n const hash = extractParam(req, \"hash\");\n if (!hash || !HASH_PATTERN.test(hash)) {\n sendError(res, 400, \"Invalid attachment hash\");\n return;\n }\n\n const canonicalHash = hash.toLowerCase() as AttachmentHash;\n const grant = await resolveByteGrant(\n req,\n canonicalHash,\n actor,\n attachmentAccess,\n urlSigner,\n );\n if (!admitGrant(res, grant)) return;\n\n const controller = new AbortController();\n req.once(\"close\", () => controller.abort());\n\n let response;\n try {\n response = await attachments.store.get(\n canonicalHash,\n controller.signal,\n grant.documentId,\n );\n } catch (err) {\n if (err instanceof AttachmentPending) {\n res.statusCode = 202;\n res.setHeader(\"Cache-Control\", \"private\");\n res.setHeader(\"Retry-After\", String(RETRY_AFTER_SECONDS));\n res.setHeader(\n \"Attachment-Pending\",\n buildPendingHeader({\n expiresAtUtc: err.expiresAtUtc,\n ...(err.metadata ?? {}),\n }),\n );\n res.end();\n return;\n }\n sendErrorFromException(res, err);\n return;\n }\n\n const { header, body } = response;\n res.statusCode = 200;\n res.setHeader(\"Cache-Control\", \"private\");\n res.setHeader(\"Content-Type\", header.mimeType);\n res.setHeader(\"Content-Length\", String(header.sizeBytes));\n res.setHeader(\n \"Content-Disposition\",\n buildContentDisposition(header.fileName),\n );\n res.setHeader(\"Attachment-Metadata\", buildMetadataHeader(header));\n\n Readable.fromWeb(body as unknown as NodeReadableStream<Uint8Array>).pipe(\n res,\n );\n };\n}\n\n// Node rejects header values above U+00FF. Escaping everything outside\n// printable ASCII keeps the header valid; JSON.parse restores the original.\nfunction toAsciiJson(value: unknown): string {\n return JSON.stringify(value).replace(\n /[\\u007f-\\uffff]/g,\n (char) => `\\\\u${char.charCodeAt(0).toString(16).padStart(4, \"0\")}`,\n );\n}\n\nexport function buildPendingHeader(pending: {\n expiresAtUtc: string | null;\n mimeType?: string;\n fileName?: string;\n sizeBytes?: number;\n}): string {\n return toAsciiJson(pending);\n}\n\nexport function buildMetadataHeader(header: {\n mimeType: string;\n fileName: string;\n sizeBytes: number;\n extension: string | null;\n createdAtUtc: string;\n lastAccessedAtUtc: string;\n}): string {\n return toAsciiJson({\n mimeType: header.mimeType,\n fileName: header.fileName,\n sizeBytes: header.sizeBytes,\n extension: header.extension,\n createdAtUtc: header.createdAtUtc,\n lastAccessedAtUtc: header.lastAccessedAtUtc,\n });\n}\n\nexport function makeStatHandler(\n attachments: AttachmentBuildResult,\n attachmentAccess: IAttachmentAccessService,\n urlSigner: AttachmentUrlSigner | null,\n) {\n return async (\n req: IncomingMessage,\n res: ServerResponse,\n _body?: unknown,\n actor?: AttachmentActorContext,\n ): Promise<void> => {\n const hash = extractParam(req, \"hash\");\n if (!hash || !HASH_PATTERN.test(hash)) {\n sendError(res, 400, \"Invalid attachment hash\");\n return;\n }\n\n const canonicalHash = hash.toLowerCase() as AttachmentHash;\n const grant = await resolveByteGrant(\n req,\n canonicalHash,\n actor,\n attachmentAccess,\n urlSigner,\n );\n if (!admitGrant(res, grant)) return;\n\n let header;\n try {\n header = await attachments.store.stat(canonicalHash);\n } catch (err) {\n sendErrorFromException(res, err);\n return;\n }\n\n res.setHeader(\"Cache-Control\", \"private\");\n if (header.status === \"pending\") {\n res.statusCode = 202;\n res.setHeader(\"Retry-After\", String(RETRY_AFTER_SECONDS));\n res.setHeader(\n \"Attachment-Pending\",\n buildPendingHeader({\n expiresAtUtc: header.expiresAtUtc,\n mimeType: header.mimeType,\n fileName: header.fileName,\n sizeBytes: header.sizeBytes,\n }),\n );\n res.end();\n return;\n }\n\n res.statusCode = 200;\n res.setHeader(\"Content-Type\", header.mimeType);\n res.setHeader(\"Content-Length\", String(header.sizeBytes));\n res.setHeader(\n \"Content-Disposition\",\n buildContentDisposition(header.fileName),\n );\n res.setHeader(\"Attachment-Metadata\", buildMetadataHeader(header));\n res.end();\n };\n}\n\nexport function makeGetReservationHandler(attachments: AttachmentBuildResult) {\n return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const reservationId = extractParam(req, \"reservationId\");\n if (!reservationId) {\n sendError(res, 400, \"Missing reservationId\");\n return;\n }\n try {\n const reservation = await attachments.reservations.get(reservationId);\n sendJson(res, 200, reservation);\n } catch (err) {\n sendErrorFromException(res, err);\n }\n };\n}\n\nexport function makeDeleteReservationHandler(\n attachments: AttachmentBuildResult,\n) {\n return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {\n const reservationId = extractParam(req, \"reservationId\");\n if (!reservationId) {\n sendError(res, 400, \"Missing reservationId\");\n return;\n }\n try {\n await attachments.reservations.delete(reservationId);\n res.statusCode = 204;\n res.end();\n } catch (err) {\n sendErrorFromException(res, err);\n }\n };\n}\n\nfunction extractParam(req: IncomingMessage, name: string): string | undefined {\n const expressParams = (\n req as IncomingMessage & {\n params?: Record<string, string>;\n }\n ).params;\n return expressParams?.[name];\n}\n\nconst MAX_DOCUMENT_ID_LEN = 512;\nconst ATTACHMENT_NOT_FOUND_BODY = { error: \"Attachment not found\" };\n// SigV4 presigned URLs cannot outlive 7 days; requests above the cap are\n// clamped rather than rejected so clients need not know the ceiling.\nconst MAX_DOWNLOAD_TARGET_TTL_SECONDS = 7 * 24 * 60 * 60;\n\n/**\n * How long a download target may live, by default.\n *\n * The authorization behind a presigned URL is decided once, when it is issued,\n * and the URL keeps working until it expires however the document's policy\n * changes in between. A week of that is a revocation that does not revoke: an\n * account deactivated, a company reassigned or a group left, and the bytes stay\n * fetchable by anyone holding the link. Minutes are enough to open a file;\n * days are only enough to lose control of it.\n *\n * A deployment that needs longer sets ATTACHMENT_DOWNLOAD_TARGET_MAX_TTL_SECONDS,\n * still bounded by the signing ceiling above.\n */\nconst DEFAULT_DOWNLOAD_TARGET_TTL_SECONDS = 300;\n\n/**\n * The ceiling in force, read per call so a deployment can set it without the\n * value being frozen at import — which is also what lets a test cover it.\n * Anything unparseable falls back to the default rather than to the maximum:\n * a typo must not quietly widen the window it was meant to narrow.\n */\nfunction downloadTargetTtlCapSeconds(): number {\n const configured = process.env.ATTACHMENT_DOWNLOAD_TARGET_MAX_TTL_SECONDS;\n if (configured === undefined) return DEFAULT_DOWNLOAD_TARGET_TTL_SECONDS;\n const parsed = Number(configured);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n logger.warn(\n `Ignoring invalid ATTACHMENT_DOWNLOAD_TARGET_MAX_TTL_SECONDS=\"${configured}\" (expected a positive integer number of seconds) — using ${DEFAULT_DOWNLOAD_TARGET_TTL_SECONDS}`,\n );\n return DEFAULT_DOWNLOAD_TARGET_TTL_SECONDS;\n }\n return Math.min(parsed, MAX_DOWNLOAD_TARGET_TTL_SECONDS);\n}\n\n/**\n * Returns the single `documentId` query value, or null when it is missing,\n * duplicated, blank, or oversized. Validation happens before authorization so\n * malformed requests never reach the access service.\n */\nfunction extractSingleDocumentId(req: IncomingMessage): string | null {\n if (!req.url) return null;\n let url: URL;\n try {\n url = new URL(req.url, \"http://switchboard.invalid\");\n } catch {\n return null;\n }\n const values = url.searchParams.getAll(\"documentId\");\n if (values.length !== 1) return null;\n const value = values[0];\n if (value.trim().length === 0 || value.length > MAX_DOCUMENT_ID_LEN) {\n return null;\n }\n return value;\n}\n\n/**\n * Returns the target TTL in seconds, or \"invalid\" when the caller's own value\n * is malformed (duplicated, non-integer, or non-positive).\n *\n * The cap applies whether or not the caller asked for a lifetime: omitting\n * `expiresIn` used to fall through to whatever the storage backend was\n * configured with, which would make the ceiling something a client opts out of\n * by saying nothing.\n */\nfunction extractExpiresIn(req: IncomingMessage): number | \"invalid\" {\n const cap = downloadTargetTtlCapSeconds();\n if (!req.url) return cap;\n let url: URL;\n try {\n url = new URL(req.url, \"http://switchboard.invalid\");\n } catch {\n return cap;\n }\n const values = url.searchParams.getAll(\"expiresIn\");\n if (values.length === 0) return cap;\n if (values.length > 1) return \"invalid\";\n const parsed = Number(values[0]);\n if (!Number.isInteger(parsed) || parsed <= 0) return \"invalid\";\n return Math.min(parsed, cap);\n}\n\ntype ByteGrant =\n | { kind: \"granted\"; documentId: string }\n | { kind: \"denied\" }\n | { kind: \"unavailable\" }\n | { kind: \"error\" };\n\nfunction singleQueryValue(\n params: URLSearchParams,\n name: string,\n): string | null {\n const values = params.getAll(name);\n return values.length === 1 ? values[0] : null;\n}\n\n/**\n * Byte routes serve only under a grant: a signed URL minted by the\n * download-target route, or a documentId the caller may read the attachment\n * through. A signed request is judged by its signature alone.\n */\nasync function resolveByteGrant(\n req: IncomingMessage,\n hash: AttachmentHash,\n actor: AttachmentActorContext | undefined,\n attachmentAccess: IAttachmentAccessService,\n urlSigner: AttachmentUrlSigner | null,\n): Promise<ByteGrant> {\n let params: URLSearchParams;\n try {\n params = new URL(req.url ?? \"\", \"http://switchboard.invalid\").searchParams;\n } catch {\n return { kind: \"denied\" };\n }\n const documentId = extractSingleDocumentId(req);\n if (documentId === null) return { kind: \"denied\" };\n\n if (params.has(\"signature\") || params.has(\"expires\")) {\n const signature = singleQueryValue(params, \"signature\");\n const expires = singleQueryValue(params, \"expires\");\n if (!urlSigner || signature === null || expires === null) {\n return { kind: \"denied\" };\n }\n return urlSigner.verify(hash, documentId, expires, signature)\n ? { kind: \"granted\", documentId }\n : { kind: \"denied\" };\n }\n\n let decision;\n try {\n decision = await attachmentAccess.canReadAttachment({\n documentId,\n attachmentRef: createRef(hash),\n userAddress: actor?.user?.address,\n appKey: actor?.user?.appKey,\n });\n } catch (err) {\n logger.error(\"Attachment access decision failed: @error\", err);\n return { kind: \"error\" };\n }\n if (decision.kind === \"projection-unavailable\") {\n return { kind: \"unavailable\" };\n }\n if (decision.kind === \"denied\") return { kind: \"denied\" };\n return { kind: \"granted\", documentId: decision.documentId };\n}\n\n/** Answers a refused grant; true when the request may proceed. */\nfunction admitGrant(\n res: ServerResponse,\n grant: ByteGrant,\n): grant is { kind: \"granted\"; documentId: string } {\n switch (grant.kind) {\n case \"granted\":\n return true;\n case \"denied\":\n // 404, never 403: a refusal must not reveal that the hash exists.\n sendJson(res, 404, ATTACHMENT_NOT_FOUND_BODY);\n return false;\n case \"unavailable\":\n sendError(res, 503, \"Attachment downloads are temporarily unavailable\");\n return false;\n case \"error\":\n sendError(res, 500, \"Internal error\");\n return false;\n }\n}\n\n/**\n * Base URL of this Switchboard as seen by the caller, used to build\n * filesystem `switchboard` download targets that point back at the signed\n * byte route.\n */\nfunction requestBaseUrl(req: IncomingMessage): string | null {\n const forwardedProto = req.headers[\"x-forwarded-proto\"];\n const proto =\n (Array.isArray(forwardedProto) ? forwardedProto[0] : forwardedProto)\n ?.split(\",\")[0]\n ?.trim() ||\n ((req.socket as { encrypted?: boolean }).encrypted ? \"https\" : \"http\");\n const forwardedHost = req.headers[\"x-forwarded-host\"];\n const host =\n (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost) ??\n req.headers.host;\n if (!host) return null;\n return `${proto}://${host}`;\n}\n\nexport function makeDownloadTargetHandler(\n attachments: AttachmentBuildResult,\n attachmentAccess: IAttachmentAccessService,\n urlSigner: AttachmentUrlSigner | null,\n) {\n return async (\n req: IncomingMessage,\n res: ServerResponse,\n _body?: unknown,\n actor?: AttachmentActorContext,\n ): Promise<void> => {\n // Target responses carry short-lived URLs and authorization decisions;\n // no intermediary may cache them.\n res.setHeader(\"Cache-Control\", \"no-store\");\n\n const hash = extractParam(req, \"hash\");\n if (!hash || !HASH_PATTERN.test(hash)) {\n sendError(res, 400, \"Invalid attachment hash\");\n return;\n }\n const documentId = extractSingleDocumentId(req);\n if (documentId === null) {\n sendError(\n res,\n 400,\n \"documentId is required exactly once as a non-empty query parameter\",\n );\n return;\n }\n const expiresIn = extractExpiresIn(req);\n if (expiresIn === \"invalid\") {\n sendError(\n res,\n 400,\n \"expiresIn must be a single positive integer number of seconds\",\n );\n return;\n }\n\n const canonicalHash = hash.toLowerCase() as AttachmentHash;\n let decision;\n try {\n decision = await attachmentAccess.canReadAttachment({\n documentId,\n attachmentRef: createRef(canonicalHash),\n userAddress: actor?.user?.address,\n appKey: actor?.user?.appKey,\n });\n } catch (err) {\n logger.error(\"Attachment access decision failed: @error\", err);\n sendError(res, 500, \"Internal error\");\n return;\n }\n\n if (decision.kind === \"projection-unavailable\") {\n sendError(res, 503, \"Attachment downloads are temporarily unavailable\");\n return;\n }\n if (decision.kind === \"denied\") {\n sendJson(res, 404, ATTACHMENT_NOT_FOUND_BODY);\n return;\n }\n\n let header;\n try {\n header = await attachments.store.stat(canonicalHash);\n } catch (err) {\n if (err instanceof AttachmentNotFound) {\n sendJson(res, 404, ATTACHMENT_NOT_FOUND_BODY);\n return;\n }\n logger.error(\"Attachment metadata lookup failed: @error\", err);\n sendError(res, 500, \"Internal error\");\n return;\n }\n if (header.status !== \"available\") {\n sendJson(res, 404, ATTACHMENT_NOT_FOUND_BODY);\n return;\n }\n\n let target: AttachmentDownloadTarget;\n if (attachments.backend && attachments.backend.kind !== \"filesystem\") {\n try {\n target = await attachments.backend.prepareDownloadTarget(\n canonicalHash,\n expiresIn,\n );\n } catch {\n // Backend errors are sanitized at the backend boundary; no URLs or\n // signatures reach this scope, and none may be logged from here.\n sendError(res, 502, \"Attachment download target unavailable\");\n return;\n }\n } else {\n if (!urlSigner) {\n sendError(res, 503, \"Attachment download signing is not configured\");\n return;\n }\n const base = requestBaseUrl(req);\n if (!base) {\n sendError(res, 500, \"Internal error\");\n return;\n }\n const signed = urlSigner.sign(\n canonicalHash,\n decision.documentId,\n expiresIn,\n );\n try {\n target = parseAttachmentDownloadTarget({\n kind: \"switchboard\",\n method: \"GET\",\n url: `${base}/attachments/${canonicalHash}?${signed.query}`,\n headers: {},\n expiresAtUtc: signed.expiresAtUtc,\n });\n } catch {\n sendError(res, 500, \"Internal error\");\n return;\n }\n }\n\n sendJson(res, 200, target);\n };\n}\n","import type { AttachmentHash } from \"@powerhousedao/reactor\";\nimport { createHmac, randomBytes, timingSafeEqual } from \"node:crypto\";\n\nexport const ATTACHMENT_URL_SIGNING_SECRET_ENV =\n \"PH_ATTACHMENT_URL_SIGNING_SECRET\";\n\nconst MIN_SECRET_LENGTH = 32;\nconst EXPIRES_PATTERN = /^[1-9]\\d{0,15}$/;\nconst SIGNATURE_PATTERN = /^[A-Za-z0-9_-]{43}$/;\n\nexport type SignedAttachmentUrl = {\n query: string;\n expiresAtUtc: string;\n};\n\n// The filesystem counterpart of an S3 presigned GET.\nexport class AttachmentUrlSigner {\n constructor(\n private readonly secret: string | Buffer,\n private readonly now: () => number = Date.now,\n ) {}\n\n sign(\n hash: AttachmentHash,\n documentId: string,\n ttlSeconds: number,\n ): SignedAttachmentUrl {\n const expires = Math.floor(this.now() / 1000) + ttlSeconds;\n const params = new URLSearchParams({\n documentId,\n expires: String(expires),\n signature: this.mac(hash, documentId, String(expires)),\n });\n return {\n query: params.toString(),\n expiresAtUtc: new Date(expires * 1000).toISOString(),\n };\n }\n\n verify(\n hash: AttachmentHash,\n documentId: string,\n expires: string,\n signature: string,\n ): boolean {\n if (!EXPIRES_PATTERN.test(expires) || !SIGNATURE_PATTERN.test(signature)) {\n return false;\n }\n if (Number(expires) * 1000 <= this.now()) {\n return false;\n }\n const expected = Buffer.from(this.mac(hash, documentId, expires));\n const presented = Buffer.from(signature);\n return (\n expected.length === presented.length &&\n timingSafeEqual(expected, presented)\n );\n }\n\n private mac(hash: string, documentId: string, expires: string): string {\n return createHmac(\"sha256\", this.secret)\n .update(\n JSON.stringify([\"attachment-download/v1\", hash, documentId, expires]),\n )\n .digest(\"base64url\");\n }\n}\n\nexport type AttachmentUrlSigning =\n | { status: \"configured\"; signer: AttachmentUrlSigner }\n | { status: \"ephemeral\"; signer: AttachmentUrlSigner }\n | { status: \"unconfigured\" };\n\n// Unset: production fails closed, anything else gets a per-process secret.\nexport function resolveAttachmentUrlSigning(\n env: Readonly<Record<string, string | undefined>> = process.env,\n): AttachmentUrlSigning {\n const secret = env[ATTACHMENT_URL_SIGNING_SECRET_ENV];\n if (secret !== undefined) {\n if (secret.trim() !== secret || secret.length < MIN_SECRET_LENGTH) {\n throw new Error(\n `${ATTACHMENT_URL_SIGNING_SECRET_ENV} must be at least ${MIN_SECRET_LENGTH} characters with no leading or trailing whitespace`,\n );\n }\n return { status: \"configured\", signer: new AttachmentUrlSigner(secret) };\n }\n if (env.NODE_ENV === \"production\") {\n return { status: \"unconfigured\" };\n }\n return {\n status: \"ephemeral\",\n signer: new AttachmentUrlSigner(randomBytes(32)),\n };\n}\n","import type { API } from \"@powerhousedao/reactor-api\";\nimport { childLogger } from \"document-model\";\nimport { mountAuthenticatedNodeRoute } from \"./mount-auth.js\";\nimport {\n makeDeleteReservationHandler,\n makeDownloadHandler,\n makeDownloadTargetHandler,\n makeGetReservationHandler,\n makeReserveHandler,\n makeStatHandler,\n makeUploadHandler,\n} from \"./routes.js\";\nimport {\n ATTACHMENT_URL_SIGNING_SECRET_ENV,\n resolveAttachmentUrlSigning,\n type AttachmentUrlSigner,\n} from \"./url-signer.js\";\n\nconst logger = childLogger([\"switchboard\", \"attachments\"]);\n\nexport type RegisterAttachmentRoutesOptions = {\n /** Overrides the signer resolved from the environment; null disables signing. */\n urlSigner?: AttachmentUrlSigner | null;\n};\n\nexport function registerAttachmentRoutes(\n api: API,\n options: RegisterAttachmentRoutesOptions = {},\n): void {\n const { attachments, attachmentAccess } = api;\n const urlSigner =\n options.urlSigner === undefined\n ? signerFromEnvironment(attachments.backend?.kind ?? \"filesystem\")\n : options.urlSigner;\n\n mountAuthenticatedNodeRoute(\n api,\n \"POST\",\n \"/attachments/reservations\",\n makeReserveHandler(attachments),\n );\n\n mountAuthenticatedNodeRoute(\n api,\n \"GET\",\n \"/attachments/reservations/:reservationId\",\n makeGetReservationHandler(attachments),\n );\n\n mountAuthenticatedNodeRoute(\n api,\n \"DELETE\",\n \"/attachments/reservations/:reservationId\",\n makeDeleteReservationHandler(attachments),\n );\n\n mountAuthenticatedNodeRoute(\n api,\n \"PUT\",\n \"/attachments/reservations/:reservationId\",\n makeUploadHandler(attachments),\n );\n\n // Anonymous-capable: authorization is purely the document's (or a signed\n // URL minted under it), exactly as it is for the document itself.\n mountAuthenticatedNodeRoute(\n api,\n \"HEAD\",\n \"/attachments/:hash\",\n makeStatHandler(attachments, attachmentAccess, urlSigner),\n { allowAnonymous: true },\n );\n\n mountAuthenticatedNodeRoute(\n api,\n \"GET\",\n \"/attachments/:hash/download-target\",\n makeDownloadTargetHandler(attachments, attachmentAccess, urlSigner),\n { allowAnonymous: true },\n );\n\n mountAuthenticatedNodeRoute(\n api,\n \"GET\",\n \"/attachments/:hash\",\n makeDownloadHandler(attachments, attachmentAccess, urlSigner),\n { allowAnonymous: true },\n );\n}\n\nfunction signerFromEnvironment(\n backend: \"filesystem\" | \"s3\",\n): AttachmentUrlSigner | null {\n const signing = resolveAttachmentUrlSigning();\n if (signing.status === \"unconfigured\") {\n if (backend === \"filesystem\") {\n logger.error(\n `${ATTACHMENT_URL_SIGNING_SECRET_ENV} is not set: filesystem attachment download targets are refused until it is`,\n );\n }\n return null;\n }\n if (signing.status === \"ephemeral\" && backend === \"filesystem\") {\n logger.warn(\n `${ATTACHMENT_URL_SIGNING_SECRET_ENV} is not set: signing attachment download URLs with a per-process secret`,\n );\n }\n return signing.signer;\n}\n","import {\n REACTOR_SCHEMA,\n supportsLiveReadModelRegistration,\n type DocumentViewDatabase,\n type InProcessReactorClientModule,\n type ReactorBuilder,\n} from \"@powerhousedao/reactor\";\nimport type { AttachmentReferenceProjectionCapability } from \"@powerhousedao/reactor-api\";\nimport {\n AttachmentReferenceReadModel,\n AttachmentSchemaCompiler,\n type IAttachmentReferenceWriter,\n} from \"@powerhousedao/reactor-attachments\";\nimport type { Kysely } from \"kysely\";\n\nexport type AttachmentReferenceReadModelRegistration = {\n attachmentReferenceWriter: IAttachmentReferenceWriter;\n baseKysely: Kysely<unknown>;\n};\n\nconst attachmentSchemaCompiler = new AttachmentSchemaCompiler();\n\nfunction createAttachmentReferenceReadModel(\n baseKysely: Kysely<unknown>,\n dependencies: {\n operationIndex: ConstructorParameters<\n typeof AttachmentReferenceReadModel\n >[1];\n writeCache: ConstructorParameters<typeof AttachmentReferenceReadModel>[2];\n processorManagerConsistencyTracker: ConstructorParameters<\n typeof AttachmentReferenceReadModel\n >[3];\n documentModelRegistry: ConstructorParameters<\n typeof AttachmentReferenceReadModel\n >[4];\n },\n attachmentReferenceWriter: IAttachmentReferenceWriter,\n): AttachmentReferenceReadModel {\n return new AttachmentReferenceReadModel(\n baseKysely.withSchema(\n REACTOR_SCHEMA,\n ) as unknown as Kysely<DocumentViewDatabase>,\n dependencies.operationIndex,\n dependencies.writeCache,\n dependencies.processorManagerConsistencyTracker,\n dependencies.documentModelRegistry,\n attachmentSchemaCompiler,\n attachmentReferenceWriter,\n );\n}\n\nexport function registerAttachmentReferenceReadModel(\n reactorBuilder: ReactorBuilder,\n registration: AttachmentReferenceReadModelRegistration,\n): void {\n reactorBuilder.withReadModelFactory(\n async ({\n documentModelRegistry,\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n }) => {\n const readModel = createAttachmentReferenceReadModel(\n registration.baseKysely,\n {\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n documentModelRegistry,\n },\n registration.attachmentReferenceWriter,\n );\n await readModel.init();\n return readModel;\n },\n );\n}\n\nexport async function registerAttachmentReferenceReadModelOnModule(\n clientModule: InProcessReactorClientModule,\n attachmentReferenceWriter: IAttachmentReferenceWriter,\n): Promise<AttachmentReferenceProjectionCapability> {\n const reactorModule = clientModule.reactorModule;\n if (!reactorModule) {\n return {\n status: \"unavailable\",\n reason: \"in-process-reactor-module-unavailable\",\n };\n }\n\n const coordinator = reactorModule.readModelCoordinator;\n if (!supportsLiveReadModelRegistration(coordinator)) {\n return {\n status: \"unavailable\",\n reason: \"live-read-model-registration-unsupported\",\n };\n }\n\n const readModel = createAttachmentReferenceReadModel(\n reactorModule.database as unknown as Kysely<unknown>,\n {\n operationIndex: reactorModule.operationIndex,\n writeCache: reactorModule.writeCache,\n processorManagerConsistencyTracker:\n reactorModule.processorManagerConsistencyTracker,\n documentModelRegistry: reactorModule.documentModelRegistry,\n },\n attachmentReferenceWriter,\n );\n\n await readModel.init();\n coordinator.addReadModel(readModel, \"pre_ready\");\n await readModel.init();\n\n return { status: \"available\" };\n}\n","import type { ILogger } from \"document-model\";\n\nconst FORCED_EXIT_MS = 15_000;\n\ntype FatalProcess = Pick<\n NodeJS.Process,\n \"on\" | \"kill\" | \"exit\" | \"pid\" | \"listenerCount\"\n> & {\n exitCode?: NodeJS.Process[\"exitCode\"];\n};\n\nconst fatalHandlers = new WeakMap<\n FatalProcess,\n (kind: string, err: unknown) => void\n>();\n\n/**\n * Sends an uncaught exception or unhandled rejection through the SIGTERM\n * shutdown that `withSignalHandlers()` installs, so the reactor and read-model\n * PGlite stores close with a shutdown checkpoint before the process exits\n * with code 1. Node's default is to exit at once, which leaves both stores\n * to WAL recovery on the next open. A store whose wasm runtime aborted\n * (ENOSPC) rejects its close; the builder logs that and still exits. A\n * shutdown that hangs is cut off after FORCED_EXIT_MS.\n *\n * A rejection is fatal only when this is its sole listener: Node exits on an\n * unhandled rejection only when nothing listens for it, and Sentry's\n * listener deliberately keeps the process running.\n */\nexport function installFatalErrorShutdown(\n logger: ILogger,\n proc: FatalProcess = process,\n): void {\n if (fatalHandlers.has(proc)) return;\n\n // Captured before shutdown starts: the builder replaces process.exit with a\n // recording shim while it drains.\n const realExit = proc.exit.bind(proc);\n let shuttingDown = false;\n\n /**\n * Logs without ever throwing. onFatal runs inside the uncaughtException\n * handler, so a logger whose transport is gone (EPIPE on a closed stdout)\n * would otherwise throw from the handler that reports throws, killing the\n * process before the shutdown it is supposed to run. A logging failure is\n * swallowed: there is nowhere left to report it.\n */\n const logFatal = (message: string, err?: unknown): void => {\n try {\n if (err === undefined) {\n logger.error(message);\n } else {\n logger.error(message, err);\n }\n } catch {\n // The log transport itself failed; shutdown must keep moving.\n }\n };\n\n const onFatal = (kind: string, err: unknown): void => {\n logFatal(`${kind}: @error`, err);\n if (shuttingDown) return;\n shuttingDown = true;\n proc.exitCode = 1;\n setTimeout(() => {\n logFatal(\"Shutdown after fatal error timed out; exiting\");\n realExit(1);\n }, FORCED_EXIT_MS).unref();\n proc.kill(proc.pid, \"SIGTERM\");\n };\n fatalHandlers.set(proc, onFatal);\n\n proc.on(\"uncaughtException\", (err) => onFatal(\"Uncaught exception\", err));\n proc.on(\"unhandledRejection\", (reason) => {\n if (proc.listenerCount(\"unhandledRejection\") > 1) return;\n onFatal(\"Unhandled rejection\", reason);\n });\n}\n\n/**\n * Routes a fatal condition the caller detected through the same shutdown as an\n * uncaught error. Returns false, and does nothing, when\n * `installFatalErrorShutdown` has not run for `proc`.\n */\nexport function triggerFatalShutdown(\n kind: string,\n err: unknown,\n proc: FatalProcess = process,\n): boolean {\n const onFatal = fatalHandlers.get(proc);\n if (!onFatal) return false;\n onFatal(kind, err);\n return true;\n}\n","import type {\n InProcessReactorModule,\n IReactorClient,\n} from \"@powerhousedao/reactor\";\nimport {\n AuthorizationPolicy,\n PrivacyPermissionAdapter,\n type DocumentPermissionService,\n type GraphQLManager,\n type IAuthorizationService,\n} from \"@powerhousedao/reactor-api\";\nimport {\n createModuleErasure,\n createPrivacySubgraph,\n DisclosureService,\n MIN_DEPLOYMENT_SECRET_BYTES,\n registerSubjectDocumentsReadModel,\n type ErasureScheduler,\n type IErasureService,\n} from \"@powerhousedao/reactor-privacy\";\nimport type { ISigner } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nexport type PrivacyOptions = {\n /** Wins over PH_PRIVACY_ENABLED. */\n enabled?: boolean;\n /** Wins over PH_PRIVACY_DEPLOYMENT_SECRET. */\n deploymentSecret?: string;\n /** The reactor's signer, for a caller-provided reactor. */\n signer?: ISigner;\n};\n\nexport type ResolvedPrivacy =\n | { enabled: false }\n | {\n enabled: true;\n deploymentSecret: string;\n intervalMs?: number;\n deadlineMs?: number;\n markerGraceMs?: number;\n purgeTimeoutMs?: number;\n };\n\n/** Enabling privacy on a switchboard that cannot run it safely. */\nexport class PrivacyBootError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"PrivacyBootError\";\n }\n}\n\nfunction positive(\n env: NodeJS.ProcessEnv,\n name: string,\n unitMs: number,\n): number | undefined {\n const raw = env[name];\n if (raw === undefined || raw.trim() === \"\") return undefined;\n const value = Number(raw);\n if (!Number.isFinite(value) || value <= 0) {\n throw new PrivacyBootError(\n `${name} must be a positive number, got \"${raw}\"`,\n );\n }\n return value * unitMs;\n}\n\nexport function resolvePrivacy(\n options: PrivacyOptions | undefined,\n env: NodeJS.ProcessEnv,\n): ResolvedPrivacy {\n const enabled = options?.enabled ?? env.PH_PRIVACY_ENABLED === \"true\";\n if (!enabled) return { enabled: false };\n const deploymentSecret =\n options?.deploymentSecret ?? env.PH_PRIVACY_DEPLOYMENT_SECRET;\n if (\n !deploymentSecret ||\n Buffer.byteLength(deploymentSecret, \"utf8\") < MIN_DEPLOYMENT_SECRET_BYTES\n ) {\n throw new PrivacyBootError(\n `PH_PRIVACY_ENABLED needs PH_PRIVACY_DEPLOYMENT_SECRET of at least ${MIN_DEPLOYMENT_SECRET_BYTES} bytes`,\n );\n }\n return {\n enabled: true,\n deploymentSecret,\n intervalMs: positive(env, \"PH_PRIVACY_INTERVAL_MS\", 1),\n deadlineMs: positive(env, \"PH_PRIVACY_DEADLINE_DAYS\", DAY_MS),\n markerGraceMs: positive(env, \"PH_PRIVACY_MARKER_GRACE_DAYS\", DAY_MS),\n purgeTimeoutMs: positive(env, \"PH_PRIVACY_PURGE_TIMEOUT_MINUTES\", 60_000),\n };\n}\n\nexport type RunningPrivacy = {\n erasure: IErasureService;\n scheduler: ErasureScheduler;\n stop(): Promise<void>;\n};\n\n/** Subject index, erasure scheduler and admin subgraph on a built reactor. */\nexport async function startPrivacy(deps: {\n config: Extract<ResolvedPrivacy, { enabled: true }>;\n reactorModule: InProcessReactorModule | undefined;\n signer: ISigner | undefined;\n authorizationService: IAuthorizationService;\n documentPermissionService: DocumentPermissionService | undefined;\n graphqlManager: GraphQLManager;\n reactorClient: IReactorClient;\n logger: ILogger;\n}): Promise<RunningPrivacy> {\n const { config, reactorModule, authorizationService } = deps;\n if (authorizationService.config.policy === AuthorizationPolicy.OPEN) {\n throw new PrivacyBootError(\n \"PH_PRIVACY_ENABLED is refused under the OPEN authorization policy: every caller, anonymous included, would administer erasure. Enable authentication (AUTH_ENABLED=true with ADMINS) first.\",\n );\n }\n if (!reactorModule) {\n throw new PrivacyBootError(\n \"PH_PRIVACY_ENABLED needs an in-process reactor module\",\n );\n }\n if (!deps.signer?.app?.key) {\n throw new PrivacyBootError(\n \"PH_PRIVACY_ENABLED needs a reactor signer (a Renown identity): every receiver refuses an unsigned purge marker\",\n );\n }\n\n const permissions = deps.documentPermissionService\n ? new PrivacyPermissionAdapter(deps.documentPermissionService)\n : undefined;\n const { service, scheduler } = createModuleErasure(reactorModule, {\n deploymentSecret: config.deploymentSecret,\n signer: deps.signer,\n permissions,\n intervalMs: config.intervalMs,\n deadlineMs: config.deadlineMs,\n markerGraceMs: config.markerGraceMs,\n purgeTimeoutMs: config.purgeTimeoutMs,\n logger: deps.logger,\n });\n\n const disclosure = new DisclosureService(\n reactorModule.database,\n config.deploymentSecret,\n permissions,\n );\n let subgraph;\n try {\n subgraph = createPrivacySubgraph({\n authorizationService,\n erasure: service,\n disclosure,\n });\n } catch (error) {\n throw new PrivacyBootError(\n error instanceof Error ? error.message : String(error),\n );\n }\n\n await registerSubjectDocumentsReadModel(reactorModule, {\n deploymentSecret: config.deploymentSecret,\n });\n await deps.graphqlManager.registerSubgraphInstance(\n {\n ...subgraph,\n path: deps.graphqlManager.getBasePath(),\n reactorClient: deps.reactorClient,\n relationalDb: undefined as never,\n },\n \"graphql\",\n false,\n );\n\n scheduler.start();\n deps.logger.info(\"Privacy add-on started: subject index, erasure scheduler\");\n return {\n erasure: service,\n scheduler,\n stop: () => scheduler.stop(),\n };\n}\n","import type {\n ReactorBuilder,\n ReactorFeatureFlags,\n} from \"@powerhousedao/reactor\";\nimport {\n ChannelScheme,\n type IDocumentModelLoader,\n type ReactorClientBuilder,\n type SignerConfig,\n} from \"@powerhousedao/reactor\";\nimport { reactorDriveDocumentModelModule } from \"@powerhousedao/reactor-drive\";\nimport { ReactorGroupV1 } from \"@powerhousedao/reactor-group\";\nimport { getUniqueDocumentModels } from \"@powerhousedao/reactor-api\";\nimport { driveDocumentModelModule } from \"@powerhousedao/shared/document-drive\";\nimport type {\n DocumentModelModule,\n SignaturePolicy,\n UpgradeManifest,\n} from \"@powerhousedao/shared/document-model\";\nimport { documentModelDocumentModelModule, type ILogger } from \"document-model\";\n\nexport type SwitchboardReactorDefaultsOptions = {\n // Extra models registered alongside the baseline (document-model, drive,\n // reactor-drive, reactor-group). Pass vetra models here in studio/dev.\n // Deduped by id.\n documentModels?: DocumentModelModule[];\n /** Upgrade manifests for versioned models, one per document type. */\n upgradeManifests?: UpgradeManifest<readonly number[]>[];\n /** Default true. */\n includeBaseModels?: boolean;\n /**\n * Channel scheme. Defaults to `ChannelScheme.SWITCHBOARD`, which populates\n * `reactorModule.syncModule.syncManager` — required by reactor-api. Set\n * to `false` if the caller configures sync itself (`withChannelScheme` or\n * `withSync`); the builder refuses both together.\n */\n channelScheme?: ChannelScheme | false;\n /** Defaults to true. Set false when the caller owns SIGINT handling. */\n signalHandlers?: boolean;\n /** Executor tuning. Omit to use the reactor's own defaults. */\n executorConfig?: {\n maxSkipThreshold?: number;\n featureFlags?: Partial<ReactorFeatureFlags>;\n };\n /** Wire dynamic document-model loading via HTTP. */\n documentModelLoader?: IDocumentModelLoader;\n logger?: ILogger;\n /**\n * Identity signer (typically from `getRenownSignerConfig`). Signs client\n * writes and the operations the executor synthesizes, in pooled workers\n * through `workerSigner`; omit for unsigned operation.\n */\n signer?: SignerConfig;\n /**\n * Which keys may sign as which users (typically from\n * `getRenownTrustPolicyConfig`); wins over one carried by `signer`.\n */\n trustPolicy?: Pick<SignerConfig, \"trustPolicy\" | \"workerTrustPolicy\">;\n /** What the client creates new documents as; see `resolveCreateSignaturePolicy`. */\n createSignaturePolicy?: SignaturePolicy;\n};\n\n/**\n * Apply switchboard's standard configuration to a reactor + client builder\n * pair. Each piece is opt-out via the options object; defaults mirror what\n * `startSwitchboard` does when building a reactor itself. Mutates both\n * builders in place.\n *\n * Does NOT touch kysely or read models — callers wire those themselves\n * (see `withKysely` / `withReadModelFactory` on the reactor builder).\n */\n/** The models switchboard always registers alongside package models. */\nexport function switchboardBaseDocumentModels() {\n return [\n documentModelDocumentModelModule,\n driveDocumentModelModule,\n reactorDriveDocumentModelModule,\n ReactorGroupV1 as unknown as DocumentModelModule,\n ];\n}\n\nexport function applySwitchboardReactorDefaults(\n reactorBuilder: ReactorBuilder,\n clientBuilder: ReactorClientBuilder,\n options: SwitchboardReactorDefaultsOptions = {},\n): void {\n const baseModels =\n options.includeBaseModels !== false ? switchboardBaseDocumentModels() : [];\n const extra = options.documentModels ?? [];\n if (baseModels.length || extra.length) {\n reactorBuilder.withDocumentModelSources(\n getUniqueDocumentModels(baseModels, extra),\n );\n }\n\n if (options.upgradeManifests?.length) {\n reactorBuilder.withUpgradeManifests(options.upgradeManifests);\n }\n\n const scheme =\n options.channelScheme === undefined\n ? ChannelScheme.SWITCHBOARD\n : options.channelScheme;\n if (scheme !== false) {\n reactorBuilder.withChannelScheme(scheme);\n }\n\n if (options.signalHandlers !== false) {\n reactorBuilder.withSignalHandlers();\n }\n\n if (\n options.executorConfig?.maxSkipThreshold !== undefined ||\n options.executorConfig?.featureFlags !== undefined\n ) {\n reactorBuilder.withExecutorConfig({\n ...(options.executorConfig.maxSkipThreshold !== undefined\n ? { maxSkipThreshold: options.executorConfig.maxSkipThreshold }\n : {}),\n ...(options.executorConfig.featureFlags !== undefined\n ? { featureFlags: options.executorConfig.featureFlags }\n : {}),\n });\n }\n\n if (options.documentModelLoader) {\n reactorBuilder.withDocumentModelLoader(options.documentModelLoader);\n }\n\n if (options.logger) {\n reactorBuilder.withLogger(options.logger);\n }\n\n if (options.signer) {\n clientBuilder.withSigner(options.signer);\n reactorBuilder.withSigner(\n options.signer.signer,\n options.signer.workerSigner,\n );\n }\n\n if (options.createSignaturePolicy) {\n clientBuilder.withCreateSignaturePolicy(options.createSignaturePolicy);\n }\n\n const trust = options.trustPolicy ?? options.signer;\n if (trust?.trustPolicy) {\n reactorBuilder.withTrustPolicy(trust.trustPolicy, trust.workerTrustPolicy);\n }\n}\n","import type { DbConfig, FileModelSource } from \"@powerhousedao/reactor\";\nimport type { ILogger } from \"document-model\";\nimport { existsSync, realpathSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\n\nexport type WorkerCountInput = number | \"auto\";\n\nexport type SwitchboardWorkerPoolOptions = {\n numWorkers: number;\n /** Whether numWorkers was given explicitly or sized from the core count. */\n mode: \"explicit\" | \"auto\";\n dbPoolSizePerWorker: number;\n acquireTimeoutMs: number;\n};\n\nexport type SwitchboardWorkerPoolInput = {\n numWorkers?: WorkerCountInput;\n dbPoolSizePerWorker?: number;\n acquireTimeoutMs?: number;\n};\n\nconst DEFAULT_DB_POOL_SIZE_PER_WORKER = 2;\nconst DEFAULT_ACQUIRE_TIMEOUT_MS = 5000;\n\n// Host reactor pool. pg-pool's own default is 10, below the bench harness's\n// floor of 16 (bench/host/src/main.ts, REACTOR_DB_POOL_SIZE_HOST). Run 8 of\n// the sweep found throughput flat from 16 to 96 but per-op index p50\n// collapsing 262ms -> 43ms, so this buys latency, not throughput — and only\n// up to whatever the connection budget in front of Postgres allows.\nconst DEFAULT_DB_POOL_SIZE_HOST = 16;\n\n// Auto-sizing: reserve cores for the host event loop (queue, read models,\n// HTTP) and cap at the top of the bench sweep envelope, which also bounds\n// the worker Postgres-connection budget.\nconst AUTO_RESERVED_CORES = 2;\nconst AUTO_WORKER_CAP = 8;\n\n/** Worker count for \"auto\": always at least 1 — auto sizes the pool, it does not turn worker mode off. */\nexport function autoWorkerCount(availableCores: number): number {\n return Math.max(\n 1,\n Math.min(AUTO_WORKER_CAP, availableCores - AUTO_RESERVED_CORES),\n );\n}\n\n/**\n * Effective worker-pool config from programmatic options (which win) and the\n * REACTOR_* env vars; null when disabled (numWorkers 0, the default).\n * `\"auto\"` sizes the pool from the machine's available cores.\n */\nexport function resolveWorkerPoolOptions(\n input: SwitchboardWorkerPoolInput | undefined,\n env: NodeJS.ProcessEnv,\n availableCores: number = os.availableParallelism(),\n): SwitchboardWorkerPoolOptions | null {\n const requested =\n input?.numWorkers ?? parseWorkerCount(env.REACTOR_WORKERS) ?? 0;\n const mode = requested === \"auto\" ? \"auto\" : \"explicit\";\n const numWorkers =\n requested === \"auto\" ? autoWorkerCount(availableCores) : requested;\n if (!Number.isInteger(numWorkers) || numWorkers < 0) {\n throw new Error(\n `workerPool.numWorkers must be \"auto\" or a non-negative integer, got ${numWorkers}`,\n );\n }\n if (numWorkers === 0) {\n return null;\n }\n return {\n numWorkers,\n mode,\n dbPoolSizePerWorker:\n input?.dbPoolSizePerWorker ??\n parseNonNegativeInt(\n env.REACTOR_DB_POOL_SIZE_WORKER,\n \"REACTOR_DB_POOL_SIZE_WORKER\",\n ) ??\n DEFAULT_DB_POOL_SIZE_PER_WORKER,\n acquireTimeoutMs:\n input?.acquireTimeoutMs ??\n parseNonNegativeInt(\n env.REACTOR_DB_ACQUIRE_TIMEOUT_MS,\n \"REACTOR_DB_ACQUIRE_TIMEOUT_MS\",\n ) ??\n DEFAULT_ACQUIRE_TIMEOUT_MS,\n };\n}\n\n/**\n * Size of the reactor's own (host) Postgres pool. Unlike the worker pool there\n * is no \"disabled\" state — the host pool is the only Postgres pool in the\n * server path, backing both reactor storage and the read models that reach it\n * via `withSchema` — so 0 is rejected rather than read as \"off\".\n *\n * The acquire timeout is deliberately not configurable here: the host pool\n * waits indefinitely today, and making it finite converts saturation from a\n * latency problem into a thrown error on the read-model path. That is a\n * behavior change worth making separately, once the retry semantics of a\n * rejected `pool.connect()` inside the read-model coordinator are settled.\n */\nexport function resolveHostPoolSize(env: NodeJS.ProcessEnv): number {\n const poolSize =\n parseNonNegativeInt(\n env.REACTOR_DB_POOL_SIZE_HOST,\n \"REACTOR_DB_POOL_SIZE_HOST\",\n ) ?? DEFAULT_DB_POOL_SIZE_HOST;\n if (poolSize < 1) {\n throw new Error(\n \"REACTOR_DB_POOL_SIZE_HOST must be at least 1; the host pool cannot be disabled\",\n );\n }\n return poolSize;\n}\n\n/**\n * DbConfig for each worker's own pool, parsed from a postgres:// URL.\n * Explicit host, database, and user are required — no pg-side defaults.\n */\nexport function buildWorkerDbConfig(\n postgresUrl: string,\n options: Pick<\n SwitchboardWorkerPoolOptions,\n \"dbPoolSizePerWorker\" | \"acquireTimeoutMs\"\n >,\n): DbConfig {\n let parsed: URL;\n try {\n parsed = new URL(postgresUrl);\n } catch {\n throw new Error(\n `Worker pool requires a valid postgres:// URL for the reactor database, got \"${postgresUrl}\"`,\n );\n }\n const database = decodeURIComponent(parsed.pathname.replace(/^\\//, \"\"));\n if (!parsed.hostname || !database) {\n throw new Error(\n `Worker pool requires a postgres URL with a host and database name, got \"${postgresUrl}\"`,\n );\n }\n if (!parsed.username) {\n throw new Error(\n `Worker pool requires explicit credentials in the postgres URL (workers open their own connections), got \"${postgresUrl}\"`,\n );\n }\n const sslmode = parsed.searchParams.get(\"sslmode\");\n const ssl =\n parsed.searchParams.get(\"ssl\") === \"true\" ||\n (sslmode !== null && sslmode !== \"disable\");\n return {\n host: parsed.hostname,\n port: parsed.port ? Number(parsed.port) : 5432,\n database,\n user: decodeURIComponent(parsed.username),\n password: decodeURIComponent(parsed.password),\n ssl,\n applicationName: \"switchboard-worker\",\n poolSize: options.dbPoolSizePerWorker,\n connectionTimeoutMillis: options.acquireTimeoutMs,\n };\n}\n\n/** Specifiers of the base models switchboard always registers. */\nconst BASE_MODEL_SPECIFIERS = [\n \"document-model\",\n \"@powerhousedao/shared/document-drive\",\n \"@powerhousedao/reactor-drive\",\n \"@powerhousedao/reactor-group/document-models\",\n];\n\n// As the reactor builder tells a module from the rest of a source's exports.\nfunction isModelModule(value: unknown): boolean {\n const candidate = value as {\n reducer?: unknown;\n documentModel?: { global?: { id?: unknown } };\n } | null;\n return (\n typeof candidate?.reducer === \"function\" &&\n typeof candidate.documentModel?.global?.id === \"string\"\n );\n}\n\n// Drops sources that fail to import or export no model. For workflow pieces\n// only: a missing model means \"unavailable to pieces\", not a failed boot.\nexport async function keepImportableSources(\n sources: FileModelSource[],\n logger: ILogger,\n): Promise<FileModelSource[]> {\n const kept: FileModelSource[] = [];\n for (const source of sources) {\n try {\n const exports = (await import(\n pathToFileURL(source.filePath).href\n )) as Record<string, unknown>;\n if (Object.values(exports).some(isModelModule)) kept.push(source);\n } catch (error) {\n logger.warn(\n `Worker model sources: \"${source.filePath}\" does not import, so workflow pieces will not see its models: ${String(error)}`,\n );\n }\n }\n return kept;\n}\n\n/**\n * Resolves base models and configured package identifiers to `{ filePath }`\n * sources for the reactor builder. Resolution to absolute paths happens here\n * because workers (and the builder) resolve bare specifiers from the reactor\n * package's own dependency context, which cannot see switchboard or project\n * packages. Unresolvable identifiers are skipped with a warning; the builder\n * fails the boot if a registered model ends up without an importable source.\n */\nexport async function resolveWorkerModelSources(\n packages: string[],\n logger: ILogger,\n options: {\n /** Cached file for a package not installed locally, e.g. from a registry. */\n resolveRemote?: (identifier: string) => Promise<string | null>;\n } = {},\n): Promise<FileModelSource[]> {\n const sources: FileModelSource[] = [];\n for (const identifier of [...BASE_MODEL_SPECIFIERS, ...packages]) {\n const filePath =\n (await resolveModelModuleFile(identifier)) ??\n (await resolveRemoteSafely(identifier, options.resolveRemote, logger));\n if (!filePath) {\n logger.warn(\n `Worker model sources: no importable document-models entry for \"${identifier}\", skipping`,\n );\n continue;\n }\n sources.push({ filePath });\n }\n return sources;\n}\n\nasync function resolveRemoteSafely(\n identifier: string,\n resolveRemote: ((identifier: string) => Promise<string | null>) | undefined,\n logger: ILogger,\n): Promise<string | null> {\n if (!resolveRemote) return null;\n try {\n return await resolveRemote(identifier);\n } catch (error) {\n logger.warn(\n `Worker model sources: could not cache \"${identifier}\" from the registry: ${String(error)}`,\n );\n return null;\n }\n}\n\n/**\n * Absolute file path of a source's models barrel. Mirrors reactor-api's\n * import-resolver: import.meta.resolve, then the project's node_modules via\n * the package.json exports map / dist layout.\n */\nasync function resolveModelModuleFile(source: string): Promise<string | null> {\n if (isFsPath(source)) {\n return resolveFromPackageDir(source, \"./document-models\");\n }\n\n const specifier = BASE_MODEL_SPECIFIERS.includes(source)\n ? source\n : `${source}/document-models`;\n\n try {\n const resolved = import.meta.resolve(specifier);\n if (resolved.startsWith(\"file:\")) {\n return fileURLToPath(resolved);\n }\n } catch {\n // fall through to node_modules resolution from the project dir\n }\n\n const { packageName, subpath } = splitSpecifier(specifier);\n const packageDir = path.join(process.cwd(), \"node_modules\", packageName);\n if (!existsSync(packageDir)) {\n return null;\n }\n let realDir: string;\n try {\n realDir = realpathSync(packageDir);\n } catch {\n return null;\n }\n return resolveFromPackageDir(realDir, subpath);\n}\n\nfunction splitSpecifier(specifier: string): {\n packageName: string;\n subpath: string;\n} {\n const parts = specifier.split(\"/\");\n const packageSegments = specifier.startsWith(\"@\") ? 2 : 1;\n const packageName = parts.slice(0, packageSegments).join(\"/\");\n const rest = parts.slice(packageSegments).join(\"/\");\n return { packageName, subpath: rest ? `./${rest}` : \".\" };\n}\n\n/** Exports-map lookup (import condition), then conventional dist paths. */\nasync function resolveFromPackageDir(\n packageDir: string,\n subpath: string,\n): Promise<string | null> {\n const fromManifest = await resolveViaManifest(packageDir, subpath);\n if (fromManifest) {\n return fromManifest;\n }\n const candidates =\n subpath === \".\"\n ? [path.join(packageDir, \"dist\", \"index.js\")]\n : [\n path.join(packageDir, \"dist\", subpath.slice(2), \"index.js\"),\n path.join(packageDir, \"dist\", `${subpath.slice(2)}.js`),\n path.join(packageDir, subpath.slice(2), \"index.js\"),\n ];\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n return candidate;\n }\n }\n return null;\n}\n\nasync function resolveViaManifest(\n packageDir: string,\n subpath: string,\n): Promise<string | null> {\n let raw: string;\n try {\n raw = await readFile(path.join(packageDir, \"package.json\"), \"utf8\");\n } catch {\n return null;\n }\n let manifest: { exports?: unknown; main?: unknown };\n try {\n manifest = JSON.parse(raw) as { exports?: unknown; main?: unknown };\n } catch {\n return null;\n }\n\n let entry: unknown;\n const exportsField = manifest.exports;\n if (\n exportsField !== null &&\n typeof exportsField === \"object\" &&\n !Array.isArray(exportsField)\n ) {\n const map = exportsField as Record<string, unknown>;\n const hasSubpathKeys = Object.keys(map).some((key) => key.startsWith(\".\"));\n entry = hasSubpathKeys ? map[subpath] : subpath === \".\" ? map : undefined;\n } else if (typeof exportsField === \"string\" && subpath === \".\") {\n entry = exportsField;\n }\n\n let target = pickImportTarget(entry);\n if (!target && subpath === \".\" && typeof manifest.main === \"string\") {\n target = manifest.main;\n }\n if (!target) {\n return null;\n }\n const resolved = path.join(packageDir, target);\n return existsSync(resolved) ? resolved : null;\n}\n\nfunction pickImportTarget(entry: unknown): string | null {\n if (typeof entry === \"string\") {\n return entry;\n }\n if (typeof entry !== \"object\" || entry === null) {\n return null;\n }\n const conditions = entry as Record<string, unknown>;\n for (const condition of [\"import\", \"node\", \"default\"]) {\n const value = conditions[condition];\n if (typeof value === \"string\") {\n return value;\n }\n }\n return null;\n}\n\nfunction isFsPath(identifier: string): boolean {\n return (\n path.isAbsolute(identifier) ||\n identifier.startsWith(\"./\") ||\n identifier.startsWith(\"../\")\n );\n}\n\nfunction parseWorkerCount(\n raw: string | undefined,\n): WorkerCountInput | undefined {\n if (raw === undefined || raw.trim() === \"\") {\n return undefined;\n }\n if (raw.trim().toLowerCase() === \"auto\") {\n return \"auto\";\n }\n const value = Number.parseInt(raw, 10);\n if (!Number.isInteger(value) || value < 0 || String(value) !== raw.trim()) {\n throw new Error(\n `REACTOR_WORKERS must be \"auto\" or a non-negative integer, got \"${raw}\"`,\n );\n }\n return value;\n}\n\nexport function parseNonNegativeInt(\n raw: string | undefined,\n name: string,\n): number | undefined {\n if (raw === undefined || raw.trim() === \"\") {\n return undefined;\n }\n const value = Number.parseInt(raw, 10);\n if (!Number.isInteger(value) || value < 0 || String(value) !== raw.trim()) {\n throw new Error(`${name} must be a non-negative integer, got \"${raw}\"`);\n }\n return value;\n}\n","import { isPostgresUrl } from \"./utils.mjs\";\nimport { parseNonNegativeInt } from \"./worker-pool.mjs\";\n\nexport type SwitchboardProjectionWorkerOptions = {\n dbPoolSize: number;\n};\n\nexport type SwitchboardProjectionWorkerInput = {\n enabled?: boolean;\n dbPoolSize?: number;\n};\n\n// Run 11 of the bench sweep saw chain.depth ~6.5 at one shard, so 8 covers the\n// working set; behind a 25-slot transaction pooler a larger pool is not spendable.\nconst DEFAULT_DB_POOL_SIZE_PROJECTION = 8;\n\nconst ON_TOKENS = [\"1\", \"true\", \"on\", \"yes\"];\nconst OFF_TOKENS = [\"0\", \"false\", \"off\", \"no\"];\n\n/**\n * Enabled by REACTOR_PROJECTION_WORKER (1/true/on/yes, case-insensitive) or\n * `input.enabled`, which wins when defined; null when off (the default).\n */\nexport function resolveProjectionWorkerOptions(\n input: SwitchboardProjectionWorkerInput | undefined,\n env: NodeJS.ProcessEnv,\n): SwitchboardProjectionWorkerOptions | null {\n const enabled =\n input?.enabled ?? parseOnOff(env.REACTOR_PROJECTION_WORKER) ?? false;\n if (!enabled) {\n return null;\n }\n const dbPoolSize =\n input?.dbPoolSize ??\n parseNonNegativeInt(\n env.REACTOR_DB_POOL_SIZE_PROJECTION,\n \"REACTOR_DB_POOL_SIZE_PROJECTION\",\n ) ??\n DEFAULT_DB_POOL_SIZE_PROJECTION;\n if (dbPoolSize < 1) {\n throw new Error(\n \"REACTOR_DB_POOL_SIZE_PROJECTION must be at least 1; the projection worker cannot run without a pool\",\n );\n }\n return { dbPoolSize };\n}\n\n/** Same preconditions as the executor worker pool; message shape mirrors server.mts. */\nexport function assertProjectionWorkerSupported(args: {\n dev: boolean;\n reactorDbUrl: string | undefined;\n}): void {\n if (args.dev) {\n throw new Error(\n \"The projection worker (REACTOR_PROJECTION_WORKER) is not supported in dev mode: Vite-loaded document models cannot cross a worker-thread boundary\",\n );\n }\n if (!args.reactorDbUrl || !isPostgresUrl(args.reactorDbUrl)) {\n throw new Error(\n \"The projection worker (REACTOR_PROJECTION_WORKER) requires a Postgres reactor database — set PH_REACTOR_DATABASE_URL or PH_SWITCHBOARD_DATABASE_URL. PGlite cannot be shared across worker threads.\",\n );\n }\n}\n\nfunction parseOnOff(raw: string | undefined): boolean | undefined {\n if (raw === undefined || raw.trim() === \"\") {\n return undefined;\n }\n const token = raw.trim().toLowerCase();\n if (ON_TOKENS.includes(token)) {\n return true;\n }\n if (OFF_TOKENS.includes(token)) {\n return false;\n }\n throw new Error(\n `REACTOR_PROJECTION_WORKER must be one of 1, true, on, yes, 0, false, off, no, got \"${raw}\"`,\n );\n}\n","import { EnvVarProvider } from \"@openfeature/env-var-provider\";\nimport { OpenFeature } from \"@openfeature/server-sdk\";\n\nexport async function initFeatureFlags() {\n // for now, we're only using env vars for feature flags\n const provider = new EnvVarProvider();\n\n await OpenFeature.setProviderAndWait(provider);\n\n return OpenFeature.getClient();\n}\n","import type { BooleanFlagSource } from \"./workflow-runtime.mjs\";\n\n/** The env var and OpenFeature flag key that can keep the MCP server unmounted. */\nexport const MCP_ENABLED = \"MCP_ENABLED\";\n\nexport interface McpFlagInput {\n featureFlags: BooleanFlagSource;\n /** The host's `mcp` option. */\n option?: boolean;\n}\n\n/** Whether to mount the MCP server at `/mcp`. On unless the host passes\n * `mcp: false` or MCP_ENABLED is \"false\": the env var can turn a host's MCP\n * off, never on, so a deployment that serves no MCP client can close the\n * endpoint without the bin growing a flag of its own. */\nexport async function resolveMcpEnabled({\n featureFlags,\n option,\n}: McpFlagInput): Promise<boolean> {\n if (option === false) return false;\n return featureFlags.getBooleanValue(MCP_ENABLED, true);\n}\n","import type { WorkflowRuntimeService } from \"@powerhousedao/reactor-workflow\";\nimport type {\n IHttpScope,\n ScopedRouteHandle,\n} from \"@powerhousedao/shared/processors\";\n\nexport const OAUTH_CALLBACK_PATH = \"oauth/callback\";\n\nfunction escapeHtml(text: string): string {\n return text.replace(\n /[&<>\"']/g,\n (char) =>\n ({\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n })[char]!,\n );\n}\n\n// The popup's last page. The editor that opened it polls the attempt, so this\n// only has to say how it went; closing itself is a courtesy.\nfunction resultPage(ok: boolean, detail: string | null): string {\n const title = ok ? \"Connected\" : \"Sign-in failed\";\n const message = ok\n ? \"You can close this window.\"\n : escapeHtml(detail ?? \"The sign-in did not finish.\");\n return `<!doctype html>\n<html><head><meta charset=\"utf-8\"><title>${title}</title>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<style>body{font:15px system-ui,sans-serif;margin:0;display:grid;place-items:center;min-height:100vh;background:#fafafa;color:#222}main{max-width:28rem;padding:1.5rem;text-align:center}h1{font-size:1.1rem}</style>\n</head><body><main><h1>${title}</h1><p>${message}</p></main>\n${ok ? \"<script>setTimeout(function(){window.close()},600)</script>\" : \"\"}\n</body></html>`;\n}\n\n// The provider's redirect lands here. Public: the provider sends no bearer,\n// and the one-time state is what the runtime checks instead.\nexport function registerOAuthCallback(\n http: IHttpScope,\n runtime: Pick<WorkflowRuntimeService, \"completeOAuth\">,\n): ScopedRouteHandle {\n return http.get(OAUTH_CALLBACK_PATH, { auth: \"public\" }, async (request) => {\n const params = new URL(request.url).searchParams;\n const state = params.get(\"state\");\n const result = state\n ? await runtime.completeOAuth({\n state,\n code: params.get(\"code\") ?? undefined,\n error: params.get(\"error\") ?? undefined,\n errorDescription: params.get(\"error_description\") ?? undefined,\n })\n : { ok: false, detail: \"The provider sent no state\", returnUrl: null };\n if (result.returnUrl && state) {\n const back = new URL(result.returnUrl);\n back.searchParams.set(\"ph_oauth\", state);\n return new Response(null, {\n status: 303,\n headers: { location: back.href, \"cache-control\": \"no-store\" },\n });\n }\n return new Response(resultPage(result.ok, result.detail), {\n status: result.ok ? 200 : 400,\n headers: {\n \"content-type\": \"text/html; charset=utf-8\",\n \"cache-control\": \"no-store\",\n \"referrer-policy\": \"no-referrer\",\n },\n });\n });\n}\n\nexport function callbackUrlOf(http: IHttpScope): string {\n return `${http.baseUrl.replace(/\\/+$/, \"\")}/${OAUTH_CALLBACK_PATH}`;\n}\n","import type {\n Context,\n IAuthorizationService,\n} from \"@powerhousedao/reactor-api\";\nimport type {\n RunRow,\n StepExecutionRow,\n WorkflowRuntimeService,\n} from \"@powerhousedao/reactor-workflow\";\nimport {\n GraphQLError,\n Kind,\n type GraphQLResolveInfo,\n type SelectionSetNode,\n} from \"graphql\";\n\ninterface FireArgs {\n workflowId: string;\n payload?: unknown;\n}\n\ninterface PieceArgs {\n packageName: string;\n version?: string | null;\n}\n\ntype BlockRef = Parameters<WorkflowRuntimeService[\"blockDescriptor\"]>[0];\n\ntype PieceSource = NonNullable<\n Parameters<WorkflowRuntimeService[\"searchPieces\"]>[1][\"sources\"]\n>[number];\n\nconst PIECE_SOURCES: readonly string[] = [\n \"local\",\n \"registry\",\n \"activepieces\",\n] satisfies PieceSource[];\n\nfunction isPieceSource(value: string): value is PieceSource {\n return PIECE_SOURCES.includes(value);\n}\n\ninterface BlockInput {\n pieceName: string;\n pieceVersion: string;\n name: string;\n kind: string;\n}\n\nfunction blockRef(input: BlockInput): BlockRef {\n if (input.kind !== \"action\" && input.kind !== \"trigger\") {\n throw new GraphQLError(\n `A block's kind is \"action\" or \"trigger\", got \"${input.kind}\"`,\n );\n }\n return {\n pieceName: input.pieceName,\n pieceVersion: input.pieceVersion,\n name: input.name,\n kind: input.kind,\n };\n}\n\ninterface RunsArgs {\n workflowId?: string;\n driveId?: string;\n limit?: number;\n excludeTriggerKinds?: string[];\n}\n\ninterface RunsPageArgs {\n workflowId?: string;\n driveId?: string;\n excludeTriggerKinds?: string[];\n paging?: { limit?: number | null; cursor?: string | null } | null;\n}\n\n// A cursor the runtime did not issue is the caller's error, not ours.\nasync function cursorAware<T>(call: () => Promise<T>): Promise<T> {\n try {\n return await call();\n } catch (error) {\n if (error instanceof Error && error.name === \"InvalidRunCursorError\") {\n throw new GraphQLError(error.message, {\n extensions: { code: \"BAD_USER_INPUT\" },\n });\n }\n throw error;\n }\n}\n\n// Whether a runs query reads any step's input or output, so a listing that\n// doesn't can skip those blobs. Fragments count as asking for them.\nfunction selectsStepData(\n selections: readonly SelectionSetNode[],\n path: string[],\n): boolean {\n let level = selections;\n for (const name of [...path, \"steps\"]) {\n const next: SelectionSetNode[] = [];\n for (const set of level) {\n for (const selection of set.selections) {\n if (selection.kind !== Kind.FIELD) return true;\n if (selection.name.value === name && selection.selectionSet) {\n next.push(selection.selectionSet);\n }\n }\n }\n if (next.length === 0) return false;\n level = next;\n }\n return level.some((set) =>\n set.selections.some(\n (selection) =>\n selection.kind !== Kind.FIELD ||\n selection.name.value === \"input\" ||\n selection.name.value === \"output\",\n ),\n );\n}\n\n// With no resolve info (a direct call) the full shape is served.\nfunction readsStepData(\n info: GraphQLResolveInfo | undefined,\n path: string[],\n): boolean {\n if (!info) return true;\n const selections = info.fieldNodes.flatMap((node) =>\n node.selectionSet ? [node.selectionSet] : [],\n );\n return selectsStepData(selections, path);\n}\n\n// A secret belongs to the reactor, not to any one document, so writing one is\n// an administrator's call — the gate the package mutations already use.\nfunction requireAdmin(\n authorizationService: IAuthorizationService,\n ctx: Context,\n): void {\n if (!authorizationService.isSupremeAdmin(ctx.user?.address)) {\n throw new GraphQLError(\"Admin access required\");\n }\n}\n\n// The runtime's retryable \"not synced here yet\", tagged for clients.\nasync function syncAware<T>(call: () => Promise<T>): Promise<T> {\n try {\n return await call();\n } catch (error) {\n if (error instanceof Error && error.name === \"WorkflowSyncingError\") {\n throw new GraphQLError(error.message, {\n extensions: { code: \"WORKFLOW_SYNCING\", retryable: true },\n });\n }\n throw error;\n }\n}\n\nfunction parseJson(value: string | null): unknown {\n if (value === null) return null;\n try {\n return JSON.parse(value) as unknown;\n } catch {\n return value;\n }\n}\n\nfunction toStepRecord(row: StepExecutionRow) {\n return {\n stepId: row.step_id,\n stepKey: row.step_key,\n pieceName: row.piece_name,\n blockName: row.block_name,\n status: row.status,\n input: parseJson(row.input),\n output: parseJson(row.output),\n port: row.port,\n error: row.error,\n errorName: row.error_name,\n startedAt: row.started_at,\n endedAt: row.ended_at,\n pieceVersion: row.piece_version,\n pieceSource: row.piece_source,\n versionMatch: row.version_match,\n versionNote: row.version_note,\n };\n}\n\nfunction toRunRecord(row: RunRow, steps: StepExecutionRow[]) {\n return {\n id: row.id,\n workflowId: row.workflow_id,\n workflowName: row.workflow_name,\n workflowVersion: row.workflow_version,\n triggerKind: row.trigger_kind,\n triggerPayload: parseJson(row.trigger_payload),\n status: row.status,\n error: row.error,\n errorName: row.error_name,\n startedAt: row.started_at,\n endedAt: row.ended_at,\n rerunOf: row.rerun_of,\n warnings: row.warnings,\n warningNotes: parseJson(row.warning_notes) ?? [],\n steps: steps.map(toStepRecord),\n };\n}\n\nexport type ReadAssertion = (\n documentId: string,\n ctx: Context,\n) => Promise<unknown>;\n\n/** Who the host signs as: the principal a target document must grant. */\nexport interface ReactorIdentity {\n address: string | null;\n key: string;\n}\n\n/** What the editor needs to decide on sign-in and grants (ADR 0005 §8). */\nexport interface ReactorAccessInfo {\n authEnforcement: boolean;\n authConditions: boolean;\n identity: ReactorIdentity | null;\n}\n\n// Strict when the host does not say.\nconst UNKNOWN_ACCESS: ReactorAccessInfo = {\n authEnforcement: true,\n authConditions: false,\n identity: null,\n};\n\nfunction signedIn(ctx: Context): boolean {\n return Boolean(ctx.user?.address);\n}\n\nexport interface OAuthRouting {\n // Absolute, or a bare path when the host's public origin is unknown.\n callbackUrl: string;\n}\n\nfunction isAbsolute(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n}\n\n// The redirect a sign-in uses: the host's own when it knows its origin,\n// otherwise the caller's, provided it names this host's callback path.\nfunction redirectUriFor(\n routing: OAuthRouting | undefined,\n requested: string | null | undefined,\n): string {\n if (!routing) throw new GraphQLError(\"This host serves no OAuth2 callback\");\n if (isAbsolute(routing.callbackUrl)) return routing.callbackUrl;\n if (!requested) {\n throw new GraphQLError(\n \"This host does not know its public URL; pass redirectUri\",\n );\n }\n let url: URL;\n try {\n url = new URL(requested);\n } catch {\n throw new GraphQLError(`\"${requested}\" is not a URL`);\n }\n if (\n (url.protocol !== \"https:\" && url.protocol !== \"http:\") ||\n url.pathname !== routing.callbackUrl ||\n url.search !== \"\" ||\n url.hash !== \"\"\n ) {\n throw new GraphQLError(\n `redirectUri must be this host's ${routing.callbackUrl}`,\n );\n }\n return url.href;\n}\n\n// Only back to the page that asked: its Origin header is the browser's.\nfunction returnUrlFor(\n requested: string | null | undefined,\n ctx: Context,\n): string | undefined {\n if (!requested) return undefined;\n const origin = ctx.headers.origin;\n let url: URL | undefined;\n try {\n url = new URL(requested);\n } catch {\n url = undefined;\n }\n if (!url || !origin || url.origin !== origin) {\n throw new GraphQLError(\"returnUrl must be on the requesting page's origin\");\n }\n return url.href;\n}\n\nexport const getResolvers = (\n runtime: WorkflowRuntimeService,\n authorizationService: IAuthorizationService,\n oauth?: OAuthRouting,\n access: ReactorAccessInfo = UNKNOWN_ACCESS,\n // The subgraph's own read gate; fields that need it refuse without one.\n assertCanRead?: ReadAssertion,\n): Record<string, unknown> => {\n return {\n Query: {\n workflowRuntime: () => ({}),\n },\n WorkflowRuntimeQueries: {\n health: () => \"ok\",\n authEnforcement: () => access.authEnforcement,\n reactorIdentity: (_parent: unknown, _args: unknown, ctx: Context) =>\n signedIn(ctx) ? access.identity : null,\n authConditions: (_parent: unknown, _args: unknown, ctx: Context) =>\n signedIn(ctx) ? access.authConditions : null,\n reactorAccessDenial: async (\n _parent: unknown,\n args: { workflowId: string },\n ctx: Context,\n ) => {\n if (!assertCanRead) throw new GraphQLError(\"No read check configured\");\n await assertCanRead(args.workflowId, ctx);\n return runtime.reactorAccessDenial(args.workflowId) ?? null;\n },\n blockDescriptor: (_parent: unknown, args: { block: BlockInput }) =>\n runtime.blockDescriptor(blockRef(args.block)),\n blockOptions: (\n _parent: unknown,\n args: {\n block: BlockInput;\n propName: string;\n input?: unknown;\n connectionId?: string | null;\n searchValue?: string | null;\n reactorConnectionId?: string | null;\n },\n ctx: Context,\n ) =>\n runtime.blockOptions(\n blockRef(args.block),\n args.propName,\n args.input,\n args.connectionId ?? undefined,\n ctx,\n args.searchValue ?? undefined,\n args.reactorConnectionId ?? undefined,\n ),\n pieceCatalog: () => runtime.pieceCatalog(),\n pieceActions: (_parent: unknown, args: PieceArgs) =>\n runtime.pieceActions(args.packageName, args.version ?? undefined),\n pieceTriggers: (_parent: unknown, args: PieceArgs) =>\n runtime.pieceTriggers(args.packageName, args.version ?? undefined),\n blockOutputTree: (\n _parent: unknown,\n args: { block: BlockInput; config?: unknown },\n ) => runtime.blockOutputTree(blockRef(args.block), args.config),\n stepOutputTree: (\n _parent: unknown,\n args: { workflowId: string; stepId: string },\n ctx: Context,\n ) => runtime.stepOutputTree(args.workflowId, args.stepId, ctx),\n blockResolutions: (\n _parent: unknown,\n args: { workflowId: string },\n ctx: Context,\n ) => runtime.blockResolutions(args.workflowId, ctx),\n pieceDetail: (_parent: unknown, args: PieceArgs) =>\n runtime.pieceDetail(args.packageName, args.version ?? undefined),\n searchPieces: (\n _parent: unknown,\n args: {\n query: string;\n kind: string;\n sources?: string[] | null;\n categories?: string[] | null;\n limit?: number | null;\n },\n ) => {\n // A misspelt value would otherwise widen the search without a word.\n if (args.kind !== \"action\" && args.kind !== \"trigger\") {\n throw new GraphQLError(\n `A search's kind is \"action\" or \"trigger\", got \"${args.kind}\"`,\n );\n }\n const unknown = (args.sources ?? []).filter(\n (source) => !isPieceSource(source),\n );\n if (unknown.length > 0) {\n throw new GraphQLError(\n `A search's sources are ${PIECE_SOURCES.join(\", \")}, got ${unknown.join(\", \")}`,\n );\n }\n return runtime.searchPieces(args.query, {\n kind: args.kind,\n sources: (args.sources ?? []).filter(isPieceSource),\n categories: args.categories ?? undefined,\n limit: args.limit ?? undefined,\n });\n },\n connections: (_parent: unknown, _args: unknown, ctx: Context) =>\n runtime.connections(ctx),\n webhookEndpoint: (\n _parent: unknown,\n args: { workflowId: string; driveId?: string | null },\n ctx: Context,\n ) =>\n syncAware(() =>\n runtime.webhookEndpoint(args.workflowId, ctx, {\n driveId: args.driveId ?? undefined,\n }),\n ),\n secret: async (_parent: unknown, args: { ref: string }) => {\n try {\n return await (await runtime.secrets()).stat(args.ref);\n } catch {\n // Unknown or malformed ref reads as \"no such secret\".\n return null;\n }\n },\n secrets: async () => (await runtime.secrets()).list(),\n oauthRedirectUri: () => oauth?.callbackUrl ?? null,\n oauthAttempt: (_parent: unknown, args: { state: string }, ctx: Context) =>\n runtime.oauthAttempt(args.state, ctx),\n triggerStates: async (_parent: unknown, _args: unknown, ctx: Context) =>\n (await runtime.triggerStates(ctx)).map((row) => ({\n workflowId: row.workflow_id,\n pieceName: row.piece_name,\n triggerName: row.trigger_name,\n status: row.status,\n intervalMs: row.interval_ms,\n nextPollAt: row.next_poll_at,\n lastPollAt: row.last_poll_at,\n lastError: row.last_error,\n consecutiveFailures: row.consecutive_failures,\n nextRenewAt: row.next_renew_at,\n renewError: row.renew_error,\n renewFailures: row.renew_failures,\n pieceVersion: row.piece_version,\n pieceSource: row.piece_source,\n versionMatch: row.version_match,\n versionNote: row.version_note,\n })),\n runs: async (\n _parent: unknown,\n args: RunsArgs,\n ctx: Context,\n info?: GraphQLResolveInfo,\n ) =>\n (\n await runtime.runs(\n { ...args, withStepData: readsStepData(info, []) },\n ctx,\n )\n ).map((record) => toRunRecord(record.row, record.steps)),\n runsPage: async (\n _parent: unknown,\n args: RunsPageArgs,\n ctx: Context,\n info?: GraphQLResolveInfo,\n ) => {\n const { paging, ...scope } = args;\n const page = await cursorAware(() =>\n runtime.runsPage(\n {\n ...scope,\n limit: paging?.limit ?? undefined,\n cursor: paging?.cursor ?? null,\n withStepData: readsStepData(info, [\"items\"]),\n },\n ctx,\n ),\n );\n return {\n items: page.records.map((record) =>\n toRunRecord(record.row, record.steps),\n ),\n hasNextPage: page.hasNextPage,\n hasPreviousPage: Boolean(paging?.cursor),\n cursor: page.cursor,\n };\n },\n run: async (_parent: unknown, args: { id: string }, ctx: Context) => {\n const record = await runtime.run(args.id, ctx);\n return record ? toRunRecord(record.row, record.steps) : null;\n },\n },\n Mutation: {\n workflowRuntime: () => ({}),\n },\n WorkflowRuntimeMutations: {\n fire: (_parent: unknown, args: FireArgs, ctx: Context) =>\n runtime.fire(args.workflowId, args.payload, \"manual\", undefined, ctx),\n testTrigger: (\n _parent: unknown,\n args: {\n workflowId: string;\n payload?: unknown;\n timeoutSeconds?: number | null;\n driveId?: string | null;\n },\n ctx: Context,\n ) =>\n syncAware(() =>\n runtime.testTrigger(args.workflowId, ctx, {\n ...(args.payload !== undefined ? { payload: args.payload } : {}),\n ...(typeof args.timeoutSeconds === \"number\"\n ? { timeoutMs: args.timeoutSeconds * 1000 }\n : {}),\n driveId: args.driveId ?? undefined,\n }),\n ),\n cancelTriggerTest: (\n _parent: unknown,\n args: { workflowId: string },\n ctx: Context,\n ) => runtime.cancelTriggerTestFor(args.workflowId, ctx),\n testStep: (\n _parent: unknown,\n args: { workflowId: string; stepId: string; driveId?: string | null },\n ctx: Context,\n ) =>\n syncAware(() =>\n runtime.testStep(args.workflowId, args.stepId, ctx, {\n driveId: args.driveId ?? undefined,\n }),\n ),\n rerun: (_parent: unknown, args: { runId: string }, ctx: Context) =>\n runtime.rerun(args.runId, ctx),\n createSecret: async (\n _parent: unknown,\n args: { value: string; label?: string | null },\n ctx: Context,\n ) => {\n requireAdmin(authorizationService, ctx);\n return (await runtime.secrets()).create({\n value: args.value,\n label: args.label ?? undefined,\n });\n },\n rotateSecret: async (\n _parent: unknown,\n args: { ref: string; value: string },\n ctx: Context,\n ) => {\n requireAdmin(authorizationService, ctx);\n return (await runtime.secrets()).rotate(args.ref, args.value);\n },\n deleteSecret: async (\n _parent: unknown,\n args: { ref: string },\n ctx: Context,\n ) => {\n requireAdmin(authorizationService, ctx);\n await (await runtime.secrets()).delete(args.ref);\n return true;\n },\n checkConnection: (\n _parent: unknown,\n args: { connectionId: string },\n ctx: Context,\n ) => runtime.checkConnection(args.connectionId, ctx),\n // Signing in mints a secret, so it takes what createSecret takes.\n startOAuth: (\n _parent: unknown,\n args: {\n connectionId: string;\n redirectUri?: string | null;\n returnUrl?: string | null;\n },\n ctx: Context,\n ) => {\n requireAdmin(authorizationService, ctx);\n const returnUrl = returnUrlFor(args.returnUrl, ctx);\n return runtime.startOAuth(args.connectionId, ctx, {\n redirectUri: redirectUriFor(oauth, args.redirectUri),\n ...(returnUrl ? { returnUrl } : {}),\n });\n },\n },\n };\n};\n","import type { DocumentNode } from \"graphql\";\nimport { gql } from \"graphql-tag\";\n\nexport const schema: DocumentNode = gql`\n \"\"\"\n WorkflowRuntime Queries\n \"\"\"\n type WorkflowRuntimeQueries {\n health: String!\n \"\"\"\n Whether this reactor enforces document auth (REACTOR_AUTH_ENFORCEMENT).\n On, a workflow needs a signed publish, and the host grants, to reach\n documents. Answered to any caller.\n \"\"\"\n authEnforcement: Boolean!\n \"\"\"\n Who this host signs as: grant it on each document a step writes. Null for\n a caller who is not signed in, or a host with no signer.\n \"\"\"\n reactorIdentity: ReactorIdentity\n \"\"\"\n REACTOR_AUTH_CONDITIONS. True: grant the host by key, with\n { match: subject.key == key }; false: by address. Null for a caller who\n is not signed in.\n \"\"\"\n authConditions: Boolean\n \"\"\"\n Why the workflow's last publish or enable got no reactor access, naming\n the publisher and the connection; null when the check passed.\n \"\"\"\n reactorAccessDenial(workflowId: String!): String\n \"\"\"\n Persisted runs, newest first. Scope them to one workflow, or to every\n workflow a drive holds; workflowId wins when both are given.\n \"\"\"\n runs(\n workflowId: String\n driveId: String\n limit: Int\n \"Trigger kinds to leave out, e.g. test.\"\n excludeTriggerKinds: [String!]\n ): [WorkflowRunRecord!]!\n \"\"\"\n The same listing a page at a time, newest journaled first. Pass a page's\n cursor back in paging to read the next; a page is short only when\n hasNextPage is false, or when the caller could read none of a long\n stretch of runs. A run that starts between pages keeps its place.\n \"\"\"\n runsPage(\n workflowId: String\n driveId: String\n excludeTriggerKinds: [String!]\n paging: WorkflowRunsPagingInput\n ): WorkflowRunResultPage!\n run(id: String!): WorkflowRunRecord\n \"\"\"\n The descriptor (props, auth, ports) of one action or trigger, as the\n version that runs it describes it; triggers come back under \"trigger\".\n \"\"\"\n blockDescriptor(block: BlockInput!): Unknown\n \"\"\"\n Resolves a dynamic prop against the current config values: a DROPDOWN\n yields { options, placeholder, disabled }, a DYNAMIC prop yields the\n resolved sub-property descriptor list.\n \"\"\"\n blockOptions(\n block: BlockInput!\n propName: String!\n input: Unknown\n connectionId: String\n \"What the author typed, for a DROPDOWN declared with refreshOnSearch.\"\n searchValue: String\n \"The step's reactor connection; its filter narrows what the resolver reads.\"\n reactorConnectionId: String\n ): Unknown\n \"\"\"\n Every piece this reactor offers: the core piece, its package pieces, and\n the pieces the registry and Activepieces publish.\n \"\"\"\n pieceCatalog: Unknown\n \"\"\"\n A piece's actions at one version; pin a step to that version.\n \"\"\"\n pieceActions(\n packageName: String!\n \"That version; the installed or latest one when omitted.\"\n version: String\n ): Unknown\n \"\"\"\n A piece's triggers at one version; pin a trigger to that version.\n \"\"\"\n pieceTriggers(\n packageName: String!\n \"That version; the installed or latest one when omitted.\"\n version: String\n ): Unknown\n \"\"\"\n Full piece detail (PieceMetadataModel-shaped), verbatim from the cloud API.\n \"\"\"\n pieceDetail(\n packageName: String!\n \"That version; the installed or latest one when omitted.\"\n version: String\n ): Unknown\n \"\"\"\n Piece and block search across the catalog, grouped by piece. The index\n builds lazily on first use; poll while status is \"indexing\".\n \"\"\"\n searchPieces(\n query: String!\n \"action | trigger\"\n kind: String!\n \"local | registry | activepieces; all when omitted.\"\n sources: [String!]\n \"Category ids, any of; all when omitted.\"\n categories: [String!]\n \"Pieces returned.\"\n limit: Int\n ): PieceSearchResult!\n \"\"\"\n Health of every registered piece trigger (poll schedule, errors).\n \"\"\"\n triggerStates: [TriggerStateRecord!]!\n \"\"\"\n Authored output shape of a block (SDL / outputSchema / sampleData).\n \"\"\"\n blockOutputTree(block: BlockInput!, config: Unknown): Unknown\n \"\"\"\n Output shape of a draft step or trigger: its latest test output\n (source \"test\", with sample and testedAt) when tested, else as blockOutputTree.\n \"\"\"\n stepOutputTree(workflowId: String!, stepId: String!): Unknown\n \"\"\"\n Every block of the draft (trigger first) as this reactor resolves it:\n which piece version runs, from where, and how it matches the pin.\n \"\"\"\n blockResolutions(workflowId: String!): [BlockResolutionRecord!]!\n \"\"\"\n Every powerhouse/connection document, for connection pickers.\n \"\"\"\n connections: [ConnectionRecord!]!\n \"\"\"\n The webhook endpoint for a workflow: the URL to hand the provider. Minted\n on first ask, whether or not the workflow is armed — an author needs the\n URL before enabling, and the armed field carries that difference. Null\n only when the host has no webhook service. With driveId, a workflow not\n synced here yet is waited for briefly, then fails as WORKFLOW_SYNCING.\n \"\"\"\n webhookEndpoint(workflowId: String!, driveId: String): WebhookEndpointRecord\n \"\"\"\n Secret metadata (label, version, status). Never the value.\n \"\"\"\n secret(ref: String!): SecretRecord\n secrets: [SecretRecord!]!\n \"\"\"\n The URL an OAuth2 app must register as its redirect. A bare path when\n this host does not know its public origin; resolve it against the URL\n this API is reached at. Null when the host serves no HTTP routes.\n \"\"\"\n oauthRedirectUri: String\n \"\"\"\n How an OAuth2 sign-in opened by startOAuth stands; null when unknown.\n \"\"\"\n oauthAttempt(state: String!): OAuthAttempt\n }\n\n type OAuthAttempt {\n connectionId: String!\n \"PENDING | EXCHANGING | OK | ERROR\"\n status: String!\n error: String\n }\n\n type OAuthStart {\n state: String!\n \"Where to send the user to sign in.\"\n authorizationUrl: String!\n redirectUri: String!\n expiresAt: String!\n }\n\n \"\"\"\n One action or trigger of a piece, as a workflow pins it. The engine's own\n blocks are the piece @powerhousedao/piece-core.\n \"\"\"\n input BlockInput {\n pieceName: String!\n \"Exact semver.\"\n pieceVersion: String!\n \"The action or trigger name.\"\n name: String!\n \"action | trigger\"\n kind: String!\n }\n\n type BlockSearchHit {\n pieceName: String!\n \"The version the hit is listed at; pin to it when picking the block.\"\n pieceVersion: String!\n \"The action or trigger name.\"\n name: String!\n pieceDisplayName: String!\n logoUrl: String!\n displayName: String!\n description: String!\n \"action | trigger\"\n kind: String!\n \"Triggers only: POLLING | WEBHOOK | APP_WEBHOOK | MANUAL\"\n strategy: String\n \"Why this block cannot run on this reactor; null when it can.\"\n unsupported: String\n }\n\n type BlockResolutionRecord {\n \"The trigger's or step's id.\"\n stepId: String!\n pieceName: String!\n \"The version the block pins.\"\n pieceVersion: String!\n \"The action or trigger name.\"\n name: String!\n \"action | trigger\"\n kind: String!\n \"The version that runs; null when nothing resolves.\"\n resolvedVersion: String\n \"local | registry | activepieces | npm; null when nothing resolves.\"\n source: String\n \"exact | compatible | fallback | installed | missing\"\n match: String!\n \"Why the version that runs differs from the pin, or why nothing resolves.\"\n note: String\n \"The newest version any source offers.\"\n latestVersion: String\n }\n\n type PieceSearchMatch {\n pieceName: String!\n pieceVersion: String!\n displayName: String!\n description: String!\n logoUrl: String!\n categories: [String!]!\n \"local | registry | activepieces\"\n source: String!\n deprecated: Boolean\n \"Why no block of the piece can run on this reactor.\"\n unsupported: String\n \"Every query token matched the piece's own name.\"\n namedPiece: Boolean!\n \"Matching blocks of the kind asked for, best first.\"\n blocks: [BlockSearchHit!]!\n }\n\n type PieceSearchResult {\n \"ready | indexing | error\"\n status: String!\n pieces: [PieceSearchMatch!]!\n indexedPieces: Int!\n error: String\n }\n\n type SecretRecord {\n ref: String!\n label: String\n version: Int!\n status: String!\n createdAt: String!\n updatedAt: String!\n }\n\n type ConnectionRecord {\n id: String!\n name: String!\n connectorId: String!\n authType: String!\n status: String!\n accountLabel: String\n }\n\n type ReactorIdentity {\n \"The address the host signs as; null without a signed-in identity.\"\n address: String\n \"The did:key that signs the host's operations.\"\n key: String!\n }\n\n type ConnectionCheckResult {\n ok: Boolean!\n detail: String\n \"\"\"\n From auth.getConnectionIdentifier when the check passes; otherwise the\n label the connection already holds.\n \"\"\"\n accountLabel: String\n }\n\n type WebhookEndpointRecord {\n workflowId: String!\n url: String!\n \"\"\"\n False when the host gave the webhook service no public origin, so the url\n above is a bare path. Switchboard always gives one, falling back to\n http://localhost:<port> when PUBLIC_URL is unset.\n \"\"\"\n absoluteUrl: Boolean!\n \"True while the workflow is ENABLED with a valid webhook trigger\"\n armed: Boolean!\n createdAt: String!\n }\n\n type TriggerStateRecord {\n workflowId: String!\n pieceName: String!\n triggerName: String!\n status: String!\n intervalMs: Int!\n nextPollAt: String\n lastPollAt: String\n lastError: String\n consecutiveFailures: Int!\n \"When a webhook trigger next renews its subscription; null if it never does.\"\n nextRenewAt: String\n \"The last failed renewal, kept apart from lastError, the poll's.\"\n renewError: String\n renewFailures: Int!\n \"The piece version the trigger armed with; null for a host-fed trigger.\"\n pieceVersion: String\n pieceSource: String\n versionMatch: String\n versionNote: String\n }\n\n type Query {\n workflowRuntime: WorkflowRuntimeQueries!\n }\n\n type WorkflowStepRunRecord {\n stepId: String!\n stepKey: String!\n pieceName: String!\n \"The action the step ran, or the trigger a trigger test sampled.\"\n blockName: String!\n status: String!\n input: Unknown\n output: Unknown\n port: String\n error: String\n \"The thrown error's name, e.g. ReactorAccessDeniedError.\"\n errorName: String\n startedAt: String\n endedAt: String\n \"The piece version that ran; null for an unresolved block.\"\n pieceVersion: String\n \"local | registry | activepieces | npm\"\n pieceSource: String\n \"exact | compatible | fallback | installed | missing\"\n versionMatch: String\n \"Why the version that ran differs from the pin.\"\n versionNote: String\n }\n\n type WorkflowRunRecord {\n id: String!\n workflowId: String!\n workflowName: String!\n workflowVersion: Int!\n triggerKind: String!\n triggerPayload: Unknown\n status: String!\n error: String\n errorName: String\n \"When the run began executing; a PENDING run's is when it was journaled.\"\n startedAt: String!\n endedAt: String\n rerunOf: String\n \"How many warningNotes the run has; a run with any is not a plain success.\"\n warnings: Int!\n \"Steps that ran a fallback piece version, edges on ports nothing emits.\"\n warningNotes: [String!]!\n steps: [WorkflowStepRunRecord!]!\n }\n\n input WorkflowRunsPagingInput {\n \"Page size; default 25, at most 100.\"\n limit: Int\n \"The cursor of the previous page.\"\n cursor: String\n }\n\n type WorkflowRunResultPage {\n items: [WorkflowRunRecord!]!\n hasNextPage: Boolean!\n \"True when the page was read after a cursor.\"\n hasPreviousPage: Boolean!\n \"Resumes after this page's last run; null for an empty page.\"\n cursor: String\n }\n\n type WorkflowStepRun {\n stepId: String!\n key: String!\n pieceName: String!\n blockName: String!\n status: String!\n input: Unknown\n output: Unknown\n port: String\n error: String\n errorName: String\n }\n\n type WorkflowRunPayload {\n runId: String\n status: String!\n error: String\n errorName: String\n steps: [WorkflowStepRun!]!\n }\n\n type WorkflowStepTestResult {\n \"Null when the test never started, e.g. an upstream block is untested.\"\n runId: String\n \"\"\"\n SUCCEEDED | FAILED | INDETERMINATE. INDETERMINATE is neither: a host call\n the block made timed out, so a write it asked for may well have landed.\n Show it distinctly - it is not a confirmed pass.\n \"\"\"\n status: String!\n output: Unknown\n error: String\n errorName: String\n durationMs: Int!\n }\n\n \"\"\"\n WorkflowRuntime Mutations\n \"\"\"\n type WorkflowRuntimeMutations {\n \"\"\"\n Fires a workflow's manual trigger (the core piece's) and runs it to completion.\n \"\"\"\n fire(workflowId: String!, payload: Unknown): WorkflowRunPayload!\n \"\"\"\n Samples the draft trigger and saves it as the trigger's last test. A piece\n trigger runs its test hook (no cursor changes). Of the core piece's\n triggers, manual takes payload, schedule samples a fire now, and webhook\n waits for the next delivery to the workflow's endpoint, up to\n timeoutSeconds (default and cap 300), and that delivery runs nothing.\n \"\"\"\n testTrigger(\n workflowId: String!\n payload: Unknown\n timeoutSeconds: Int\n driveId: String\n ): Unknown\n \"\"\"\n Stops a webhook trigger test that is waiting; false when none was.\n \"\"\"\n cancelTriggerTest(workflowId: String!): Boolean!\n \"\"\"\n Runs one draft step against the latest test outputs of the blocks it\n reads, journaled as a \"test\" run and noted as the step's lastTest.\n driveId waits for an unsynced workflow, as for webhookEndpoint.\n \"\"\"\n testStep(\n workflowId: String!\n stepId: String!\n driveId: String\n ): WorkflowStepTestResult!\n \"\"\"\n Resumes a FAILED run: succeeded steps replay from the journal,\n execution restarts at the failure. Produces a new run.\n \"\"\"\n rerun(runId: String!): WorkflowRunPayload!\n \"\"\"\n Mints a managed secret and returns its ref; the value is stored\n encrypted and is never readable back over any API.\n \"\"\"\n createSecret(value: String!, label: String): SecretRecord!\n \"\"\"\n Replaces the value behind an existing ref; documents stay untouched.\n \"\"\"\n rotateSecret(ref: String!, value: String!): SecretRecord!\n \"\"\"\n Tombstones a secret; its value becomes unrecoverable.\n \"\"\"\n deleteSecret(ref: String!): Boolean!\n \"\"\"\n Runs the piece's auth.validate against the connection's credentials,\n then auth.getConnectionIdentifier for the account label, and records the\n outcome on the connection document.\n \"\"\"\n checkConnection(connectionId: String!): ConnectionCheckResult!\n \"\"\"\n Opens an OAuth2 sign-in with the connection's own app (client_id in its\n config, client_secret in its secretRefs). redirectUri is needed only when\n oauthRedirectUri is a bare path. With returnUrl, which must be on the\n caller's origin, the callback sends the browser back there with\n ?ph_oauth=<state> instead of closing the window.\n \"\"\"\n startOAuth(\n connectionId: String!\n redirectUri: String\n returnUrl: String\n ): OAuthStart!\n }\n\n type Mutation {\n workflowRuntime: WorkflowRuntimeMutations!\n }\n`;\n","import { BaseSubgraph, type SubgraphClass } from \"@powerhousedao/reactor-api\";\nimport type { WorkflowRuntimeService } from \"@powerhousedao/reactor-workflow\";\nimport type { DocumentNode } from \"graphql\";\nimport {\n getResolvers,\n type OAuthRouting,\n type ReactorAccessInfo,\n} from \"./resolvers.js\";\nimport { schema } from \"./schema.js\";\n\n/** The runtime's read/write surface. The runtime itself is composed by the\n * host, so the subgraph only serves what it is handed. */\nexport function createWorkflowRuntimeSubgraph(\n runtime: WorkflowRuntimeService,\n oauth?: OAuthRouting,\n access?: ReactorAccessInfo,\n): SubgraphClass {\n return class WorkflowRuntimeSubgraph extends BaseSubgraph {\n name = \"workflow-runtime\";\n typeDefs: DocumentNode = schema;\n // A field initializer runs after super(), so the authorization service\n // the secret mutations gate on is already in place.\n resolvers = getResolvers(\n runtime,\n this.authorizationService,\n oauth,\n access,\n (documentId, ctx) => this.assertCanRead(documentId, ctx),\n );\n additionalContextFields = {};\n };\n}\n","// Switchboard composes the workflow runtime from what it holds (the reactor\n// module) and what startAPI hands back. The intake is a read model.\nimport {\n REACTOR_SCHEMA,\n supportsLiveReadModelRegistration,\n type AttachmentRef,\n type DocumentViewDatabase,\n type InProcessReactorClientModule,\n type IReactorClient,\n type IRelationalDb,\n type ModelManifestEntry,\n type ReactorBuilder,\n} from \"@powerhousedao/reactor\";\nimport {\n AuthorizationPolicy,\n ForbiddenError,\n callerSubject,\n createCanonicalDocumentIdResolver,\n type CanonicalDocumentId,\n type Context,\n type IAttachmentAccessService,\n type IAuthorizationService,\n type IPackagePieceSource,\n type PackagePieceEntry,\n type SubgraphClass,\n} from \"@powerhousedao/reactor-api\";\nimport { parseRef } from \"@powerhousedao/reactor-attachments\";\nimport type * as WorkflowEngine from \"@powerhousedao/reactor-workflow\";\nimport type {\n AttachmentClientLike,\n AttachmentReadRequest,\n HostIdentity,\n WorkflowCaller,\n WorkflowRuntimeHostDeps,\n} from \"@powerhousedao/reactor-workflow\";\nimport type {\n IHttpScope,\n IWebhookScope,\n ScopedRouteHandle,\n} from \"@powerhousedao/shared/processors\";\nimport type { Principal } from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport {\n callbackUrlOf,\n registerOAuthCallback,\n} from \"./workflow/oauth-callback.js\";\nimport type {\n ReactorAccessInfo,\n ReactorIdentity,\n} from \"./workflow/resolvers.js\";\nimport { triggerFatalShutdown } from \"./fatal-shutdown.mjs\";\nimport { createWorkflowRuntimeSubgraph } from \"./workflow/subgraph.js\";\n\ntype WorkflowEngineModule = typeof WorkflowEngine;\n\n/** The npm name the workflow package owns: its HTTP namespace and its models. */\nexport const WORKFLOW_PACKAGE_NAME = \"@powerhousedao/workflow\";\nexport const WORKFLOW_TELEMETRY_SCOPE = \"@powerhousedao/reactor-workflow\";\n\n/** The env var and OpenFeature flag key that turns workflows on. */\nexport const PH_WORKFLOWS_ENABLED = \"PH_WORKFLOWS_ENABLED\";\n\n/** Whether the operation intake is indexing. Same shape as the attachment\n * reference projection, because it is the same limitation. */\nexport type WorkflowTriggersCapability =\n | { status: \"available\" }\n | {\n status: \"unavailable\";\n reason:\n | \"in-process-reactor-module-unavailable\"\n | \"live-read-model-registration-unsupported\"\n | \"workflow-singleton-lost\";\n };\n\nconst SINGLETON_LOST: WorkflowTriggersCapability = {\n status: \"unavailable\",\n reason: \"workflow-singleton-lost\",\n};\n\n/** The slice of switchboard's OpenFeature client this needs. */\nexport interface BooleanFlagSource {\n getBooleanValue(flagKey: string, defaultValue: boolean): Promise<boolean>;\n}\n\nexport interface WorkflowsFlagInput {\n featureFlags: BooleanFlagSource;\n /** The host's own answer; wins over everything. */\n override?: boolean;\n /** `workflows.enabled` from the powerhouse config file. */\n configEnabled?: boolean;\n /** Defaults to process.env; the tests pass their own. */\n env?: Record<string, string | undefined>;\n}\n\n/** Precedence, unchanged from the reactor-api resolver this replaces: the\n * host's option, then PH_WORKFLOWS_ENABLED, then the config file, then off. */\nexport async function resolveWorkflowsEnabled({\n featureFlags,\n override,\n configEnabled = false,\n env = process.env,\n}: WorkflowsFlagInput): Promise<boolean> {\n if (override !== undefined) return override;\n\n // The env layer is switchboard's OpenFeature client, which casts only\n // \"true\"/\"false\": the numeric forms reactor-api took are answered here.\n const raw = env[PH_WORKFLOWS_ENABLED]?.trim();\n if (raw === \"1\") return true;\n if (raw === \"0\") return false;\n\n return featureFlags.getBooleanValue(PH_WORKFLOWS_ENABLED, configEnabled);\n}\n\n// The package manager reports an unresolvable package and continues; for one\n// the host added itself that is a misconfigured switchboard, not a degraded\n// one. The manager imports this subpath moments later, so the cache absorbs it.\nexport async function assertWorkflowPackageLoadable(\n load: () => Promise<unknown> = () =>\n import(\"@powerhousedao/workflow/document-models\"),\n): Promise<void> {\n try {\n await load();\n } catch (error) {\n throw new Error(\n `Workflows are enabled but ${WORKFLOW_PACKAGE_NAME} could not be loaded`,\n { cause: error },\n );\n }\n}\n\n/**\n * Whether a composition failure was the workflow singleton refusing this\n * process, rather than something that should take the boot down.\n *\n * Matched by NAME: the engine loads lazily, and importing it here for the\n * constructor would defeat that. The fields the engine's error carries are\n * typed optional for the same reason.\n */\nexport function isWorkflowSingletonConflict(\n error: unknown,\n): error is Error & { owner?: string; expiresAt?: string; wouldBe?: string } {\n return (\n error instanceof Error && error.name === \"WorkflowSingletonConflictError\"\n );\n}\n\n/** How often a host refused the singleton at boot tries the claim again: the\n * engine's renewal period. */\nexport const WORKFLOW_SINGLETON_RETRY_MS = 20_000;\n\nexport interface WorkflowSingletonRetry {\n /** Stops retrying; waits for an attempt in flight, and stops what it\n * composed if the host is going away. */\n stop(): Promise<void>;\n}\n\n/**\n * Retries the singleton claim for a host that booted without it, and hands\n * the runtime over once a claim succeeds.\n *\n * Without it a rolling deploy whose slots have different owner names ends\n * with nobody running workflows: the new pod is refused while the old one\n * still holds the lease, and nothing retries once the old pod releases it.\n * Only for a host that never composed; one that lost the lease after\n * composing stays without workflows until it restarts.\n */\nexport function retryWorkflowSingleton(options: {\n compose: () => Promise<ComposedWorkflowRuntime>;\n onComposed: (workflows: ComposedWorkflowRuntime) => Promise<void>;\n logger: ILogger;\n intervalMs?: number;\n}): WorkflowSingletonRetry {\n const intervalMs = options.intervalMs ?? WORKFLOW_SINGLETON_RETRY_MS;\n let stopped = false;\n let timer: NodeJS.Timeout | undefined;\n let attempt: Promise<void> | undefined;\n\n const run = async () => {\n let workflows: ComposedWorkflowRuntime;\n try {\n workflows = await options.compose();\n } catch (error) {\n if (!isWorkflowSingletonConflict(error)) {\n options.logger.error(\n \"Composing the workflow runtime after the singleton came free failed: @error\",\n error,\n );\n }\n schedule();\n return;\n }\n if (stopped) {\n await workflows.stop();\n return;\n }\n options.logger.info(\n \"Claimed the workflow singleton after boot; starting the workflow runtime\",\n );\n await options.onComposed(workflows);\n };\n\n const schedule = () => {\n if (stopped) return;\n timer = setTimeout(() => {\n timer = undefined;\n attempt = run().finally(() => {\n attempt = undefined;\n });\n }, intervalMs);\n timer.unref();\n };\n\n schedule();\n return {\n async stop() {\n stopped = true;\n if (timer) clearTimeout(timer);\n timer = undefined;\n await attempt?.catch(() => undefined);\n },\n };\n}\n\n/**\n * What a host does once its composed runtime has shut down for losing the\n * workflow singleton.\n *\n * Taken over by another claim: that process runs workflows now, so this one\n * stays without them until it restarts. Unrenewable with no takeover seen (a\n * database blip): nothing else runs them, so the process goes down through\n * the fatal shutdown and its supervisor restarts it, which re-claims at boot.\n * A host with no fatal shutdown installed (embedded) stays without workflows\n * and says a restart is needed.\n */\nexport function workflowSingletonLossHandler(\n logger: ILogger,\n fatal: (kind: string, error: unknown) => boolean = triggerFatalShutdown,\n): (loss: WorkflowEngine.WorkflowSingletonLoss) => void {\n return (loss) => {\n if (loss.reason === \"taken\") {\n logger.error(\n `Another process (\"${loss.heldBy ?? \"unknown\"}\") took the workflow ` +\n \"singleton. This Switchboard has stopped its workflow runtime \" +\n \"and runs no workflows until it is restarted; everything else \" +\n \"serves normally.\",\n );\n return;\n }\n const error = new Error(\n \"The workflow singleton lease could not be renewed for \" +\n `${Math.round(loss.silentMs)}ms; no other process was seen taking it ` +\n \"over\",\n );\n if (fatal(\"Workflow singleton lease unrenewable\", error)) return;\n logger.error(\n `${error.message}. This Switchboard has stopped its workflow runtime ` +\n \"and runs no workflows until it is restarted.\",\n );\n };\n}\n\n/** The importable models piece workers load: the boot list, and a type's entries. */\nexport interface ModelManifestSource {\n modelManifest(): ModelManifestEntry[];\n modelEntries(documentType: string): ModelManifestEntry[];\n}\n\nexport function modelManifestSource(\n builder: Pick<\n ReactorBuilder,\n \"getResolvedModelManifest\" | \"getImportableEntries\"\n >,\n): ModelManifestSource {\n return {\n modelManifest: () => builder.getResolvedModelManifest() ?? [],\n modelEntries: (documentType) => builder.getImportableEntries(documentType),\n };\n}\n\nexport interface ComposeWorkflowRuntimeDeps {\n reactorClient: IReactorClient;\n /** The registry this host installs packages from; pieces come from it too.\n * Absent on a host that installs from none, and only the cloud is read. */\n pieceRegistryUrl?: string;\n /** Where the trigger read model registers; absent leaves the intake\n * unavailable rather than quietly dropping every document trigger. */\n clientModule?: InProcessReactorClientModule;\n relationalDb: IRelationalDb;\n /** False when relationalDb outlives the working directory, so the secret\n * store needs PH_WORKFLOWS_SECRETS_MASTER_KEY rather than a generated key. */\n secretsKeyFile?: false;\n attachments: AttachmentClientLike;\n /** Decides a step's attachment reads like the attachment routes do;\n * absent, a step reads none but the files its own run wrote. */\n attachmentAccess?: IAttachmentAccessService;\n webhooks?: IWebhookScope;\n /** The workflow package's HTTP namespace; the OAuth2 callback lives on it.\n * Absent leaves OAuth2 connections unable to sign in. */\n http?: IHttpScope;\n authorizationService: IAuthorizationService;\n /** Where the pieces installed packages ship come from; absent leaves the\n * runtime with none and only published bundles resolvable. */\n pieces?: IPackagePieceSource;\n logger: ILogger;\n /** Absent leaves pieces with no document models in their worker. */\n models?: ModelManifestSource;\n /** Where run, step, worker and reactor-call spans and metrics go. */\n telemetry?: WorkflowEngine.WorkflowRuntimeHostDeps[\"telemetry\"];\n /** Overridden by the tests; production always loads the real engine. */\n load?: () => Promise<WorkflowEngineModule>;\n /**\n * Whether to claim the workflow singleton before composing (plan agreed\n * decision 3). On by default, and the only honest setting for a real host:\n * two replicas over one run journal fail each other's live runs. A suite\n * that composes several runtimes over separate databases turns it off.\n */\n singletonLease?: boolean;\n /**\n * Where this host's read-model database lives — the Postgres URL, or the\n * absolute PGlite directory. It is the stable half of the default singleton\n * owner name, so a restart of THIS slot re-claims its own lease at once\n * instead of waiting out the 60s TTL for a killed process's claim. Hashed\n * before it is used, so a connection string's credentials go no further.\n */\n storageId?: string;\n /**\n * True when the journal's database is an embedded one no other process can\n * open (PGlite). Nothing can then take the lease, so a renewal that stalls\n * (a busy queue, a blocked event loop) is logged rather than turning\n * workflows off.\n */\n exclusiveJournal?: boolean;\n /** Called once when the composed runtime has shut down for losing the\n * singleton; see {@link workflowSingletonLossHandler}. */\n onSingletonLost?: (loss: WorkflowEngine.WorkflowSingletonLoss) => void;\n}\n\nexport interface ComposedWorkflowRuntime {\n subgraph: SubgraphClass;\n /** Whether document operations reach the runtime at all; unavailable once\n * the singleton is lost. */\n readonly triggers: WorkflowTriggersCapability;\n /** The owner name this host holds the workflow singleton under; undefined\n * when the lease was not taken (a suite that opted out). */\n singletonOwner?: string;\n start(): Promise<void>;\n stop(): Promise<void>;\n}\n\n// The engine's own access check, answered as BaseSubgraph answers it: the\n// host ACL (an admin passes, another policy fails closed, an unresolvable\n// identifier is a denial), then the reactor's read gate as the caller.\nfunction readAssertion(\n authorizationService: IAuthorizationService,\n reactorClient: IReactorClient,\n): WorkflowRuntimeHostDeps[\"assertCanRead\"] {\n const resolveCanonical = createCanonicalDocumentIdResolver(reactorClient);\n const hostCanRead = async (identifier: string, ctx: Context) => {\n if (authorizationService.isSupremeAdmin(ctx.user?.address)) return;\n if (\n authorizationService.config.policy !==\n AuthorizationPolicy.DOCUMENT_PERMISSIONS\n ) {\n throw new ForbiddenError();\n }\n let documentId: CanonicalDocumentId;\n try {\n documentId = await resolveCanonical(identifier);\n } catch {\n throw new ForbiddenError();\n }\n const canRead = await authorizationService.canRead(\n documentId,\n ctx.user?.address,\n );\n if (!canRead) throw new ForbiddenError(\"to read this document\");\n };\n return async (identifier: string, caller: WorkflowCaller) => {\n const ctx = caller as Context;\n await hostCanRead(identifier, ctx);\n let served: boolean;\n try {\n served = await reactorClient.isServed(identifier, {\n subject: callerSubject(ctx.user),\n });\n } catch {\n throw new ForbiddenError();\n }\n if (!served) throw new ForbiddenError(\"to read this document\");\n };\n}\n\n/** The same, for a design-time call that writes what it names. */\nfunction writeAssertion(\n authorizationService: IAuthorizationService,\n reactorClient: IReactorClient,\n): WorkflowRuntimeHostDeps[\"assertCanWrite\"] {\n const resolveCanonical = createCanonicalDocumentIdResolver(reactorClient);\n return async (identifier: string, caller: WorkflowCaller) => {\n const ctx = caller as Context;\n if (authorizationService.isSupremeAdmin(ctx.user?.address)) return;\n if (\n authorizationService.config.policy !==\n AuthorizationPolicy.DOCUMENT_PERMISSIONS\n ) {\n throw new ForbiddenError();\n }\n let documentId: CanonicalDocumentId;\n try {\n documentId = await resolveCanonical(identifier);\n } catch {\n throw new ForbiddenError();\n }\n const canWrite = await authorizationService.canWrite(\n documentId,\n ctx.user?.address,\n );\n if (!canWrite) throw new ForbiddenError(\"to write this document\");\n };\n}\n\n/** What the reactor enforces and who it signs as; unknown counts as enforced. */\nexport function reactorAccessOf(\n clientModule: InProcessReactorClientModule | undefined,\n): ReactorAccessInfo {\n const flags = clientModule?.reactorModule?.featureFlags;\n const signer = clientModule?.signer;\n const key = signer?.app?.key;\n const identity: ReactorIdentity | null = key\n ? { address: signer.user?.address ?? null, key }\n : null;\n return {\n authEnforcement: flags ? flags.authEnforcement : true,\n authConditions: flags?.authConditions === true,\n identity,\n };\n}\n\n/** Whom a run's created documents also grant: the host, as §8 grants it. */\nexport function hostPrincipalOf(\n access: ReactorAccessInfo,\n): Principal | undefined {\n const identity = access.identity;\n if (!identity) return undefined;\n if (access.authConditions) {\n return { match: { eq: [{ attr: \"subject.key\" }, { lit: identity.key }] } };\n }\n return identity.address ? { address: identity.address } : undefined;\n}\n\n/** Who the host signs as, so a publish it signs makes no run user. */\nexport function hostIdentityOf(\n access: ReactorAccessInfo,\n): HostIdentity | undefined {\n const identity = access.identity;\n if (!identity) return undefined;\n return identity.address\n ? { address: identity.address, key: identity.key }\n : { key: identity.key };\n}\n\nconst REFERENCE_INDEX_RETRIES = 8;\nconst REFERENCE_INDEX_RETRY_MS = 250;\n\n/** A step reads an attachment the way the download route serves one: when the\n * run's user may read a document that references it. The candidates are the\n * documents the run was handed; the reference index can trail the operation\n * that fired the trigger, so a denial is retried briefly. */\nexport function attachmentReadPolicy(\n access: IAttachmentAccessService | undefined,\n retryMs = REFERENCE_INDEX_RETRY_MS,\n): (request: AttachmentReadRequest) => Promise<boolean> {\n return async (request) => {\n try {\n if (parseRef(request.ref as AttachmentRef).version !== 1) return false;\n } catch {\n return false;\n }\n if (!access || request.documentIds.length === 0) return false;\n const subject = request.runUser?.subject;\n const caller = {\n ...(subject?.address ? { userAddress: subject.address } : {}),\n ...(subject?.key ? { appKey: subject.key } : {}),\n };\n for (let attempt = 0; attempt <= REFERENCE_INDEX_RETRIES; attempt++) {\n for (const documentId of request.documentIds) {\n const result = await access.canReadAttachment({\n documentId,\n attachmentRef: request.ref,\n ...caller,\n });\n if (result.kind === \"allowed\") return true;\n }\n if (attempt < REFERENCE_INDEX_RETRIES) {\n await new Promise((resolve) => setTimeout(resolve, retryMs));\n }\n }\n return false;\n };\n}\n\n// The trigger read model lives exactly as long as its runtime: tearDown takes\n// it off the coordinator, so a later compose in this process can register.\ninterface TriggersRegistration {\n tornDown: boolean;\n detach?: () => void;\n}\n\n// Live registration is the capability the attachment reference index needs\n// too, so a coordinator without it reads unavailable for the same reason.\nasync function registerWorkflowTriggersReadModel(\n engine: WorkflowEngineModule,\n runtime: WorkflowEngine.WorkflowRuntimeService,\n clientModule: InProcessReactorClientModule | undefined,\n registration: TriggersRegistration,\n): Promise<WorkflowTriggersCapability> {\n const reactorModule = clientModule?.reactorModule;\n if (!reactorModule) {\n return {\n status: \"unavailable\",\n reason: \"in-process-reactor-module-unavailable\",\n };\n }\n\n const coordinator = reactorModule.readModelCoordinator;\n if (!supportsLiveReadModelRegistration(coordinator)) {\n return {\n status: \"unavailable\",\n reason: \"live-read-model-registration-unsupported\",\n };\n }\n\n // Schema-qualified: the cursor row lives in the reactor's own ViewState.\n const readModel = new engine.WorkflowTriggersReadModel(\n (reactorModule.database as unknown as Kysely<unknown>).withSchema(\n REACTOR_SCHEMA,\n ) as unknown as Kysely<DocumentViewDatabase>,\n reactorModule.operationIndex,\n reactorModule.writeCache,\n reactorModule.processorManagerConsistencyTracker,\n runtime,\n );\n await readModel.init();\n // Torn down while it replayed: registered now, it would outlive the runtime.\n if (registration.tornDown) return SINGLETON_LOST;\n coordinator.addReadModel(\n readModel,\n engine.WORKFLOW_TRIGGERS_READ_MODEL_STAGE,\n );\n registration.detach = () => coordinator.removeReadModel(readModel);\n\n return { status: \"available\" };\n}\n\n// The runtime holds pieces; reactor-api is what resolves them. Rebound on\n// every change, so a package rebuilt while this runs needs no restart.\nexport function bindPackagePieces(\n registry: { setPieces(pieces: readonly PackagePieceEntry[]): void },\n source: IPackagePieceSource,\n): void {\n // Sorted by package name, so which package keeps a contested piece is stable.\n const apply = (byPackage: Map<string, PackagePieceEntry[]>) => {\n registry.setPieces(\n [...byPackage.entries()]\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .flatMap(([, pieces]) => pieces),\n );\n };\n // The initial load already happened inside startAPI, so what it reported is\n // read here rather than waited for.\n apply(source.getPieces());\n source.onPiecesChange(apply);\n}\n\n// Builds the runtime, registers its intake, and returns its GraphQL face plus\n// the lifecycle the host drives. The engine loads lazily: off means unloaded.\nexport async function composeWorkflowRuntime(\n deps: ComposeWorkflowRuntimeDeps,\n): Promise<ComposedWorkflowRuntime> {\n const load = deps.load ?? (() => import(\"@powerhousedao/reactor-workflow\"));\n\n let engine: WorkflowEngineModule;\n try {\n engine = await load();\n } catch (error) {\n throw new Error(\n \"Workflows are enabled but @powerhousedao/reactor-workflow could not be loaded\",\n { cause: error },\n );\n }\n\n // BEFORE anything else touches the run journal. Opening the journal runs\n // its orphan/abandoned sweeps, which close out every RUNNING and PENDING\n // run that is not this process's — i.e. a second replica booting fails the\n // first replica's live runs. The claim is what makes the plan's singleton\n // decision structural instead of documented; it refuses by name.\n const loss: SingletonLoss = { lost: false };\n const lease =\n deps.singletonLease === false\n ? undefined\n : await engine.acquireWorkflowSingletonLease({\n relationalDb: deps.relationalDb,\n logger: deps.logger,\n ...(deps.storageId ? { storageId: deps.storageId } : {}),\n selfFence: deps.exclusiveJournal !== true,\n onLost: (lost) => {\n loss.lost = true;\n if (lost.reason === \"taken\") loss.heldBy = lost.heldBy;\n loss.tearDown?.();\n // While composing, the compose itself fails with a conflict and\n // the host retries the claim.\n if (loss.composed) deps.onSingletonLost?.(lost);\n },\n });\n\n try {\n const composed = await composeClaimed(engine, deps, lease, loss);\n if (loss.lost) throw singletonLostError(engine, loss, lease);\n loss.composed = true;\n return composed;\n } catch (error) {\n // Renewing since the claim, so a compose that fails hands it back, after\n // stopping whatever runtime it had already built.\n loss.tearDown?.();\n await lease?.release();\n throw error;\n }\n}\n\ntype SingletonLease = Awaited<\n ReturnType<WorkflowEngineModule[\"acquireWorkflowSingletonLease\"]>\n>;\n\n// Losing the lease shuts the runtime down; tearDown is set once it exists.\ninterface SingletonLoss {\n lost: boolean;\n composed?: boolean;\n heldBy?: string;\n tearDown?: () => void;\n}\n\nfunction singletonLostError(\n engine: WorkflowEngineModule,\n loss: SingletonLoss,\n lease: SingletonLease | undefined,\n): Error {\n return new engine.WorkflowSingletonConflictError(\n loss.heldBy ?? \"unknown\",\n \"unknown\",\n lease?.owner ?? \"unknown\",\n );\n}\n\nasync function composeClaimed(\n engine: WorkflowEngineModule,\n deps: ComposeWorkflowRuntimeDeps,\n lease: SingletonLease | undefined,\n loss: SingletonLoss,\n): Promise<ComposedWorkflowRuntime> {\n // The same registry the host installs packages from, so a piece it indexes\n // is reachable without a second setting to keep in step.\n engine.setPieceRegistryUrl(deps.pieceRegistryUrl);\n\n // Before the runtime exists: a restored trigger asks for a piece as soon as\n // the supervisor starts, and the catalog is served from the same holder.\n if (deps.pieces) bindPackagePieces(engine.packagePieces, deps.pieces);\n\n // Creating the runtime opens the journal, whose sweeps fail every run not\n // in this process: never after the lease has gone.\n if (loss.lost) throw singletonLostError(engine, loss, lease);\n const access = reactorAccessOf(deps.clientModule);\n const models = deps.models;\n const runtime = engine.createWorkflowRuntime({\n relationalDb: deps.relationalDb,\n secretsKeyFile: deps.secretsKeyFile,\n reactorClient: deps.reactorClient,\n assertCanRead: readAssertion(deps.authorizationService, deps.reactorClient),\n subjectOf: (caller) => callerSubject((caller as Context).user),\n assertCanWrite: writeAssertion(\n deps.authorizationService,\n deps.reactorClient,\n ),\n // Unknown flags leave it absent, which the engine takes as on.\n ...(deps.clientModule?.reactorModule\n ? { authEnforcement: access.authEnforcement }\n : {}),\n hostPrincipal: hostPrincipalOf(access),\n hostIdentity: hostIdentityOf(access),\n ...(models\n ? {\n modelManifest: () => models.modelManifest(),\n modelEntries: (documentType: string) =>\n models.modelEntries(documentType),\n }\n : {}),\n webhooks: deps.webhooks,\n attachments: deps.attachments,\n canReadAttachmentRef: attachmentReadPolicy(deps.attachmentAccess),\n logger: deps.logger,\n ...(deps.telemetry ? { telemetry: deps.telemetry } : {}),\n });\n\n // Set before anything below can throw, so a failed compose stops it.\n const routes: { oauthCallback?: ScopedRouteHandle } = {};\n const registration: TriggersRegistration = { tornDown: false };\n const tearDown = () => {\n if (registration.tornDown) return;\n registration.tornDown = true;\n routes.oauthCallback?.dispose();\n runtime.shutdown();\n // After the shutdown, so a batch already queued for it is refused and\n // replays on the next owner instead of being acknowledged here.\n registration.detach?.();\n };\n loss.tearDown = tearDown;\n\n const triggers = await registerWorkflowTriggersReadModel(\n engine,\n runtime,\n deps.clientModule,\n registration,\n );\n // Lost while registering: mount nothing a torn-down runtime would leave.\n if (registration.tornDown) throw singletonLostError(engine, loss, lease);\n if (triggers.status === \"available\") {\n deps.logger.info(\n `Workflow trigger read model registered (${engine.WORKFLOW_TRIGGERS_READ_MODEL}, ${engine.WORKFLOW_TRIGGERS_READ_MODEL_STAGE})`,\n );\n } else {\n // Loudly: the runtime still serves GraphQL and still runs webhook and\n // schedule triggers, so nothing else says the document ones are dead.\n deps.logger.error(\n \"Workflow document triggers are NOT armed (@reason): this reactor's \" +\n \"read-model coordinator takes no live registration, so no document \" +\n \"operation reaches the runtime. Webhook and schedule triggers are \" +\n \"unaffected.\",\n triggers.reason,\n );\n }\n\n let stopped = false;\n if (deps.http) {\n routes.oauthCallback = registerOAuthCallback(deps.http, runtime);\n }\n\n return {\n subgraph: createWorkflowRuntimeSubgraph(\n runtime,\n deps.http ? { callbackUrl: callbackUrlOf(deps.http) } : undefined,\n access,\n ),\n get triggers() {\n return loss.lost ? SINGLETON_LOST : triggers;\n },\n ...(lease ? { singletonOwner: lease.owner } : {}),\n\n async start() {\n if (registration.tornDown) return;\n // The endpoint family first: a restored webhook trigger asks for its URL\n // as soon as the supervisor starts.\n await runtime.registerWebhookEndpoint();\n // Lost or stopped while registering.\n if (loss.lost || stopped) return;\n runtime.startTriggerSupervisor();\n // So the first search in the editor finds the catalog indexed.\n runtime.warmPieceSearch();\n },\n\n async stop() {\n if (stopped) return;\n stopped = true;\n tearDown();\n // Released, so the next boot owns workflows immediately instead of\n // waiting out the lease TTL.\n await lease?.release();\n },\n };\n}\n","import type { PGlite } from \"@electric-sql/pglite\";\nimport {\n HardenedPGliteDialect,\n type HardenedPGliteDialectOptions,\n} from \"@powerhousedao/reactor\";\nimport type {\n DatabaseIntrospector,\n Dialect,\n DialectAdapter,\n Driver,\n QueryCompiler,\n} from \"kysely\";\nimport type { ILogger } from \"document-model\";\nimport { triggerFatalShutdown } from \"./fatal-shutdown.mjs\";\n\ntype FatalProcess = Parameters<typeof triggerFatalShutdown>[2];\n\n/** A poisoned store cannot recover in process, so it takes the fatal shutdown and a supervisor restart. */\nexport function reactorPgliteDialectOptions(\n logger: ILogger,\n proc?: FatalProcess,\n): Partial<HardenedPGliteDialectOptions> {\n return {\n onDiagnostic: (message, error) =>\n logger.error(`[pglite-dialect] ${message}: @error`, error),\n onPoisoned: (cause) => {\n if (!triggerFatalShutdown(\"PGlite session poisoned\", cause, proc)) {\n logger.error(\"PGlite session poisoned: @error\", cause);\n }\n },\n };\n}\n\ntype IntrospectedDatabase = Parameters<Dialect[\"createIntrospector\"]>[0];\n\n// kysely-pglite-dialect's driver.destroy() only nulls its reference to the\n// PGlite client — it never calls pglite.close(). Without close(), the store\n// misses its shutdown checkpoint and the next open runs WAL recovery. This\n// wrapper closes the dialect's PGlite as part of the reactor's\n// database.destroy() chain.\nexport class ClosablePGliteDialect implements Dialect {\n readonly #inner: HardenedPGliteDialect;\n readonly #pglite: PGlite;\n\n constructor(\n pglite: PGlite,\n options: Partial<HardenedPGliteDialectOptions> = {},\n ) {\n this.#pglite = pglite;\n this.#inner = new HardenedPGliteDialect(pglite, options);\n }\n\n createAdapter(): DialectAdapter {\n return this.#inner.createAdapter();\n }\n\n createDriver(): Driver {\n const driver = this.#inner.createDriver();\n const pglite = this.#pglite;\n const innerDestroy = driver.destroy.bind(driver);\n driver.destroy = async () => {\n await innerDestroy();\n if (pglite.closed) return;\n try {\n await pglite.close();\n } catch (err) {\n // An aborted runtime (Postgres PANIC) has nothing left to flush.\n if (!/PGlite aborted/.test(String(err))) throw err;\n }\n };\n return driver;\n }\n\n createQueryCompiler(): QueryCompiler {\n return this.#inner.createQueryCompiler();\n }\n\n createIntrospector(db: IntrospectedDatabase): DatabaseIntrospector {\n return this.#inner.createIntrospector(db);\n }\n}\n","import { syncTree } from \"@powerhousedao/reactor-api/pglite-node\";\nimport type { ILogger } from \"document-model\";\nimport { promises as fs } from \"node:fs\";\nimport {\n CURRENT_PG_MAJOR,\n isSupportedMajor,\n loadPGliteModule,\n loadPgDump,\n readPgVersionFile,\n type SupportedPgMajor,\n} from \"./pglite-version.js\";\n\ntype PGliteLike = {\n waitReady: Promise<void>;\n exec: (sql: string) => Promise<unknown>;\n close: () => Promise<void>;\n dumpDataDir: (compression: \"none\") => Promise<Blob>;\n};\n\ntype PGliteCtor = {\n new (dataDir: string, options?: Record<string, unknown>): PGliteLike;\n new (options: Record<string, unknown>): PGliteLike;\n};\n\nfunction backupPath(dataDir: string, major: number): string {\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n return `${dataDir}.backup-pg${major}-${stamp}`;\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await fs.stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction logRestoreFailure(\n dataDir: string,\n sql: string,\n err: unknown,\n logger: ILogger,\n): void {\n const errObj = err as {\n message?: string;\n position?: string | number;\n severity?: string;\n code?: string;\n detail?: string;\n where?: string;\n };\n const position =\n typeof errObj.position === \"string\"\n ? parseInt(errObj.position, 10)\n : typeof errObj.position === \"number\"\n ? errObj.position\n : NaN;\n\n logger.error(\n `[pglite-migration] Restore failed for ${dataDir}: code=${errObj.code ?? \"\"} severity=${errObj.severity ?? \"\"} message=${errObj.message ?? \"\"} sqlLength=${sql.length}`,\n );\n\n if (Number.isFinite(position) && position > 0) {\n const zeroBased = position - 1;\n const start = Math.max(0, zeroBased - 200);\n const end = Math.min(sql.length, zeroBased + 200);\n const before = sql.slice(start, zeroBased);\n const at = sql.slice(zeroBased, zeroBased + 1);\n const after = sql.slice(zeroBased + 1, end);\n logger.error(\n `[pglite-migration] SQL context around position ${position}:\\n${before}»${at}«${after}`,\n );\n } else {\n logger.error(\n `[pglite-migration] No position info. First 2000 chars of dump:\\n${sql.slice(0, 2000)}`,\n );\n }\n}\n\n/**\n * Migrate a filesystem PGLite data directory from a legacy PG major to the\n * current one. Renames the existing dir to a timestamped backup, dumps via the\n * matching legacy `pg_dump`, restores into a fresh current-version PGLite at\n * the original path. On failure, the original dir is restored from the backup.\n *\n * No-op when the dir is missing or already at the current major.\n */\nexport async function migratePgliteDir(\n dataDir: string,\n logger: ILogger,\n): Promise<void> {\n const major = await readPgVersionFile(dataDir);\n if (major === null) {\n logger.info(\n `[pglite-migration] No PG_VERSION at ${dataDir}; skipping migration`,\n );\n return;\n }\n if (major === CURRENT_PG_MAJOR) return;\n\n if (!isSupportedMajor(major)) {\n throw new Error(\n `Unsupported legacy PGlite data dir: PG_VERSION=${major} for ${dataDir}`,\n );\n }\n\n const backupDir = backupPath(dataDir, major);\n logger.info(\n `[pglite-migration] Migrating ${dataDir} from PG${major} to PG${CURRENT_PG_MAJOR}; backup: ${backupDir}`,\n );\n\n await fs.rename(dataDir, backupDir);\n\n let sql: string;\n try {\n const [legacyMod, pgDump] = await Promise.all([\n loadPGliteModule(major as SupportedPgMajor),\n loadPgDump(major as SupportedPgMajor),\n ]);\n const LegacyPGlite = (legacyMod as unknown as { PGlite: PGliteCtor })\n .PGlite;\n // pglite-tools 0.2.x talks to the server through files in the PGlite\n // FS, and 0.2.17's NODEFS write drops the view's byteOffset, so every\n // query arrives as zeros. Dump from an in-memory copy instead.\n const onDisk = new LegacyPGlite(backupDir);\n let tar: Blob;\n try {\n await onDisk.waitReady;\n tar = await onDisk.dumpDataDir(\"none\");\n } finally {\n await onDisk.close();\n }\n const pg = new LegacyPGlite({ loadDataDir: tar });\n try {\n await pg.waitReady;\n const file = await pgDump({ pg });\n sql = await file.text();\n } finally {\n await pg.close();\n }\n } catch (err) {\n await rollback(dataDir, backupDir, err, logger);\n throw err;\n }\n\n try {\n const currentMod = await loadPGliteModule(CURRENT_PG_MAJOR);\n const CurrentPGlite = (currentMod as unknown as { PGlite: PGliteCtor })\n .PGlite;\n // Stock NodeFS never syncs to the host; one pass after close does.\n const pg = new CurrentPGlite(dataDir, { relaxedDurability: false });\n try {\n await pg.waitReady;\n try {\n await pg.exec(\"SET standard_conforming_strings = off;\");\n } catch (gucErr) {\n logger.warn(\n `[pglite-migration] Could not force standard_conforming_strings=off: ${String(gucErr)}`,\n );\n }\n try {\n await pg.exec(sql);\n } catch (execErr) {\n logRestoreFailure(dataDir, sql, execErr, logger);\n throw execErr;\n }\n } finally {\n await pg.close();\n }\n syncTree(dataDir);\n } catch (err) {\n await rollback(dataDir, backupDir, err, logger);\n throw err;\n }\n\n logger.info(\n `[pglite-migration] Migration of ${dataDir} complete. Backup retained at ${backupDir}; remove it manually once you have verified the upgrade.`,\n );\n}\n\nasync function rollback(\n dataDir: string,\n backupDir: string,\n originalError: unknown,\n logger: ILogger,\n): Promise<void> {\n try {\n if (await pathExists(dataDir)) {\n await fs.rm(dataDir, { recursive: true, force: true });\n }\n if (await pathExists(backupDir)) {\n await fs.rename(backupDir, dataDir);\n }\n } catch (rollbackErr) {\n logger.error(\n `[pglite-migration] Migration AND rollback failed for ${dataDir}. Original error: ${String(originalError)}; rollback error: ${String(rollbackErr)}; backup may still exist at ${backupDir}.`,\n );\n return;\n }\n logger.error(\n `[pglite-migration] Migration failed for ${dataDir}; rolled back from ${backupDir}. Original error: ${String(originalError)}`,\n );\n}\n","import {\n preparePgliteDataDir,\n removeStalePgliteFiles,\n type ConversionDeps,\n} from \"@powerhousedao/reactor-api/pglite-node\";\nimport type { ILogger } from \"document-model\";\nimport { promises as fs } from \"node:fs\";\nimport { migratePgliteDir } from \"./pglite-migration.js\";\nimport {\n CURRENT_PG_MAJOR,\n openForVerify,\n readPgVersionFile,\n type SupportedPgMajor,\n} from \"./pglite-version.js\";\n\nexport interface PglitePreflightOptions {\n /** Local PGlite data dirs; Postgres URLs are already filtered out. */\n dirs: string[];\n forcePgVersion?: SupportedPgMajor;\n migratePglite?: boolean;\n /** PH_PGLITE_IN_MEMORY: the dirs are never opened, so nothing is touched. */\n inMemory: boolean;\n logger: ILogger;\n /** Test hook; defaults to the module-matched stock NodeFS. */\n openForVerify?: ConversionDeps[\"openForVerify\"];\n}\n\nconst RM_OPTIONS = {\n recursive: true,\n force: true,\n maxRetries: 10,\n retryDelay: 100,\n} as const;\n\n/**\n * Prepares every local PGlite dir before the first open and returns the\n * PG major detected on disk per dir. Conversion of an AtomicNodeFs snapshot\n * runs for every dir, before the major is read, so a pure-snapshot PG16 dir\n * is converted and then migrated.\n */\nexport async function runPglitePreflight(\n options: PglitePreflightOptions,\n): Promise<Map<string, number>> {\n const { dirs, logger } = options;\n const detectedMajors = new Map<string, number>();\n if (options.inMemory || dirs.length === 0) return detectedMajors;\n\n if (options.forcePgVersion !== undefined) {\n if (options.migratePglite) {\n logger.warn(\n \"PH_FORCE_PG_VERSION is set; ignoring --migrate-pglite/PH_MIGRATE_PGLITE because the data dirs will be wiped.\",\n );\n }\n logger.warn(\n `PH_FORCE_PG_VERSION=${options.forcePgVersion} set; wiping PGLite data dirs and re-initializing at PG${options.forcePgVersion}.`,\n );\n for (const dir of dirs) {\n // Conversion siblings go too, or the next boot refuses to initdb beside them.\n for (const p of [dir, `${dir}.converting`, `${dir}.old`]) {\n await fs.rm(p, RM_OPTIONS);\n }\n logger.info(`Wiped PGLite data dir ${dir}`);\n }\n return detectedMajors;\n }\n\n const deps: ConversionDeps = {\n openForVerify: options.openForVerify ?? openForVerify,\n logger,\n };\n for (const dir of dirs) {\n await preparePgliteDataDir(dir, deps);\n const major = await readPgVersionFile(dir);\n if (major !== null) detectedMajors.set(dir, major);\n }\n\n if (options.migratePglite) {\n for (const [dir, major] of detectedMajors) {\n if (major === CURRENT_PG_MAJOR) continue;\n await migratePgliteDir(dir, logger);\n // The migration's own close leaves a lockfile in the new dir.\n await removeStalePgliteFiles(dir, logger);\n const after = await readPgVersionFile(dir);\n if (after !== null) detectedMajors.set(dir, after);\n }\n } else {\n for (const [dir, major] of detectedMajors) {\n if (major === CURRENT_PG_MAJOR) continue;\n logger.warn(\n `PGLite data dir at ${dir} was created with PG${major} but Switchboard ships PG${CURRENT_PG_MAJOR}. Running on legacy PGLite. Re-start with --migrate-pglite (or PH_MIGRATE_PGLITE=true) to upgrade.`,\n );\n }\n }\n return detectedMajors;\n}\n","import type { ReactorFeatureFlags } from \"@powerhousedao/reactor\";\n\nexport type ResolvedReactorFeatureFlags = {\n flags: Partial<ReactorFeatureFlags>;\n /** Names of the flags set to true, in prerequisite order. */\n enabled: string[];\n};\n\n/**\n * Enforcement flags from the REACTOR_* env vars. Each flag requires the ones\n * before it; the reactor rejects an inconsistent set rather than enforcing less\n * than the operator asked for, so this reports what was asked without\n * correcting it.\n */\nexport function resolveReactorFeatureFlags(\n env: NodeJS.ProcessEnv,\n): ResolvedReactorFeatureFlags {\n const flags: Partial<ReactorFeatureFlags> = {\n documentDecisions: env.REACTOR_DOCUMENT_DECISIONS === \"true\",\n authEnforcement: env.REACTOR_AUTH_ENFORCEMENT === \"true\",\n authGroups: env.REACTOR_AUTH_GROUPS === \"true\",\n authConditions: env.REACTOR_AUTH_CONDITIONS === \"true\",\n };\n\n const enabled = Object.entries(flags)\n .filter(([, isEnabled]) => isEnabled)\n .map(([name]) => name);\n\n return { flags, enabled };\n}\n","import {\n DEFAULT_SIGNATURE_POLICY,\n isSignaturePolicy,\n type SignaturePolicy,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\n\n/**\n * What new documents are created as, from CREATE_SIGNATURE_POLICY. Without a\n * signer this switchboard cannot write to a v2-required document, so it falls\n * back to legacy and says so.\n */\nexport function resolveCreateSignaturePolicy(\n env: NodeJS.ProcessEnv,\n options: { hasSigner: boolean; logger?: Pick<ILogger, \"warn\"> },\n): SignaturePolicy {\n const raw = env.CREATE_SIGNATURE_POLICY?.trim();\n let policy: SignaturePolicy = DEFAULT_SIGNATURE_POLICY;\n if (raw) {\n if (!isSignaturePolicy(raw)) {\n throw new Error(\n `CREATE_SIGNATURE_POLICY must be \"legacy\" or \"v2-required\", got \"${raw}\"`,\n );\n }\n policy = raw;\n }\n\n if (policy === \"v2-required\" && !options.hasSigner) {\n options.logger?.warn(\n \"No signer is configured, so new documents are created legacy: a v2-required document refuses the unsigned operations this switchboard would write. Set up an identity (ph login) to create v2-required documents.\",\n );\n return \"legacy\";\n }\n return policy;\n}\n","import type { FactorySpec, SignerConfig } from \"@powerhousedao/reactor\";\nimport {\n createRenownTrustPolicy,\n DEFAULT_KEYPAIR_PATH,\n DEFAULT_RENOWN_URL,\n NodeKeyStorage,\n RenownBuilder,\n RenownCryptoBuilder,\n resolveSwitchboardEndpoint,\n type IRenown,\n type RenownTrustPolicyOptions,\n type SwitchboardRequestFn,\n} from \"@renown/sdk/node\";\nimport { childLogger } from \"document-model\";\nimport { createRequire } from \"node:module\";\nimport { resolve } from \"node:path\";\n\nconst logger = childLogger([\"switchboard\", \"renown\"]);\n\nexport interface RenownOptions {\n /** Path to the keypair file. Defaults to .ph/.keypair.json in cwd */\n keypairPath?: string;\n /** If true, won't generate a new keypair if none exists */\n requireExisting?: boolean;\n /** Base url of the Renown instance to use */\n baseUrl?: string;\n}\n\n/**\n * Initialize Renown for the Switchboard instance.\n * This allows Switchboard to authenticate with remote services\n * using the same identity established during `ph login`.\n */\nexport async function initRenown(\n options: RenownOptions = {},\n): Promise<IRenown | null> {\n const {\n keypairPath,\n requireExisting = false,\n baseUrl = DEFAULT_RENOWN_URL,\n } = options;\n\n const keyStorage = new NodeKeyStorage(keypairPath, {\n logger,\n });\n\n // Check if we have an existing keypair\n const existingKeyPair = await keyStorage.loadKeyPair();\n\n if (!existingKeyPair && requireExisting) {\n throw new Error(\n \"No existing keypair found and requireExisting is true. \" +\n 'Run \"ph login\" to create one.',\n );\n }\n\n if (!existingKeyPair) {\n logger.info(\"No existing keypair found. A new one will be generated.\");\n }\n\n const renownCrypto = await new RenownCryptoBuilder()\n .withKeyPairStorage(keyStorage)\n .build();\n\n const renown = await new RenownBuilder(\"switchboard\", {})\n .withCrypto(renownCrypto)\n .withBaseUrl(baseUrl)\n .build();\n\n logger.info(\"Switchboard identity initialized: @did\", renownCrypto.did);\n\n return renown;\n}\n\n/**\n * Get the signer config for the given renown instance.\n *\n * @param renown - The renown instance\n * @param keypairPath - Where `initRenown` loaded the key from\n */\nexport function getRenownSignerConfig(\n renown: IRenown,\n keypairPath?: string,\n): SignerConfig {\n return {\n signer: renown.signer,\n workerSigner: getRenownWorkerSignerSpec(renown, keypairPath),\n };\n}\n\n/**\n * What a pooled executor worker imports to sign as this switchboard: it\n * reloads the key `initRenown` stored, and takes the user known at boot.\n */\nexport function getRenownWorkerSignerSpec(\n renown: IRenown,\n keypairPath?: string,\n): FactorySpec {\n const { signer } = renown;\n const user = signer.user;\n return {\n module: {\n filePath: createRequire(import.meta.url).resolve(\"@renown/sdk/node\"),\n exportName: \"createNodeRenownSigner\",\n },\n initArgs: {\n appName: signer.app?.name ?? \"switchboard\",\n keypairPath: resolve(keypairPath ?? DEFAULT_KEYPAIR_PATH),\n ...(signer.app?.key ? { did: signer.app.key } : {}),\n ...(user\n ? {\n user: {\n address: user.address,\n networkId: user.networkId,\n chainId: user.chainId,\n },\n }\n : {}),\n },\n };\n}\n\n/** Where switchboard reads the credentials binding keys to wallets. */\nexport type RenownTrustSource =\n | { source: \"remote\"; renownUrl?: string; switchboardUrl?: string }\n | { source: \"self\"; request: SwitchboardRequestFn };\n\n/**\n * Admits a key as a signer for an address a Renown credential binds it to; a\n * `self` source has no worker spec. In process, the switchboard's own key is\n * matched against its current user; the worker spec carries the user known now.\n */\nexport async function getRenownTrustPolicyConfig(\n trustSource: RenownTrustSource,\n renown: IRenown | null,\n): Promise<Pick<SignerConfig, \"trustPolicy\" | \"workerTrustPolicy\">> {\n const ownSigner = renown?.signer;\n if (trustSource.source === \"self\") {\n return {\n trustPolicy: createRenownTrustPolicy({\n switchboard: trustSource.request,\n ownSigner,\n }),\n };\n }\n\n const switchboardUrl = await resolveSwitchboardEndpoint({\n switchboardUrl: trustSource.switchboardUrl,\n baseUrl: trustSource.renownUrl,\n });\n const options = {\n ...(switchboardUrl ? { switchboard: switchboardUrl } : {}),\n ...(trustSource.renownUrl ? { renownUrl: trustSource.renownUrl } : {}),\n } satisfies RenownTrustPolicyOptions;\n const self = ownIdentity(renown);\n return {\n trustPolicy: createRenownTrustPolicy({ ...options, ownSigner }),\n workerTrustPolicy: {\n module: {\n filePath: createRequire(import.meta.url).resolve(\"@renown/sdk/node\"),\n exportName: \"createRenownTrustPolicy\",\n },\n initArgs: { ...options, ...(self ? { self } : {}) },\n },\n };\n}\n\n/**\n * Fails the boot when pooled workers would refuse every signed write the\n * in-process executor accepts: a `self` Renown source gives workers no trust\n * policy spec, so under authEnforcement they apply the default.\n */\nexport function assertWorkerTrustPolicy(config: {\n workers: number;\n authEnforcement: boolean;\n renownSource: \"self\" | \"remote\";\n}): void {\n if (\n config.workers > 0 &&\n config.authEnforcement &&\n config.renownSource === \"self\"\n ) {\n throw new Error(\n \"The executor worker pool (REACTOR_WORKERS) cannot verify signers when \" +\n \"REACTOR_AUTH_ENFORCEMENT is on and Renown credentials are read from \" +\n 'this switchboard (\"self\" via auth.renown.source or RENOWN_SOURCE): ' +\n \"pooled workers have no trust policy and would refuse every signed \" +\n \"write. Set RENOWN_SOURCE=remote, or disable the worker pool \" +\n \"(REACTOR_WORKERS=0).\",\n );\n }\n}\n\nfunction ownIdentity(\n renown: IRenown | null,\n): RenownTrustPolicyOptions[\"self\"] | undefined {\n const key = renown?.signer.app?.key;\n if (!key) {\n return undefined;\n }\n const user = renown.signer.user;\n return {\n key,\n user: {\n address: user?.address ?? \"\",\n networkId: user?.networkId ?? \"\",\n chainId: user?.chainId ?? 0,\n },\n };\n}\n","import type {\n UnsupportedStoredDocuments,\n UnsupportedStoredProtocolError,\n} from \"@powerhousedao/reactor\";\n\nconst MODES: readonly UnsupportedStoredDocuments[] = [\"refuse\", \"read-only\"];\n\n/** From REACTOR_UNSUPPORTED_STORED_DOCUMENTS; `input` wins when defined. */\nexport function resolveUnsupportedStoredDocuments(\n input: UnsupportedStoredDocuments | undefined,\n env: NodeJS.ProcessEnv,\n): UnsupportedStoredDocuments {\n if (input !== undefined) return input;\n const raw = env.REACTOR_UNSUPPORTED_STORED_DOCUMENTS?.trim();\n if (!raw) return \"refuse\";\n if (!(MODES as readonly string[]).includes(raw)) {\n throw new Error(\n `REACTOR_UNSUPPORTED_STORED_DOCUMENTS must be \"refuse\" or \"read-only\", got \"${raw}\"`,\n );\n }\n return raw as UnsupportedStoredDocuments;\n}\n\n/** The reactor refused to start over documents this build does not run. */\nexport class StoredDocumentsRefusedError extends Error {\n readonly versions: readonly { protocol: string; version: number }[];\n readonly documents: number;\n\n constructor(cause: UnsupportedStoredProtocolError) {\n const named = cause.versions\n .map(({ protocol, version }) => `${protocol} ${version}`)\n .join(\", \");\n super(\n `Refusing to start: ${cause.documents} stored document(s) require ${named}, which this switchboard does not run. ` +\n `Either start a switchboard build that runs ${named}, or set REACTOR_UNSUPPORTED_STORED_DOCUMENTS=read-only to start with those documents read-only.`,\n { cause },\n );\n this.name = \"StoredDocumentsRefusedError\";\n this.versions = cause.versions;\n this.documents = cause.documents;\n }\n\n static isError(error: unknown): error is StoredDocumentsRefusedError {\n return Error.isError(error) && error.name === \"StoredDocumentsRefusedError\";\n }\n}\n","#!/usr/bin/env node\nimport type { PGlite } from \"@electric-sql/pglite\";\nimport { metrics, trace } from \"@opentelemetry/api\";\nimport { getConfig } from \"@powerhousedao/config/node\";\nimport {\n createDurableNodeFs,\n resolvePgliteFsync,\n type DurableNodeFs,\n} from \"@powerhousedao/reactor-api/pglite-node\";\nimport { ReactorInstrumentation } from \"@powerhousedao/opentelemetry-instrumentation-reactor\";\nimport {\n DriveCollectionId,\n EventBus,\n IN_MEMORY_PGLITE_STORAGE_FACTS,\n PGLITE_PATH_STORAGE_FACTS,\n POSTGRES_STORAGE_FACTS,\n REACTOR_SCHEMA,\n ReactorBuilder,\n ReactorClientBuilder,\n createHybridProjectionCoordinatorFactory,\n instrumentPgPool,\n parseDriveUrl,\n type Database,\n type InProcessReactorClientModule,\n type JwtHandler,\n type PoolInstrumentation,\n type ReactorStorageFacts,\n UnsupportedStoredProtocolError,\n} from \"@powerhousedao/reactor\";\nimport {\n HttpPackageLoader,\n ImportPackageLoader,\n PackageManagementService,\n PackagesSubgraph,\n PGLITE_UTC_PARSERS,\n initializeAndStartAPI,\n resolveRenownConfig,\n type CanonicalDocumentId,\n type ClientInitializerDependencies,\n type CredentialVerifier,\n type IPackageLoader,\n type ResolvedRenownConfig,\n} from \"@powerhousedao/reactor-api\";\nimport { httpsHooksPath } from \"@powerhousedao/reactor-api/https-hooks\";\nimport type { VitePackageLoader } from \"@powerhousedao/reactor-api/vite\";\nimport { createRemoteAttachmentService } from \"@powerhousedao/reactor-attachments\";\nimport { createAttachmentClient } from \"@powerhousedao/reactor-attachments/client\";\nimport {\n DriveNodeView,\n NodeProcessor,\n ReactorDriveClient,\n createReactorDriveResolvers,\n reactorDriveSubgraphTypeDefs,\n type ReactorDriveDatabase,\n} from \"@powerhousedao/reactor-drive\";\nimport type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport {\n createLocalCredentialVerifier,\n RENOWN_READ_MODEL_SUBGRAPH,\n type CredentialCheck,\n type IRenown,\n type SwitchboardRequestFn,\n} from \"@renown/sdk/node\";\nimport * as Sentry from \"@sentry/node\";\nimport { childLogger, setLogLevel, type ILogger } from \"document-model\";\nimport dotenv from \"dotenv\";\nimport { Kysely, PostgresDialect } from \"kysely\";\nimport { register } from \"node:module\";\nimport net from \"node:net\";\nimport path from \"path\";\nimport { Pool } from \"pg\";\nimport type { ViteDevServer } from \"vite\";\nimport { registerAttachmentRoutes } from \"./attachments/index.js\";\nimport {\n registerAttachmentReferenceReadModel,\n registerAttachmentReferenceReadModelOnModule,\n} from \"./attachment-reference-read-model.mjs\";\nimport {\n installFatalErrorShutdown,\n triggerFatalShutdown,\n} from \"./fatal-shutdown.mjs\";\nimport {\n resolvePrivacy,\n startPrivacy,\n type RunningPrivacy,\n} from \"./privacy.mjs\";\nimport { applySwitchboardReactorDefaults } from \"./builder-defaults.mjs\";\nimport {\n assertProjectionWorkerSupported,\n resolveProjectionWorkerOptions,\n} from \"./projection-worker.mjs\";\nimport {\n buildWorkerDbConfig,\n resolveHostPoolSize,\n keepImportableSources,\n resolveWorkerModelSources,\n resolveWorkerPoolOptions,\n} from \"./worker-pool.mjs\";\nimport { initProfilerFromEnv } from \"./profiler.js\";\nimport { initFeatureFlags } from \"./feature-flags.js\";\nimport { resolveMcpEnabled } from \"./mcp-flag.mjs\";\nimport {\n WORKFLOW_PACKAGE_NAME,\n WORKFLOW_TELEMETRY_SCOPE,\n composeWorkflowRuntime,\n modelManifestSource,\n assertWorkflowPackageLoadable,\n isWorkflowSingletonConflict,\n resolveWorkflowsEnabled,\n retryWorkflowSingleton,\n workflowSingletonLossHandler,\n type WorkflowSingletonRetry,\n type ComposedWorkflowRuntime,\n type ModelManifestSource,\n} from \"./workflow-runtime.mjs\";\nimport {\n ClosablePGliteDialect,\n reactorPgliteDialectOptions,\n} from \"./pglite-dialect.js\";\nimport { runPglitePreflight } from \"./pglite-preflight.js\";\nimport {\n CURRENT_PG_MAJOR,\n isSupportedMajor,\n loadNodeFsClass,\n loadPGliteModule,\n type SupportedPgMajor,\n} from \"./pglite-version.js\";\nimport { resolveReactorFeatureFlags } from \"./reactor-feature-flags.mjs\";\nimport { resolveCreateSignaturePolicy } from \"./create-signature-policy.mjs\";\nimport {\n assertWorkerTrustPolicy,\n getRenownSignerConfig,\n getRenownTrustPolicyConfig,\n initRenown,\n} from \"./renown.js\";\nimport type { StartServerOptions, SwitchboardReactor } from \"./types.js\";\nimport {\n StoredDocumentsRefusedError,\n resolveUnsupportedStoredDocuments,\n} from \"./unsupported-stored-documents.mjs\";\nimport {\n addDefaultDrive,\n addDefaultReactorDrive,\n isPostgresUrl,\n} from \"./utils.mjs\";\n\nconst defaultLogger = childLogger([\"switchboard\"]);\n\nconst LogLevel = (process.env.LOG_LEVEL as ILogger[\"level\"] | \"\") || \"info\";\nsetLogLevel(LogLevel);\n\ndotenv.config();\n\n// Feature flag constants\nconst DOCUMENT_MODEL_SUBGRAPHS_ENABLED = \"DOCUMENT_MODEL_SUBGRAPHS_ENABLED\";\nconst DOCUMENT_MODEL_SUBGRAPHS_ENABLED_DEFAULT = true;\n\nconst DEFAULT_PORT = process.env.PORT ? Number(process.env.PORT) : 4001;\n\n// How many ports forward from the requested one we will try before giving up.\nconst PORT_FALLBACK_ATTEMPTS = 20;\n\n// Retired with the snapshot store; PGlite writes through NodeFS now.\nlet flushIntervalWarned = false;\nfunction warnIgnoredFlushInterval(logger: ILogger): void {\n if (flushIntervalWarned) return;\n if (process.env.PGLITE_FLUSH_INTERVAL_MS === undefined) return;\n flushIntervalWarned = true;\n logger.warn(\n \"PGLITE_FLUSH_INTERVAL_MS is set but ignored: PGlite stores no longer snapshot on a timer.\",\n );\n}\n\n// A Postgres PANIC (ENOSPC on WAL, a failed fdatasync) aborts the wasm\n// runtime; the store is dead and the process must not keep accepting writes.\nfunction onPgliteAbort(logger: ILogger, what: unknown): void {\n if (!triggerFatalShutdown(\"PGlite aborted\", what)) {\n logger.error(\"PGlite aborted: @error\", what);\n }\n}\n\n// 0.2.17 syncs WAL through open_datasync, which NODEFS cannot honour, so on\n// PG16 only checkpoints reach the host disk; commits are durable against an\n// app crash, not a power loss, until --migrate-pglite upgrades the dir.\nfunction pgliteFsyncFor(\n major: SupportedPgMajor,\n dir: string,\n logger: ILogger,\n): boolean {\n const wanted = resolvePgliteFsync(process.env);\n if (wanted && major === 16) {\n logger.warn(\n `PGlite data dir ${dir} is PG16 on pglite-legacy-02, which does not sync commits to disk; only checkpoints are synced until --migrate-pglite upgrades it.`,\n );\n }\n return wanted;\n}\n\n/** Preloads the module's NodeFS so the returned factory can be synchronous. */\nasync function loadDurableFsFactory(\n major: SupportedPgMajor,\n logger: ILogger,\n): Promise<(dir: string) => DurableNodeFs> {\n const NodeFS = await loadNodeFsClass(major);\n return (dir) =>\n createDurableNodeFs(NodeFS, dir, {\n fsync: pgliteFsyncFor(major, dir, logger),\n logger,\n onAbort: (what) => onPgliteAbort(logger, what),\n });\n}\n\n// When set, runs both reactor and read-model PGLite instances purely in-memory.\nconst PGLITE_IN_MEMORY = process.env.PH_PGLITE_IN_MEMORY === \"1\";\n\n/**\n * Attempt to bind a throwaway TCP server to the given port. Resolves true if\n * the port is free, false if the OS reports it in use. Any other error is\n * surfaced so we don't silently mask real issues (permissions, bad host, …).\n */\nexport function isPortAvailable(port: number): Promise<boolean> {\n return new Promise((resolve, reject) => {\n const tester = net.createServer();\n tester.once(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\" || err.code === \"EACCES\") {\n resolve(false);\n } else {\n reject(err);\n }\n });\n tester.once(\"listening\", () => {\n tester.close(() => resolve(true));\n });\n // Bind on the unspecified IPv6 address so we detect collisions with both\n // IPv6 and IPv4 listeners (Node maps `::` to dual-stack on most systems).\n tester.listen({ port, host: \"::\" });\n });\n}\n\n/** The powerhouse.config.json this run reads, defaulting to the cwd copy. */\nfunction resolveConfigPath(configFile: string | undefined): string {\n return configFile ?? path.join(process.cwd(), \"powerhouse.config.json\");\n}\n\n// An unreadable config file means \"not configured\", not a boot failure:\n// workflows are opt-in.\nfunction readConfigWorkflowsEnabled(configPath: string): boolean {\n try {\n return getConfig(configPath).workflows?.enabled ?? false;\n } catch {\n return false;\n }\n}\n\nasync function resolveServerPort(\n requested: number,\n strictPort: boolean,\n logger: ILogger,\n): Promise<number> {\n if (strictPort) return requested;\n for (let i = 0; i < PORT_FALLBACK_ATTEMPTS; i++) {\n const candidate = requested + i;\n if (await isPortAvailable(candidate)) {\n if (candidate !== requested) {\n logger.info(\n `Port ${requested} is in use. Falling back to port ${candidate}.`,\n );\n }\n return candidate;\n }\n }\n // Couldn't find a free port in the window; let the caller surface the\n // original EADDRINUSE when the real bind attempts runs.\n return requested;\n}\n\n/**\n * The reactor's storage handle, plus the pool instrumentation when that\n * storage is Postgres. The PGlite branch has no `pg.Pool` at all, so the\n * instrumentation is absent rather than optional-by-convention.\n */\ntype ReactorStorage = {\n kysely: Kysely<Database>;\n poolInstrumentation: PoolInstrumentation | undefined;\n storageFacts: ReactorStorageFacts;\n};\n\nexport async function createReactorKysely(opts: {\n reactorDbUrl: string | undefined;\n reactorPgliteDir: string | null;\n reactorPgliteMajor: SupportedPgMajor | null;\n inMemory: boolean;\n /**\n * Resolved lazily: only the Postgres branch has a host pool, and\n * {@link resolveHostPoolSize} throws on a bad REACTOR_DB_POOL_SIZE_HOST.\n * Called eagerly it would fail a PGlite-backed server over a value that\n * path never reads.\n */\n hostPoolSize: () => number;\n logger: ILogger;\n}): Promise<ReactorStorage> {\n const {\n reactorDbUrl,\n reactorPgliteDir,\n reactorPgliteMajor,\n inMemory,\n hostPoolSize,\n logger,\n } = opts;\n\n if (reactorDbUrl && isPostgresUrl(reactorDbUrl)) {\n const connectionString = reactorDbUrl.includes(\"?\")\n ? reactorDbUrl\n : `${reactorDbUrl}?sslmode=disable`;\n const poolSize = hostPoolSize();\n const pool = new Pool({ connectionString, max: poolSize });\n // Named to match the reactor's own convention for the pools it opens\n // itself (`reactor-worker-N`, `projection-shard-N`).\n const poolInstrumentation = instrumentPgPool(pool, \"reactor-host\");\n logger.info(\n `Using PostgreSQL for reactor storage (host pool max ${poolSize})`,\n );\n return {\n kysely: new Kysely<Database>({ dialect: new PostgresDialect({ pool }) }),\n poolInstrumentation,\n storageFacts: POSTGRES_STORAGE_FACTS,\n };\n }\n\n if (!reactorPgliteDir || reactorPgliteMajor === null) {\n throw new Error(\"Reactor PGLite directory not resolved\");\n }\n const { PGlite } = await loadPGliteModule(reactorPgliteMajor);\n const pglite = inMemory\n ? new PGlite()\n : new PGlite({\n fs: (await loadDurableFsFactory(reactorPgliteMajor, logger))(\n reactorPgliteDir,\n ),\n });\n logger.info(\n inMemory\n ? `Using in-memory PGlite (PG${reactorPgliteMajor}) for reactor storage [PH_PGLITE_IN_MEMORY=1]`\n : `Using PGlite (PG${reactorPgliteMajor}) for reactor storage at ${reactorPgliteDir}`,\n );\n return {\n kysely: new Kysely<Database>({\n dialect: new ClosablePGliteDialect(\n pglite,\n reactorPgliteDialectOptions(logger),\n ),\n }),\n poolInstrumentation: undefined,\n storageFacts: inMemory\n ? IN_MEMORY_PGLITE_STORAGE_FACTS\n : PGLITE_PATH_STORAGE_FACTS,\n };\n}\n\n/** Derive the remote attachment service config for switchboard's own `/attachments/*` API. */\nexport function deriveAttachmentServiceConfig(\n options: Pick<StartServerOptions, \"attachmentServiceUrl\" | \"https\">,\n serverPort: number,\n renown: IRenown | null,\n): { remoteUrl: string; jwtHandler: JwtHandler | undefined } {\n const protocol = options.https ? \"https\" : \"http\";\n const remoteUrl =\n options.attachmentServiceUrl ??\n process.env.PH_SWITCHBOARD_PUBLIC_URL ??\n `${protocol}://localhost:${serverPort}`;\n const jwtHandler: JwtHandler | undefined = renown\n ? async (url: string) =>\n renown.user\n ? renown.getBearerToken({ expiresIn: 10, aud: url })\n : undefined\n : undefined;\n return { remoteUrl, jwtHandler };\n}\n\nasync function initServer(\n serverPort: number,\n options: StartServerOptions,\n renown: IRenown | null,\n renownConfig: ResolvedRenownConfig,\n) {\n const {\n dev,\n packages = [],\n remoteDrives = [],\n logger = defaultLogger,\n } = options;\n logger.level = LogLevel;\n const dbPath =\n options.dbPath ??\n process.env.DATABASE_URL ??\n process.env.PH_SWITCHBOARD_DATABASE_URL;\n\n // use postgres url for read model storage if available, otherwise use local PGlite path\n const readModelPath = dbPath || \".ph/read-storage\";\n\n const reactorDbUrl =\n options.dbPath ??\n process.env.PH_REACTOR_DATABASE_URL ??\n process.env.PH_SWITCHBOARD_DATABASE_URL;\n // When the caller passes in a reactor, the reactor-side PGLite dir is\n // unused — the caller owns its own storage. Only the read-model dir is\n // still needed by reactor-api itself.\n const reactorPath = reactorDbUrl || \"./.ph/reactor-storage\";\n const reactorPgliteDir = options.reactor\n ? null\n : !reactorDbUrl || !isPostgresUrl(reactorDbUrl)\n ? reactorPath\n : null;\n const readModelPgliteDir =\n !dbPath || !isPostgresUrl(dbPath) ? readModelPath : null;\n\n // PGLite pre-flight: convert legacy single-file snapshot stores, clear\n // stale lock and WAL temp files, then detect PG_VERSION and migrate or\n // warn. Under PH_FORCE_PG_VERSION the dirs are wiped instead.\n const pgliteDirs = [reactorPgliteDir, readModelPgliteDir].filter(\n (d): d is string => d !== null,\n );\n warnIgnoredFlushInterval(logger);\n const detectedMajors = await runPglitePreflight({\n dirs: pgliteDirs,\n forcePgVersion: options.forcePgVersion,\n migratePglite: options.migratePglite,\n inMemory: PGLITE_IN_MEMORY,\n logger,\n });\n\n function resolvePgliteMajorForDir(dir: string): SupportedPgMajor {\n if (options.forcePgVersion !== undefined) return options.forcePgVersion;\n const detected = detectedMajors.get(dir);\n if (detected === undefined) return CURRENT_PG_MAJOR;\n if (!isSupportedMajor(detected)) {\n throw new Error(\n `Unsupported PGLite data dir at ${dir}: PG_VERSION=${detected}`,\n );\n }\n return detected;\n }\n\n const reactorPgliteMajor = reactorPgliteDir\n ? resolvePgliteMajorForDir(reactorPgliteDir)\n : null;\n const readModelPgliteMajor = readModelPgliteDir\n ? resolvePgliteMajorForDir(readModelPgliteDir)\n : null;\n\n let workerPool = resolveWorkerPoolOptions(options.workerPool, process.env);\n if (workerPool && options.reactor) {\n logger.warn(\n \"Worker pool configuration ignored: the caller-provided reactor owns its own executor\",\n );\n workerPool = null;\n }\n if (workerPool) {\n if (dev) {\n throw new Error(\n \"The executor worker pool (REACTOR_WORKERS) is not supported in dev mode: Vite-loaded document models cannot cross a worker-thread boundary\",\n );\n }\n assertWorkerTrustPolicy({\n workers: workerPool.numWorkers,\n authEnforcement:\n resolveReactorFeatureFlags(process.env).flags.authEnforcement === true,\n renownSource: renownConfig.source,\n });\n if (!reactorDbUrl || !isPostgresUrl(reactorDbUrl)) {\n throw new Error(\n \"The executor worker pool (REACTOR_WORKERS) requires a Postgres reactor database — set PH_REACTOR_DATABASE_URL or PH_SWITCHBOARD_DATABASE_URL. PGlite cannot be shared across worker threads.\",\n );\n }\n }\n\n const unsupportedStoredDocuments = resolveUnsupportedStoredDocuments(\n options.unsupportedStoredDocuments,\n process.env,\n );\n\n let projectionWorker = resolveProjectionWorkerOptions(\n options.projectionWorker,\n process.env,\n );\n if (projectionWorker && options.reactor) {\n logger.warn(\n \"Projection worker configuration ignored: the caller-provided reactor owns its own read-model coordinator\",\n );\n projectionWorker = null;\n }\n if (projectionWorker) {\n assertProjectionWorkerSupported({ dev: dev === true, reactorDbUrl });\n }\n\n // The reactor-api owns its own PGlite/HTTP/WS resources but has no shutdown\n // path of its own; we register `api.dispose` as a reactor shutdown hook so\n // those resources drain inside the reactor's SIGINT chain. The reference\n // is forward — `initializeClient` runs (and registers the hook) before\n // `initializeAndStartAPI` returns the api — so the closure reads `apiRef`\n // at hook-fire time, not at registration time.\n const apiRef: { current: { dispose: () => Promise<void> } | undefined } = {\n current: undefined,\n };\n let driveNodeView: DriveNodeView | undefined;\n\n // HTTP registry package loading\n const configPath = resolveConfigPath(options.configFile);\n const config = getConfig(configPath);\n const registryUrl =\n options.registryUrl ??\n process.env.PH_REGISTRY_URL ??\n config.packageRegistryUrl;\n const registryPackages = process.env.PH_REGISTRY_PACKAGES;\n const dynamicModelLoading =\n options.dynamicModelLoading ?? process.env.DYNAMIC_MODEL_LOADING === \"true\";\n let httpLoader: HttpPackageLoader | undefined;\n\n if (registryUrl) {\n // Register HTTP/HTTPS module loader hooks for dynamic package imports\n register(httpsHooksPath, import.meta.url);\n httpLoader = new HttpPackageLoader({ registryUrl });\n registryPackages?.split(\",\").forEach((p) => {\n const name = p.trim();\n if (!packages.includes(name)) {\n packages.push(name);\n }\n });\n }\n\n // Registry packages import from the on-disk cache, which workers can read.\n const registryPackageNames = new Set(\n (registryPackages ?? \"\")\n .split(\",\")\n .map((p) => p.trim())\n .filter(Boolean),\n );\n const resolveRegistryModels = async (\n identifier: string,\n ): Promise<string | null> =>\n httpLoader && registryPackageNames.has(identifier)\n ? ((await httpLoader.cachePackage(identifier)).entries.documentModels ??\n null)\n : null;\n\n const reactorLogger = logger.child([\"reactor\"]);\n // Latched: dropWriteReady reports the fatal once per dropped job.\n let projectionWorkerFatalFired = false;\n const onProjectionWorkerFatal = (shardId: string, reason: Error) => {\n if (projectionWorkerFatalFired) {\n return;\n }\n projectionWorkerFatalFired = true;\n reactorLogger.error(\n `Projection worker ${shardId} died; shutting down so the supervisor restarts a healthy process`,\n reason,\n );\n // SIGTERM takes the builder's withSignalHandlers() path: kill, hooks, db.\n process.kill(process.pid, \"SIGTERM\");\n };\n // Resolved in startSwitchboard, which owns the flag mechanism; initServer\n // only reads the answer.\n const workflowsEnabled = options.workflows?.enabled === true;\n // Resolved before the reactor is built: a bad secret fails the boot early.\n const privacyConfig = resolvePrivacy(options.privacy, process.env);\n\n // Through the package manager like any other, so one route carries the\n // models, the subgraphs and the piece.\n if (workflowsEnabled) {\n await assertWorkflowPackageLoadable();\n if (!packages.includes(WORKFLOW_PACKAGE_NAME)) {\n packages.push(WORKFLOW_PACKAGE_NAME);\n }\n }\n\n // Set only when we build the reactor ourselves; a caller-provided one keeps\n // its own lifecycle and must not be torn down here.\n let ownedReactorModule: InProcessReactorClientModule | undefined;\n // The models workers and workflow pieces import; only on a reactor built here.\n let workerModels: ModelManifestSource | undefined;\n // Bound once the api serves the renown read model; see `localCredentialCheck`.\n let localRenownRequest: SwitchboardRequestFn | undefined;\n const initializeClient = async (\n documentModels: DocumentModelModule[],\n {\n attachmentReferenceWriter,\n upgradeManifests,\n }: ClientInitializerDependencies,\n ) => {\n // When the caller hands us a pre-built reactor module, reuse it\n // instead of constructing one. The caller owns the reactor lifecycle\n // and must call `switchboard.shutdown()` from their own teardown to\n // drain /graphql, MCP, attachments, etc.\n if (options.reactor) {\n const attachmentReferenceProjection =\n await registerAttachmentReferenceReadModelOnModule(\n options.reactor,\n attachmentReferenceWriter,\n );\n if (options.reactor.reactorModule) {\n const instrumentation = new ReactorInstrumentation(\n options.reactor.reactorModule,\n );\n instrumentation.start();\n reactorLogger.info(\n \"Reactor metrics instrumentation started (using caller-provided reactor)\",\n );\n }\n return {\n module: options.reactor,\n attachmentReferenceProjection,\n };\n }\n\n const {\n kysely: baseKysely,\n poolInstrumentation,\n storageFacts,\n } = await createReactorKysely({\n reactorDbUrl,\n reactorPgliteDir,\n reactorPgliteMajor,\n inMemory: PGLITE_IN_MEMORY,\n hostPoolSize: () => resolveHostPoolSize(process.env),\n logger,\n });\n\n const maxSkipThreshold = parseInt(process.env.MAX_SKIP_THRESHOLD ?? \"\", 10);\n const hasSkipThreshold = !isNaN(maxSkipThreshold) && maxSkipThreshold > 0;\n if (hasSkipThreshold) {\n logger.info(`Reactor maxSkipThreshold set to ${maxSkipThreshold}`);\n }\n\n // Flip these per document-sharing fleet, never per node: replay decisions\n // are consensus outcomes, so nodes that disagree on the flags diverge.\n const { flags: reactorFeatureFlags, enabled: enabledFeatureFlags } =\n resolveReactorFeatureFlags(process.env);\n if (enabledFeatureFlags.length > 0) {\n logger.info(\n `Reactor feature flags enabled: ${enabledFeatureFlags.join(\", \")}`,\n );\n }\n\n const reactorBuilder = new ReactorBuilder()\n .withEventBus(new EventBus())\n .withKysely(baseKysely)\n .withStorageFacts(storageFacts)\n .withFeatures({\n legacyProcessorIds:\n process.env.REACTOR_LEGACY_PROCESSOR_IDS !== \"false\",\n })\n .withUnsupportedStoredDocuments(unsupportedStoredDocuments);\n\n // Feeds `module.pools`, which ReactorInstrumentation reads to emit\n // reactor.db.pool.{acquire.wait_duration,size,idle,waiting}. Without this\n // the host pool is the one pool the reactor opens that nobody can see.\n // Note the gauges observe pg-pool only: behind a transaction-mode\n // pgbouncer, `waiting` can read 0 while requests queue in the pooler.\n if (poolInstrumentation) {\n reactorBuilder.withInstrumentedPool(poolInstrumentation);\n }\n\n const clientBuilder = new ReactorClientBuilder().withReactorBuilder(\n reactorBuilder,\n );\n\n // Subpath import keeps vetra's React editors out of the node bundle.\n const vetraDocumentModels: DocumentModelModule[] = dev\n ? (\n Object.values(\n await import(\"@powerhousedao/vetra/document-models\"),\n ) as unknown[]\n ).filter(\n (m): m is DocumentModelModule =>\n typeof m === \"object\" &&\n m !== null &&\n \"documentModel\" in m &&\n \"reducer\" in m,\n )\n : [];\n\n applySwitchboardReactorDefaults(reactorBuilder, clientBuilder, {\n documentModels: [...documentModels, ...vetraDocumentModels],\n upgradeManifests,\n executorConfig:\n hasSkipThreshold || enabledFeatureFlags.length > 0\n ? {\n ...(hasSkipThreshold ? { maxSkipThreshold } : {}),\n ...(enabledFeatureFlags.length > 0\n ? { featureFlags: reactorFeatureFlags }\n : {}),\n }\n : undefined,\n documentModelLoader:\n httpLoader && dynamicModelLoading\n ? httpLoader.documentModelLoader\n : undefined,\n logger: reactorLogger,\n signer: renown\n ? getRenownSignerConfig(renown, options.identity?.keypairPath)\n : undefined,\n createSignaturePolicy: resolveCreateSignaturePolicy(process.env, {\n hasSigner: renown !== null,\n logger,\n }),\n trustPolicy: reactorFeatureFlags.authEnforcement\n ? await getRenownTrustPolicyConfig(\n renownConfig.source === \"self\"\n ? {\n source: \"self\",\n request: (query, variables) =>\n localRenownRequest\n ? localRenownRequest(query, variables)\n : Promise.reject(\n new Error(\n \"The local renown read model is not bound yet\",\n ),\n ),\n }\n : {\n source: \"remote\",\n renownUrl: renownConfig.url,\n switchboardUrl: renownConfig.switchboardUrl,\n },\n renown,\n )\n : undefined,\n });\n\n // File sources give workers and workflow pieces importable paths for the\n // models registered above; the builder dedupes them.\n if (workerPool || projectionWorker || workflowsEnabled) {\n const sources = await resolveWorkerModelSources(packages, reactorLogger, {\n resolveRemote: resolveRegistryModels,\n });\n // Workers need every model; workflow pieces can do without a broken one.\n reactorBuilder.withDocumentModelSources(\n workerPool || projectionWorker\n ? sources\n : await keepImportableSources(sources, reactorLogger),\n );\n }\n workerModels = modelManifestSource(reactorBuilder);\n\n if (workerPool) {\n if (!reactorDbUrl) {\n throw new Error(\n \"unreachable: worker pool enabled without a reactor database URL\",\n );\n }\n // The builder fails the boot if a model lacks an importable source.\n reactorBuilder.withWorkerPool({\n numWorkers: workerPool.numWorkers,\n db: buildWorkerDbConfig(reactorDbUrl, workerPool),\n });\n reactorLogger.info(\n `Executor worker pool enabled: ${workerPool.numWorkers} worker threads${\n workerPool.mode === \"auto\" ? \" (auto-sized from cores)\" : \"\"\n }`,\n );\n }\n\n reactorBuilder.withReadModelFactory(\n async ({\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n }) => {\n const nodeProcessor = new NodeProcessor(\n baseKysely as unknown as Kysely<unknown>,\n REACTOR_SCHEMA,\n operationIndex,\n writeCache,\n processorManagerConsistencyTracker,\n );\n await nodeProcessor.init();\n return nodeProcessor;\n },\n );\n\n registerAttachmentReferenceReadModel(reactorBuilder, {\n baseKysely: baseKysely as unknown as Kysely<unknown>,\n attachmentReferenceWriter,\n });\n\n if (projectionWorker) {\n if (!reactorDbUrl) {\n throw new Error(\n \"unreachable: projection worker enabled without a reactor database URL\",\n );\n }\n const db = {\n ...buildWorkerDbConfig(reactorDbUrl, {\n dbPoolSizePerWorker: projectionWorker.dbPoolSize,\n acquireTimeoutMs: workerPool?.acquireTimeoutMs ?? 5000,\n }),\n applicationName: \"switchboard-projection\",\n };\n reactorBuilder.withReadModelCoordinatorFactory(\n createHybridProjectionCoordinatorFactory({\n shardCount: 1,\n poolSize: projectionWorker.dbPoolSize,\n db,\n onFatal: onProjectionWorkerFatal,\n }),\n );\n reactorLogger.info(\n `Projection worker enabled: 1 worker thread, pool size ${projectionWorker.dbPoolSize}`,\n );\n }\n\n reactorBuilder.withShutdownHook(async () => {\n if (apiRef.current) await apiRef.current.dispose();\n });\n\n let module: InProcessReactorClientModule;\n try {\n module = await clientBuilder.buildModule();\n } catch (error) {\n try {\n await baseKysely.destroy();\n } catch (destroyError) {\n logger.error(\n \"Aborting boot: reactor database destroy failed: @error\",\n destroyError,\n );\n }\n if (UnsupportedStoredProtocolError.isError(error)) {\n throw new StoredDocumentsRefusedError(error);\n }\n throw error;\n }\n\n if (module.reactorModule) {\n const instrumentation = new ReactorInstrumentation(module.reactorModule);\n instrumentation.start();\n reactorLogger.info(\"Reactor metrics instrumentation started\");\n }\n\n const reactorDriveSchemaDb = baseKysely.withSchema(\n REACTOR_SCHEMA,\n ) as unknown as Kysely<ReactorDriveDatabase>;\n driveNodeView = new DriveNodeView(reactorDriveSchemaDb);\n const reactorDriveClient = new ReactorDriveClient({\n reactor: module.client,\n readModel: driveNodeView,\n });\n\n ownedReactorModule = module;\n if (options.fatalErrorShutdown) {\n installFatalErrorShutdown(logger);\n }\n\n return {\n module,\n reactorDriveClient,\n attachmentReferenceProjection: { status: \"available\" as const },\n };\n };\n\n // Tear down a partially booted stack in the reactor's own order: reactor\n // first, then the api's HTTP/WS/db handles, then the database pool.\n const abortBoot = async (api: { dispose: () => Promise<void> }) => {\n const owned = ownedReactorModule;\n if (owned) {\n try {\n await owned.reactor.kill().completed;\n } catch (error) {\n logger.error(\"Aborting boot: reactor shutdown failed: @error\", error);\n }\n }\n try {\n await api.dispose();\n } catch (error) {\n logger.error(\"Aborting boot: api dispose failed: @error\", error);\n }\n if (owned?.reactorModule) {\n try {\n await owned.reactorModule.database.destroy();\n } catch (error) {\n logger.error(\"Aborting boot: database destroy failed: @error\", error);\n }\n }\n };\n\n // Reading credentials from this switchboard's own reactor needs the GraphQL\n // manager, which only exists once the api is up: bind the check afterwards.\n let localCredentialCheck: CredentialCheck | undefined;\n const verifyCredential: CredentialVerifier | undefined =\n renownConfig.source === \"self\"\n ? (params) =>\n localCredentialCheck\n ? localCredentialCheck(params)\n : Promise.reject(\n new Error(\"The local renown read model is not bound yet\"),\n )\n : undefined;\n\n let defaultDriveUrl: undefined | string = undefined;\n\n // TODO get path from powerhouse config\n const basePath = process.cwd();\n\n // import Vite loader only if dev mode is enabled\n let vite: ViteDevServer | undefined;\n let viteLoader: VitePackageLoader | undefined;\n if (dev) {\n const { VitePackageLoader, createViteLogger, startViteServer } =\n await import(\"@powerhousedao/reactor-api/vite\");\n vite = await startViteServer(process.cwd(), createViteLogger(logger));\n // Packages switchboard brings, like the workflow package, resolve from\n // here when the project does not install them itself.\n viteLoader = VitePackageLoader.build(vite, {\n resolveFrom: [import.meta.url],\n });\n }\n\n // Vetra is builder-only and bundled (not CDN-loadable); lazy-load its\n // processor only in dev, where builder tooling runs (e.g. `ph vetra`).\n const vetraProcessorFactory = dev\n ? (await import(\"@powerhousedao/vetra/processors\")).processorFactory\n : undefined;\n\n // get paths to local document models\n if (!options.disableLocalPackages) {\n packages.push(basePath);\n }\n\n // create loaders\n const packageLoaders: IPackageLoader[] = [];\n if (viteLoader) {\n packageLoaders.push(viteLoader);\n } else {\n packageLoaders.push(new ImportPackageLoader());\n }\n if (httpLoader) {\n packageLoaders.push(httpLoader);\n registryPackages?.split(\",\").forEach((p) => {\n const name = p.trim();\n if (!packages.includes(name)) {\n packages.push(name);\n }\n });\n }\n\n const apiLogger = logger.child([\"reactor-api\"]);\n // When the read-model store is on disk, hand reactor-api a factory that\n // constructs the matching PGLite (current or legacy) for the detected\n // PG_VERSION. reactor-api calls the factory synchronously, so the legacy\n // module is preloaded above.\n let pgliteFactory:\n | ((connectionString: string | undefined) => PGlite)\n | undefined;\n if (readModelPgliteDir && readModelPgliteMajor !== null) {\n const { PGlite: ReadModelPGlite } =\n await loadPGliteModule(readModelPgliteMajor);\n const durableFs = await loadDurableFsFactory(readModelPgliteMajor, logger);\n pgliteFactory = PGLITE_IN_MEMORY\n ? () => new ReadModelPGlite({ parsers: PGLITE_UTC_PARSERS })\n : (connectionString) =>\n new ReadModelPGlite({\n fs: durableFs(connectionString ?? readModelPgliteDir),\n parsers: PGLITE_UTC_PARSERS,\n });\n }\n\n const api = await initializeAndStartAPI(\n initializeClient,\n {\n port: serverPort,\n dbPath: readModelPath,\n pgliteFactory,\n https: options.https,\n packageLoaders: packageLoaders.length > 0 ? packageLoaders : undefined,\n packages: packages,\n processorConfig: options.processorConfig,\n processors: vetraProcessorFactory\n ? { \"@powerhousedao/vetra\": [vetraProcessorFactory] }\n : {},\n configFile: configPath,\n mcp: options.mcp ?? true,\n logger: apiLogger,\n enableDocumentModelSubgraphs: options.enableDocumentModelSubgraphs,\n // Already resolved for this reactor's own identity; reuse it so an\n // invalid RENOWN_SOURCE is reported once, not once per resolution.\n renown: renownConfig,\n verifyCredential,\n },\n \"switchboard\",\n );\n apiRef.current = api;\n\n if (renownConfig.source === \"self\") {\n const { graphqlManager: manager } = api;\n // Refuse to serve rather than 401 every request: the read model comes from\n // a loaded package, so a missing one is a deployment mistake.\n if (!manager.hasSubgraphHandler(RENOWN_READ_MODEL_SUBGRAPH)) {\n await abortBoot(api);\n throw new Error(\n 'Renown credential verification is set to \"self\" (auth.renown.source ' +\n `or RENOWN_SOURCE) but no loaded package serves the ` +\n `\"${RENOWN_READ_MODEL_SUBGRAPH}\" subgraph. Install one ` +\n \"(@powerhousedao/renown-package) or set RENOWN_SOURCE=remote.\",\n );\n }\n localRenownRequest = (query, variables) =>\n manager.executeSubgraphQuery(\n RENOWN_READ_MODEL_SUBGRAPH,\n query,\n variables,\n );\n localCredentialCheck = createLocalCredentialVerifier(localRenownRequest, {\n onError: (error) =>\n logger.error(\"Renown read model query failed: @error\", error),\n });\n logger.info(\n \"Renown credentials will be verified against this switchboard's own \" +\n \"renown read model\",\n );\n }\n\n registerAttachmentRoutes(api);\n\n const attachmentService = createRemoteAttachmentService(\n deriveAttachmentServiceConfig(options, serverPort, renown),\n );\n\n if (process.env.SENTRY_DSN) {\n // Register Sentry error handler after all routes are established.\n // The adapter calls the framework-specific Sentry setup internally.\n api.httpAdapter.setupSentryErrorHandler(Sentry);\n }\n\n const { client, graphqlManager, documentModelRegistry } = api;\n\n const lateSubgraphs: Promise<unknown>[] = [];\n\n // The workflow runtime is a switchboard component: composed from what the\n // api handed back, registered like any other late subgraph.\n let workflows: ComposedWorkflowRuntime | undefined;\n let workflowsRetry: WorkflowSingletonRetry | undefined;\n const composeWorkflows = () =>\n composeWorkflowRuntime({\n reactorClient: client,\n clientModule: options.reactor ?? ownedReactorModule,\n relationalDb: api.relationalDb,\n // A Postgres read model outlives the pod; a key file beside it would not.\n secretsKeyFile: readModelPgliteDir === null ? false : undefined,\n // The stable half of the default singleton owner name. Absolute, so\n // two Switchboards in different working directories differ.\n storageId:\n readModelPgliteDir === null\n ? readModelPath\n : path.resolve(readModelPath),\n attachments: createAttachmentClient(api.attachments.service),\n attachmentAccess: api.attachmentAccess,\n // The workflow package's own HTTP namespace: its webhook endpoints live\n // under it, not under the reactor's.\n webhooks: api.httpRoutes.scopeFor(WORKFLOW_PACKAGE_NAME).webhooks,\n http: api.httpRoutes.scopeFor(WORKFLOW_PACKAGE_NAME),\n authorizationService: api.authorizationService,\n // The manager that already loads this reactor's packages: the project it\n // runs in is one of them, so its own pieces arrive with the rest.\n pieces: api.packageManager,\n pieceRegistryUrl: registryUrl,\n models: workerModels,\n logger: logger.child([\"workflow-runtime\"]),\n exclusiveJournal: readModelPgliteDir !== null,\n onSingletonLost: workflowSingletonLossHandler(logger),\n // The providers observability.mts registered before this module loaded.\n telemetry: {\n tracer: trace.getTracer(WORKFLOW_TELEMETRY_SCOPE),\n meter: metrics.getMeter(WORKFLOW_TELEMETRY_SCOPE),\n },\n });\n const registerWorkflowSubgraph = (composed: ComposedWorkflowRuntime) => {\n const WorkflowRuntimeSubgraph = composed.subgraph;\n const workflowSubgraph = new WorkflowRuntimeSubgraph({\n reactorClient: client,\n http: graphqlManager.scopeForPackage(WORKFLOW_PACKAGE_NAME),\n relationalDb: api.relationalDb,\n analyticsStore: undefined as never,\n graphqlManager,\n syncManager: api.syncManager,\n authorizationService: graphqlManager.getAuthorizationService(),\n path: graphqlManager.getBasePath(),\n attachments: api.attachmentClientProvider,\n });\n return graphqlManager\n .registerSubgraphInstance(workflowSubgraph, \"graphql\", false)\n .catch((error: unknown) => {\n logger.error(\n \"Failed to register workflow-runtime subgraph: @error\",\n error,\n );\n });\n };\n if (workflowsEnabled) {\n try {\n workflows = await composeWorkflows();\n } catch (error) {\n // Without the claim this host must not run workflows, but everything\n // else it serves still works, so it boots without the runtime.\n if (!isWorkflowSingletonConflict(error)) throw error;\n logger.warn(\n `Workflows are enabled but another live process (\"${error.owner ?? \"unknown\"}\") ` +\n `holds the workflow singleton until ${error.expiresAt ?? \"unknown\"}. ` +\n \"This Switchboard has booted WITHOUT the workflow runtime: no \" +\n \"trigger of any kind fires here, and the workflow GraphQL face is \" +\n \"absent. Everything else serves normally. Stop the other owner, or \" +\n \"set PH_WORKFLOWS_SINGLETON_OWNER to the same stable name on the \" +\n \"slot that owns workflows. This Switchboard retries the claim and \" +\n \"starts workflows once the lease comes free.\",\n );\n workflowsRetry = retryWorkflowSingleton({\n compose: composeWorkflows,\n onComposed: async (composed) => {\n workflows = composed;\n await registerWorkflowSubgraph(composed);\n // Past boot nothing else rebuilds the router, so a subgraph\n // registered now is not served until this runs.\n try {\n await graphqlManager.updateRouter();\n } catch (error) {\n logger.error(\n \"Mounting the late workflow-runtime subgraph failed: @error\",\n error,\n );\n }\n await composed.start();\n logger.info(\"Workflow runtime started\");\n },\n logger,\n });\n }\n }\n if (workflows) {\n lateSubgraphs.push(registerWorkflowSubgraph(workflows));\n await workflows.start();\n logger.info(\"Workflow runtime started\");\n api.inspection?.facts.setWorkflows(true);\n }\n\n let privacy: RunningPrivacy | undefined;\n if (privacyConfig.enabled) {\n // The caller's reactor signs with a signer only the caller knows.\n const signer = options.reactor\n ? options.privacy?.signer\n : (options.privacy?.signer ?? renown?.signer);\n try {\n privacy = await startPrivacy({\n config: privacyConfig,\n reactorModule: (options.reactor ?? ownedReactorModule)?.reactorModule,\n signer,\n authorizationService: api.authorizationService,\n documentPermissionService: api.documentPermissionService,\n graphqlManager,\n reactorClient: client,\n logger: logger.child([\"privacy\"]),\n });\n } catch (error) {\n await workflowsRetry?.stop();\n await workflows?.stop();\n await abortBoot(api);\n throw error;\n }\n }\n\n // Ahead of the api: the runtime's store lives in the read-model database\n // that dispose closes, and its children outlive the reactor otherwise.\n const shutdown = async () => {\n await privacy?.stop();\n await workflowsRetry?.stop();\n await workflows?.stop();\n await api.dispose();\n };\n apiRef.current = { dispose: shutdown };\n\n // Wire up dynamic package management if HTTP loader is configured\n if (httpLoader) {\n const packageManagementService = new PackageManagementService({\n defaultRegistryUrl: registryUrl,\n httpLoader,\n documentModelRegistry,\n packageManager: api.packageManager,\n });\n\n packageManagementService.setOnModelsChanged(() => {\n graphqlManager\n .regenerateDocumentModelSubgraphs()\n .catch((error: unknown) => {\n logger.error(\n \"Failed to regenerate document model subgraphs: @error\",\n error,\n );\n });\n });\n\n const packagesSubgraph = new PackagesSubgraph({\n relationalDb: undefined as never,\n analyticsStore: undefined as never,\n reactorClient: client,\n graphqlManager,\n syncManager: api.syncManager,\n path: graphqlManager.getBasePath(),\n authorizationService: graphqlManager.getAuthorizationService(),\n packageManagementService,\n http: graphqlManager.scopeForPackage(\"@powerhousedao/switchboard\"),\n attachments: api.attachmentClientProvider,\n });\n\n lateSubgraphs.push(\n graphqlManager\n .registerSubgraphInstance(packagesSubgraph, \"graphql\", false)\n .catch((error: unknown) => {\n logger.error(\"Failed to register packages subgraph: @error\", error);\n }),\n );\n }\n\n if (driveNodeView) {\n graphqlManager.setAdditionalContextFields({\n readModel: driveNodeView,\n });\n\n const authorizationService = graphqlManager.getAuthorizationService();\n const reactorDriveSubgraph = {\n name: \"reactor-drive\",\n path: graphqlManager.getBasePath(),\n resolvers: createReactorDriveResolvers({\n reactorClient: client,\n readModel: driveNodeView,\n hostCanRead: (documentId, address) =>\n authorizationService.canRead(\n documentId as CanonicalDocumentId,\n address,\n ),\n }),\n typeDefs: reactorDriveSubgraphTypeDefs,\n reactorClient: client,\n relationalDb: undefined as never,\n };\n\n lateSubgraphs.push(\n graphqlManager\n .registerSubgraphInstance(reactorDriveSubgraph, \"graphql\", false)\n .catch((error: unknown) => {\n logger.error(\n \"Failed to register reactor-drive subgraph: @error\",\n error,\n );\n }),\n );\n }\n\n void (async () => {\n await Promise.all(lateSubgraphs);\n try {\n await graphqlManager.updateRouter(true);\n } catch (error) {\n logger.error(\n \"Final router update before readiness failed: @error\",\n error,\n );\n }\n api.readiness.markReady();\n })();\n\n // Create default drive if provided\n if (options.drive) {\n if (!renown) {\n throw new Error(\"Cannot create default drive without Renown identity\");\n }\n\n const driveType = options.drive.documentType ?? \"powerhouse/document-drive\";\n if (driveType === \"powerhouse/reactor-drive\") {\n defaultDriveUrl = await addDefaultReactorDrive(\n client,\n options.drive,\n serverPort,\n );\n } else {\n defaultDriveUrl = await addDefaultDrive(\n client,\n options.drive,\n serverPort,\n );\n }\n }\n\n // add vite middleware after express app is initialized if applicable\n if (vite) {\n api.httpAdapter.mountRawMiddleware(vite.middlewares);\n }\n\n // Connect to remote drives AFTER packages are loaded\n if (remoteDrives.length > 0) {\n for (const remoteDriveUrl of remoteDrives) {\n let driveId: string | undefined;\n\n try {\n const { syncManager } = api;\n const parsed = parseDriveUrl(remoteDriveUrl);\n driveId = parsed.driveId;\n const remoteName = `remote-drive-${driveId}-${crypto.randomUUID()}`;\n await syncManager.add(remoteName, DriveCollectionId.forDrive(driveId), {\n type: \"gql\",\n parameters: { url: parsed.graphqlEndpoint },\n });\n logger.debug(\"Remote drive @remoteDriveUrl synced\", remoteDriveUrl);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"already exists\")\n ) {\n logger.debug(\n \"Remote drive already added: @remoteDriveUrl\",\n remoteDriveUrl,\n );\n driveId = remoteDriveUrl.split(\"/\").pop();\n } else {\n logger.error(\n \"Failed to connect to remote drive @remoteDriveUrl: @error\",\n remoteDriveUrl,\n error,\n );\n }\n } finally {\n // Construct local URL once in finally block\n if (!defaultDriveUrl && driveId) {\n const protocol = options.https ? \"https\" : \"http\";\n defaultDriveUrl = `${protocol}://localhost:${serverPort}/d/${driveId}`;\n }\n }\n }\n }\n\n return {\n defaultDriveUrl,\n api,\n reactor: client,\n attachmentService,\n attachmentReferenceProjection: api.attachmentReferenceProjection,\n // Read live: losing the singleton turns it unavailable.\n get workflowTriggers() {\n return workflows?.triggers;\n },\n workflowsEnabled,\n modelManifest: () => workerModels?.modelManifest() ?? [],\n privacy: privacy ? { erasure: privacy.erasure } : undefined,\n mcpEnabled: options.mcp !== false,\n renown,\n port: serverPort,\n shutdown,\n };\n}\n\n/**\n * Boot the switchboard HTTP/GraphQL/MCP stack on top of a reactor.\n *\n * If `options.reactor` is provided, the switchboard reuses it instead of\n * building its own — the caller then owns the reactor's lifecycle and is\n * responsible for invoking `SwitchboardReactor.shutdown()` from their own\n * teardown / SIGINT path. The switchboard will not reach into the caller's\n * reactor; killing the reactor alone leaves the api/GraphQL/MCP resources\n * dangling until the process exits.\n *\n * When `options.reactor` is omitted, the switchboard builds and owns the\n * reactor. `shutdown()` on the returned handle only drains the api (HTTP\n * server, GraphQL, MCP, attachments); the reactor itself is torn down by\n * its own signal handlers (`withSignalHandlers`), which call `kill()` and\n * trigger the `withShutdownHook` chain that disposes the api. Programmatic\n * full teardown isn't currently exposed — wire it via SIGINT/SIGTERM.\n */\nexport const startSwitchboard = async (\n options: StartServerOptions = {},\n): Promise<SwitchboardReactor> => {\n const requestedPort = options.port ?? DEFAULT_PORT;\n const logger = options.logger ?? defaultLogger;\n // Here as well as in index.mts, so a host embedding the server is profiled.\n if (process.env.PYROSCOPE_SERVER_ADDRESS) {\n await initProfilerFromEnv(process.env).catch((error: unknown) =>\n logger.error(\"Error starting profiler: @error\", error),\n );\n }\n const serverPort = await resolveServerPort(\n requestedPort,\n options.strictPort ?? false,\n logger,\n );\n\n // Initialize feature flags\n const featureFlags = await initFeatureFlags();\n\n const enableDocumentModelSubgraphs = await featureFlags.getBooleanValue(\n DOCUMENT_MODEL_SUBGRAPHS_ENABLED,\n options.enableDocumentModelSubgraphs ??\n DOCUMENT_MODEL_SUBGRAPHS_ENABLED_DEFAULT,\n );\n\n options.enableDocumentModelSubgraphs = enableDocumentModelSubgraphs;\n\n options.mcp = await resolveMcpEnabled({ featureFlags, option: options.mcp });\n\n const configPathForFlags = resolveConfigPath(options.configFile);\n const workflowsEnabled = await resolveWorkflowsEnabled({\n featureFlags,\n override: options.workflows?.enabled,\n configEnabled: readConfigWorkflowsEnabled(configPathForFlags),\n });\n options.workflows = { enabled: workflowsEnabled };\n // This switchboard's own identity authenticates against the same Renown\n // instance it verifies incoming credentials against, unless told otherwise.\n const renownConfig = resolveRenownConfig(\n getConfig(resolveConfigPath(options.configFile)).auth?.renown,\n process.env,\n logger,\n );\n options.identity = {\n ...options.identity,\n baseUrl: options.identity?.baseUrl ?? renownConfig.url,\n };\n\n logger.info(\n \"Feature flags: @flags\",\n JSON.stringify(\n {\n DOCUMENT_MODEL_SUBGRAPHS_ENABLED: enableDocumentModelSubgraphs,\n MCP_ENABLED: options.mcp,\n PH_WORKFLOWS_ENABLED: workflowsEnabled,\n },\n null,\n 2,\n ),\n );\n\n // Initialize Renown if identity options are provided or keypair exists\n let renown: IRenown | null = null;\n try {\n renown = await initRenown(options.identity);\n } catch (e) {\n logger.warn(\"Failed to initialize ConnectCrypto: @error\", e);\n if (options.identity.requireExisting) {\n throw new Error(\n 'Identity required but failed to initialize. Run \"ph login\" first.',\n { cause: e },\n );\n }\n }\n\n try {\n return await initServer(serverPort, options, renown, renownConfig);\n } catch (e) {\n Sentry.captureException(e);\n if (StoredDocumentsRefusedError.isError(e)) {\n logger.error(e.message);\n } else {\n logger.error(\"App crashed: @error\", e);\n }\n throw e;\n }\n};\n\nexport {\n applySwitchboardReactorDefaults,\n type SwitchboardReactorDefaultsOptions,\n} from \"./builder-defaults.mjs\";\nexport * from \"./types.js\";\nexport {\n StoredDocumentsRefusedError,\n resolveUnsupportedStoredDocuments,\n} from \"./unsupported-stored-documents.mjs\";\n\nif (import.meta.main) {\n await startSwitchboard({ fatalErrorShutdown: true });\n}\n"],"names":["fs","logger","logger","fs","fs","path"],"mappings":"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","debug_id":"e3d937b6-2957-5fe0-8fc9-bf4fd0c18c93"}
|