# @suaveplan/api-db-worker-proxy - Worker-side proxy for the api `db` capability. Marshals AdminDatabaseLike / TenantScopedDatabaseLike calls — including transactions and cursor streams — over @suaveplan/api-worker-rpc MessagePort frames. Tenant scoping is enforced main-side; this package is the transport. Worker-side proxy for the api `db` capability. Marshals `AdminDatabaseLike` and `TenantScopedDatabaseLike` calls — including transactions and cursor streams — over `@suaveplan/api-worker-rpc` `MessagePort` frames. The main side mounts a real DB adapter and exposes it over a port; the worker side imports a proxy whose API surface is identical to a direct adapter. Worker plugin code never touches a real connection and never sees plaintext credentials. **Tenant scoping is enforced on the main side**, NOT in this package. `wrapTenantScoped(adminProxy, tenantId)` is purely a type-level facade that prevents an admin reference from being passed where a tenant reference is required by the type system. ## Documentation tree - [error-codes](./src/error-codes/error-codes.md) — This module owns the `DB_PROXY.*` stable error-code registry for `@suaveplan/api-db-worker-proxy`. Every failure that c… - [error-codes](./src/error-codes/error-codes.md) — This module owns the `DB_PROXY.*` stable error-code registry for `@suaveplan/api-db-worker-proxy`. Every failure that c… - [main-side](./src/main-side/main-side.md) — This module exposes `createDbProxyServer(port, db)` — the trusted-side endpoint of the worker DB proxy. It mounts an `A… - [main-side](./src/main-side/main-side.md) — This module exposes `createDbProxyServer(port, db)` — the trusted-side endpoint of the worker DB proxy. It mounts an `A… - [tenant-scoped](./src/tenant-scoped/tenant-scoped.md) — This module exposes `wrapTenantScoped(adminProxy, tenantId)` — a worker-side type-level facade that re-presents an admi… - [tenant-scoped](./src/tenant-scoped/tenant-scoped.md) — This module exposes `wrapTenantScoped(adminProxy, tenantId)` — a worker-side type-level facade that re-presents an admi… - [testing](./src/testing/testing.md) — This module provides the in-memory test harness for `@suaveplan/api-db-worker-proxy`. It exposes `createInMemoryAdminDb… - [testing](./src/testing/testing.md) — This module provides the in-memory test harness for `@suaveplan/api-db-worker-proxy`. It exposes `createInMemoryAdminDb… - [worker-side](./src/worker-side/worker-side.md) — This module exposes `createDbProxyClient(port, { engine })` — the untrusted-side endpoint of the worker DB proxy. It re… - [worker-side](./src/worker-side/worker-side.md) — This module exposes `createDbProxyClient(port, { engine })` — the untrusted-side endpoint of the worker DB proxy. It re… ## Why Per the `api-plugin` foundation design (§5.1, §5.2a, §6) every capability ships in two surfaces: a direct in-process binding for trusted plugins and a `MessagePort`-marshalled binding for isolated / sandboxed plugins. The `db` capability is special because 1. its operations return result sets that can be huge (favouring streaming over buffering), 2. its operations are transactional (transaction handles MUST be bound to a single connection lifetime), and 3. tenant-scoping is a security boundary the worker MUST NOT be able to circumvent. This package is Stage-3 worker-isolation infrastructure: every Stage-4 DB adapter (`api-db-adapter`, `api-db-sqlite-memory`, …) MUST pass conformance under BOTH the direct surface AND this proxy surface before can be declared complete. ## Install ```bash bun add @suaveplan/api-db-worker-proxy ``` ## Quick start ```ts // main side import { createDbProxyServer } from "@suaveplan/api-db-worker-proxy/main-side"; import { MessageChannel, Worker } from "node:worker_threads"; const channel = new MessageChannel(); createDbProxyServer(channel.port1, realAdminAdapter); const worker = new Worker("./plugin-worker.js"); worker.postMessage({ port: channel.port2, engine: "sqlite" }, [channel.port2]); ``` ```ts // worker side import { parentPort } from "node:worker_threads"; import { createDbProxyClient } from "@suaveplan/api-db-worker-proxy/worker-side"; import { wrapTenantScoped } from "@suaveplan/api-db-worker-proxy/tenant-scoped"; import type { TenantId } from "@suaveplan/api-types/tenant"; parentPort?.once("message", (boot) => { const admin = createDbProxyClient(boot.port, { engine: boot.engine }); // request scope const tenantScoped = wrapTenantScoped(admin, "acme" as TenantId); void runHandler(tenantScoped); }); ``` ## Surface - `./main-side` — `createDbProxyServer(port, db)`: serves an `AdminDatabaseLike` over `@suaveplan/api-worker-rpc`. Returns a `DbProxyServer` with `close()`, `activeTransactionCount()`, `activePreparedCount()`. - `./worker-side` — `createDbProxyClient(port, { engine })`: returns an `AdminDatabaseLike`-shaped object plus `cursor(sql, params)` for streaming row iteration and `dispose()` for abrupt teardown. - `./tenant-scoped` — `wrapTenantScoped(adminProxy, tenantId)`: type-level facade. Tenant scoping is enforced main-side. - `./error-codes` — `DB_PROXY.*` codes: `UNSAFE_NOT_PERMITTED`, `TRANSACTION_LOST`, `STREAM_INTERRUPTED`, `PROXY_DISPOSED`. - `./testing` — `createInMemoryAdminDb` + `createDbProxyHarness` for downstream conformance suites. ## Co-located docs - [`src/main-side/main-side.md`](./src/main-side/main-side.md) - [`src/worker-side/worker-side.md`](./src/worker-side/worker-side.md) - [`src/tenant-scoped/tenant-scoped.md`](./src/tenant-scoped/tenant-scoped.md) - [`src/error-codes/error-codes.md`](./src/error-codes/error-codes.md) - [`src/testing/testing.md`](./src/testing/testing.md)