Files
suaveplan/openspec/changes/enterprise-server-sandbox/proposal.md
2026-05-27 23:29:53 +01:00

3.5 KiB

OpenSpec Change: enterprise-server-sandbox

  • Change ID: enterprise-server-sandbox
  • Package: @suaveplan/sandbox
  • Category: server/infra
  • Target path: server/infra/sandbox
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/sandbox.md

Summary

Deliver @suaveplan/sandbox on the genesis branch from scratch at enterprise quality. The package provides an isolated code execution sandbox for running untrusted or user-supplied JavaScript/TypeScript snippets. It supports two backends: a WorkerSandbox using Bun's Worker API (in-process isolation) and a ProcessSandbox using Bun.spawn for stronger OS-level isolation. Both expose run(code, context, options): Promise<SandboxResult> with configurable resource limits (CPU timeout, memory limit, I/O restrictions), structured output capture, and abort signal support. Genesis adds Zod-validated options, @suaveplan/error codes, OTel spans, and comprehensive security-oriented tests.


Current-state audit (genesis)

Does not exist on genesis — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) Typed SANDBOX.* error codes
@suaveplan/types (workspace:^) LoggerLike
@suaveplan/logger (workspace:^) Structured logs
zod (catalog:forms) Options validation
@opentelemetry/api (catalog:observability) Spans + counters + histograms
tslib (catalog:build) Runtime helpers

Public API surface

interface Sandbox {
  run(code: string, context?: SandboxContext, options?: RunOptions): Promise<SandboxResult>
  dispose(): Promise<void>
}

function createWorkerSandbox(options?: SandboxOptions): Sandbox
function createProcessSandbox(options?: SandboxOptions): Sandbox

interface SandboxOptions {
  timeoutMs?: number        // default: 5000
  memoryLimitMb?: number    // default: 128
  allowNetwork?: boolean    // default: false
  allowFs?: boolean         // default: false
  logger?: LoggerLike
}

type SandboxContext = Record<string, unknown>   // serializable globals injected into sandbox

interface RunOptions {
  timeoutMs?: number        // override per-run
  signal?: AbortSignal
}

interface SandboxResult {
  ok: boolean
  value?: unknown           // returned value (JSON-serializable)
  stdout: string
  stderr: string
  duration: number
  exitCode?: number
  error?: string
}

type SandboxErrorCode =
  | 'SANDBOX_TIMEOUT'
  | 'SANDBOX_MEMORY_EXCEEDED'
  | 'SANDBOX_EXECUTION_ERROR'
  | 'SANDBOX_INVALID_CODE'
  | 'SANDBOX_DISPOSED'

function registerSandboxErrorCodes(): void

File layout

server/infra/sandbox/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/
      sandbox.types.ts
      sandbox.types.md
    worker/
      worker-sandbox/
        worker-sandbox.ts
        worker-sandbox.md
        worker-sandbox.types.ts
        worker-sandbox.test.ts
        index.ts
      sandbox-worker-script.ts   # Worker inner script
    process/
      process-sandbox/
        process-sandbox.ts
        process-sandbox.md
        process-sandbox.types.ts
        process-sandbox.test.ts
        index.ts
  tests/
    security.test.ts
    integration.test.ts

Impact

Downstream: user-defined rule engines, server-side template execution, AI tool calls. Server-only, Bun runtime required.

Prerequisites on Verdaccio: @suaveplan/error, @suaveplan/types, @suaveplan/logger.