# @suaveplan/api-sandbox-vm - vm.Context-based sandbox host for Suaveplan API plugins running in the sandboxed tier. Builds a frozen-globals vm.Context, loads the plugin via vm.SourceTextModule, rejects dynamic-code constructors and node built-in imports, and routes every fs/fetch/capability call through a host-installed permission-gated proxy surface. Server-tier. `vm.Context`-based sandbox host for Suaveplan API plugins running in the sandboxed tier. The package owns the `vm.Context`+frozen-globals half of the design doc §2.2 sandboxed tier; the worker-thread bulkhead is owned by `@suaveplan/api-worker-host` and the permission policy is owned by `@suaveplan/api-sandbox-permissions`. ## Documentation tree - [error-codes](./src/error-codes/error-codes.md) — This module owns the stable `SAND_VM.*` error-code registry used by every public failure path inside the `@suaveplan/ap… - [error-codes](./src/error-codes/error-codes.md) — This module owns the stable `SAND_VM.*` error-code registry used by every public failure path inside the `@suaveplan/ap… - [frozen-globals](./src/globals/frozen-globals/frozen-globals.md) — This module assembles the curated global allowlist visible inside the `vm.Context` sandbox. Every binding installed her… - [frozen-globals](./src/globals/frozen-globals/frozen-globals.md) — This module assembles the curated global allowlist visible inside the `vm.Context` sandbox. Every binding installed her… - [otel](./src/otel/instruments.md) — This module owns the OpenTelemetry instrument bundle emitted by the `vm.Context` sandbox host. Instruments are bound la… - [instruments](./src/otel/instruments.md) — This module owns the OpenTelemetry instrument bundle emitted by the `vm.Context` sandbox host. Instruments are bound la… - [permissions](./src/permissions/checker.md) — `createPermissionChecker` compiles a `PermissionsManifest` (from `@suaveplan/api-types`) into a structural `PermissionC… - [checker](./src/permissions/checker.md) — `createPermissionChecker` compiles a `PermissionsManifest` (from `@suaveplan/api-types`) into a structural `PermissionC… - [fetch-proxy](./src/proxies/fetch-proxy/fetch-proxy.md) — `installFetchProxy` returns the outbound-HTTP surface visible inside the sandbox. Every request gates through the suppl… - [fetch-proxy](./src/proxies/fetch-proxy/fetch-proxy.md) — `installFetchProxy` returns the outbound-HTTP surface visible inside the sandbox. Every request gates through the suppl… - [fs-proxy](./src/proxies/fs-proxy/fs-proxy.md) — `installFsProxy` returns the sandbox-side filesystem surface that plugins consume. Every method gates the call through… - [fs-proxy](./src/proxies/fs-proxy/fs-proxy.md) — `installFsProxy` returns the sandbox-side filesystem surface that plugins consume. Every method gates the call through… - [install-capability-proxies](./src/proxies/install-capability-proxies/install-capability-proxies.md) — `installCapabilityProxies` wires the host-supplied capability bags into the sandbox's `vm.Context`. The sandbox sees on… - [install-capability-proxies](./src/proxies/install-capability-proxies/install-capability-proxies.md) — `installCapabilityProxies` wires the host-supplied capability bags into the sandbox's `vm.Context`. The sandbox sees on… - [sandbox](./src/sandbox/sandbox.md) — `createVmSandbox` is the keystone entry point of the package. It materialises a `vm.Context` whose `globalThis` is seal… - [sandbox](./src/sandbox/sandbox.md) — `createVmSandbox` is the keystone entry point of the package. It materialises a `vm.Context` whose `globalThis` is seal… - [testing](./src/testing/run-vm-conformance.md) — `runVmSandboxConformance(label, makeSandbox)` is the public conformance suite every `vm.Context`-based sandbox host mus… - [run-vm-conformance](./src/testing/run-vm-conformance.md) — `runVmSandboxConformance(label, makeSandbox)` is the public conformance suite every `vm.Context`-based sandbox host mus… ## Modules | Subpath | Purpose | |---|---| | `./sandbox/sandbox` | `createVmSandbox`, `VmSandboxHandle` — builds the frozen `vm.Context` and drives the plugin lifecycle. | | `./globals/frozen-globals/frozen-globals` | Curated allowlist of globals visible inside the sandbox. | | `./proxies/fs-proxy/fs-proxy` | Host-proxied filesystem with permission-gated `readFile`/`writeFile`. | | `./proxies/fetch-proxy/fetch-proxy` | Host-proxied outbound HTTP with permission gating. | | `./proxies/install-capability-proxies/install-capability-proxies` | Wires the consolidated `__suaveplan` receiver into the context. | | `./permissions/checker` | In-package default `PermissionChecker` built from a `PermissionsManifest`. | | `./error-codes/error-codes` | `SAND_VM.*` registry + `SandboxVmError`. | | `./otel/instruments` | OTel histogram + three counters (boot duration, eval rejection, module-import rejection, permission denial). | | `./testing` | `runVmSandboxConformance(label, makeSandbox)` — the conformance harness every vm sandbox host must pass. | ## Quick start ```ts import { createVmSandbox, createPermissionChecker, } from "@suaveplan/api-sandbox-vm"; const checker = createPermissionChecker({ policy: { fs: ["/srv/data/**"], fetch: ["api.example.com"] }, }); const handle = createVmSandbox({ pluginId: "demo", logger, checker, pluginModule: { kind: "path", path: "/srv/plugins/demo.mjs" }, proxies: { fs: realFsAdapter, fetch: realFetchAdapter, capabilities: {}, }, }); await handle.start(); // ... later handle.dispose(); ``` ## Runtime requirement Node ≥ 20 with `--experimental-vm-modules` enabled at process start. `vm.SourceTextModule` is gated behind that flag; the sandbox raises `SAND_VM.SOURCE_TEXT_MODULE_FAILED` with a clear hint when the flag is missing. ## Conformance Pass the package's testing harness against any custom `vm`-based sandbox host: ```ts import { runVmSandboxConformance } from "@suaveplan/api-sandbox-vm/testing"; runVmSandboxConformance("my-host", makeMyHandle); ```