Architecture¶
Vibepit runs entirely on your local machine — there is no cloud service, no remote API, and no account required. It orchestrates three components — a host CLI, a proxy container, and a sandbox container — connected by an isolated Docker network. This page explains how these pieces fit together and why each design choice exists.
Overview¶
When you run vibepit, the CLI orchestrates a small cluster of containers on your machine:
┌─────────────────────────────────────────────────┐
│ Host │
│ │
│ ┌───────────┐ │
│ │ vibepit │ creates network, starts │
│ │ CLI │ containers, manages credentials │
│ └─────┬─────┘ │
│ │ │
│ ──────┼──── isolated network (10.x.x.0/24) ── │
│ │ │
│ ┌─────┴─────┐ ┌──────────────┐ │
│ │ Proxy │ │ Sandbox │ │
│ │ Container│◄──────│ Container │ │
│ │ │ │ (your code) │ │
│ └─────┬─────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ Internet (filtered) │
└─────────────────────────────────────────────────┘
The sandbox container has no direct internet access. All outbound traffic — DNS queries and HTTP/HTTPS requests — must pass through the proxy, which enforces allowlist rules before forwarding anything.
Host CLI¶
The vibepit binary runs on your host machine and orchestrates everything:
- Creates an isolated network with a random subnet.
- Starts the proxy container on that network with a static IP.
- Starts the sandbox container configured to route all traffic through the proxy.
- Generates ephemeral mTLS credentials so that only the CLI can talk to the proxy's control API.
- Attaches your terminal to the sandbox container's shell (in
runmode) or waits for the daemon to accept connections (inupmode).
The CLI supports two operating modes:
runmode (default): attaches your terminal directly to the sandbox container. The session lifecycle is tied to the firstvibepitprocess — when you exit, the containers are cleaned up.- Daemon mode (
up/connect/down): starts the containers in the background with an SSH server. You connect withvibepit connect, and sessions inside the sandbox persist across disconnects. The containers run until you explicitly stop them withvibepit down.
After setup, the CLI also provides runtime commands — allow-http, allow-dns, monitor, and status — that communicate with the proxy's control API to modify the allowlist, observe traffic, or inspect session state. See the CLI Reference for command details.
Isolated Network¶
Each session gets its own internal Docker bridge network with a random 10.x.x.0/24 subnet. The network is created with Internal: true, which means Docker does not attach a gateway to the external network — containers on this network have no direct route to the internet.
This is the foundation of Vibepit's network isolation. Rather than trying to block specific destinations, the architecture starts from zero access and requires all traffic to pass through the proxy. The proxy is the only component connected to both the isolated network and the default bridge network, making it the sole path to the outside world.
The random subnet avoids collisions with other Docker networks on the host. In the unlikely event of a collision (~1 in 65,000), network creation fails with a "pool overlaps" error and you can retry.
Proxy Container¶
The proxy runs on a minimal distroless base image (gcr.io/distroless/base-debian13) with no shell, no package manager, and no unnecessary libraries. It runs the vibepit proxy binary, which is bind-mounted from the host along with its configuration file.
The proxy container is connected to two networks: the isolated session network (with a static IP so the sandbox container can find it) and the default bridge network (so it can reach the internet). This dual-homed setup is what makes it the gatekeeper for all outbound traffic.
Three services¶
The proxy runs three services in a single process:
-
HTTP proxy (dynamic port) — handles both HTTP and HTTPS (via
CONNECTtunneling). Every request is checked against the HTTP allowlist, which matches on domain and port. Requests to non-allowed destinations are rejected. A separate CIDR blocklist prevents connections to private and link-local IP ranges, blocking attempts to reach the Docker host or other local services. -
DNS server (port 53) — receives all DNS queries from the sandbox container. Allowed domains are forwarded to an upstream resolver (defaults to
9.9.9.9). Everything else returnsNXDOMAIN, preventing DNS-based data exfiltration. -
Control API (dynamic port) — an mTLS-secured HTTP API used by the CLI's
allow-http,allow-dns, andmonitorcommands. The port is published to127.0.0.1on the host, so only local processes with the correct client certificate can connect. See Control API below for details.
All three services share the same allowlist, which is updated atomically at runtime when you use allow-http or allow-dns.
Sandbox Container¶
The sandbox container is your workspace. It runs an Ubuntu-based image with common development tools, configured with several hardening measures:
- Read-only root filesystem — the container's root filesystem is mounted read-only. A writable
/tmp(tmpfs) is available for temporary files. - Dropped capabilities — all Linux capabilities are dropped (
CAP_DROP: ALL). - No new privileges — the
no-new-privilegessecurity option prevents privilege escalation via setuid binaries or other mechanisms. - Non-root user — processes run as the
codeuser, matched to your host UID. - Init process — an init process (tini) runs as PID 1 to handle signal forwarding and zombie reaping.
The container has your project directory bind-mounted (read-write), a persistent vibepit-home volume mounted at /home/code, and a persistent vibepit-linuxbrew volume mounted at /home/linuxbrew. These volumes survive across sessions, preserving shell state and installed Homebrew tools. The project's .vibepit configuration directory is hidden inside the sandbox to prevent the agent from modifying its own allowlist rules.
Environment variables HTTP_PROXY and HTTPS_PROXY are set to point at the proxy container's static IP. DNS is configured through the container runtime's DNS settings to use the proxy's DNS server on port 53. Together, these ensure all outbound traffic is routed through the proxy without any additional configuration.
For a complete description of the security controls, see Security Model.
Data Flow¶
All outbound traffic from the sandbox container follows two paths:
DNS queries: The sandbox container's DNS is configured to use the proxy container's IP on port 53. When a process inside the container resolves a hostname, the query goes to the proxy's DNS server. If the domain is on the DNS allowlist, the query is forwarded to the upstream resolver (9.9.9.9 by default). Otherwise, the proxy returns NXDOMAIN.
HTTP/HTTPS requests: The HTTP_PROXY and HTTPS_PROXY environment variables direct all HTTP traffic through the proxy. For HTTPS, the proxy uses CONNECT tunneling — it sees the destination hostname and port but not the encrypted payload. The proxy checks the destination against the HTTP allowlist and the CIDR blocklist before establishing the connection. Blocked requests receive an immediate rejection.
The CIDR blocklist is applied to resolved IP addresses and blocks all private ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), loopback (127.0.0.0/8), and link-local addresses by default. This prevents the sandbox container from reaching the Docker host, other containers, or local network services, even if a domain resolves to a private IP.
Host access via host.vibepit: The one exception to CIDR blocking is the synthetic hostname host.vibepit. The proxy's DNS server resolves it to the host machine's gateway IP, and the HTTP proxy forwards requests to it — but only on ports explicitly listed in allow-host-ports in the project config. This lets the sandbox reach local development services (databases, API servers) without opening up all private IP ranges. See Configure Network Presets for configuration details.
SSH Server¶
In daemon mode (vibepit up), the sandbox container runs an SSH server instead of directly attaching a terminal. The server listens on port 2222 inside the sandbox container.
Authentication¶
Each Vibepit session generates two ephemeral Ed25519 keypairs:
- A client keypair — the private key stays on the host, the public key is passed to the sandbox container as the authorized key.
- A host keypair — the private key is bind-mounted into the sandbox container, the public key stays on the host for host key verification.
Both keypairs are stored in the session credentials directory ($XDG_STATE_HOME/vibepit/sessions/<sessionID>/). When vibepit connect connects, it uses the client private key for authentication and verifies the server's host key against the stored public key.
Port forwarding¶
The SSH port is not published directly from the sandbox container. Instead, the proxy container forwards TCP traffic from a random host port to the sandbox's port 2222. This preserves network isolation — the sandbox container remains on the internal network with no direct host connectivity.
Session management¶
The SSH server manages persistent shell sessions inside the sandbox. Each shell session is a PTY-backed process that survives SSH client disconnects:
- When you connect with
vibepit connectand detached sessions exist, the server presents a selector screen where you can reattach to an existing session or start a new one. - Sessions persist until the shell process exits or the sandbox container stops.
- The server enforces a limit of 50 concurrent sessions.
Command execution¶
vibepit exec supports non-interactive command execution. When a command is passed (e.g., vibepit exec ls -la), the server runs the command via the user's shell and returns its exit code without creating a persistent session.
Session Lifecycle¶
There are two session lifecycles, one for each operating mode.
Run mode (default)¶
Startup¶
- The CLI generates a session ID and creates an internal Docker network with a random
10.x.x.0/24subnet. - Ephemeral mTLS credentials (Ed25519 CA, server cert, client cert) are generated. The CA private key is used to sign both certificates and then discarded — it never touches disk.
- The proxy container is created on both the session network and the bridge network, with the server certificate and CA cert passed via environment variables.
- The sandbox container is created on the session network only, with proxy environment variables pointing to the proxy's static IP.
- Client credentials (CA cert, client cert, client key) are written to
$XDG_STATE_HOME/vibepit/sessions/<sessionID>/so thatallow-http,allow-dns, andmonitorcan authenticate to the control API from separate processes. - The CLI attaches your terminal to the sandbox container and starts it.
Reattach¶
If you run vibepit again in the same project directory while a session is still running, the CLI detects the existing sandbox container and opens a new shell (exec) inside it rather than creating a new session. This means multiple terminal windows can share one session.
Cleanup¶
When you exit the shell, the sandbox container's entrypoint exits and the container stops. The CLI then:
- Removes the sandbox container.
- Removes the proxy container.
- Removes the session network.
- Deletes the credential files from the session directory.
Daemon mode (up/connect/down)¶
Startup¶
- Steps 1–5 are the same as run mode.
- Ephemeral Ed25519 SSH keypairs are generated and written to the session credentials directory. See SSH Server > Authentication for details.
- The sandbox container runs the
vibedentrypoint instead of a shell. This initializes the sandbox environment and starts the SSH server. - The CLI waits for the SSH daemon to accept connections, then prints the SSH address and exits.
Connect¶
You connect to the running session with vibepit connect. See SSH Server for authentication, session management, and command execution details.
Cleanup¶
Containers run until you explicitly stop them with vibepit down, which:
- Stops and removes all session containers (sandbox and proxy).
- Removes the session network.
- Deletes all credential files (mTLS and SSH keys) from the session directory.
Control API¶
The control API uses mutual TLS (mTLS) with ephemeral Ed25519 certificates to authenticate CLI commands. This prevents unauthorized processes from modifying the allowlist or reading traffic logs.
The trust model works as follows:
- At session startup, the CLI generates an ephemeral CA and uses it to sign a server certificate (for the proxy) and a client certificate (for the CLI).
- The CA private key is discarded immediately after signing. No new certificates can be issued for this session.
- The server certificate has a SAN of
127.0.0.1and is used by the control API listener. - The client certificate is stored in the session directory and loaded by
allow-http,allow-dns, andmonitorwhen they connect. - Both sides require TLS 1.3 and verify the peer certificate against the ephemeral CA.
The control API port is published only to 127.0.0.1, so it is not reachable from the network. Combined with mTLS, this means only the user who started the session can issue control commands.