Matchlock – Secures AI agent workloads with a Linux-based sandbox
Source: Hacker News
Matchlock
Matchlock is a CLI tool for running AI agents in ephemeral microVMs. It provides:
- Network allow‑listing – only explicitly permitted connections are allowed.
- Secret injection via MITM proxy – secrets are injected safely without ever touching the VM’s filesystem.
- Secure defaults – everything else (network, storage, etc.) is blocked out‑of‑the‑box.
Your secrets never enter the VM, keeping your workloads isolated and secure.
Why Matchlock?
AI agents need to run code, but giving them unrestricted access to your machine is risky. Matchlock provides a full Linux environment that:
- Boots in under a second – isolated, disposable, and locked down by default.
- Injects real credentials in‑flight when the agent calls an API; the sandbox only ever sees a placeholder.
- Seals the network by default – nothing can leave the sandbox unless you explicitly allow it.
- Protects your secrets – even if the agent is tricked into running malicious code, your keys never leak and there’s nowhere for data to escape.
Inside the sandbox the agent gets a complete Linux environment where it can:
- Install packages
- Write files
- Create any temporary mess it needs
Outside, your host machine remains untouched. Each sandbox runs on its own copy‑on‑write filesystem that vanishes when you’re done. The experience is identical whether you’re on a Linux server or a MacBook—same CLI, same behavior.
Quick Start
System Requirements
- Linux – KVM support required
- macOS – Apple Silicon
Install
brew tap jingkaihe/essentials
brew install matchlock
Usage
Basic
matchlock run --image alpine:latest cat /etc/os-release
matchlock run --image alpine:latest -it sh
Network allow‑list
matchlock run --image python:3.12-alpine \
--allow-host "api.openai.com" python agent.py
Secret injection (never enters the VM)
export ANTHROPIC_API_KEY=sk-xxx
matchlock run --image python:3.12-alpine \
--secret ANTHROPIC_API_KEY@api.anthropic.com python call_api.py
Long‑lived sandboxes
# Create a persistent VM (prints the VM ID)
matchlock run --image alpine:latest --rm=false
# Attach to an existing VM
matchlock exec vm-abc12345 -it sh
Lifecycle commands
matchlock list # list running VMs
matchlock kill # stop a VM
matchlock rm # remove a VM
matchlock prune # clean up stopped VMs
Build from a Dockerfile (uses BuildKit‑in‑VM)
matchlock build -f Dockerfile -t myapp:latest .
Pre‑build a rootfs from a registry image (caches for faster startup)
matchlock build alpine:latest
Image management
# List all images
matchlock image ls
# Remove a local image
matchlock image rm myapp:latest
# Import an image from a tarball
docker save myapp:latest | \
matchlock image import myapp:latest
SDK
Matchlock ships with Go and Python SDKs that let you embed sandboxes directly in your application. You can programmatically launch VMs, execute commands, stream output, and write files.
Go
package main
import (
"fmt"
"os"
"github.com/jingkaihe/matchlock/pkg/sdk"
)
func main() {
// Create a client with the default configuration.
client, _ := sdk.NewClient(sdk.DefaultConfig())
defer client.Close()
// Define the sandbox.
sandbox := sdk.New("alpine:latest").
AllowHost("dl-cdn.alpinelinux.org", "api.anthropic.com").
AddSecret("ANTHROPIC_API_KEY", os.Getenv("ANTHROPIC_API_KEY"), "api.anthropic.com")
// Launch the sandbox and install curl.
client.Launch(sandbox)
client.Exec("apk add --no-cache curl")
// The VM only ever sees a placeholder – the real key never enters the sandbox.
result, _ := client.Exec("echo $ANTHROPIC_API_KEY")
fmt.Print(result.Stdout) // prints "SANDBOX_SECRET_a1b2c3d4..."
// Stream a request to Anthropic's API.
curlCmd := `curl -s --no-buffer https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-haiku-4-5-20251001","max_tokens":1024,"stream":true,
"messages":[{"role":"user","content":"Explain TCP to me"}]}'`
client.ExecStream(curlCmd, os.Stdout, os.Stderr)
}
Python (PyPI)
pip install matchlock
# or
uv add matchlock
import os
import sys
from matchlock import Client, Config, Sandbox
# Define the sandbox.
sandbox = (
Sandbox("alpine:latest")
.allow_host("dl-cdn.alpinelinux.org", "api.anthropic.com")
.add_secret(
"ANTHROPIC_API_KEY", os.environ["ANTHROPIC_API_KEY"], "api.anthropic.com"
)
)
curl_cmd = """curl -s --no-buffer https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"model":"claude-haiku-4-5-20251001","max_tokens":1024,"stream":true,
"messages":[{"role":"user","content":"Explain TCP/IP."}]}'"""
# Use the client to launch the sandbox and run commands.
with Client(Config()) as client:
client.launch(sandbox)
client.exec("apk add --no-cache curl")
client.exec_stream(curl_cmd, stdout=sys.stdout, stderr=sys.stderr)
Full examples
- Go:
- Python:
Architecture
graph LR
subgraph Host
CLI["Matchlock CLI"]
Policy["Policy Engine"]
Proxy["Transparent Proxy + TLS MITM"]
VFS["VFS Server"]
CLI --> Policy
CLI --> Proxy
Policy --> Proxy
end
subgraph VM["Micro‑VM (Firecracker / Virtualization.framework)"]
Agent["Guest Agent"]
FUSE["/workspace (FUSE)"]
Image["Any OCI Image (Alpine, Ubuntu, etc.)"]
Agent --- Image
FUSE --- Image
end
Proxy -- "vsock :5000" --> Agent
VFS -- "vsock :5001" --> FUSE
The diagram shows the relationship between the Matchlock CLI, Policy Engine, Transparent Proxy, and VFS Server on the host side, and the Guest Agent, FUSE workspace, and OCI image inside a micro‑VM (Firecracker or Virtualization.framework). Communication between host and VM occurs over vsock ports 5000 and 5001.
Network Modes
| Platform | Mode | Mechanism |
|---|---|---|
| Linux | Transparent proxy | nftables DNAT on ports 80/443 |
| macOS | NAT (default) | Virtualization.framework built‑in NAT |
| macOS | Interception | gVisor userspace TCP/IP at L4 (--allow-host / --secret) |
Docs
See the full developer reference in AGENTS.md.
License
MIT