Запуск пользовательского кода — одна из самых рискованных задач для любого сервиса. Python-скрипт пользователя может содержать while True, обратиться к файловой системе хоста или отправить данные по сети. Обычные контейнеры Docker не дают полной изоляции: они делят ядро с хостом, и уязвимости ядра потенциально превращаются в побег из контейнера.
В августе 2026 года Саймон Виллисон (simonwillison.net) поручил Claude Fable 5 протестировать smolvm как песочницу для ненадёжного Python и JavaScript. Результат — отчёт из 14 тестов, подтверждающий: аппаратная виртуализация работает, cold start занимает менее секунды, а полный стек изоляции (CPU, RAM, сеть, файловая система, таймауты) — на месте.
Разбираем кейс: зачем hardware-isolated VM вместо контейнеров, как smolvm решает каждую проблему и где у неё ограничения.

Зачем VM-песочница, а не Docker-контейнер
Основной аргумент smolvm — это уровень изоляции.
Контейнеры Docker используют namespaces и cgroups одного Linux-ядра с хостом. Если в ядре есть уязвимость (а kernel CVE публикуются постоянно), процесс внутри контейнера может выйти за его пределы. Для большинства серверных задач это нормальный риск. Для запуска враждебного пользовательского кода — нет.
smolvm создаёт полноценную виртуальную машину с собственным гостевым ядром. Гипервизор (KVM на Linux, Hypervisor.framework на macOS, WHP на Windows) аппаратно изолирует каждую VM от хоста и друг от друга. Утечка из VM означает взлом гипервизора — это на порядки сложнее, чем побег из контейнера.

Как smolvm реализует полный стек изоляции
Исследование Саймона Виллисона показало, что smolvm 1.8.3 предоставляет все нужные механизмы изоляции флагами CLI — без кастомных обёрток и хаков.
Вот ключевые требования и как smolvm их закрывает:
| Ограничение RAM | --mem <MiB> — эластичное выделение через virtio-balloon | 1 ГБ аллокация при --mem 256 → MemoryError внутри гостя, хост не пострадал |
| Ограничение CPU | --cpus <N> | Fork-bomb при --cpus 1 завершился за ~1 с, нагрузка на хост 0.69 |
| Защита от while True | --timeout <dur> (выполняется агентом внутри гостя) | Процесс убит ровно по таймеру, zero leftover VMM-процессов |
| Запрет сети | По умолчанию сеть выключена | wget и DNS-запросы оба завершаются ошибкой |
| Только указанные файлы | -v HOST:GUEST[:ro] — тома только директории через virtiofs | Read-only: строго |
| Защита от заполнения диска | --storage <GiB> | dd остановлен на 2.9 ГБ при --storage 3, fs заполнена на 100% |
| Защита от fork bomb | Ядро гостя управляет PID-пространством | VM завершилась за ~1 с, хост не пострадал |
| Снижение привилегий | --unprivileged | CapEff падает с 1ffffffffff (полные) до a80425fb (стандартный набор) |
Важный нюанс: --overlay не защищает от записи
Саймон обнаружил ошибку при тестировании: флаг --overlay не ограничивает запись в корневую файловую систему гостя. Процесс dd спокойно записал 4 ГБ на 20-ГБ диск storage. Для защиты от заполнения диска нужен именно --storage — корневая FS гостя лежит на этом диске (через overlayfs), и при его заполнении запись корректно завершается с ошибкой ENOSPC.
Это не теоретическая проблема: если вы разворачиваете песочницу для чужого кода и используете --overlay вместо --storage, пользовательский скрипт может исчерпать все ресурсы хоста.
Таймауты: два слоя защиты
--timeout реализован на двух уровнях:
- Агент внутри гостя убивает процесс по дедлайну, возвращает захваченный вывод. Это основной механизм.
- Клиент на хосте ставит свой таймаут на чтение — подстраховка, если гостевой агент завис.
Для полноты защиты Саймон рекомендует обернуть вызов CLI ещё и в coreutils timeout: если VM «заблокировалась», она всё равно будет демонтирована (эфемерные VM уничтожаются при завершении в любом случае).
In a few runs Claude tried to terminate the malware process once it noticed the compromise, but Auto Mode denied the cleanup command.
— Simon Willison, 27.08.2026
Этот эпизод (Johann Rehberger взломал Claude Code auto mode с 80% успеха) показывает: программные песочницы (auto mode, --restricted) не заменяют аппаратную изоляцию. Автоматическое средство защиты может само стать частью проблемы.
Практический разбор: как использовать smolvm для запуска ненадёжного кода

Установка
curl -sSL https://smolmachines.com/install.sh | bash
Для версии 1.8.3 (на момент публикации) — если installer застрял за прокси:
curl -sSL https://smolmachines.com/install.sh | bash -s -- --version 1.8.3
Устанавливается в ~/.smolvm, симлинк в ~/.local/bin. Без демона.
Требования: macOS 11+ (Apple Silicon или Intel), Linux с /dev/kvm, Windows с WHP. Внутри большинства контейнеров/VM (включая Claude Code for web — Firecracker guest) не работает из-за отсутствия nested virt.
Быстрый старт: запуск скрипта в изолированной VM
# Запуск Python-скрипта пользователя — полностью изолированно
smolvm machine run \
--image ./python.tar \
--cpus 1 --mem 512 \
--timeout 30s \
--storage 3 \
--unprivileged \
-v "$PWD/in:/in:ro" \
-v "$PWD/out:/out" \
-- python3 /in/transform.py
Каждый запуск (machine run) — эфемерная VM. После завершения скрипта VM уничтожается. Никакого кросс-задачевого заражения: всё, к чему может обратиться задача — /in (read-only), /out (read-write) и 3-гигабайтный временный диск.
Offline-образы: сеть не нужна
Ключевой момент: без флага --net в VM вообще нет сетевого устройства. Не «сеть отключена на уровне firewall» — устройства нет. Проверено в исходном коде: plan_launch_network возвращает backend None, если не запрошены --net/--ports/политики egress.
Для работы с пользовательским кодом нужен образ Python/Node. Сетевые --image python:3.12-alpine при --net off завершаются ошибкой. Решение — локальные offline-образы:
# Один раз: сохранить образ
docker save python:3.12-alpine -o python.tar
docker save node:20-alpine -o node.tar
# Всегда: запускать без сети
smolvm machine run --image ./python.tar -- python3 /in/script.py
Или упаковать в .smolmachine артефакт:
smolvm pack create --image python:3.12-alpine -o ./python312
./python312 run -- python3 /in/script.py
Постоянная VM для высокой пропускной способности
Эфемерные VM (~0.6–1.5 с на cold start) подходят для одиночных задач. Если нужно выполнять много заданий подряд, выигрывает persistent VM:
# Создать и запустить
smolvm machine create --name sandbox -s Smolfile
smolvm machine start --name sandbox
# Запускать задачи через exec (~50 мс вместо 600+ мс)
smolvm machine exec --name sandbox -- python3 /in/task1.py
smolvm machine exec --name sandbox -- node /in/task2.js
Холодный старт постоянной VM: ~1.5 с (create + start). Warm exec: 48–79 мс. Разница в 30 раз.
Для максимальной производительности — machine fork (Copy-on-Write клон работающей «золотой» VM) с пулом --hold-слотов. Типичный сценарий: одна VM разворачивает среду, форк-пул раздаёт копии задачам.
Архитектура: как выглядит сервис трансформации данных
Саймон предложил референтную архитектуру для задачи «пользователь прислал код и данные — вернуть результат»:
┌──────────────────────── host ───────────────────────┐
user code ──► │ orchestrator ──► smolvm machine run │
user data ──► │ stage task dir: /tasks/<id>/in (code + data) │
│ collect results: /tasks/<id>/out │
└──────────────────────────────────────────────────────┘
per task: --cpus 1 --mem 512 --storage 3 --timeout 30s --unprivileged
-v /tasks/<id>/in:/in:ro -v /tasks/<id>/out:/out (no --net)
- Один
machine runна задачу = нулевое кросс-задачевое заражение - Exit code, stdout, stderr пробрасываются через CLI
- Масштабирование:
smolvm serveна unix-сокете + persistent VM или fork-пулы, когда bottleneck — boot VM
HTTP API
smolvm предоставляет HTTP API через smolvm serve. На localhost API не аутентифицирован — рекомендуется привязка к unix-сокету с файловыми permissions:
smolvm serve --listen $XDG_RUNTIME_DIR/smolvm.sock
В fleet-режиме (хостинг smol cloud) API использует mTLS, а loopback-дверь ограничена health-эндпоинтами — защита от SSRF-пивота через registry ref на 127.0.0.1.
Нашли баг: timeoutSecs vs timeout_secs
Во время тестирования HTTP API Саймон обнаружил, что поле timeout_secs (snake_case) молча игнорируется — таймаут не срабатывает, запрос выполняется все 300 секунд по умолчанию. Причина: в коде smolvm ExecRequest использует serde(rename_all = "camelCase"), и правильное имя — timeoutSecs.
Это не просто опечатка. Serde по умолчанию не отвергает неизвестные поля: любое поле с «неправильным» именем пропадает без ошибки. В версии 1.8.3 это может удивить разработчиков, которые привыкли к strict validation.
Во втором раунде тестов Саймон подтвердил: при правильном timeoutSecs таймаут срабатывает ровно через 5 с, и машина остаётся здоровой.
Ограничения: что не умеет smolvm
Nested virt
smolvm требует аппаратную поддержку виртуализации на хосте: /dev/kvm (Linux), Hypervisor.framework (macOS), WHP (Windows). Внутри большинства контейнеров и VM (включая среду Claude Code for web, которая сама работает в Firecracker) вложенная виртуализация недоступна. Саймон решал эту проблему через GitHub Actions runners, которые предоставляют KVM.
Это значит: smolvm не подходит как песочница внутри уже запущенной VM (типичный сценарий cloud-платформы). Для этого лучше смотреть на Firecracker или gVisor.
Только Linux-гости
Хост может быть macOS, Linux или Windows. Гость — всегда Linux. Запуск Windows-гостей или macOS-гостей не поддерживается.
Тома только директории
Volume mounts работают на уровне директорий, а не файлов. Чтобы передать один файл — стейджить его в директорию.
Нет ICMP
Сеть (при --net) работает только по TCP/UDP. ICMP-пинги не проходят.
Нет GPU-изоляции
GPU-доступ (через --gpu / --cuda) реализован через API-ремотинг virtio-gpu или CUDA-over-vsock. Это процессорный уровень изоляции, а не аппаратный. Для GPU-задач с полной изоляцией smolvm не подходит.
Сравнение с другими вариантами
| Критерий | smolvm | Docker | QEMU | Firecracker | gVisor |
|---|---|---|---|---|---|
| Тип изоляции | VM + ядро гостя | Namespace + общее ядро | Namespace в shared VM | VM + ядро гостя | User-space ядро |
| Cold start | <200 мс (ephemeral ~600 мс) | ~100 мс | ~секунды | <125 мс | ~100 мс |
| macOS нативно | Да | Через Docker VM | Да (krunkit) | Нет | Нет |
| Портативные артефакты | .smolmachine | Образы (нужен daemon) | Нет | Нет | Нет |
smolvm занимает нишу между Docker (быстро, но слабая изоляция) и QEMU/Firecracker (сильная изоляция, но сложнее в настройке). Портативность .smolmachine-артефактов и кроссплатформенность — главные отличия.
Связь с безопасностью агентов
Тема песочниц для ненадёжного кода пересекается с трендом недели: серия атак на Claude Code auto mode. Johann Rehberger показал атаку с 80% успеха: zip-архив с struct.py, который подменяет import base64, — и агент выполняет враждебный код.
Ответ Anthropic — флаг --restricted (убирает доступ к командам/коду и WebFetch). Но программная песочница всегда уступает аппаратной.
The only safe way to run agents if there’s any risk of attracting the attention of an adversarial attack is with a sandbox.
— Simon Willison, 27.08.2026

smolvm решает эту задачу на аппаратном уровне: даже если промпт-инъекция сработала и агент запустил враждебный код, этот код работает в изолированной VM без сети, с лимитами CPU/RAM/диска и read-only входными данными.
Российские реалии
smolvm — open-source проект (Apache-2.0), доступен для скачивания глобально. На момент публикации:
- Установка:
curl -sSL https://smolmachines.com/install.sh | bashработает без VPN из РФ. GitHub-репозиторий smol-machines/smolvm доступен. - smol cloud (хостинг managed VM): требует аккаунт на smolmachines.com, оплата картами. Доступность из РФ не проверена — сервис новый и niche.
- Claude Code for web (среда, из которой проводился тест): доступен с аккаунтом Anthropic, оплата международными картами или через посредников.
- Альтернативы для РФ: Docker и Firecracker доступны для установки на выделенных серверах; gVisor — через Google Cloud (недоступен напрямую из РФ, но работает через VPN). Для локальной изоляции — любой гипервизор (KVM на bare-metal Linux).
- Русскоязычное освещение: на момент публикации smolvm в рунете (Habr, vc.ru, Tproger) не освещалось. Проект слишком новый и niche-овый для русскоязычного сообщества.
Выводы
smolvm не заменяет Docker для повседневной разработки. Его ниша — запуск ненадёжного кода с аппаратной изоляцией: пользовательские скрипты, AI-агенты, data transformation tasks. Cold start ~600 мс, warm exec ~50 мс, offline-образы, полный стек изоляции из коробки.
Ключевые уроки из кейса:
- Hardware isolation ≠ container isolation. Для ненадёжного кода контейнер — компромисс, а VM — безопасность.
--overlayне защищает от заполнения диска. Используйте--storage.- HTTP API требует camelCase полей.
timeoutSecs, неtimeout_secs. - Offline-образы — обязательны для безсетевой песочницы.
docker save+--image ./file.tar. - Nested virt — главное ограничение. smolvm не работает внутри других VM/контейнеров.
Для сервисов, которые выполняют пользовательский код (независимо, прислал его программист или сгенерировал AI-агент), smolvm — один из самых практичных вариантов hardware isolation на 2026 год.
