# Worker-proxy factory — `@suaveplan/api-auth-bearer-static` _(no submodules)_ ## Purpose The worker-proxy surface is the second of the two Stage-4 surfaces this adapter ships (`§5.2a`). It lets the host install the bearer-static resolver in the main process while the actual token table lives inside an isolated worker. The host's `SessionResolverChain` calls `resolve(request)` exactly as for the direct surface; under the hood, the proxy marshals the request across a `MessagePort` using `@suaveplan/api-worker-rpc`, the worker's resolver runs, and the `Principal | null` decision is returned to the host. The proxy intentionally keeps the token table out of the host's address space — useful when the host is sandboxed and the worker holds privileged credentials, or when operators want a single auth daemon serving multiple host workers. ## Features - Bidirectional RPC over a `MessagePort` (or any `PortLike`) — no process / address-space assumptions. - Idempotent `dispose()` — coalesces multiple calls and closes the underlying RPC client exactly once. - Header set, request method, URL, and (optional) `peerCertificate` cross the boundary; opaque body and abort signal are out of scope (resolution does not need them). - Server-side mount via `serveBearerStaticResolverOnPort` — owns the real resolver and the token table. - Passes the same `runAuthSessionConformance` harness as the direct surface. ## Usage: Basic ```ts import { createBearerStaticResolverWorkerProxy, serveBearerStaticResolverOnPort, } from "@suaveplan/api-auth-bearer-static/proxy"; import { createInProcessPortPair } from "@suaveplan/api-worker-rpc"; const { clientPort, serverPort } = createInProcessPortPair(); // Worker side — owns the real table. const server = serveBearerStaticResolverOnPort(serverPort, { tokens: [ { token: "T1", principal: { id: "svc", scopes: [], claims: {} }, }, ], }); // Host side — sees only a proxy. const proxy = createBearerStaticResolverWorkerProxy(clientPort); const request = new Request("https://api/x", { headers: { Authorization: "Bearer T1" }, }); const principal = await proxy.resolve(request); await proxy.dispose(); server.close("done"); ``` ## Usage: Advanced ```ts import { Worker, MessageChannel } from "node:worker_threads"; import { createBearerStaticResolverWorkerProxy, } from "@suaveplan/api-auth-bearer-static/proxy"; // Real Node worker boundary. The worker script imports // `serveBearerStaticResolverOnPort` and calls it against its own port. const { port1, port2 } = new MessageChannel(); const worker = new Worker("./auth-worker.mjs", { transferList: [port2] }); const resolver = createBearerStaticResolverWorkerProxy(port1 as never); // Place in chain alongside direct adapters — host treats both uniformly. import { SessionResolverChain } from "@suaveplan/api-auth"; const chain = new SessionResolverChain([resolver]); ``` ## API Reference - `serveBearerStaticResolverOnPort(port: PortLike, options: BearerStaticResolverOptions): { close(reason?: string): void }` — mount the server-side handlers. - `createBearerStaticResolverWorkerProxy(port: PortLike): SessionResolver` — host-side proxy. ## Implementation Notes The marshalled envelope is `{url, method, headers, peerCertificate?}` — the minimal subset of `Request` that the bearer-static decision actually needs. `peerCertificate` is structured-clone-friendly because the host attaches whatever shape the listener surfaced; tests pass plain objects. The proxy never sends the request body, because token resolution does not consult it. Errors thrown by the server-side resolver (e.g. an unexpected runtime fault) propagate as the standard `WORKER_RPC` error envelope and surface in the host as a thrown `WorkerRpcError` — the host's resolver chain treats this as a fail-closed `RESOLVER_THREW` per the documented `api-auth` chain semantics. `dispose()` calls the remote `dispose` first to drain the server-side state, then closes the local client so the underlying port can be garbage-collected.