Voltar às atualizações
New releaseJul 27, 2026

matchlock v0.2.17

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.

Compartilhar

Matchlock

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.

Por que Matchlock?

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.

Início Rápido

Requisitos de Sistema

  • Linux com suporte a KVM
  • macOS em Apple Silicon

Instalar

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>

Uso

# 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

SDK

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):

  • Padrão (não definido): IPs privados são bloqueados sempre que uma configuração de rede é enviada.
  • Bloqueio explícito: chame .WithBlockPrivateIPs(true) (ou .BlockPrivateIPs()).
  • Permissão explícita: chame .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: true
  • BlockPrivateIPs: false (ou true)

Para sandboxes totalmente offline (sem NIC convidado / sem egresso), use:

  • CLI: --no-network
  • Construtor Go SDK: .WithNoNetwork()
  • Construtor Python SDK: .with_no_network()
  • Construtor TypeScript SDK: .withNoNetwork()

Python (PyPI)

pip install matchlock
# or
uv add matchlock
import os
import sys

from matchlock import Client, Sandbox

sandbox = (
    Sandbox("python:3.12-alpine")
    .allow_host(
        "dl-cdn.alpinelinux.org",
        "files.pythonhosted.org", "pypi.org",
        "astral.sh", "github.com", "objects.githubusercontent.com",
        "api.anthropic.com",
    )
    .add_secret(
        "ANTHROPIC_API_KEY", os.environ["ANTHROPIC_API_KEY"], "api.anthropic.com"
    )
)

SCRIPT = """\
# /// script
# requires-python = ">=3.12"
# dependencies = ["anthropic"]
# ///
import anthropic, os

client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
with client.messages.stream(
    model="claude-haiku-4-5-20251001",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Explain TCP/IP."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
print()
"""

with Client() as client:
    client.launch(sandbox)
    client.exec("pip install --quiet uv")
    client.write_file("/workspace/ask.py", SCRIPT)
    client.exec_stream("uv run /workspace/ask.py", stdout=sys.stdout, stderr=sys.stderr)

client.remove()

TypeScript

npm install matchlock-sdk
import { Client, Sandbox } from "matchlock-sdk";

const SCRIPT = `import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

const stream = anthropic.messages
  .stream({
    model: "claude-haiku-4-5-20251001",
    max_tokens: 1024,
    messages: [{ role: "user", content: "Explain TCP/IP." }],
  })
  .on("text", (text) => {
    process.stdout.write(text);
  });

await stream.finalMessage();
process.stdout.write("\\n");
`;

const client = new Client();
try {
  const sandbox = new Sandbox("node:22-alpine")
    .allowHost("registry.npmjs.org", "*.npmjs.org", "api.anthropic.com")
    .addSecret("ANTHROPIC_API_KEY", process.env.ANTHROPIC_API_KEY ?? "", "api.anthropic.com");

  await client.launch(sandbox);
  await client.exec(
    "npm init -y >/dev/null 2>&1 && npm install --quiet --no-bin-links @anthropic-ai/sdk",
    { workingDir: "/workspace" },
  );
  await client.writeFile("/workspace/ask.mjs", SCRIPT);
  await client.execStream("node ask.mjs", {
    workingDir: "/workspace",
    stdout: process.stdout,
    stderr: process.stderr,
  });
} finally {
  await client.close();
  await client.remove();
}

Mais exemplos no diretório examples/:

DescriçãoExemplo
Transmite resposta da API Anthropic com injeção de segredo (Go)examples/go/basic/
Terminal interativo com PTY usando ExecInteractive (Go)examples/go/exec_modes/
Injeta chave de API via hook de interceptação de rede (Go)examples/go/network_interception/
Hooks de interceptação VFS para mutações de operações de arquivo (Go)examples/go/vfs_hooks/
Transmite resposta da API Anthropic (Python)examples/python/basic/
Modos de execução stream, pipe e interativo (Python)examples/python/exec_modes/
Injeta chave de API via hook de interceptação de rede (Python)examples/python/network_interception/
Hooks de interceptação VFS para mutações de operações de arquivo (Python)examples/python/vfs_hooks/
Transmite resposta da API Anthropic (TypeScript)examples/typescript/basic/
Modos de execução stream, pipe e interativo (TypeScript)examples/typescript/exec_modes/
Injeta chave de API via hook de interceptação de rede (TypeScript)examples/typescript/network_interception/
CLI do Claude Code em micro-VM com bootstrap do GitHubexamples/claude-code/
Claude Code com Docker dentro do sandbox via SDKexamples/claude-code-with-docker/
Claude Code com assinatura Claude Pro/Max no sandboxexamples/claude-danger/
CLI do OpenAI Codex em micro-VM com bootstrap do GitHubexamples/codex/
Daemon Docker dentro do sandbox com systemdexamples/docker-in-sandbox/
Chatbot Streamlit usando Agent Client Protocolexamples/agent-client-protocol/
Automação de navegador com Kodelet e Playwright MCPexamples/playwright/

Arquitetura

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

Modos de Rede

PlataformaModoMecanismo
LinuxProxy transparentenftables DNAT nas portas 80/443
macOSNAT (padrão)NAT embutido do Virtualization.framework
macOSInterceptação (com --allow-host/--secret)TCP/IP em espaço de usuário do gVisor na camada 4

Documentação

Licença

MIT

Categorias