░▒▓██████▓▒░
░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░ Octavius Labs
░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░░▒▓█▓▒░
░▒▓██████▓▒░
Note: This project was built with the help of Claude Code.
AI agents are increasingly capable of writing and executing code. But running
AI-generated code is inherently risky—the code might have bugs, access resources
it shouldn't, or behave unexpectedly. You can't just eval() it and hope for
the best.
Assist TEE solves this problem by providing a secure sandbox where AI agents can execute code without risking your infrastructure. Every execution runs in a hardware-isolated gVisor container with:
- No network access - Code can't phone home or exfiltrate data
- No filesystem access - Can only read its own modules
- Resource limits - CPU, memory, and time constraints prevent runaway processes
- Fresh containers - Each execution starts clean, no state leakage between runs
The result: AI agents can write and run TypeScript/JavaScript code with the confidence that even malicious code can't escape the sandbox.
Assist TEE uses a two-phase execution model inspired by AWS Lambda:
- Setup Phase (~500ms-2s): Upload your code modules once, install dependencies with network access, then lock down the environment
- Execute Phase (~100-200ms): Run your handler function many times against the prepared environment—fast, isolated, and stateless
┌─────────────┐ Setup ┌─────────────────┐ Execute ┌─────────────┐
│ AI Agent │ ──────────────▶│ TEE API (Go) │ ──────────────▶ │ gVisor │
│ │ modules + │ │ event data │ Container │
│ │ dependencies │ PostgreSQL │ │ (Deno) │
└─────────────┘ └─────────────────┘ └─────────────┘
Your code exports a simple handler function—just like Lambda:
export async function handler(event: any, context: any) {
const { a, b } = event.data;
return { sum: a + b };
}- Cloudflare's Code Mode - AI writes code to interact with MCP servers
- Anthropic's Code Execution with MCP - Secure code execution for AI agents
- Lambda-style handlers - Familiar
export async function handler(event, context)pattern - Two-phase execution - Setup once, execute many times for optimal performance
- Strong isolation - gVisor runtime provides VM-level security
- Environment reuse - Prepared environments stored in Docker volumes
- Automatic cleanup - TTL-based garbage collection of unused environments
- Execution tracking - All executions logged to PostgreSQL
assist-tee/
├── services/
│ ├── api/ # Go API service
│ │ ├── cmd/api/ # Main entry point
│ │ ├── internal/ # Internal packages
│ │ │ ├── database/ # PostgreSQL connection
│ │ │ ├── models/ # Data structures
│ │ │ ├── executor/ # Docker operations
│ │ │ ├── handlers/ # HTTP handlers
│ │ │ └── reaper/ # Background cleanup
│ │ ├── Dockerfile # API service container
│ │ ├── go.mod # Go dependencies
│ │ └── go.sum
│ └── runtime/ # Deno runtime service
│ ├── runner.ts # Stdin wrapper that calls user handlers
│ └── Dockerfile # Runtime container
├── docs/ # Documentation
│ ├── design.md # Architecture documentation
│ ├── GVISOR.md # gVisor security configuration
│ ├── BUILD.md # Build and compilation guide
│ ├── TYPESCRIPT_EXAMPLES.md # TypeScript usage examples
│ ├── DEPENDENCIES.md # External dependency management
│ ├── TESTING.md # Testing guide
│ └── SECURITY.md # Security model
├── examples/ # Example user code
│ ├── main.ts
│ └── utils.ts
├── scripts/
│ └── test-full-flow.sh # End-to-end test
├── docker-compose.yml # Production (gVisor enabled)
├── docker-compose.dev.yml # Development (gVisor disabled)
├── Makefile # Build and run commands
├── README.md # This file
├── QUICKSTART.md # 5-minute getting started
└── .gitignore
- Docker 24.0+ with Docker Compose
- gVisor runtime (runsc) installed
- Linux OS (for gVisor support)
# Install runsc runtime
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.list
sudo apt-get update && sudo apt-get install -y runsc
# Configure Docker to use gVisor
sudo runsc install
sudo systemctl restart docker
# Verify installation
docker run --rm --runtime=runsc hello-worldPull the pre-built images and run:
# Pull images
docker pull octaviusdeployment/assist-tee-api:latest
docker pull octaviusdeployment/assist-tee-rt-deno:latest
# Start PostgreSQL
docker run -d --name tee-postgres \
-e POSTGRES_USER=tee \
-e POSTGRES_PASSWORD=tee \
-e POSTGRES_DB=tee \
-p 5432:5432 \
postgres:16-alpine
# Start the API
docker run -d --name tee-api \
-p 8080:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DB_HOST=host.docker.internal \
-e DB_USER=tee \
-e DB_PASSWORD=tee \
-e DB_NAME=tee \
-e RUNTIME_IMAGE=octaviusdeployment/assist-tee-rt-deno:latest \
-e DISABLE_GVISOR=true \
octaviusdeployment/assist-tee-api:latest
# Verify it's running
curl http://localhost:8080/healthNote: Set
DISABLE_GVISOR=trueon macOS/Windows. On Linux with gVisor installed, remove this flag for full sandboxing.
# Clone the repo
git clone https://github.com/your-org/assist-tee.git
cd assist-tee
# Build all service images
make build
# Start all services (API + PostgreSQL)
make run
# Check logs
docker-compose logs -f tee-api
# Verify it's running
curl http://localhost:8080/healthCreate a new execution environment with your code:
curl -X POST http://localhost:8080/environments/setup \
-H "Content-Type: application/json" \
-d '{
"mainModule": "main.ts",
"modules": {
"main.ts": "export async function handler(event, context) {\n const { a, b } = event.data;\n return { sum: a + b };\n}",
"utils.ts": "export const add = (a, b) => a + b;"
},
"ttlSeconds": 3600
}'With external dependencies:
curl -X POST http://localhost:8080/environments/setup \
-H "Content-Type: application/json" \
-d '{
"mainModule": "main.ts",
"modules": {
"main.ts": "import { format } from \"npm:date-fns@3\";\nexport async function handler(event, context) {\n return { formatted: format(new Date(), \"PPP\") };\n}"
},
"dependencies": {
"npm": ["date-fns@3"],
"deno": ["https://deno.land/std@0.224.0/async/delay.ts"]
},
"ttlSeconds": 3600
}'Dependencies are downloaded during setup (with network) and cached for execution (without network). See docs/DEPENDENCIES.md for details.
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"volumeName": "tee-env-550e8400-...",
"mainModule": "main.ts",
"status": "ready",
"createdAt": "2025-01-15T10:30:00Z",
"executionCount": 0,
"ttlSeconds": 3600
}Run your code multiple times in the same environment:
ENV_ID="550e8400-e29b-41d4-a716-446655440000"
curl -X POST http://localhost:8080/environments/$ENV_ID/execute \
-H "Content-Type: application/json" \
-d '{
"data": { "a": 5, "b": 3 },
"env": { "DEBUG": "true" },
"limits": {
"timeoutMs": 5000,
"memoryMb": 128
}
}'Response:
{
"id": "exec-abc123...",
"exitCode": 0,
"stdout": "{\"sum\":8}",
"stderr": "",
"durationMs": 127
}curl http://localhost:8080/environmentscurl -X DELETE http://localhost:8080/environments/$ENV_IDYour code must export a handler function:
// main.ts
export async function handler(event: any, context: any) {
// event.data = input data from execution request
// event.env = environment variables
// context.executionId = unique execution ID
// context.environmentId = environment ID
// Import other modules
const { add } = await import("./utils.ts");
// Process data
const result = add(event.data.a, event.data.b);
// Return result (auto JSON serialized)
return {
sum: result,
timestamp: new Date().toISOString(),
};
}// utils.ts
export function add(a: number, b: number): number {
return a + b;
}# Run basic functionality test
./scripts/test-full-flow.sh# Run all security and sandboxing tests
./scripts/test-all-security.shThis runs 5 comprehensive test suites:
- ✓ Basic functionality - Environment setup, execution, cleanup
- ✓ Network sandboxing - HTTP, DNS, WebSocket blocking
- ✓ Filesystem sandboxing - Read/write restrictions, command blocking
- ✓ Deno permissions - All permission flags (net, read, write, run, ffi, hrtime)
- ✓ Dependency handling - Local vs remote imports, npm packages
# Test network isolation
./scripts/test-network-sandbox.sh
# Test filesystem restrictions
./scripts/test-filesystem-sandbox.sh
# Test Deno permissions
./scripts/test-permissions.sh
# Test dependency handling
./scripts/test-dependencies.shSee docs/TESTING.md for detailed testing documentation.
Client → TEE API (Go) → PostgreSQL (metadata)
→ Docker Volumes (code storage)
→ gVisor Containers (execution)
- Setup: Create volume, write modules, store metadata (~500ms-2s)
- Execute: Spawn container, pipe JSON to stdin, run handler, capture stdout (~100-200ms)
- Cleanup: Automatic reaping after TTL expires
# Build API service
cd services/api && docker build -t tee-api:latest .
# Build runtime service
cd services/runtime && docker build -t deno-runtime:latest .
# Or use Makefile
make build-api
make build-runtimeThe services are designed to run together via docker-compose, but you can run them individually:
# Start PostgreSQL first
docker run -d --name postgres \
-e POSTGRES_USER=tee \
-e POSTGRES_PASSWORD=tee \
-e POSTGRES_DB=tee \
-p 5432:5432 \
postgres:16-alpine
# Run API service
docker run -d --name tee-api \
-p 8080:8080 \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DB_HOST=postgres \
tee-api:latest
# Runtime service runs on-demand (spawned by API)- gVisor Runtime: Hardware virtualization for kernel isolation
- Network Isolation:
--network=noneby default (configurable via whitelist) - Read-only Filesystem: Container root is read-only
- Resource Limits: Memory, CPU, and timeout limits enforced
- Permission Whitelisting: Fine-grained control over network access and environment variables
Configure allowed network domains and environment variables during setup:
curl -X POST http://localhost:8080/setup \
-H "Content-Type: application/json" \
-d '{
"mainModule": "main.ts",
"modules": {"main.ts": "export async function handler(e,c) { return await fetch(\"https://api.example.com/data\").then(r => r.json()); }"},
"permissions": {
"allowNet": ["api.example.com", "cdn.example.com:443"],
"allowEnv": ["API_KEY", "DEBUG"]
}
}'- allowNet: List of domains the code can access (enables
--network=bridge+ Deno--allow-net=...) - allowEnv: List of env var names that can be passed from execute requests to the container
See docs/SECURITY.md for details.
Environment variables for the API service:
| Variable | Default | Description |
|---|---|---|
PORT |
8080 |
Server port |
DB_HOST |
postgres |
PostgreSQL host |
DB_PORT |
5432 |
PostgreSQL port |
DB_USER |
tee |
PostgreSQL user |
DB_PASSWORD |
tee |
PostgreSQL password |
DB_NAME |
tee |
PostgreSQL database |
LOG_LEVEL |
info |
Log level (debug, info, warn, error) |
RUNTIME_IMAGE |
octaviusdeployment/assist-tee-rt-deno:latest |
Docker image for runtime execution |
BEARER_TOKEN |
(required) | Authentication token for API endpoints |
DISABLE_BEARER_TOKEN |
false |
Set to true to disable auth ( |
DISABLE_GVISOR |
false |
Set to true or 1 to disable gVisor ( |
For development on macOS/Windows where gVisor is not available:
Option 1: Use dev compose file (Recommended)
make run-dev
# or
docker-compose -f docker-compose.dev.yml up -dOption 2: Set environment variable
# docker-compose.yml
environment:
- DISABLE_GVISOR=trueOption 3: Set when running
DISABLE_GVISOR=true go run cmd/api/main.go- Code is NOT sandboxed with hardware virtualization
- User code can potentially access the host kernel
- This mode should ONLY be used for local development
- DO NOT USE IN PRODUCTION!
The service will display prominent warnings on startup when gVisor is disabled.
# Install Go dependencies (from services/api/)
cd services/api && go mod download
# Run locally (requires PostgreSQL)
cd services/api && go run cmd/api/main.go
# Build
cd services/api && go build -o tee-api ./cmd/api
# Run tests
cd services/api && go test ./...curl http://localhost:8080/environments | jqdocker volume ls | grep tee-env# API logs
docker-compose logs -f tee-api
# Database
docker exec -it tee-postgres psql -U tee -d tee
SELECT * FROM environments ORDER BY created_at DESC LIMIT 10;
SELECT * FROM executions ORDER BY started_at DESC LIMIT 10;| Operation | Latency | Notes |
|---|---|---|
| Environment setup | 500ms - 2s | One-time cost |
| Execution | 100-200ms | Per execution |
| Volume I/O | <10ms | Reading modules |
| DB operations | <5ms | Metadata lookups |
# Check if runsc is installed
which runsc
# Verify Docker can use runsc
docker run --rm --runtime=runsc hello-world
# Check Docker daemon config
cat /etc/docker/daemon.json# Check Docker socket is mounted
docker exec tee-api ls -la /var/run/docker.sock
# Check permissions
docker exec tee-api docker ps# Check PostgreSQL is running
docker-compose ps postgres
# Test connection
docker exec tee-api nc -zv postgres 5432- Dependency pre-installation in setup phase
- Warm container pools for faster execution
- Multi-language support (Python, Go, Rust)
- WebSocket streaming for long-running executions
- Metrics and observability dashboards
- Fine-grained Deno permissions enforcement
- Environment snapshots and cloning
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please read our Contributing Guide and Code of Conduct before submitting a pull request.
If you discover a security vulnerability, please do NOT open a public issue. Instead, email the maintainers directly. See CONTRIBUTING.md for details.