clide Security & Threat Model
April 23, 2026 · View on GitHub
This document describes the trust boundaries, attack surface, known threats, and mitigations for clide.
Trust boundaries
┌─────────────────────────────────────────────────────┐
│ Host Machine │
│ │
│ .env (secrets) ──────────────────────────────┐ │
│ Project directory ─────────────────────────┐ │ │
│ │ │ │
│ ┌──────────────────────────────────────────▼──▼─┐ │
│ │ clide Container │ │
│ │ User: clide (uid=1000, non-root) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────┐ │ │
│ │ │ firewall.sh (iptables egress allowlist) │ │ │
│ │ └──────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ (allowlisted endpoints only) │ │
│ └──────────────────────────│────────────────────┘ │
│ │ │
└─────────────────────────────│────────────────────────┘
▼
Internet (restricted)
api.anthropic.com
api.githubcopilot.com
api.github.com / github.com
registry.npmjs.org
What the host trusts the container with
- Read-write access to your project directory — mounted at
/workspace. The container can read, write, and delete files in this directory. - API credentials —
GH_TOKEN,ANTHROPIC_API_KEY,CLAUDE_CODE_OAUTH_TOKEN, andOPENAI_API_KEYare passed in via.envand available as environment variables inside the container. - Network access — restricted to the egress allowlist by default.
What the container does NOT have
- Access to the rest of the host filesystem (only
/workspaceis mounted) - Root privileges during normal operation (gosu drops to
clideuid=1000 before any workload starts) - Unrestricted internet access (egress firewall allowlist — when
NET_ADMINis available) - Access to other containers or host services beyond what Docker networking exposes
Attack surface
Web terminal (ttyd)
- Exposure: ttyd binds to
0.0.0.0:7681by default, exposing a full shell over HTTP. - Risk: Anyone who can reach that port gets an interactive shell as
clidewith access to your project files and API credentials. - Mitigations:
- Basic auth enforced by default (
TTYD_USER+TTYD_PASSrequired; container refuses to start without them unlessTTYD_NO_AUTH=trueis explicitly set) - Bind to
127.0.0.1only if not using a reverse proxy (setTTYD_PORT=127.0.0.1:7681in.env) - Use a TLS-terminating reverse proxy (e.g. Caddy) in production — see
DEPLOY.md
- Basic auth enforced by default (
Mounted workspace
- Exposure: The container has read-write access to everything under
PROJECT_DIR(default: parent directory of the clide repo). - Risk: A compromised or misbehaving AI agent could modify, delete, or exfiltrate source code and committed secrets.
- Mitigations:
- Use git to track changes; review diffs before committing
- Point
PROJECT_DIRat a specific repo rather than a broad parent directory - The egress firewall limits where data can be sent
API credentials in environment
- Exposure: Tokens (
GH_TOKEN,ANTHROPIC_API_KEY, etc.) are present as environment variables inside the container. - Risk: A process running inside the container can read and exfiltrate these tokens.
- Mitigations:
- Egress firewall restricts outbound traffic to known endpoints — a stolen token can't easily be sent to an attacker's server
- Use fine-grained PATs with minimal permissions (e.g.
GH_TOKENonly needs "Copilot Requests") - Rotate tokens regularly; set expiry dates on PATs
Egress / network
- Exposure: Containers can make outbound network requests.
- Risk: An AI agent could exfiltrate data, download malicious payloads, or establish reverse shells.
- Mitigations:
- iptables egress allowlist restricts outbound to known good endpoints
REJECT(notDROP) so connection failures are immediate and visible- DNS is allowed (required for hostname resolution) — a motivated attacker could use DNS tunneling; this is a known limitation of IP-based egress filtering
Container privilege
- Exposure: The entrypoint must start as root to apply iptables rules.
- Risk: A vulnerability in the startup scripts could allow privilege retention.
- Mitigations:
gosu clideis called before any user-facing workload; the process tree runs as uid=1000cap_drop: ALL+cap_add: NET_ADMIN— only the firewall capability is retained, dropped after useno-new-privileges: true— prevents setuid escalationpids_limit,mem_limit,cpus— resource guardrails against runaway processes
Threat scenarios
| Threat | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Unauthenticated web terminal access | Medium (if exposed on network) | High (full shell) | Basic auth required by default; use reverse proxy + TLS |
| AI agent exfiltrates source code | Low | High | Egress firewall limits destinations; review agent output |
| AI agent deletes project files | Low | Medium | Use git; review before committing |
| API token theft via network | Low | Medium | Egress firewall; token scoping; rotation |
| Container escape to host | Very Low | High | Non-root user; minimal capabilities; no privileged mode |
| Malicious dependency in npm install | Low | Medium | Pin versions (#7); review package.json |
| DNS tunneling for data exfiltration | Very Low | Low | Known limitation of IP-based egress filtering |
Docker socket is NOT mounted
The Docker socket (/var/run/docker.sock) is intentionally not mounted into any clide container. This is a critical security boundary:
- Why it matters: Mounting the Docker socket gives a container full control over the Docker daemon — it can create privileged containers, mount the host filesystem, and effectively achieve root access on the host. This is the most common container escape vector.
- Current state: Neither
docker-compose.ymlnor any entrypoint script references the Docker socket. The container has no access to Docker commands or the Docker API. - CI enforcement: Any PR that adds a
/var/run/docker.sockvolume mount should be rejected. Consider adding a CI check that grepsdocker-compose*.ymlandDockerfilefordocker.sockand fails the build if found:# Example CI guard (add to your CI pipeline) if grep -r 'docker\.sock' docker-compose*.yml Dockerfile 2>/dev/null; then echo "FAIL: Docker socket must never be mounted into the container" exit 1 fi - If you need Docker-in-Docker: Use a purpose-built DinD sidecar with its own isolated daemon rather than sharing the host socket. This is not currently supported or needed by clide.
Deployment hardening recommendations
For production or shared deployments (e.g. production/shared):
- Always use TLS — put clide behind Caddy or another TLS-terminating proxy. Never expose ttyd directly over HTTP on a public network.
- Enable basic auth — set
TTYD_USERandTTYD_PASSin.env. Do not useTTYD_NO_AUTH=truein production. - Scope
PROJECT_DIR— mount only the specific repo you're working on, not a broad parent directory. - Use minimal-permission tokens — create fine-grained PATs with only the permissions each CLI needs.
- Set token expiry — don't create non-expiring tokens. Rotate on a schedule.
- Review egress — if you need to add hosts to
CLIDE_ALLOWED_HOSTS, understand why before adding them. - Monitor logs —
docker compose logs -f webwill show firewall warnings and auth events.
Reporting security issues
Please report security vulnerabilities privately to the repository owner rather than opening a public issue.