Files
suaveplan/packages/api-stack/api/api-sandbox-vm/README.md
2026-05-27 23:29:53 +01:00

6.0 KiB

@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 — This module owns the stable SAND_VM.* error-code registry used by every public failure path inside the `@suaveplan/ap…
    • error-codes — This module owns the stable SAND_VM.* error-code registry used by every public failure path inside the `@suaveplan/ap…
  • frozen-globals — This module assembles the curated global allowlist visible inside the vm.Context sandbox. Every binding installed her…
    • frozen-globals — This module assembles the curated global allowlist visible inside the vm.Context sandbox. Every binding installed her…
  • otel — This module owns the OpenTelemetry instrument bundle emitted by the vm.Context sandbox host. Instruments are bound la…
    • instruments — This module owns the OpenTelemetry instrument bundle emitted by the vm.Context sandbox host. Instruments are bound la…
  • permissions — createPermissionChecker compiles a PermissionsManifest (from @suaveplan/api-types) into a structural `PermissionC…
    • checker — createPermissionChecker compiles a PermissionsManifest (from @suaveplan/api-types) into a structural `PermissionC…
  • fetch-proxy — installFetchProxy returns the outbound-HTTP surface visible inside the sandbox. Every request gates through the suppl…
    • fetch-proxy — installFetchProxy returns the outbound-HTTP surface visible inside the sandbox. Every request gates through the suppl…
  • fs-proxy — installFsProxy returns the sandbox-side filesystem surface that plugins consume. Every method gates the call through…
    • fs-proxy — installFsProxy returns the sandbox-side filesystem surface that plugins consume. Every method gates the call through…
  • install-capability-proxies — installCapabilityProxies wires the host-supplied capability bags into the sandbox's vm.Context. The sandbox sees on…
    • install-capability-proxies — installCapabilityProxies wires the host-supplied capability bags into the sandbox's vm.Context. The sandbox sees on…
  • sandbox — createVmSandbox is the keystone entry point of the package. It materialises a vm.Context whose globalThis is seal…
    • sandbox — createVmSandbox is the keystone entry point of the package. It materialises a vm.Context whose globalThis is seal…
  • testing — runVmSandboxConformance(label, makeSandbox) is the public conformance suite every vm.Context-based sandbox host mus…
    • run-vm-conformance — 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

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:

import { runVmSandboxConformance } from "@suaveplan/api-sandbox-vm/testing";

runVmSandboxConformance("my-host", makeMyHandle);