kap
[!WARNING] This is experimental and may have bugs. Use at your own risk.
Secure capsule for AI coding agents, built on devcontainers. kap runs as a sidecar that isolates your agent from the network and controls what it can reach:
- Domain allowlist - only approved domains are reachable from the container
- MCP tool allowlist - only approved tools are callable on remote MCP servers
- CLI proxy -
gh,aws, etc. are proxied with per-command allowlists - Credential isolation - tokens and API keys live on the sidecar and never enter the app container
- Remote monitoring - monitor and steer agents from your phone over local WiFi
Quick start
Domain allowlist
The config is a flat domain allowlist in kap.toml. kap init generates a starting list with safe defaults for common ecosystems (GitHub, npm, PyPI, RubyGems, crates.io, Maven, CocoaPods, Go, APT, and AI providers).
[]
= [
"github.com",
"*.github.com",
"crates.io",
"*.crates.io",
"*.ubuntu.com",
]
# deny overrides allow:
= ["gist.github.com"]
Wildcards (*.github.com) match subdomains but not the bare domain. Deny rules always win.
MCP proxy
The MCP proxy sits between the agent and remote MCP servers. The agent connects to the proxy over unauthenticated HTTP on the internal network. The proxy injects credentials when forwarding to the upstream server. The app container has no access to OAuth tokens, API keys, or any other secrets.
Registering servers
Register MCP servers on the host:
# OAuth (opens browser)
# API key via headers
After registering, add each server to kap.toml with an allow_tools list (see below). The agent connects to http://172.28.0.3:3129/<name> instead of the real server. kap handles auth and tool filtering.
Tool allowlist
Each server needs a [[mcp.servers]] entry in kap.toml with an explicit allow_tools list. Same model as the domain allowlist - only what's listed is permitted:
[]
# Allow all tools
[[]]
= "context7"
= ["*"]
# Allow only read/search operations
[[]]
= "github"
= ["get_*", "list_*", "search_*"]
Wildcards work the same as domain patterns (get_* matches get_issue, get_user, etc.).
CLI proxy
The CLI proxy lets the app container run tools like gh or aws without direct access to credentials. Commands are forwarded to the sidecar which validates them against an allowlist and executes with the configured env vars.
[]
[[]]
= "gh"
= ["pr *", "issue *", "repo *", "search *", "auth status"]
= ["GH_TOKEN"]
[[]]
= "aws"
= ["s3 ls *", "s3 cp *", "sts get-caller-identity"]
= ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
deny overrides allow. Tools must be installed on the sidecar (gh is included by default; add others via [compose] build).
Compose overlay
By default, the kap sidecar pulls from ghcr.io/6/kap:latest. To build from source or use a different image, add a [compose] section to kap.toml:
# Pull from a registry (default if [compose] is omitted)
[]
= "ghcr.io/6/kap:latest"
# Or build from source
[]
= { = "..", = ".devcontainer/Dockerfile", = "proxy" }
The overlay is regenerated by kap init-env on every devcontainer up. Don't edit docker-compose.kap.yml directly.
Remote access
Monitor and steer AI agents running in devcontainers from your phone over local WiFi.
Scan the QR code on your phone to open the web UI. It auto-pairs and gives you:
- Status -container state, proxy health, denied request count
- Logs -live streaming proxy events, filterable by denied-only
- Agent -Claude Code session timelines, tool calls, cancel button, follow-up prompts
The daemon runs on the host and uses docker exec to reach into both containers. All API endpoints require a bearer token issued during QR pairing. Settings for light/dark mode and font size.
Architecture
┌──────────────────────────────────────────────────┐
│ Internal network │
│ │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ App container │ │ Proxy sidecar │ │
│ │ (isolated) │ │ │ │
│ │ HTTP_PROXY ──┼───►│ domain proxy │──► Internet
│ │ │ │ :3128 │ │
│ │ DNS ─────────┼───►│ DNS forwarder │──► Upstream DNS
│ │ │ │ :53 │ │
│ │ MCP servers ─┼───►│ MCP proxy │──► MCP servers
│ │ via http:// │ │ :3129 │ │
│ │ proxy:3129 │ │ │ │
│ │ gh/aws/etc ──┼───►│ CLI proxy │──► APIs
│ │ (shims) │ │ :3130 │ │
│ │ │ │ │ │
│ │ (no tokens) │ │ (credentials) │ │
│ └──────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────┘
- The app container has no external network route. All traffic goes through the proxy sidecar.
- DNS queries only resolve allowed domains. Disallowed domains get NXDOMAIN.
- Blocked domain requests get a 403. Blocked MCP tool calls get a JSON-RPC error.
- Credentials never enter the app container. OAuth tokens, API keys, and GH_TOKEN live on the proxy sidecar only. The proxy injects auth when forwarding upstream.
Security model
Network isolation is kernel-enforced, not proxy-based. The Docker internal: true network has no default gateway, so the app container has no IP route to the outside world. Unsetting HTTP_PROXY or making direct TCP connections doesn't bypass it. Packets have nowhere to go. The only reachable host is the proxy sidecar on the internal network.
MCP server domains are intentionally not in the domain allowlist. The agent can only reach them through kap's MCP proxy, which enforces tool filtering. Connecting directly would be blocked by the network.
Known limitations:
- Domain fronting: a CONNECT request to an allowed CDN domain could route to an attacker's backend via SNI/Host manipulation. kap sees the CONNECT domain, not the backend.
- Container escape: a kernel exploit that breaks out of the container bypasses all isolation. Not specific to kap. Running Docker inside a VM (e.g., Docker Desktop, Firecracker) adds defense-in-depth.
- No TLS inspection: for HTTPS, kap sees
CONNECT domain:443but cannot inspect request paths, headers, or bodies inside the tunnel. A process with valid credentials for an allowed domain can do anything that domain permits.
Commands
| Command | Purpose |
|---|---|
kap init |
Scaffold kap into a project |
kap up |
Start the devcontainer |
kap down |
Stop and remove the devcontainer |
kap exec [cmd] |
Run a command in the devcontainer (default: shell) |
kap list |
List running devcontainers |
kap status |
Check if everything is wired correctly |
kap why-denied |
Show denied requests from the proxy log |
kap mcp add <name> <url> [--header] |
Register an MCP server (OAuth or API key) |
kap mcp get <name> |
Show server details and tools list |
kap mcp list |
List registered servers |
kap remote start |
Start the remote access daemon |
kap remote pair |
Show QR code for phone pairing |
kap remote devices |
List paired devices |
kap remote revoke <id> |
Revoke a paired device |
Development
This repo includes a .devcontainer/ that dogfoods kap itself. It builds from source, runs with GitHub/Rust/APT/AI domains allowed, and proxies Context7 as a sample MCP server. Set CONTEXT7_API_KEY in your environment to try it (free key from context7.com/dashboard). Open in VS Code or run: