Matchlock: microVM sandboxes for AI agents, with secrets held outside the guest
Matchlock secures AI agent workloads with a Linux-based sandbox.
At a glance
- What is it?
- Matchlock is a Go CLI and SDK that runs agent workloads in ephemeral microVMs with a host-side MITM proxy for allowlisting and credential injection. It is marked experimental, and the README's own caveats should shape any adoption decision.
- Who is it for?
- Adopt Matchlock if you are running agents that execute generated code or shell commands on a developer machine or build host and you want the credential to stay on the host rather than inside the guest. Do not adopt it if you need a stable API surface today: the README labels the project experimental and subject to breaking changes, and the SDK's private-IP defaults have already been reworked between releases.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 51 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Matchlock targets: agents that run code you did not write
An agent that can execute shell commands needs somewhere to execute them. Running those commands directly on a laptop or a CI runner means the agent inherits the machine's filesystem, its network position, and any credentials in its environment. The README frames this plainly: agents need to run code, and giving them unrestricted access to your machine is a risk. Matchlock's answer is to hand the agent a full Linux environment that boots in under a second, per the README's claim, and to treat that environment as disposable. The audience is narrow and specific. It is people wiring agents into developer workstations or build infrastructure who want isolation at the VM boundary rather than the process boundary, and who are willing to run a host setup step that installs whatever the sandbox needs. The README states the same CLI and behaviour apply whether you are on a Linux server or a MacBook, which matters if your developers and your CI run different operating systems.
Two mechanisms carry the design: a sealed network and a placeholder secret
The first mechanism is the network allowlist. When you pass `--allow-host` or `--secret`, the README says Matchlock seals the network: only traffic to explicitly allowed hosts gets through and everything else is blocked. That is a default-deny posture triggered by the presence of those flags, not a separate mode you opt into. The second mechanism is secret injection. You pass `--secret ANTHROPIC_API_KEY@api.anthropic.com`, and the real credential is injected in-flight by a host-side MITM proxy. The guest sees a placeholder. The Go SDK example makes this concrete: after adding a secret, executing `echo $ANTHROPIC_API_KEY` inside the sandbox prints a value shaped like `SANDBOX_SECRET_a1b2c3d4...`. The README states the real key never enters the sandbox. The two mechanisms interlock. Because the proxy terminates the connection, it can substitute credentials on the way out, and because the allowlist is enforced at the same layer, a compromised agent has no destination for exfiltrated data beyond the hosts you named. The README also notes volume overlay mounts are isolated snapshots that vanish when the run ends, so filesystem writes do not persist to the host by default.
Install paths and the host setup step that gates everything
The README lists Linux with KVM support and macOS on Apple Silicon as the system requirements. There is no Intel Mac path documented. The quickest route is the install script, which the README says detects the OS and uses Homebrew on macOS and rpm or deb packages on Debian and RHEL flavoured distributions: `curl -fsSL https://raw.githubusercontent.com/jingkaihe/matchlock/main/scripts/install.sh | bash`. You can pin a version with `bash -s -- --version 0.2.4`. Homebrew users can instead run `brew tap jingkaihe/essentials` followed by `brew install matchlock`. On Debian or Ubuntu the documented sequence is `sudo dpkg -i ./matchlock_<version>_linux_amd64.deb`, then `sudo apt-get install -f`, then `matchlock diagnose`. The RPM path is `sudo dnf install ./matchlock_<version>_linux_amd64.rpm` followed by the same diagnose call. That diagnose command is the real gate. If it reports missing host setup, the README directs you to `sudo matchlock setup linux`, and to `sudo matchlock setup user <name>` to enroll a specific user explicitly. Budget time for this step. A CLI that boots microVMs needs host-level privileges configured before the first `matchlock run` will work, and the failure mode if you skip it is a diagnose report rather than a working sandbox.
Running sandboxes: one-shot, long-lived, and the allowlist as a runtime object
The basic invocation is `matchlock run --image alpine:latest cat /etc/os-release`, with `-it sh` for an interactive shell and `--no-network` for a fully offline run. The interesting mode is the long-lived sandbox. Passing `--rm=false` or `-d` prints a VM ID and leaves the sandbox running, after which `matchlock exec vm-abc12345 -it sh` attaches to it and `matchlock port-forward vm-abc12345 8080:8080` forwards host port 8080 to guest port 8080. Ports can also be published at startup with `-p 8080:8080`. The allowlist becomes mutable in this mode. The README shows `matchlock run --image alpine:latest --rm=false --network-intercept` to keep interception enabled even with an empty allowlist, then `matchlock allow-list add <vm-id> api.openai.com,api.anthropic.com` and `matchlock allow-list delete <vm-id> api.openai.com` to change it while the VM is alive. That is a real design decision: a long-running agent can be granted a new destination without restarting the sandbox, and revoked from one without losing its filesystem state. Lifecycle commands are `matchlock list`, `kill`, `rm`, and `prune`. Builds go through `matchlock build -f Dockerfile -t myapp:latest .`, which the README says uses BuildKit inside the VM, and `matchlock build alpine:latest` pre-builds a rootfs from a registry image so later startups hit a cache. Images can be listed, removed, or imported from a tarball with `docker save myapp:latest | matchlock image import myapp:latest`.
The SDK exposes network interception, and its private-IP defaults are a moving target
Matchlock ships Go, Python, and TypeScript SDKs. The Go example constructs a sandbox with a builder: `sdk.New("alpine:latest").AllowHost(...).AddSecret(...)`, then `client.Launch(sandbox)` and `client.Exec(ctx, ...)`. Streaming output goes through `client.ExecStream(ctx, curlCmd, os.Stdout, os.Stderr)`. The private-IP handling deserves attention because the README documents a default that is easy to get wrong. For the ranges `10/8`, `172.16/12`, and `192.168/16`, the default when unset is that private IPs are blocked whenever a network config is sent. You can make that explicit with `.WithBlockPrivateIPs(true)` or `.BlockPrivateIPs()`, or override it with `.AllowPrivateIPs()` or `.WithBlockPrivateIPs(false)`. The README warns that if you call `client.Create(...)` directly instead of using the builder, you must set `BlockPrivateIPsSet: true` alongside `BlockPrivateIPs: false` or `true`. That is a sharp edge: the set-flag exists because the zero value of a boolean cannot distinguish unset from false, and getting it wrong silently changes whether your agent can reach internal services. The SDK also supports request and response mutation through `WithNetworkInterception`, with rules carrying a `Phase` of `NetworkHookPhaseBefore` or `NetworkHookPhaseAfter`, an action such as `NetworkHookActionMutate`, `SetHeaders`, and `BodyReplacements` built from `NetworkBodyTransform` find-and-replace pairs. The README mentions SSE data-line transforms as part of this surface.
Where Matchlock is the wrong tool
The README opens with an experimental banner: the project is in active development and subject to breaking changes. That is not boilerplate. The release cadence shown in the repository metadata runs v0.2.15 through v0.2.17 across roughly six weeks, which is a normal patch rhythm for a pre-1.0 tool, and the private-IP semantics documented above read like a behaviour that has been revised rather than settled. If you are building a hosted product where sandbox behaviour is part of your contract with customers, pinning a version and reading the diff before every bump is the cost of entry. The platform requirements rule out other cases entirely. Linux without KVM support is not covered, and neither is an Intel Mac. Windows is absent from the system requirements. There is also a structural limit in the secret-injection model: the MITM proxy only helps for traffic that goes through it. A secret injected for `api.anthropic.com` is scoped to that host by the `@host` syntax, and the README does not describe a path for credentials used by a binary that ignores proxy environment variables or pins a certificate. The placeholder scheme assumes the agent's HTTP client trusts the proxy's certificate chain. If your workload speaks a non-HTTP protocol, the network interception layer as documented has nothing to say about it.
Matchlock versus running the agent in a plain container
The obvious alternative is Docker or Podman with a locked-down container, and the difference is not cosmetic. A container shares the host kernel. A microVM does not, which is why Matchlock's README can describe VM-level isolation as a distinct property rather than a configuration of the same thing. The second difference is where the credential lives. In a container, the usual pattern is to pass the API key as an environment variable or a mounted file, which means the secret is present inside the workload and any code the agent runs can read it. Matchlock inverts that: the real key stays on the host and the guest holds a placeholder. A container-based setup can approximate this with an egress proxy and a secret broker, but you are then assembling the allowlist, the proxy, and the substitution logic yourself. The third difference is startup and state management. The README claims sub-second boots and describes overlay mounts as snapshots that vanish, which is a different default from a container's writable layer that persists until you remove it. What you give up is familiarity. Container tooling has a decade of operational knowledge behind it, and Matchlock's `matchlock setup linux` step and KVM dependency mean it does not drop into an arbitrary CI image the way `docker run` does.
Maintenance cost, licence, and what to check before committing
Matchlock is MIT licensed, which permits commercial use, modification, and redistribution provided the copyright notice and permission notice are preserved. That is a permissive licence with no copyleft obligation on your own code, though you should read the LICENSE file in the repository rather than rely on a summary, and this is not legal advice. The practical maintenance cost sits in three places. The first is the host setup: `matchlock setup linux` and `matchlock setup user <name>` are privileged operations, so any machine that runs Matchlock needs that provisioning baked into its image rather than applied by hand. The second is the release cadence. At v0.2.17 with breaking changes declared, upgrade cost is real, and the private-IP default documented in the README is exactly the kind of thing that changes between minor versions. Read the release notes before bumping. The third is image caching. The README offers `matchlock build alpine:latest` to pre-build a rootfs for faster startup, which implies that without a pre-built rootfs you pay a pull or build cost on first use. If your agent images are large, that cache is not optional. The Go SDK also exposes `WithNetworkMTU(1200)` and `AddHost("api.internal", "10.0.0.10")` for host-to-IP mapping, so environments with unusual MTU requirements or internal DNS have a documented knob, but each of those is a configuration you now own. Before writing Matchlock into a service, run `matchlock diagnose` on the actual target host, confirm the `BlockPrivateIPsSet` semantics in the version you pin, and decide whether you are calling the builder or `client.Create(...)` directly, because the README treats those two paths differently.
Editorial conclusion
Adopt Matchlock if you are running agents that execute generated code or shell commands on a developer machine or build host and you want the credential to stay on the host rather than inside the guest. Do not adopt it if you need a stable API surface today: the README labels the project experimental and subject to breaking changes, and the SDK's private-IP defaults have already been reworked between releases. Verify first that `matchlock diagnose` reports a clean host setup on your target machine, and confirm the KVM or Apple Silicon requirement matches your fleet before you write the SDK calls into a service.
Community notes