Files
2026-05-27 23:29:53 +01:00

57 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `@suaveplan/api-router-worker-bridge` — request/response marshalling across the worker boundary
**Package**: `@suaveplan/api-router-worker-bridge`
**Status**: PROPOSED
**Type**: new-package
**Stage**: 3 (Worker isolation infrastructure)
## Why
Per design doc §6: when a plugin runs in `isolated` or `sandboxed` tier, the per-request pipeline executes steps 1–7 in the main process (intake, route match, auth, tenant, rate-limit, RBAC, validation, capability scoping), then marshals the result across `MessagePort` to the worker for step 8 (the user-code handler). Critically: the raw `Request` object is NEVER marshalled. Only the already-validated `body` / `params` / `query` cross the boundary. This is what lets worker-tier plugins gain isolation without losing any of the host-enforced security guarantees.
`api-router-worker-bridge` is the pair of half-bridges (main-side + worker-side) that implement that envelope. It sits between the main-process router (`api-router-hono` in Stage 4) and the worker-side request dispatcher (in `api-worker-host`).
## What changes
Net-new package. Two halves:
**ADDED exports:**
- `./main-side` — `BridgeMain`: accepts a route hit from `api-host`'s pipeline; constructs an `HttpDispatchEnvelope` (validated body, params, query, principal, tenantId, requestId, trace context); sends across the worker port via `api-worker-rpc`; awaits the response envelope; rehydrates the response into a `Response` object for the main-side response-validation + compose steps.
- `./worker-side` — `BridgeWorker`: receives `HttpDispatchEnvelope` on the worker port; reconstructs a `RequestContext` (capability proxies already wired by `api-worker-host`); invokes the route handler; serialises the return value as an `HttpResponseEnvelope`; supports streaming via `Transferable` `ReadableStream`.
- `./envelope-schemas` — Zod schemas for `HttpDispatchEnvelope` and `HttpResponseEnvelope`. Validated on both sides.
- `./streaming-marshal` — request-body streams AND response-body streams via `Transferable`; back-pressure propagated.
- `./header-allowlist` — only headers in the bridge's allowlist (or matching the configurable per-route allowlist) cross the boundary; the rest are dropped on the main side so worker code cannot read `Authorization` / `Cookie` etc.
- `./error-codes` — `BRIDGE_*` codes (`BRIDGE_ENVELOPE_INVALID`, `BRIDGE_HANDLER_THROW`, `BRIDGE_HEADER_NOT_ALLOWED`, `BRIDGE_RESPONSE_NOT_SERIALISABLE`, `BRIDGE_REQUEST_BODY_STREAM_CLOSED`).
## Out of scope
- **Marshalling the raw `Request`** — explicitly forbidden by §6. The bridge marshals only the validated parts.
- **Routing itself** — `api-router` / `api-router-hono` own URL matching. This package picks up after step 2.
- **Capability proxies** — `api-db-worker-proxy` etc. live in their own packages. This package only marshals the request envelope.
- **Auth / RBAC** — those run in the main process before the bridge. The bridge merely receives the resolved `principal` and `tenantId`.
## Source mapping
Genesis-original. Design doc sections: §6 (per-request data flow, especially the "isolated / sandboxed" paragraph), §5.2a.
| Requirement | Design doc section |
|---|---|
| Raw `Request` NEVER marshalled — only validated body/params/query | §6 |
| Steps 1–7 + 9–11 always main-process | §6 |
| Streams transferred via `Transferable` | §6, §5.2a |
| Auth / RBAC / tenant resolution never run inside worker | §6 |
## Verification approach
- `src/main-side/main-side.test.ts` — given a fake `RouteMatch` + validated body, envelope is sent across paired `MessageChannel`; response rehydrates.
- `src/worker-side/worker-side.test.ts` — given an envelope, handler is invoked with reconstructed `ctx.request`; response envelope serialises.
- `src/envelope-schemas/envelope-schemas.test.ts` — Zod schemas reject malformed envelopes; valid envelopes round-trip.
- `src/streaming-marshal/streaming-marshal.test.ts` — large request body stream + large response body stream both transfer; back-pressure ack works.
- `src/header-allowlist/header-allowlist.spec-conformance.test.ts` — `Authorization`, `Cookie`, `Set-Cookie` MUST NOT cross by default; configurable allowlist works.
- `tests/no-raw-request.test.ts` — pin the invariant: the wire envelope never contains a `Request` object reference; the raw header bag is filtered.
## Migration impact
None — additive only. `api-host` will wire this in Stage 4 once `api-router-hono` lands.