Constrained Docker Environment for AI Coding Agents
AI-Cabin provides a secure, reproducible, and agent-agnostic environment for AI coding tools. Run AI agents (OpenCode, Pi.dev, or future tools) in a sandboxed Docker container with controlled access to your filesystem, network, and credentials.
- Greywall Sandboxing: Filesystem restrictions, seccomp profiles, capability limits
- Greyproxy Integration: Credential management with domain allowlisting
- Localhost-only Dashboard: No LAN exposure for web interfaces
- Docker Isolation: Complete environment separation from host
- Works with OpenCode, Pi.dev, and future AI coding tools
- Same workflow and conventions across all agents
- Agent-agnostic skills system for consistent behavior
- Shared Files: User and AI work together in real-time
- Persistent Sessions: Continue work across multiple runs
- Clean Separation:
desk/β Cross-project organization (tasks, skills, docs)workdir/β Project code and git repositories
- Same environment on any machine
- No dependency conflicts with host
- Full toolchain in container (Go, Node.js, databases, etc.)
- Docker + Docker Compose
- Greywall (installed on host) - GreyhavenHQ/greywall
- Greyproxy (running on host for credential management) - GreyhavenHQ/greyproxy
- Git
- direnv (optional, recommended for environment management)
- Scaleway access key (for AI model access)
# Clone AI-Cabin
git clone https://github.com/JulienVdG/AI-Cabin.git
cd AI-Cabin
# Bootstrap a new environment (creates desk, workdir, home directories)
./templates/bin/bootstrap-cabin.sh /home/user/ai-cabin-prodThis creates:
/home/user/ai-cabin-prod/
βββ .envrc # Environment variables
βββ home/ # AI_CABIN_HOME (agent data)
βββ desk/ # AI_CABIN_DESK (skills, tasks, docs)
βββ workdir/ # AI_CABIN_WORKDIR (git repos)
Required variables:
export AI_CABIN_HOME=/home/user/ai-cabin-prod/home/
export AI_CABIN_DESK=/home/user/ai-cabin-prod/desk/
export AI_CABIN_WORKDIR=/home/user/ai-cabin-prod/workdir/
export SCW_PROJECT_ID=your-scaleway-project-id
export OPENCODE_SERVER_PASSWORD=your-password # For OpenCode WebUI
export GIT_AGENT_NAME="AI Agent + $(git config --global user.name)" # Git name for agent
export GIT_AGENT_EMAIL=$(git config --global user.email) # Git email for agentOption 1: Source .envrc manually (without direnv)
# Source the environment before each session
source ~/ai-cabin-prod/.envrc
cd cabin/opencode-go
# Verify variables are set
env | grep AI_CABIN
# Setup and start
make setup
make docker-upOption 2: Use direnv (automatic)
# Copy .envrc to your cabin
cp /home/user/ai-cabin-prod/.envrc cabin/opencode-go/.envrc
# Allow direnv (loads .envrc automatically when entering directory)
cd cabin/opencode-go
direnv allow
# Setup and start (variables loaded automatically)
make setup
make docker-upWeb Interface:
- Navigate to http://localhost:9090
- Enter your
OPENCODE_SERVER_PASSWORD
TUI (Terminal UI):
make opencodeSandboxed Shell:
make docker-greyshellβββββββββββββββββββββββββββββββββββββββββββββββββββ
β HOST β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β AI-Cabin Container β β
β β βββββββββββββββββββββββββββββββββββββ β β
β β β Greywall (Sandbox) β β β
β β β ββββββββββββββββ β β β
β β β β AI Agent β β β β
β β β β (OpenCode/ β ββββββββββββ β β β
β β β β Pi.dev) β β β β β
β β β ββββββββββββββββ β β β β
β β βββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β² β β
β β Bind mounts β Network β
β ββββββββ΄βββββββββββββββ ββββββββββΌββββββββββ β
β β Host Directories β β Greyproxy β β
β β β β (HTTP/SOCKS5) β β
β βββββββββββββββββββββββ ββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SHARED WORKSPACE β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β β desk/ β β workdir/ β β
β ββββββββββββββββββββ€ ββββββββββββββββββββ€ β
β β - AGENTS.md β β - product-a/ β β
β β - TODO.md β β - product-b/ β β
β β - skills/ β β - personal/ β β
β β - retro.md β β β β
β β β β (Git repos) β β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β β² β² β
β β Cross-project β Project code β
β β organization β & docs β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Cabin | Agent | Language | Use Case |
|---|---|---|---|
opencode-go |
OpenCode | Go | Production Go projects |
pi-go |
Pi.dev | Go | Alternative agent for Go |
| Command | Description |
|---|---|
make setup |
Setup environment (directories + config) |
make docker-up |
Start cabin in background |
make docker-down |
Stop cabin |
make docker-build |
Rebuild container |
make docker-restart-agent |
Restart agent container |
| Command | Description |
|---|---|
make opencode |
Continue OpenCode session (TUI) |
make docker-shell |
Get bash shell inside container |
make docker-greyshell |
Get greywall-sandboxed shell |
make docker-logs |
Follow agent logs |
| Variable | Description | Example | Default |
|---|---|---|---|
AI_CABIN_HOME |
Host home for agent data | /home/user/ai-cabin/ |
Required |
AI_CABIN_DESK |
Shared desk directory | /home/user/ai-cabin/desk/ |
Required |
AI_CABIN_WORKDIR |
Git repositories | /home/user/workdir/ |
Required |
SCW_PROJECT_ID |
Scaleway project ID | 12345678-... |
Required |
GIT_AGENT_NAME |
Git name for agent commits | AI Agent + John Doe |
AI Agent |
GIT_AGENT_EMAIL |
Git email for agent commits | user@example.com |
ai-agent@vdg.name |
CONTAINER_WORKDIR |
Container workdir path (advanced) | /workspace/ |
${AI_CABIN_WORKDIR} |
Greyproxy handles credentials and network access:
- Automatic Injection:
SCW_SECRET_KEY, API tokens - Domain Allowlist: Only approved domains accessible
- Audit Log: All requests logged for review
- Dashboard: http://localhost:43080 (host only)
Requirements:
- Greyproxy running on host
- CA certificate at
~/.local/share/greyproxy/ca-cert.pem
Firewall Configuration (Recommended for Production):
By default, greyproxy listens on 0.0.0.0 (all interfaces). To secure it from LAN access while preserving Docker access:
1. Identify Docker subnet:
- Check Docker configuration
cat /etc/docker/daemon.json
- Extract subnet (if customized in daemon.json)
jq -r '."default-address-pools"[].base' < /etc/docker/daemon.json
- Default subnet (if no daemon.json config): 172.17.0.0/16
2. Configure UFW (Ubuntu/Debian):
- Allow Docker (adapt subnet to your config)
- Custom subnet (e.g., 100.64.0.0/15):
sudo ufw allow from 100.64.0.0/15 to any port 43051,43052,43053,43080 proto tcp comment "greyproxy docker" sudo ufw allow from 100.64.0.0/15 to any port 43053 proto udp comment "greyproxy dns docker"
- OR default subnet (172.17.0.0/16):
sudo ufw allow from 172.17.0.0/16 to any port 43051,43052,43053,43080 proto tcp comment "greyproxy docker" sudo ufw allow from 172.17.0.0/16 to any port 43053 proto udp comment "greyproxy dns docker"
- Custom subnet (e.g., 100.64.0.0/15):
- Allow localhost and block the rest (optional - UFW should do this by default)
sudo ufw allow from 127.0.0.1 to any port 43051,43052,43053,43080 proto tcp comment "greyproxy localhost" sudo ufw deny from any to any port 43051,43052,43053,43080 proto tcp comment "greyproxy deny external"
- Reload UFW
sudo ufw reload
Verification:
# Test local access (should work)
curl http://127.0.0.1:43080/api/health
# Test Docker access (should work)
docker exec <container> curl http://host.docker.internal:43080/api/health
# Test external access (should be blocked)
curl --connect-timeout 2 http://<your-IP>:43080/api/health- Bootstrap Script - Environment setup guide
- Cabin Docs - Detailed cabin configuration
Apache License 2.0 - See LICENSE for details.
Contributions welcome! Please read our development guide and open an issue before submitting PRs.
You're the captain. AI is just another passenger. Stay in control. π’
