Security Model¶
Vibepit applies defense-in-depth controls to reduce risk when you run AI coding agents. No single layer is sufficient on its own; each control limits the blast radius if another fails. This page explains what each control does and why it exists.
For a catalog of specific threats and mitigations, see the Threat Model. For how the components fit together at runtime, see the Architecture.
Default-deny network posture¶
A Vibepit session starts with no network access. DNS resolution, HTTP/HTTPS requests, and direct IP connections all fail unless you explicitly allowlist them. This forces you to declare every external dependency up front, making the agent's network surface auditable and reproducible.
Container hardening¶
The sandbox container runs with several kernel-level restrictions:
Read-only root filesystem. The container's root filesystem is mounted read-only (ReadonlyRootfs: true). This prevents the agent from persisting modifications to system binaries, libraries, or configuration files. A writable /tmp (mounted as tmpfs) is available for transient scratch data, and the home directory is a persistent volume, but the OS layer itself cannot be altered.
All Linux capabilities dropped. The container starts with every Linux capability removed (CapDrop: ["ALL"]). Capabilities like CAP_NET_RAW, CAP_SYS_ADMIN, and CAP_DAC_OVERRIDE are unavailable, which prevents raw socket creation, filesystem namespace manipulation, and permission bypass.
no-new-privileges. The no-new-privileges security option prevents processes inside the container from gaining additional privileges through setuid or setgid binaries. Even if such a binary exists on a mounted volume, executing it will not escalate privileges.
Non-root code user. The sandbox container runs as the unprivileged code user. If an attacker escapes the process sandbox but remains inside the container, they operate without root privileges, limiting what they can access on the host kernel.
Init process. The container runs with an init process (Init: true) as PID 1. This ensures proper signal forwarding to child processes and reaps zombie processes, preventing resource leaks during long-running agent sessions.
Network isolation¶
Each session creates an internal Docker bridge network (Internal: true). Containers on an internal network have no default route to the host or the internet. The only path to the outside is through the proxy container, which is dual-homed: it connects to both the internal session network and the default bridge network. All DNS and HTTP/HTTPS traffic from the sandbox routes through this proxy, where it is subject to allowlist filtering.
CIDR blocking¶
Even if a domain resolves and passes the allowlist, the proxy blocks connections to private and reserved IP ranges:
| CIDR | Purpose |
|---|---|
10.0.0.0/8 |
Private network (RFC 1918) |
172.16.0.0/12 |
Private network (RFC 1918) |
192.168.0.0/16 |
Private network (RFC 1918) |
127.0.0.0/8 |
Localhost / loopback |
169.254.0.0/16 |
Link-local (APIPA) |
fc00::/7 |
IPv6 unique local addresses |
fe80::/10 |
IPv6 link-local addresses |
::1/128 |
IPv6 loopback |
These blocks apply by default regardless of allowlist rules, preventing an allowlisted domain from being used to reach internal services via DNS rebinding or other IP-level attacks. The CIDRBlocker accepts additional custom ranges via block-cidr if your environment requires broader restrictions.
If you deliberately need to reach an otherwise-blocked range, allow-cidr punches an explicit exception: any IP within an allow-cidr range is permitted even if it falls inside a blocked range.
DNS filtering¶
The proxy runs a DNS server on port 53, configured as the sole resolver for sandbox containers. Only domains that match an allowlist rule receive a valid response; all other queries are refused.
Allowlist rules support two forms:
- Exact match:
example.commatches onlyexample.com. - Wildcard:
*.example.commatches exactly one subdomain label (such asapi.example.com) but does not match the apex domainexample.comor deeper subdomains likea.b.example.com. Use**.example.comto match one or more subdomain levels.
You can add DNS rules at startup via configuration or at runtime using the allow-dns command. Rules are additive and applied atomically using lock-free concurrency, so updates do not block in-flight queries.
HTTP/HTTPS filtering¶
The proxy filters all HTTP and HTTPS traffic using a domain:port allowlist. HTTPS connections use the CONNECT method, so the proxy sees the target hostname and port without terminating TLS.
Each rule specifies a domain pattern and a port pattern:
- Domain matching follows the same exact and wildcard semantics as DNS filtering.
- Port patterns accept an exact port number or
*for any port.
A request must match both the domain and port components of at least one rule to be allowed. Rules are purely additive: you can add them at startup or at runtime with the allow-http command, but you cannot remove them during a session.
mTLS control API¶
The proxy exposes a control API for runtime administration (adding allowlist entries, streaming logs). This API is secured with mutual TLS (mTLS) to prevent the sandbox container or other processes from issuing unauthorized control commands.
Each session generates ephemeral Ed25519 key pairs:
- An ephemeral CA signs a server certificate and a client certificate.
- The CA private key is discarded immediately after signing. No additional certificates can be issued for the session.
- The server certificate's Subject Alternative Name (SAN) is restricted to
127.0.0.1, so it is only reachable from the host. - TLS 1.3 is enforced as the minimum version.
- The server requires and verifies client certificates against the ephemeral CA (
RequireAndVerifyClientCert).
Because the CA key is discarded after signing, an attacker who compromises the proxy at runtime cannot mint new client certificates. Server credentials are passed to the proxy container via environment variables and never touch disk. Client credentials (CA cert, client cert, client key) are written to $XDG_STATE_HOME/vibepit/sessions/<sessionID>/ with 0600 permissions so that CLI subcommands can authenticate from separate processes. These files are deleted when the session ends.
SSH authentication¶
In daemon mode (vibepit up), the sandbox container runs an SSH server for remote access. SSH authentication uses ephemeral Ed25519 keypairs generated per session:
- Two keypairs are generated at session startup: a client keypair and a host keypair.
- The client private key and host public key remain on the host in
$XDG_STATE_HOME/vibepit/sessions/<sessionID>/with0600permissions (private keys) and0644permissions (public keys). - The host private key is bind-mounted read-only into the sandbox container. The client public key is passed via environment variable.
- Only public key authentication is accepted — no passwords.
- The SSH server verifies the connecting client's key against the single authorized public key.
- The SSH client (
vibepit connect) verifies the server's host key against the stored public key, preventing MITM attacks.
All SSH credentials are deleted when the session ends (vibepit down). Because keys are never reused across sessions, a compromised key cannot grant access to another.
SSH network access¶
The SSH port is not directly accessible from the host. It is forwarded through the proxy container, which publishes it to 127.0.0.1 on a random port. This means SSH is only reachable from the local machine.
Proxy image¶
The proxy container runs on gcr.io/distroless/base-debian13. Distroless images contain no shell, no package manager, and no OS-level utilities. This minimizes the attack surface of the proxy itself: even if an attacker achieves code execution inside the proxy container, there are no tools available to escalate or pivot.
What this is not¶
Vibepit is not VM-level isolation. The sandbox container shares the host kernel, which means:
- Container escapes through kernel vulnerabilities can grant host access.
- Runtime bugs in Docker or Podman can weaken isolation guarantees.
- Host misconfiguration (such as mounting the Docker socket into the container) can bypass all controls.
Treat Vibepit as defense in depth: multiple independent controls that collectively reduce risk. It is not absolute containment. For workloads that require stronger isolation guarantees, consider running Vibepit inside a VM.
Container runtimes and VM boundaries¶
Some container runtimes run containers inside a lightweight Linux VM:
- Docker Desktop (macOS, Windows, and Linux) runs a VM using the platform's hypervisor (Apple Virtualization framework, Hyper-V/WSL2, or KVM).
- Podman Machine (macOS, Windows) runs a Fedora CoreOS VM.
When a VM is present, a container escape lands in the VM guest, not directly on your host. This adds a boundary that does not exist when using Docker Engine on Linux or Podman on Linux (including Podman Desktop), where containers run natively and share the host kernel.
However, these VMs are designed for developer convenience, not as security boundaries. File sharing, socket forwarding, and networking features bridge the VM boundary in various ways, and the project directory is bind-mounted through it. Vibepit does not control or harden this VM layer, so you should not treat it as a guaranteed security control — but it does reduce the practical risk of a container escape reaching your host.