
Sandbox efêmero de microVM para agentes de IA com listagem de permissões de rede, injeção de segredos via proxy MITM e isolamento em nível de VM. Inicializa em menos de um segundo, suporta SDKs Go/Python/TypeScript.
Experimental: Este projeto ainda está em desenvolvimento ativo e sujeito a mudanças significativas.
Matchlock é uma ferramenta CLI para executar agentes de IA em microVMs efêmeras - com lista de permissões de rede, injeção de segredos via proxy MITM e isolamento em nível de VM. Seus segredos nunca entram na VM.
Agentes de IA precisam executar código, mas dar-lhes acesso irrestrito à sua máquina é um risco. Matchlock permite que você entregue a um agente um ambiente Linux completo que inicializa em menos de um segundo - isolado e descartável.
Quando você passa --allow-host ou --secret, Matchlock sela a rede - apenas tráfego para hosts explicitamente permitidos passa, e todo o resto é bloqueado. Quando seu agente chama uma API, as credenciais reais são injetadas em voo pelo host. O sandbox só vê um espaço reservado. Mesmo que o agente seja enganado para executar algo malicioso, suas chaves não vazam e não há para onde os dados irem. Dentro, o agente obtém um ambiente Linux completo para fazer o que precisar. Ele pode instalar pacotes, escrever arquivos e fazer bagunça. Lá fora, sua máquina não sente nada. Montagens de sobreposição de volume são snapshots isolados que desaparecem quando você termina. Mesmo CLI e mesmo comportamento, seja em um servidor Linux ou em um MacBook.
Veja docs/install.md para detalhes completos de instalação.
Instalação Rápida
O script abaixo detecta o sistema operacional e instala o matchlock usando Homebrew no macOS e rpm/deb em distribuições Linux Debian/RHEL.
curl -fsSL https://raw.githubusercontent.com/jingkaihe/matchlock/main/scripts/install.sh | bash
# Or install a specific release
curl -fsSL https://raw.githubusercontent.com/jingkaihe/matchlock/main/scripts/install.sh | bash -s -- --version 0.2.4
Homebrew
A instalação baseada em Homebrew é suportada tanto no macOS quanto no Linux:
brew tap jingkaihe/essentials
brew install matchlock
Debian / Ubuntu (.deb)
sudo dpkg -i ./matchlock_<version>_linux_amd64.deb
sudo apt-get install -f
matchlock diagnose
Fedora / RHEL / CentOS Stream (.rpm)
sudo dnf install ./matchlock_<version>_linux_amd64.rpm
matchlock diagnose
Se matchlock diagnose relatar configuração de host ausente, execute:
sudo matchlock setup linux
Para inscrever um usuário específico explicitamente, execute:
sudo matchlock setup user <name>
# Básico
matchlock run --image alpine:latest cat /etc/os-release
matchlock run --image alpine:latest -it sh
matchlock run --image alpine:latest --no-network -- sh -lc 'echo offline'
# Lista de permissões de rede
matchlock run --image python:3.12-alpine \
--allow-host "api.openai.com" python agent.py
# Mantenha a interceptação ativada mesmo com uma lista de permissões vazia,
# para que hosts possam ser adicionados/removidos em tempo de execução.
matchlock run --image alpine:latest --rm=false --network-intercept
matchlock allow-list add <vm-id> api.openai.com,api.anthropic.com
matchlock allow-list delete <vm-id> api.openai.com
# Injeção de segredos (nunca entra na VM)
export ANTHROPIC_API_KEY=sk-xxx
matchlock run --image python:3.12-alpine \
--secret [email protected] python call_api.py
# Sandboxes de longa duração
matchlock run --image alpine:latest --rm=false # prints VM ID
matchlock run --image nginx:latest -d # same as above, detached
matchlock exec vm-abc12345 -it sh # attach to it
matchlock port-forward vm-abc12345 8080:8080 # forward host:8080 -> guest:8080
# Publicar portas na inicialização
matchlock run --image alpine:latest --rm=false -p 8080:8080
# Ciclo de vida
matchlock list | kill | rm | prune
# Construir a partir de Dockerfile (usa BuildKit na VM)
matchlock build -f Dockerfile -t myapp:latest .
# Pré-construir rootfs a partir de imagem de registro (armazena em cache para inicialização mais rápida)
matchlock build alpine:latest
# Gerenciamento de imagens
matchlock image ls # List all images
matchlock image rm myapp:latest # Remove a local image
docker save myapp:latest | matchlock image import myapp:latest # Import from tarball
Matchlock oferece SDKs em Go, Python e TypeScript para incorporar sandboxes diretamente em sua aplicação. Você pode iniciar VMs, executar comandos, transmitir saída e gerenciar arquivos programaticamente.
Go
package main
import (
"context"
"fmt"
"os"
"github.com/jingkaihe/matchlock/pkg/sdk"
)
func main() {
ctx := context.Background()
client, err := sdk.NewClient(sdk.DefaultConfig())
if err != nil {
panic(err)
}
defer client.Close(0)
defer client.Remove()
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")
if _, err := client.Launch(sandbox); err != nil {
panic(err)
}
if _, err := client.Exec(ctx, "apk add --no-cache curl"); err != nil {
panic(err)
}
// The VM only ever sees a placeholder - the real key never enters the sandbox
result, err := client.Exec(ctx, "echo $ANTHROPIC_API_KEY")
if err != nil {
panic(err)
}
fmt.Print(result.Stdout) // prints "SANDBOX_SECRET_a1b2c3d4..."
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"}]}'`
if _, err := client.ExecStream(ctx, curlCmd, os.Stdout, os.Stderr); err != nil {
panic(err)
}
}
Comportamento de IP privado do SDK Go (10/8, 172.16/12, 192.168/16):
.WithBlockPrivateIPs(true) (ou .BlockPrivateIPs())..AllowPrivateIPs() ou .WithBlockPrivateIPs(false).sandbox := sdk.New("alpine:latest").
AllowHost("api.openai.com").
AddHost("api.internal", "10.0.0.10").
WithNetworkMTU(1200).
AllowPrivateIPs() // explicit override: block_private_ips=false
// Interceptação de rede do SDK (mutação de requisição/resposta, formatação de corpo, transformação de linha de dados SSE)
sandbox = sandbox.WithNetworkInterception(&sdk.NetworkInterceptionConfig{
Rules: []sdk.NetworkHookRule{
{
Phase: sdk.NetworkHookPhaseBefore,
Action: sdk.NetworkHookActionMutate,
Hosts: []string{"api.openai.com"},
SetHeaders: map[string]string{"X-Trace-Id": "trace-123"},
},
{
Phase: sdk.NetworkHookPhaseAfter,
Action: sdk.NetworkHookActionMutate,
Hosts: []string{"api.openai.com"},
BodyReplacements: []sdk.NetworkBodyTransform{
{Find: "internal-id", Replace: "redacted"},
},
},
},
})
Se você usar client.Create(...) diretamente (sem o construtor), defina:
BlockPrivateIPsSet: trueBlockPrivateIPs: false (ou true)Para sandboxes totalmente offline (sem NIC convidado / sem egresso), use:
--no-network.WithNoNetwork().with_no_network().withNoNetwork()Python (PyPI)
pip install matchlock
# or
uv add matchlock
import os
import sys
from matchlock import Client, Sandbox