Lightweight Session Sandbox — Isolated Shell via Namespace + Overlayfs
- Lightweight: Linux Namespace + bubblewrap + overlayfs (fuse-overlayfs or kernel overlay)
- Isolated: Per-user + per-session with writable overlay layer
- Multi-Protocol: HTTP REST / WebSocket / gRPC / MCP
- Persistent: overlayfs layering, changes don't affect base
- Auto Cleanup: TTL-based session cleanup
┌─────────────────────────────────────────────┐
│ API Layer (HTTP/WS + gRPC + MCP) │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Session Manager │
│ - GetOrCreate(user, session) │
│ - TTL cleanup │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ Isolation Layer │
│ bwrap + overlayfs (fuse / kernel / none) │
│ ├── PID/IPC/UTS Namespace │
│ ├── overlay: lower + upper + merged │
│ └── PTY (pseudo-terminal) │
└─────────────────────────────────────────────┘
# Check dependencies
./scripts/check-env.sh
# Build
make build
# Run
./dist/strata runThe commands above target Linux because bubblewrap depends on Linux namespaces.
On Apple silicon with macOS 26 or later, Apple's container tool can run the same
Linux stack without Docker. See Apple Container.
# Create session
curl -X POST http://localhost:2280/api/sessions \
-H "Content-Type: application/json" \
-d '{"owner_id": "alice", "session_id": "task-001"}'
# Execute command
curl -X POST http://localhost:2280/api/sessions/alice/task-001/exec \
-H "Content-Type: application/json" \
-d '{"command": "ls -la"}'
# Interactive shell (WebSocket)
wscat -c 'ws://localhost:2280/api/ws/alice/task-001/shell'| Method | Path | Description |
|---|---|---|
| POST | /api/sessions |
Create session |
| DELETE | /api/sessions/{uid}/{sid} |
Close session |
| POST | /api/sessions/{uid}/{sid}/exec |
Execute command |
| GET | /api/stats |
Stats |
| GET | /api/ws/{uid}/{sid}/shell |
WebSocket |
MCP available at http://localhost:2280/mcp/
For AI agents:
npx tsx scripts/strata-mcp.ts| Variable | Default | Description |
|---|---|---|
STRATA_SERVER_ADDR |
:2280 |
Listen address (HOST:PORT combined) |
STRATA_SERVER_SHELL_TIMEOUT |
30s |
Default shell command timeout (HTTP/MCP exec) |
STRATA_SANDBOX_SESSION_ROOT |
/tmp/strata/sessions |
Session directory |
STRATA_SANDBOX_SESSION_TTL |
30m |
Session timeout |
STRATA_SANDBOX_MAX_SESSIONS |
100 |
Max sessions |
STRATA_SANDBOX_OVERLAY_DRIVER |
fuse |
fuse/kernel/none |
All settings are loaded from STRATA_* environment variables. The table lists the
most commonly used values.
All API endpoints support two identity modes:
Path-based (explicit):
curl -X POST http://localhost:2280/api/sessions/alice/task-001/exec \
-d '{"command": "ls"}'Header-based (alternative):
curl -X POST http://localhost:2280/api/sessions/exec \
-H "X-Owner-Id: alice" \
-H "X-Session-Id: task-001" \
-d '{"command": "ls"}'Header priority is higher than path values.
| Package | Install |
|---|---|
meson |
apt install meson |
libcap-dev |
apt install libcap-dev |
| Package | Install |
|---|---|
bubblewrap (bwrap) |
Build from github.com/containers/bubblewrap |
fuse-overlayfs |
apt install fuse-overlayfs |
git clone https://github.com/containers/bubblewrap
cd bubblewrap
meson _builddir
meson compile -C _builddir
meson test -C _builddir
sudo meson install -C _builddirNote: do NOT apply file capabilities (
sudo setcap cap_sys_admin+ip ...) to bwrap — modern bubblewrap rejects that configuration (Unexpected capabilities but not setuid, old file caps config?). Non-root use relies on unprivileged user namespaces (Linux ≥ 5.11): enablekernel.unprivileged_userns_clone=1on Debian, or allow unprivileged user namespaces on Ubuntu 24.04+ (kernel.apparmor_restrict_unprivileged_userns=0).
- Linux kernel ≥ 5.11
- Unprivileged user namespaces (required for non-root bwrap and the
kerneloverlay driver) - Go ≥ 1.25 (build only)
For macOS, use the Apple Container deployment guide. It runs the Linux runtime in a lightweight VM and is not a native macOS sandbox backend.
MIT