██████ ██ ██ ██████ ███████
██ ██ ██ ██ ██ ██
██ ██ ██ ██ ██ █████
██ ██ ██ ██ ██ ██
██████ ███████ ██ ██████ ███████
sandboxed agentic terminal v3
──────────────────────────────────────
your project ──bind mount──► /workspace
.env secrets ──env vars────► container
browser :7681 ──ttyd───────► web shell
┌────────────────────────────────────┐
│ claude copilot codex gh bash │
│ web (ttyd + tmux) │
└──────────────┬─────────────────────┘
MITM intercept proxy
firewall.sh
egress allowlist
│
┌────────┼────────┐
▼ ▼ ▼
intercept egress session
.jsonl .jsonl events
│
▼
api.anthropic.com claude
api.githubcopilot.com copilot
api.github.com gh
api.openai.com codex
*everything else REJECT
──────────────────────────────────────
every request captured. every
connection logged. nothing leaves
without a record.
A sandboxed environment for running AI coding agents (Claude Code, GitHub Copilot CLI, Codex CLI, GitHub CLI) that captures structured interaction data from every session. CLIDE is a research instrument: it intercepts API traffic between agents and providers, logs all outbound connections, and records session events — producing machine-readable datasets for studying how AI agents interact with codebases.
CLIDE captures three streams of structured data from every agent session. This is the core value proposition — not just sandboxing, but complete observability over agent behavior.
MITM proxy intercepts all traffic between the agent and its provider API. Every prompt, every completion, every tool call — captured with full request and response bodies.
/var/log/clide/intercept.jsonl
Each line is a JSON object:
| Field | Type | Description |
|---|---|---|
timestamp |
ISO 8601 | When the request was made |
method |
string | HTTP method (POST, GET, etc.) |
url |
string | Full request URL |
request_headers |
object | Request headers (auth tokens redacted) |
request_body |
object | Full request payload — prompts, messages, tool definitions |
response_status |
int | HTTP status code |
response_headers |
object | Response headers |
response_body |
object | Full response — completions, tool calls, token counts |
duration_ms |
int | Round-trip time |
Every outbound network connection the agent attempts, whether allowed or blocked by the firewall.
/var/log/clide/egress.jsonl
| Field | Type | Description |
|---|---|---|
timestamp |
ISO 8601 | Connection attempt time |
destination |
string | Hostname or IP |
port |
int | Destination port |
protocol |
string | tcp, udp |
action |
string | ALLOW or REJECT |
rule |
string | Which firewall rule matched |
This tells you exactly what the agent tried to reach — and whether it was permitted. Rejected connections are especially interesting: they reveal what the agent wanted to do but couldn't.
Agent tool invocations, file edits, and shell commands are captured as structured events:
/var/log/clide/session-events.jsonl
| Field | Type | Description |
|---|---|---|
timestamp |
ISO 8601 | Event time |
event_type |
string | tool_use, file_edit, command, file_read |
agent |
string | Which CLI produced the event |
detail |
object | Event-specific payload (tool name, file path, command, etc.) |
With these three streams you can reconstruct complete agent sessions:
- Token economics — measure prompt/completion token counts per task
- Tool use patterns — which tools agents reach for, in what order, how often
- Egress behavior — what external resources agents try to access (and what happens when blocked)
- Latency profiling — provider response times under different prompt sizes
- Comparative analysis — run the same task across Claude, Copilot, and Codex; diff the data
The iptables egress allowlist isn't just security — it's an experimental control. You decide exactly what the agent can reach, creating reproducible conditions for studying agent behavior under different network constraints.
| Host | Used by |
|---|---|
api.anthropic.com |
Claude Code |
api.githubcopilot.com |
GitHub Copilot CLI |
api.github.com |
GitHub Copilot CLI, GitHub CLI |
github.com |
GitHub CLI |
registry.npmjs.org |
npm package updates |
api.openai.com |
Codex CLI |
auth.openai.com |
Codex CLI — device code auth |
DNS (port 53) and loopback traffic are always allowed.
Add hosts via CLIDE_ALLOWED_HOSTS in .env:
CLIDE_ALLOWED_HOSTS=pypi.org,files.pythonhosted.orgDisable the firewall entirely with CLIDE_FIREWALL=0 (unrestricted egress — no rejected connections logged).
The firewall uses iptables and requires the NET_ADMIN capability (already set in docker-compose.yml). If unavailable, it degrades gracefully with a warning.
Trust boundary: the host trusts the container with a read-write mount of your project directory and your API credentials via
.env. The container cannot reach the internet beyond the allowlisted endpoints (whenNET_ADMINis available). SeeSECURITY.mdfor the full threat model.
-
Clone and configure credentials:
git clone https://github.com/itscooleric/clide cd clide cp .env.example .env # then edit with your tokens
-
Add your tokens to
.env:GH_TOKEN=your_github_pat_here # Claude auth — pick one: CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-xxxxx # subscription (recommended) # ANTHROPIC_API_KEY=sk-ant-xxxxx # API credits
-
Build and run:
docker compose build ./clide web # web terminal at http://localhost:7681 ./clide claude # Claude Code CLI ./clide shell # interactive shell with all CLIs
Your project is mounted at /workspace inside the container. Data logs appear in /var/log/clide/.
| CLI | Command | Auth env var |
|---|---|---|
| Claude Code | claude |
CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY |
| GitHub Copilot CLI | copilot |
GH_TOKEN |
| GitHub CLI | gh |
GH_TOKEN |
| Codex CLI (OpenAI) | codex |
OPENAI_API_KEY |
| GitLab CLI | glab |
GITLAB_TOKEN + GITLAB_HOST |
OAuth token (recommended): Uses your Claude Pro/Max subscription — no API credits consumed. Generate with claude setup-token on any machine with a browser, valid for 1 year.
API key: Pay-per-use. Create at https://console.anthropic.com/settings/keys.
Set one (not both) in .env. If both are present, OAuth takes priority.
Set OPENAI_API_KEY in .env, or use device code flow inside the container:
codex auth login --auth device./clide web # start web terminal at http://localhost:7681
./clide shell # interactive shell with all CLIs
./clide claude # run Claude Code CLI
./clide copilot # run GitHub Copilot CLI
./clide codex # run Codex CLI
./clide gh repo view # run GitHub CLI with args
./clide help # show all commandsmake web # start web terminal
make shell # interactive shell
make claude # Claude Code CLI
make codex # Codex CLI
make help # show all targetsdocker compose run --rm shell
docker compose up -d web
PROJECT_DIR=/path/to/repo docker compose run --rm shellCtrl+Shift+P > Run Task > choose from web terminal, copilot, or shell targets.
tmux is installed and enabled by default in the web terminal. Run multiple agents side-by-side: Claude in one pane, Copilot in another, monitoring data logs in a third.
Every browser tab attaches to the same named session (main), so refreshing re-attaches rather than spawning a new shell.
For make shell / ./clide shell, tmux is opt-in:
CLIDE_TMUX=1| Key | Action |
|---|---|
Ctrl-b | |
Split pane horizontally |
Ctrl-b - |
Split pane vertically |
Ctrl-b <arrow> |
Move between panes |
Ctrl-b d |
Detach (session stays alive) |
| Mouse | Click to focus, scroll, drag to resize |
The container includes a Python 3 virtualenv at /opt/pyenv with pytest and ruff pre-installed. The venv is clide-owned — pip install works without sudo.
Note:
pip installrequires outbound PyPI access. Addpypi.org,files.pythonhosted.orgtoCLIDE_ALLOWED_HOSTSif the firewall is enabled.
safe.directory = * is pre-configured so volume-mounted repos work without ownership errors. Appropriate for a single-user sandbox — see SECURITY.md for caveats.
| Doc | Contents |
|---|---|
SECURITY.md |
Threat model, trust boundaries, attack surface, hardening |
RUNBOOK.md |
Health checks, logs, rebuilds, credential rotation, troubleshooting |
DEPLOY.md |
Production deployment with Caddy reverse proxy |
| OS | Status | Notes |
|---|---|---|
| Linux | Supported | Native Docker — full functionality including egress firewall |
| macOS (Apple Silicon) | Supported | Docker Desktop required; arm64 builds natively |
| macOS (Intel) | Supported | Docker Desktop required |
| Windows (WSL2) | Supported | Docker Desktop with WSL2 backend required |
| Windows (no WSL2) | Partial | Egress firewall requires NET_ADMIN; availability varies |
| Component | Minimum |
|---|---|
| Docker Engine | 20.10+ |
| Docker Compose | v2.0+ (docker compose, not docker-compose) |
| CPU | amd64 or arm64 |
| Browser (web terminal) | Chrome, Firefox, Safari, Edge |
Environment Variable Reference
| Variable | Where set | Default | Description |
|---|---|---|---|
CLIDE_UID |
Build arg | 1000 |
UID for clide user. Set to $(id -u) to match host. |
CLIDE_GID |
Build arg | 1000 |
GID for clide group. Set to $(id -g) to match host. |
CLIDE_FIREWALL |
Runtime .env |
1 |
Set to 0 to disable egress allowlist. |
CLIDE_ALLOWED_HOSTS |
Runtime .env |
(empty) | Additional hostnames for egress allowlist. |
CLIDE_TMUX |
Runtime .env |
(off) | Set to 1 to enable tmux in shell mode. |
| Variable | Description |
|---|---|
GH_TOKEN |
GitHub PAT with Copilot Requests permission |
CLAUDE_CODE_OAUTH_TOKEN |
Claude OAuth token (subscription) |
ANTHROPIC_API_KEY |
Anthropic API key (pay-per-use) |
OPENAI_API_KEY |
OpenAI API key for Codex CLI |
GITLAB_TOKEN |
GitLab PAT |
GITLAB_HOST |
GitLab instance URL |
GIT_AUTHOR_NAME / GIT_COMMITTER_NAME |
Git authorship inside container |
GIT_AUTHOR_EMAIL / GIT_COMMITTER_EMAIL |
Git email inside container |
TTYD_USER / TTYD_PASS |
Web terminal authentication |
Build args are baked in at docker compose build. Runtime vars take effect on container start — no rebuild needed.
- Tokens don't expire unless you set an expiry. OAuth tokens from
claude setup-tokenare valid for 1 year. .envis gitignored. Don't commit it.- Rebuild with latest CLI versions:
docker compose build --no-cache - Data logs persist across container restarts when
/var/log/clide/is volume-mounted.