mbox is a Kubernetes-native execution platform for programmable sandboxes, runtime sessions, previews, artifacts, and policy boundaries.
The product provides a web console and API for creating runnable development sandboxes, configuring environment templates, connecting runtime sessions, exposing previews, collecting execution outputs, and managing the policies that make those workflows safe in a shared Kubernetes cluster.
The project is independent at the product layer. Its core language is environment template, sandbox, runtime session, execution task, preview, artifact, policy, and credential boundary. External agents, IDEs, CI systems, release tools, and human operators are clients of the platform; mbox does not include an agent brain and is not primarily a CI/CD product.
Long term, mbox should have several coordinated technical surfaces:
- server side: Go API server, controllers,
agent-sandboxintegration, and Kubernetes resources - web app: human-facing operational console
- CLI: scriptable operation for developers, CI, and platform users
- API docs: published product API contract
- SDK package: Node.js or Go package for automation clients
- People can create and enter sandboxes through terminal, IDE, notebook, browser, or preview endpoints.
- Platform users can define templates for language stacks, tools, startup commands, resources, storage, network access, and lifecycle rules.
- External clients can run controlled sessions and tasks inside Kubernetes execution environments.
- Previews and artifacts make runtime outputs inspectable without exposing direct Kubernetes access.
- CI and deployment systems can be built as upper-layer clients after the lower-level runtime primitives are stable.
- Operators can enforce quota, RBAC, network policy, credential boundaries, and cleanup rules.
- PRODUCT.md: product direction, users, scope, and product limits.
- ARCHITECTURE.md: system layers, runtime design, security boundaries, and Kubernetes integration.
- ROADMAP.md: staged execution plan from prototype to production platform.
- AGENTS.md: instructions for future coding agents working in this repo.
- docs/architecture.md: short implementation architecture handoff for the current slice.
- docs/server-api.md: currently implemented server routes, config, data model, and runtime projection.
- docs/web-console.md: Vite console structure, local proxy behavior, UI scope, and verification.
- docs/runbook.md: local startup, verification, runtime smoke, and troubleshooting commands.
- docs/ia.md: current web-console information architecture.
- docs/references.md: runtime, Kubernetes, and frontend reference notes.
- docs/research-agent-sandbox.md: notes about using
kubernetes-sigs/agent-sandboxas the interactive sandbox runtime substrate.
This repository now contains the first vertical slice: a Go API server backed by Postgres for mbox product records plus a separate Vite web console.
Implemented resources:
ProjectEnvironmentTemplateSandbox- TypeScript SDK package under
sdk/typescriptfor external automation clients - Vite console views for listing projects, creating/editing templates, and launching sandboxes
- template library for ready-to-run environments, with user-facing runtime type, use case, entrypoints, resource preset, validation status, and advanced image/command/policy fields
- simplified sandbox launch that asks for project, template, and name while deriving slug, namespace, and ServiceAccount defaults
- sandbox stop/start actions that pause and resume the projected runtime without deleting the product record
- browser terminal, workspace storage, manually declared preview ports, asynchronous execution tasks with cancellation, artifact reference records, and lightweight logs/events for ready sandbox runtimes
This slice persists mbox product state in Postgres. When the runtime controller is explicitly enabled, it reconciles Sandbox records into agent-sandbox SandboxTemplate and SandboxClaim resources.
Start the full local development stack:
./scripts/dev.shThis starts local Postgres, the Go API server, and the Vite web console. Open http://127.0.0.1:5174.
Runtime access is disabled by default so ordinary local startup does not write Kubernetes resources. To enable the agent-sandbox controller, terminal, execution tasks, logs, events, and preview proxy:
MBOX_KUBE_CONTEXT=kind-agent-sandbox ./scripts/dev.sh --runtimeIf you already have Postgres running, skip Docker Postgres:
DATABASE_URL='postgres://mbox:mbox@127.0.0.1:5432/mbox?sslmode=disable' ./scripts/dev.sh --no-dockerStart a local Postgres:
docker run --name mbox-postgres \
-e POSTGRES_USER=mbox \
-e POSTGRES_PASSWORD=mbox \
-e POSTGRES_DB=mbox \
-p 5432:5432 \
-d postgres:17Run the API server:
DATABASE_URL='postgres://mbox:mbox@127.0.0.1:5432/mbox?sslmode=disable' go run ./cmd/mbox-serverThe server listens on 127.0.0.1:18080 by default. Override it with MBOX_LISTEN_ADDR.
Run the Vite web console in a second shell:
cd web
npm install
npm run devOpen http://127.0.0.1:5174. During local development, Vite proxies /healthz and /v1/* to the API server at 127.0.0.1:18080. If you override MBOX_LISTEN_ADDR, set MBOX_API_PROXY_TARGET before starting Vite:
MBOX_API_PROXY_TARGET=http://127.0.0.1:19080 npm run devThe web dev port defaults to 5174 to avoid colliding with other local Vite projects. Override it with MBOX_WEB_PORT.
The runtime controller is disabled by default so local API development does not write to a Kubernetes cluster. Enable it explicitly when you want mbox to reconcile Sandbox records into agent-sandbox resources:
export MBOX_RUNTIME_CONTROLLER_ENABLED=true
export MBOX_RUNTIME_ACCESS_ENABLED=true
export MBOX_KUBECONFIG="$HOME/.kube/config"
export MBOX_KUBE_CONTEXT="<context-name>"
go run ./cmd/mbox-serverOptional runtime settings:
MBOX_RUNTIME_RECONCILE_INTERVAL: reconcile loop interval, for example5s.MBOX_RUNTIME_ACCESS_ENABLED: enables terminal, task execution, logs, events, and runtime target routes when set totrue.MBOX_AGENT_SANDBOX_WARM_POOL:agent-sandboxwarm pool policy, for examplenoneordefault.
When enabled, mbox ensures the sandbox namespace exists, creates a scoped sandbox ServiceAccount with token automount disabled, creates or updates a SandboxTemplate, and creates a SandboxClaim in that namespace. The generated pod template uses the configured sandbox ServiceAccount and also disables token automount. If the template has storageRequest, mbox projects a workspace PVC template and mounts it at the template working directory, defaulting to /workspace. The mbox Postgres record remains the product source of truth; Kubernetes resources are the runtime projection.
Stopping a sandbox pauses the projected runtime by scaling the resolved agent-sandbox Sandbox to zero replicas. This releases the Pod and running processes while keeping the mbox record and runtime reference. Workspace data is preserved only when it lives on the persistent workspace PVC; files written to container-local paths can be lost during stop/start. Starting a stopped sandbox marks it pending and scales the runtime back to one replica.
MBOX_RUNTIME_ACCESS_ENABLED=true enables /v1/sandboxes/{id}/terminal, /tasks, /runtime, /logs, /events, and /ports. The terminal route is a WebSocket proxy from the browser to Kubernetes pods/exec; execution tasks enqueue non-interactive pods/exec commands, persist output records, and can be canceled while running on the current API server; declared TCP preview ports are proxied through the mbox API server to the resolved runtime Pod. Artifact routes are product metadata routes for registering output references and do not require direct runtime access. Ordinary sandbox pods still do not receive broad Kubernetes credentials.
Run tests:
go test ./...
cd web && npm run build
cd sdk/typescript && npm install && npm run buildPostgres integration tests are opt-in because they write to the configured test database:
export MBOX_TEST_DATABASE_URL='postgres://mbox:mbox@127.0.0.1:5432/mbox_test?sslmode=disable'
go test ./internal/postgresCreate a project:
curl -sS -X POST http://127.0.0.1:18080/v1/projects \
-H 'content-type: application/json' \
-d '{
"name": "Demo Project",
"repositoryUrl": "https://github.com/example/demo",
"defaultNamespace": "mbox-demo"
}'Create a Node.js workspace template:
curl -sS -X POST http://127.0.0.1:18080/v1/templates \
-H 'content-type: application/json' \
-d '{
"name": "Node.js Workspace",
"image": "node:22-bookworm-slim",
"startupCommand": ["sh", "-c", "mkdir -p /workspace && cd /workspace && echo mbox node sandbox ready && tail -f /dev/null"],
"workingDir": "/workspace",
"cpuRequest": "250m",
"memoryRequest": "512Mi",
"storageRequest": "2Gi",
"exposedPorts": [
{"name": "web", "port": 3000, "protocol": "TCP"}
],
"metadata": {
"runtimeType": "Node.js",
"useCase": "Web app preview",
"resourcePreset": "Small",
"validationStatus": "not_tested"
}
}'Create a sandbox by using the returned project and template IDs:
curl -sS -X POST http://127.0.0.1:18080/v1/sandboxes \
-H 'content-type: application/json' \
-d '{
"projectId": "<project-id>",
"templateId": "<template-id>",
"name": "Demo Sandbox"
}'If a project has defaultTemplateId, sandbox creation can use project defaults:
curl -sS -X PATCH http://127.0.0.1:18080/v1/projects/<project-id> \
-H 'content-type: application/json' \
-d '{"defaultTemplateId":"<template-id>"}'
curl -sS -X POST http://127.0.0.1:18080/v1/sandboxes \
-H 'content-type: application/json' \
-d '{
"projectId": "<project-id>",
"name": "Demo Sandbox"
}'In these paths, mbox derives the slug from the name when slug is omitted, uses the project defaultNamespace, and defaults the sandbox ServiceAccount to mbox-sandbox.
List resources:
curl -sS http://127.0.0.1:18080/v1/projects
curl -sS http://127.0.0.1:18080/v1/templates
curl -sS http://127.0.0.1:18080/v1/sandboxesRun a controlled task after the sandbox reaches running with runtime access enabled:
curl -sS -X POST http://127.0.0.1:18080/v1/sandboxes/<sandbox-id>/tasks \
-H 'content-type: application/json' \
-d '{
"command": ["sh", "-lc", "pwd && ls -la"],
"timeoutSeconds": 60
}'
curl -sS http://127.0.0.1:18080/v1/sandboxes/<sandbox-id>/tasks
curl -sS -X POST http://127.0.0.1:18080/v1/tasks/<task-id>/cancel
curl -sS -X POST http://127.0.0.1:18080/v1/sandboxes/<sandbox-id>/artifacts \
-H 'content-type: application/json' \
-d '{
"taskId": "<task-id>",
"kind": "report",
"name": "Test report",
"uri": "workspace:///workspace/reports/test.json",
"contentType": "application/json"
}'The first SDK package lives in sdk/typescript. It is a thin client for the public HTTP API, aimed at agents, IDE integrations, CI scripts, release tools, and other external callers. It does not run an agent loop inside mbox; it only makes the platform primitives easier to call.
Build it locally:
cd sdk/typescript
npm install
npm run buildRun a task and register a referenced artifact:
import { MboxClient } from "@mbox/sdk"
const mbox = new MboxClient({ baseUrl: "http://127.0.0.1:18080" })
const task = await mbox.createExecutionTask("<sandbox-id>", {
command: ["sh", "-lc", "npm test -- --reporter=json > /workspace/reports/test.json"],
timeoutSeconds: 300,
metadata: { caller: "external-agent" },
})
const finished = await mbox.waitForTask(task.id)
await mbox.createArtifact("<sandbox-id>", {
taskId: finished.id,
kind: "report",
name: "Test report",
uri: "workspace:///workspace/reports/test.json",
contentType: "application/json",
})With the server running and both MBOX_RUNTIME_CONTROLLER_ENABLED=true and MBOX_RUNTIME_ACCESS_ENABLED=true, run the cluster smoke test against a kubeconfig context that already has agent-sandbox installed:
export MBOX_API_URL=http://127.0.0.1:18080
export MBOX_KUBECONFIG="$HOME/.kube/config"
export MBOX_KUBE_CONTEXT=kind-agent-sandbox
./scripts/smoke-agent-sandbox.shThe smoke test creates a project, a BusyBox terminal template, and a sandbox through the mbox API. It then verifies the generated SandboxClaim, resolved Sandbox, ready Pod, disabled ServiceAccount token automount, pod logs, workspace exec, API status mapping, and delete cleanup path.
With runtime mode enabled, launch a sandbox from the Node.js workspace template and wait for it to reach running. The Runtime Workspace shows a starting panel while the SandboxClaim and Pod are still pending, then enables Terminal, Logs, Events, Storage, Preview, and Tasks.
In the terminal tab, start a service in the background:
cat > server.js <<'EOF'
const http = require('http')
http.createServer((req, res) => {
res.end('hello from mbox node preview')
}).listen(3000, '0.0.0.0')
EOF
node server.js > server.log 2>&1 &Open the Preview tab, make sure port 3000 is declared as web, and use Open after the sandbox is running. If the template did not declare the port, add it in the Preview tab; this saves the sandbox ports field through PATCH /v1/sandboxes/{id}.