4.1 KiB
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 anyPortLike) — 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)
peerCertificatecross 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
runAuthSessionConformanceharness as the direct surface.
Usage: Basic
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
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.