Skip to main content
Glama
README.md13.3 kB
# @prodisco/sandbox-server A gRPC-based sandbox server for secure TypeScript/JavaScript code execution with Kubernetes and Prometheus context. Designed to decouple code execution from MCP servers, enabling flexible deployment options and improved isolation. ## Table of Contents - [Features](#features) - [Installation](#installation) - [Quick Start](#quick-start) - [Execution Modes](#execution-modes) - [Synchronous Execution](#synchronous-execution) - [Streaming Execution](#streaming-execution) - [Async Execution with Polling](#async-execution-with-polling) - [Test Execution](#test-execution) - [Sandbox Environment](#sandbox-environment) - [Allowed Modules](#allowed-modules) - [Available Globals](#available-globals) - [Custom Module Configuration](#custom-module-configuration) - [Configuration](#configuration) - [Environment Variables](#environment-variables) - [Transport Security](#transport-security) - [API Reference](#api-reference) - [SandboxClient](#sandboxclient) - [Singleton Pattern](#singleton-pattern) - [Docker & Kubernetes Deployment](#docker--kubernetes-deployment) - [Development](#development) - [Protocol](#protocol) - [Architecture](#architecture) - [License](#license) ## Features - **Secure Sandboxed Execution** - Run untrusted code in Node.js VM with restricted module access - **TypeScript Support** - Execute TypeScript directly with esbuild transformation - **Multiple Execution Modes** - Synchronous, streaming, async, and test execution - **Unit Testing** - Built-in test runner with structured results using uvu assertions - **Kubernetes Integration** - Pre-configured access to `@kubernetes/client-node` - **Prometheus/Loki Support** - Query metrics and logs from within scripts - **Script Caching** - Automatic caching with content-based deduplication - **Flexible Transport** - Unix socket (default) or TCP with optional TLS/mTLS - **Container Ready** - Dockerfile and Kubernetes manifests included ## Installation ```bash npm install @prodisco/sandbox-server ``` ## Quick Start ### Starting the Server ```bash # Using the CLI npx sandbox-server # Or programmatically import { startServer } from '@prodisco/sandbox-server/server'; const server = await startServer({ socketPath: '/tmp/prodisco-sandbox.sock', cacheDir: '/tmp/prodisco-scripts', }); ``` ### Using the Client ```typescript import { SandboxClient } from '@prodisco/sandbox-server/client'; const client = new SandboxClient(); // Simple execution const result = await client.execute({ code: ` const k8s = require('@kubernetes/client-node'); console.log('Kubernetes client loaded:', typeof k8s.CoreV1Api); `, timeoutMs: 30000, }); console.log(result.success); // true console.log(result.output); // "Kubernetes client loaded: function" ``` ## Execution Modes | Mode | Method | Use Case | |------|--------|----------| | **Synchronous** | `execute()` | Simple scripts, short execution time | | **Streaming** | `executeStream()` | Real-time output, long-running scripts | | **Async** | `executeAsync()` | Background execution with polling | | **Test** | `executeTest()` | Unit testing with structured results | ### Synchronous Execution ```typescript const result = await client.execute({ code: 'console.log("Hello, World!");', timeoutMs: 30000, }); // Result: // { // success: true, // output: "Hello, World!", // executionTimeMs: 45 // } ``` ### Streaming Execution ```typescript for await (const chunk of client.executeStream({ code: longRunningScript })) { if (chunk.type === 'output') { process.stdout.write(chunk.data); } else if (chunk.type === 'error') { process.stderr.write(chunk.data); } else if (chunk.type === 'result') { console.log('Finished:', chunk.data.success); } } ``` ### Async Execution with Polling ```typescript // Start execution const { executionId } = await client.executeAsync({ code: ` for (let i = 0; i < 10; i++) { console.log("Processing:", i); await new Promise(r => setTimeout(r, 100)); } `, }); // Poll for status const status = await client.getExecution(executionId, { wait: true }); console.log(status.state); // 'completed' console.log(status.output); // Processing output // Or cancel if needed await client.cancelExecution(executionId); ``` ### Test Execution Run unit tests with structured results using the built-in uvu test framework: ```typescript const result = await client.executeTest({ code: ` function fibonacci(n: number): number[] { if (n <= 0) return []; if (n === 1) return [0]; const seq = [0, 1]; for (let i = 2; i < n; i++) { seq.push(seq[i-1] + seq[i-2]); } return seq; } `, tests: ` test("fibonacci(0) returns empty array", () => { assert.equal(fibonacci(0), []); }); test("fibonacci(5) returns correct sequence", () => { assert.equal(fibonacci(5), [0, 1, 1, 2, 3]); }); test("each number is sum of previous two", () => { const seq = fibonacci(10); for (let i = 2; i < seq.length; i++) { assert.is(seq[i], seq[i-1] + seq[i-2]); } }); `, }); // Result: // { // success: true, // summary: { total: 3, passed: 3, failed: 0, skipped: 0 }, // tests: [ // { name: 'fibonacci(0) returns empty array', passed: true, durationMs: 0 }, // { name: 'fibonacci(5) returns correct sequence', passed: true, durationMs: 0 }, // { name: 'each number is sum of previous two', passed: true, durationMs: 1 } // ], // executionTimeMs: 45 // } ``` **Important:** In test mode, `test()` and `assert` are pre-injected globals. Do NOT import them. **Available Assertions:** | Assertion | Description | |-----------|-------------| | `assert.is(a, b)` | Strict equality (`===`) | | `assert.equal(a, b)` | Deep equality for objects/arrays | | `assert.ok(val)` | Truthy check | | `assert.not(val)` | Falsy check | | `assert.throws(fn)` | Expects function to throw | ## Sandbox Environment ### Allowed Modules By default, the sandbox allows these modules: - `@kubernetes/client-node` - Kubernetes API client - `@prodisco/prometheus-client` - Prometheus metrics queries - `@prodisco/loki-client` - Loki log queries - `simple-statistics` - Statistical analysis functions - `uvu` - Test framework (always available for test mode) ### Available Globals ```typescript // Console methods console.log(), console.error(), console.warn(), console.info() // Timers setTimeout, setInterval, clearTimeout, clearInterval // Built-ins Promise, JSON, Buffer, Date, Math, Array, Object, String, Number, Boolean, Error // Environment process.env // Read-only access to environment variables ``` ### Custom Module Configuration Configure allowed modules via environment variable or config file: ```bash # Environment variable (JSON array or comma-separated) export SANDBOX_ALLOWED_MODULES='["@kubernetes/client-node","lodash"]' # Or via config file export PRODISCO_CONFIG_PATH=/path/to/config.yaml ``` Config file format: ```yaml libraries: - name: "@kubernetes/client-node" description: "Kubernetes API client" - name: "lodash" description: "Utility library" ``` ## Configuration ### Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `SANDBOX_SOCKET_PATH` | `/tmp/prodisco-sandbox.sock` | Unix socket path | | `SANDBOX_USE_TCP` | `false` | Use TCP instead of Unix socket | | `SANDBOX_TCP_HOST` | `0.0.0.0` / `localhost` | TCP host (server/client) | | `SANDBOX_TCP_PORT` | `50051` | TCP port | | `SCRIPTS_CACHE_DIR` | `/tmp/prodisco-scripts` | Cache directory | | `SANDBOX_ALLOWED_MODULES` | (see above) | Allowed require() modules | | `SANDBOX_MODULES_BASE_PATH` | `process.cwd()` | node_modules resolution base | ### Transport Security | Variable | Description | |----------|-------------| | `SANDBOX_TRANSPORT_MODE` | `insecure`, `tls`, or `mtls` | | `SANDBOX_TLS_CERT_PATH` | Server certificate path | | `SANDBOX_TLS_KEY_PATH` | Server private key path | | `SANDBOX_TLS_CA_PATH` | CA certificate path | | `SANDBOX_TLS_CLIENT_CERT_PATH` | Client cert (mTLS) | | `SANDBOX_TLS_CLIENT_KEY_PATH` | Client key (mTLS) | ## API Reference ### SandboxClient ```typescript class SandboxClient { constructor(options?: SandboxClientOptions); // Synchronous execution execute(options: ExecuteOptions): Promise<ExecuteResult>; // Streaming execution executeStream(options: ExecuteOptions): AsyncGenerator<StreamChunk>; executeStreamWithAbort(options: ExecuteOptions, signal?: AbortSignal): AsyncGenerator<StreamChunk>; // Async execution executeAsync(options: ExecuteOptions): Promise<{ executionId: string; state: ExecutionState }>; getExecution(id: string, options?: GetExecutionOptions): Promise<ExecutionStatus>; waitForExecution(id: string): Promise<ExecutionStatus>; cancelExecution(id: string): Promise<CancelResult>; listExecutions(options?: ListOptions): Promise<ExecutionSummary[]>; // Test execution executeTest(options: ExecuteTestOptions): Promise<TestExecutionResult>; // Cache management listCache(filter?: string): Promise<CacheEntry[]>; clearCache(): Promise<number>; // Health check healthCheck(): Promise<{ healthy: boolean; kubernetesContext: string }>; waitForHealthy(timeoutMs: number): Promise<boolean>; close(): void; } ``` ### Singleton Pattern ```typescript import { getSandboxClient, closeSandboxClient } from '@prodisco/sandbox-server/client'; // Get or create shared client instance const client = getSandboxClient({ socketPath: '/tmp/sandbox.sock' }); // Close when done closeSandboxClient(); ``` ## Docker & Kubernetes Deployment ### Building the Container ```bash docker build -f packages/sandbox-server/Dockerfile -t prodisco/sandbox-server:latest . ``` ### Kubernetes Deployment ```bash # Deploy to cluster kubectl apply -f packages/sandbox-server/k8s/deployment.yaml # Port forward for local access kubectl -n prodisco port-forward service/sandbox-server 50051:50051 ``` ### Connecting to Containerized Server ```typescript // Via port-forward const client = new SandboxClient({ useTcp: true, tcpHost: 'localhost', tcpPort: 50051, }); // Via in-cluster DNS const client = new SandboxClient({ useTcp: true, tcpHost: 'sandbox-server.prodisco.svc.cluster.local', tcpPort: 50051, }); ``` ## Development ### Building ```bash npm run build ``` ### Running Tests ```bash # All tests npm test # Watch mode npm run test:watch # Security tests npm run test:security # E2E tests (requires kind cluster) npm run test:e2e:setup npm run test:e2e npm run test:e2e:teardown ``` ### Regenerating Protobuf Types ```bash npm run proto:generate ``` ### Development Server ```bash npm run dev ``` ## Protocol The gRPC service is defined in `proto/sandbox.proto`. Key RPCs: ```protobuf service SandboxService { rpc Execute(ExecuteRequest) returns (ExecuteResponse); rpc ExecuteStream(ExecuteRequest) returns (stream ExecuteChunk); rpc ExecuteAsync(ExecuteRequest) returns (ExecuteAsyncResponse); rpc GetExecution(GetExecutionRequest) returns (GetExecutionResponse); rpc CancelExecution(CancelExecutionRequest) returns (CancelExecutionResponse); rpc ListExecutions(ListExecutionsRequest) returns (ListExecutionsResponse); rpc ExecuteTest(ExecuteTestRequest) returns (ExecuteTestResponse); rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse); rpc ListCache(ListCacheRequest) returns (ListCacheResponse); rpc ClearCache(ClearCacheRequest) returns (ClearCacheResponse); } ``` ## Architecture See [docs/grpc-sandbox-architecture.md](../../docs/grpc-sandbox-architecture.md) for detailed architecture documentation. ``` +---------------------------------------------------------------------+ | MCP Server | | +-------------------+ +--------------------------------------+ | | | searchTools | | runSandbox Tool | | | | (API discovery) | | (thin gRPC client wrapper) | | | +-------------------+ +------------------+-------------------+ | | | | +----------------------------------------------+-----------------------+ | gRPC over Unix Socket v +---------------------------------------------------------------------+ | Sandbox gRPC Server | | +---------------------------------------------------------------+ | | | SandboxService | | | | (Execute, ExecuteStream, ExecuteAsync, ExecuteTest, ...) | | | +---------------------------------------------------------------+ | | | | | +----------------+ +-------+--------+ +----------------------+ | | | Executor | | Execution | | CacheManager | | | | (VM + esbuild) | | Registry | | (dedup, persist) | | | +----------------+ +----------------+ +----------------------+ | +---------------------------------------------------------------------+ ``` ## License MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/harche/ProDisco'

If you have feedback or need assistance with the MCP directory API, please join our Discord server