Files
skills/docs/api-stack/api/api-auth-bearer-static/src/proxy/create-bearer-static-resolver-worker-proxy.md
2026-05-29 08:20:56 +01:00

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 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

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.