gRPC sandbox server for code execution with Kubernetes context
npm install @prodisco/sandbox-serverA 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.
- Features
- Installation
- Quick Start
- Execution Modes
- Synchronous Execution
- Streaming Execution
- Async Execution with Polling
- Test Execution
- Sandbox Environment
- Allowed Modules
- Available Globals
- Custom Module Configuration
- Configuration
- Environment Variables
- Transport Security
- API Reference
- SandboxClient
- Singleton Pattern
- Docker & Kubernetes Deployment
- Development
- Protocol
- Architecture
- License
- 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
``bash`
npm install @prodisco/sandbox-server
`bashUsing the CLI
npx sandbox-server
const server = await startServer({
socketPath: '/tmp/prodisco-sandbox.sock',
cacheDir: '/tmp/prodisco-scripts',
});
`
`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"
`
| Mode | Method | Use Case |
|------|--------|----------|
| Synchronous | execute() | Simple scripts, short execution time |executeStream()
| Streaming | | Real-time output, long-running scripts |executeAsync()
| Async | | Background execution with polling |executeTest()
| Test | | Unit testing with structured results |
`typescript
const result = await client.execute({
code: 'console.log("Hello, World!");',
timeoutMs: 30000,
});
// Result:
// {
// success: true,
// output: "Hello, World!",
// executionTimeMs: 45
// }
`
`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);
}
}
`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);
`
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 |
By default, the sandbox allows these modules:
- @kubernetes/client-node - Kubernetes API client@prodisco/prometheus-client
- - Prometheus metrics queries@prodisco/loki-client
- - Loki log queriessimple-statistics
- - Statistical analysis functionsuvu
- - Test framework (always available for test mode)
`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
`
Configure allowed modules via environment variable or config file:
`bashEnvironment variable (JSON array or comma-separated)
export SANDBOX_ALLOWED_MODULES='["@kubernetes/client-node","lodash"]'
Config file format:
`yaml
libraries:
- name: "@kubernetes/client-node"
description: "Kubernetes API client"
- name: "lodash"
description: "Utility library"
`Configuration
$3
| 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 |$3
| 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
$3
`typescript
class SandboxClient {
constructor(options?: SandboxClientOptions); // Synchronous execution
execute(options: ExecuteOptions): Promise;
// Streaming execution
executeStream(options: ExecuteOptions): AsyncGenerator;
executeStreamWithAbort(options: ExecuteOptions, signal?: AbortSignal): AsyncGenerator;
// Async execution
executeAsync(options: ExecuteOptions): Promise<{ executionId: string; state: ExecutionState }>;
getExecution(id: string, options?: GetExecutionOptions): Promise;
waitForExecution(id: string): Promise;
cancelExecution(id: string): Promise;
listExecutions(options?: ListOptions): Promise;
// Test execution
executeTest(options: ExecuteTestOptions): Promise;
// Cache management
listCache(filter?: string): Promise;
clearCache(): Promise;
// Health check
healthCheck(): Promise<{ healthy: boolean; kubernetesContext: string }>;
waitForHealthy(timeoutMs: number): Promise;
close(): void;
}
`$3
`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
$3
`bash
docker build -f packages/sandbox-server/Dockerfile -t prodisco/sandbox-server:latest .
`$3
`bash
Deploy to cluster
kubectl apply -f packages/sandbox-server/k8s/deployment.yamlPort forward for local access
kubectl -n prodisco port-forward service/sandbox-server 50051:50051
`$3
`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
$3
`bash
npm run build
`$3
`bash
All tests
npm testWatch mode
npm run test:watchSecurity tests
npm run test:securityE2E tests (requires kind cluster)
npm run test:e2e:setup
npm run test:e2e
npm run test:e2e:teardown
`$3
`bash
npm run proto:generate
`$3
`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 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) | |
| +----------------+ +----------------+ +----------------------+ |
+---------------------------------------------------------------------+
``MIT