Все статьи
Вайбкодинг · 6 мин

Как запустить ИИ-агента в своей песочнице и не бояться за систему

Показываю на self-hosted песочнице plimsoll, как дать агенту писать и выполнять код локально: какой уровень изоляции ставить, как подключить API без утечки ключей и что делать, если запуск откажет.

Олег ЕмельяновДелаю продукты с помощью ИИ

Отдаёшь агенту терминал напрямую — и код, который он написал сам, выполняется с теми же правами, что и ты. Если скрипт удалит не ту папку или откроет не тот порт, разбираться будешь сам, а у агента просто не было способа проверить свою догадку перед запуском.

Я искал self-hosted песочницу, которая не просто «изолирует», а прямо говорит, насколько сильно изолировала именно этот запуск — и нашёл plimsoll.

Почему агенту нельзя доверять терминал напрямую

Агент пишет код по догадке: прочитал задачу, собрал решение, хочет его выполнить. Если выполнение идёт прямо в твоей системе, у тебя нет способа узнать заранее, насколько надёжна граница между этим кодом и твоей машиной — она просто есть или её просто нет.

Мне нужен был инструмент, который отвечает не «выполнено», а «выполнено вот с такой стеной вокруг». Если стена слабее, чем я просил, — запуск не должен состояться вообще.

Что такое plimsoll и как он изолирует код

plimsoll — self-hosted песочница для кода, который написал ИИ-агент. Каждый результат несёт в себе isolation tier — уровень изоляции, с которым реально прошёл именно этот запуск. Уровней четыре, от слабого к сильному: process, container, kernel, vm.

Запрос может задать floor — минимальный уровень, который он согласен принять. Если демон способен дать только более слабую стену, код вообще не запускается: вместо результата приходит ошибка ErrInsufficientIsolation.

Название пришло от Plimsoll line — отметки на борту корабля, по которой видно предельную загрузку, и которую может проверить кто угодно снаружи.

Линия на борту корабля дала имя уровню изоляции в коде

Термин · Plimsoll line · Осадка судна · Отметка на борту корабля, по которой видно предельную загрузку — её может проверить кто угодно снаружи

Четыре уровня изоляции — от process без стены до отдельной VM

Isolation tier в plimsoll · process · Нет стены ядра — не для чужого кода · container · Обычный Docker-контейнер · kernel · gVisor закрывает ядро от контейнера · vm · Отдельная виртуальная машина

Поднимаю песочницу у себя: шаги

Сервер называется plimsolld, и он запускает ровно один provider — какой, решает переменная SANDBOX_PROVIDER. Если её не задать, работает Disabled provider: он не выполняет вообще ничего, то есть выполнение кода не включится само по себе.

Первый запуск я сделал без Docker, без сети и без credential — на provider wasm, который гоняет JavaScript на QuickJS прямо в твоём процессе. Это уровень process: стены вокруг кода нет, для чужого непроверенного кода так запускать нельзя.

Настоящую стену ставит sudo ./docker/install-gvisor.sh: он устанавливает закреплённую версию gVisor и регистрирует её раннер, runsc, в docker. Клиентский код при этом не меняется ни строкой — тот же код просто начинает идти на уровне kernel вместо process.

Насколько это важно, видно на реальном прогоне: агент написал controller для cart-pole — тележки на рельсе с шарниром сверху, которую нужно удерживать вертикально толчками влево-вправо. Симулятор, собранный в WebAssembly, встроен в образ песочницы и считает каждый тик — 10 мс. Controller был единственным файлом, который я отправил через обычный project API, и прогон шёл на уровне container.

Раннер записывает всю траекторию и хэширует её в SHA-256. Принятый контроллер я прогнал два раза — отпечатки совпали. А первый черновик агента перепутал знак у двух коэффициентов в формуле.

Черновик агента с перепутанным знаком роняет маятник раньше шести секунд

Когда упал маятник · Черновик агента · 5,96 с · Знак у двух коэффициентов неверный

Один запуск примера — это три прогона в песочнице: принятый контроллер два раза и черновик один раз. Повторить можно командой make docker-images && go run ./examples/oracle.

Подключаю агента к песочнице через API

Код внутри песочницы не должен держать мой API-ключ — и в plimsoll он его и не держит. Грант даёт одному запуску право вызвать перечисленные маршруты одного API. Код внутри обращается через маленький клиент, который plimsoll сам кладёт в песочницу; настоящий вызов делает broker снаружи — он проверяет вызывающего и точный маршрут и только потом прикладывает credential.

Credential выпускается заново на каждый run, то есть это может быть короткоживущий токен, и внутрь песочницы он вообще не попадает. Код, который попробует вызвать API мимо этого клиента напрямую, дальше не пройдёт: правила проверяет broker, а не клиент внутри песочницы.

Агент получает доступ к API без доступа к ключу

Грант · один run — один API · Брокер · сам прикладывает ключ · Один раз · новый токен на каждый run

Эта же схема работает из агентных фреймворков. Для Trigger.dev есть готовый executeCode tool: на сессии — одной песочнице, которая живёт несколько вызовов, — код идёт ячейками, и переменные с файлами переживают следующий вызов. Каждый результат инструмента прямо говорит, переживут ли они следующий вызов, с какого tier прошёл запуск и какой SHA-256 у записи этого прогона.

Что агент может и не может внутри

Без гранта у запуска нет сети вообще — не «ограниченная сеть», а никакой. Если я не выдал явного доступа к конкретному API, агент физически не может постучаться наружу.

Docker provider можно дополнительно зажать shipped seccomp profile — списком системных вызовов, которые контейнеру разрешено делать к ядру. Сессия — это одна доверенная зона, и plimsoll привязывает её к вызывающему, а не к его клиентам: если через один сервис идёт код разных заказчиков, каждому нужна своя отдельная сессия, а не общая.

Что делать, если песочница зависла или сломалась

Если запуск отказал, у ошибки либо есть марка с причиной — request, permission, protocol, unsupported, isolation, environment, capacity — либо её нет. Марка означает, что код не выполнился вообще; sandbox.NotDispatchedReason(err) читает эту марку независимо от того, идёт provider в твоём процессе или за демоном.

Ошибка без марки может означать, что код уже начал выполняться — такую нельзя автоматически ретраить. Для проверки цепочки запусков есть отдельная программа plimsoll-attest: она подписывает уже проверенные результаты, откажется принять цепочку с пропущенным звеном и умеет переиграть прогон заново.

Марка на ошибке решает, можно ли повторить запуск

Если запуск отказали · Марка есть · Ничего не запустилось · Марки нет · Мог успеть выполниться — не ретраить · plimsoll-attest · Проверяет цепочку и переигрывает прогон

Сам проект честно называет себя pre-1.0 с одним автором и без внешних пользователей: breaking changes прилетают без пути устаревания — специально. Независимого аудита безопасности не было, и свои ревью автор тоже не публикует — вместо обещаний в SECURITY.md написано, что проверено и что можно проверить самому.

Реальная проверка — это CI: job audit гоняет build, vet, race tests, lint и govulncheck на каждый push, audit-docker прогоняет тот же набор под runc с seccomp-профилем и подтверждает read-only root и отказы broker, а workflow gvisor — то же самое под runsc, то есть на уровне kernel. Зелёный audit-docker — это факт, что отказы и ограничения действительно сработали в этом прогоне, а не обещание.

Вопросы и ответы

Что такое isolation tier в plimsoll?

Это уровень изоляции, с которым реально прошёл конкретный запуск кода: process, container, kernel или vm — от слабого к сильному. Значение приходит в самом результате запуска, и запрос может задать floor — минимальный уровень, ниже которого код просто не выполнится.

Как дать ИИ-агенту доступ к своему API без утечки ключей?

Через грант: он разрешает одному запуску вызывать перечисленные маршруты одного API через клиент, который plimsoll сам кладёт в песочницу. Настоящий вызов и credential прикладывает broker снаружи песочницы, а не код внутри, — ключ туда вообще не попадает и выпускается заново на каждый run.

Можно ли уже сейчас доверять plimsoll в проде?

Проект сам называет себя pre-1.0 с одним автором: breaking changes приходят без пути устаревания, и независимого аудита безопасности не проводилось. Вместо обещаний есть открытый CI — audit, audit-docker и workflow под gVisor — который на каждом прогоне проверяет read-only root, seccomp и отказы broker.

Источники

Читать ещё