
Эфемерная песочница microVM для AI-агентов с сетевым белым списком, инъекцией секретов через MITM-прокси и изоляцией на уровне ВМ. Загружается менее чем за секунду, поддерживает SDK для Go/Python/TypeScript.
Экспериментально: Этот проект всё ещё находится в активной разработке и может претерпевать критические изменения.
Matchlock — это CLI-инструмент для запуска ИИ-агентов в эфемерных микро-ВМ — с белым списком сети, внедрением секретов через MITM-прокси и изоляцией на уровне ВМ. Ваши секреты никогда не попадают внутрь ВМ.
ИИ-агентам нужно запускать код, но давать им неограниченный доступ к вашей машине — рискованно. Matchlock позволяет предоставить агенту полноценную среду Linux, которая загружается менее чем за секунду — изолированную и одноразовую.
Когда вы передаёте --allow-host или --secret, Matchlock блокирует сеть — проходит только трафик к явно разрешённым хостам, всё остальное блокируется. Когда ваш агент вызывает API, реальные учётные данные подставляются на лету хостом. Песочница видит только заглушку. Даже если агента обманом заставят выполнить что-то вредоносное, ваши ключи не утекут, и данные некуда отправить. Внутри агент получает полноценную среду Linux, где может делать всё необходимое: устанавливать пакеты, записывать файлы, устроить беспорядок. Снаружи ваша машина ничего не замечает. Тома поверхностного монтирования — это изолированные снимки, которые исчезают после завершения работы. Один и тот же CLI и одинаковое поведение, работает ли вы на Linux-сервере или на MacBook.
Полные инструкции по установке см. в docs/install.md.
Быстрая установка
Скрипт ниже определяет ОС и устанавливает matchlock с помощью Homebrew на macOS и rpm/deb на дистрибутивах Linux на основе Debian/RHEL.
curl -fsSL https://raw.githubusercontent.com/jingkaihe/matchlock/main/scripts/install.sh | bash
# Или установить конкретную версию
curl -fsSL https://raw.githubusercontent.com/jingkaihe/matchlock/main/scripts/install.sh | bash -s -- --version 0.2.4
Homebrew
Установка через Homebrew поддерживается как на macOS, так и на 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
Если matchlock diagnose сообщает о недостающей настройке хоста, выполните:
sudo matchlock setup linux
Чтобы явно зарегистрировать конкретного пользователя, выполните:
sudo matchlock setup user <name>
# Базовое
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'
# Белый список сети
matchlock run --image python:3.12-alpine \
--allow-host "api.openai.com" python agent.py
# Оставить перехват включённым даже с пустым белым списком,
# чтобы хосты можно было добавлять/удалять во время выполнения.
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
# Внедрение секретов (никогда не попадает в ВМ)
export ANTHROPIC_API_KEY=sk-xxx
matchlock run --image python:3.12-alpine \
--secret [email protected] python call_api.py
# Долгоживущие песочницы
matchlock run --image alpine:latest --rm=false # выводит ID ВМ
matchlock run --image nginx:latest -d # то же, что выше, в фоновом режиме
matchlock exec vm-abc12345 -it sh # подключиться к ней
matchlock port-forward vm-abc12345 8080:8080 # перенаправить host:8080 -> guest:8080
# Публикация портов при запуске
matchlock run --image alpine:latest --rm=false -p 8080:8080
# Жизненный цикл
matchlock list | kill | rm | prune
# Сборка из Dockerfile (использует BuildKit в ВМ)
matchlock build -f Dockerfile -t myapp:latest .
# Предварительная сборка rootfs из образа реестра (кэшируется для более быстрого запуска)
matchlock build alpine:latest
# Управление образами
matchlock image ls # Список всех образов
matchlock image rm myapp:latest # Удалить локальный образ
docker save myapp:latest | matchlock image import myapp:latest # Импорт из tarball
Matchlock поставляет SDK для Go, Python и TypeScript для встраивания песочниц непосредственно в ваше приложение. Вы можете запускать ВМ, выполнять команды, передавать потоковый вывод и управлять файлами программно.
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)
}
}
Поведение Go SDK с частными IP-адресами (10/8, 172.16/12, 192.168/16):
.WithBlockPrivateIPs(true) (или .BlockPrivateIPs())..AllowPrivateIPs() или .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
// SDK network interception (request/response mutation, body shaping, SSE data-line transform)
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"},
},
},
},
})
Если вы используете client.Create(...) напрямую (без билдера), установите:
BlockPrivateIPsSet: trueBlockPrivateIPs: false (или true)Для полностью автономных песочниц (без гостевого сетевого интерфейса / без исходящего трафика) используйте:
--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