Назад к обновлениям
New releaseSep 3, 2026

vpod v0.8.1

Лёгкие и безопасные Linux-песочницы для недоверенных процессов. Работают в браузере и на сервере.

Поделиться

Vpod

Что такое vpod?

vpod — это лёгкая, переносимая песочница, которая предоставляет непроверенному процессу мгновенное Linux-окружение. Она использует архитектуру RISC‑V и полностью работает внутри WebAssembly.

  • Быстрый запуск: загрузка менее чем за секунду.
  • Переносимость: работает где угодно без какой-либо настройки.
  • Изоляция: всё состояние выполнения остаётся внутри WASM-песочниц.

Как это работает

vpod запускает полноценную RISC‑V систему (RV64GC, одно виртуальное ядро), скомпилированную в WebAssembly. Внутри загружается реальное ядро Linux с реальным пользовательским пространством, поэтому оболочки, инструменты и демоны ведут себя так же, как на реальном оборудовании.

Снимки. Вместо загрузки Linux с нуля vpod восстанавливает снимок: сохранённое состояние машины (регистры CPU, RAM, файловая система), захваченное сразу после загрузки. Восстановление занимает значительно меньше секунды. Приостановка работает аналогичным образом в обратном направлении: на диск записываются только изменённые страницы памяти, поэтому вы можете поставить песочницу на паузу и возобновить её позже, даже из другого процесса.

Упреждающая трансляция. Чистая эмуляция «инструкция за инструкцией» медленна, а WebAssembly исключает JIT во время выполнения. Поэтому на этапе сборки снимка самые горячие пути гостевого кода транслируются из RISC‑V в нативный код, который компилируется непосредственно в сам WASM-модуль. Во время выполнения эмулятор переключается на эти транслированные блоки, когда гостевой код совпадает, и возвращается к интерпретатору, когда нет. Это даёт примерно 5-кратное ускорение на задачах, интенсивно использующих CPU, без какого-либо влияния на изоляцию: транслированный код проходит через те же проверки MMU и памяти, что и интерпретируемый.

Граница WASI. WASM-компонент взаимодействует с хостом исключительно через WASI 0.2. Гость никогда не видит файловые дескрипторы, сокеты или память хоста: доступ к файловой системе осуществляется через явно смонтированные каталоги, а сеть — через пользовательский сетевой стек внутри компонента, который запрашивает у хоста только обычные исходящие сокеты. Всё остальное (гостевое ядро, процессы, память) находится внутри линейной памяти WASM и исчезает вместе с ней.

Спецификация RV64GC

G (расширения общего назначения)

  • I: Базовый набор 64-битных целочисленных инструкций.
  • M: Аппаратное умножение и деление, полезно для хеширования и криптографии.
  • A: Атомарные операции для потокобезопасных программ.
  • F/D: Числа с плавающей запятой одинарной и двойной точности, подходят для научных вычислений и ML-инференса.

C (сжатые инструкции) Уменьшает размер кода на 30%, улучшая скорость выборки инструкций и эффективность использования памяти. Это важно при запуске полного пользовательского пространства Linux в нашей ограниченной по памяти WASM-среде.

[!NOTE] Векторное расширение V не реализовано. Инструкции RVV выполнялись бы как эмулируемый RISC-V; сквозной передачи SIMD на CPU хоста нет. Добавление V увеличило бы накладные расходы на эмуляцию без какого-либо выигрыша в производительности для векторизованных нагрузок.

Начало работы

TypeScript SDK

npm install @capsule-run/vpod
import { Sandbox } from "@capsule-run/vpod";

const sandbox = await Sandbox.create();

// Состояние сохраняется между вызовами
await sandbox.commands.run("export API_KEY=secret");
const key = await sandbox.commands.run("echo $API_KEY");
console.log(key.stdout); // secret

// Python REPL — переменные сохраняются
await sandbox.code.run("data = [1, 2, 3]");
const total = await sandbox.code.run("print(sum(data))");
console.log(total.text); // 6

await sandbox.close();

Тот же пакет работает во вкладке браузера, где снимок кэшируется в приватном хранилище источника, а не на диске.

[!IMPORTANT] Первый вызов Sandbox.create() загружает снимок по умолчанию (alpine) и кэширует его локально, если он ещё не присутствует.

Python SDK

pip install vpod
from vpod import Sandbox

# Выполнение команды
sandbox = Sandbox.create()
result = sandbox.commands.run("whoami")
print(result.stdout)  # root
sandbox.close()

# Постоянная сессия — состояние сохраняется между вызовами
with Sandbox.create() as sandbox:
    sandbox.commands.run("export API_KEY=secret")
    result = sandbox.commands.run("echo $API_KEY")
    print(result.stdout)  # secret

# Python REPL — переменные сохраняются
with Sandbox.create() as sandbox:
    sandbox.code.run("import requests")
    sandbox.code.run("data = [1, 2, 3]")
    result = sandbox.code.run("print(sum(data))")
    print(result.text)  # 6

CLI

curl -fsSL https://install.vpod.sh | sh
Или установка через PowerShell (Windows)
irm https://install.vpod.sh | iex
# Загрузка снимка
vpod pull alpine:latest

# Запуск интерактивной оболочки
vpod

Документация

Посетите документацию Vpod.

Ограничения

  • Накладные расходы на эмуляцию: внутри WebAssembly нет аппаратной виртуализации, поэтому весь гостевой код эмулируется. Накладные расходы полностью зависят от рабочей нагрузки: задачи, связанные с вводом-выводом и сетью, работают почти с нативной скоростью, тогда как тяжёлые задачи, интенсивно использующие CPU, работают заметно медленнее даже с AOT-трансляцией. Если ваша нагрузка в основном «запустить инструмент, прочитать файл, вызвать API», вы ничего не заметите.
  • Нет доступа к GPU: CUDA, Metal и аппаратные ML-ускорители недоступны. Поддержка может быть добавлена в будущем с помощью wasi-nn.

Участие в разработке

Приветствуются любые вклады — от сообщений об ошибках до поддержки новых устройств. Откройте issue, чтобы обсудить что-либо существенное перед реализацией.

Предварительные требования

  • Rust (последняя стабильная версия) с целью wasm32-wasip2: rustup target add wasm32-wasip2
  • Python 3.10+ для Python SDK
  • Node 20+ для TypeScript SDK
  • Zig (0.16) и bsdtar, нужны только если вы собираете снимки самостоятельно

Настройка среды разработки

# Однократно: генерация AOT-заглушки (в свежем клоне нет транслированных блоков)
./scripts/aot-stub.sh

# Сборка WASM-компонента (библиотека + CLI). Копирует оба уровня в sdks/python/vpod/
./scripts/build-wasm.sh

# Установка хостового CLI
cargo install --path crates/vpod

# Установка Python SDK в режиме разработки
pip install -e "sdks/python[dev]"

# Сборка TypeScript SDK. Подхватывает компонент из каталога Python SDK
cd sdks/typescript && npm install && npm run build

npm run build по умолчанию использует --tier aot; CI фиксирует --tier base. Сборка отказывается использовать компонент старше самого нового файла в crates/, поэтому перезапускайте ./scripts/build-wasm.sh после изменений в эмуляторе. Изменение эмулятора проявляется только через гостя, поэтому устаревший компонент компилируется и проходит почти все тесты.

Запуск тестов

CI запускает их на каждом PR, поэтому запускайте их перед отправкой:

cargo fmt --all -- --check                        # форматирование
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all                                  # тесты Rust

# Интеграционные тесты Python SDK (требуется WASM-библиотека на месте)
cp target/wasm32-wasip2/release/vpod_wasi_lib.wasm sdks/python/vpod/
pytest sdks/python/tests/ -v -m integration

# TypeScript SDK (из sdks/typescript)
npm run typecheck
npm test                  # модульные
npm run test:all          # модульные + интеграционные, требуется локальный снимок
npm run test:perf         # регрессии гостевого времени, точные константы

Тесты TypeScript импортируют собранный dist/, а не src/, поэтому сначала выполните сборку. Они ищут снимок в общем каталоге кэша; VPOD_TEST_SNAPSHOT=/path/to/x.snap указывает на другое место.

Для сквозного тестирования браузера npm run dev обслуживает страницу с включёнными COOP/COEP, а node dev/run-network.mjs --browser chrome управляет ею в headless-режиме.

Использование локально собранного снимка

SDK по умолчанию загружают из registry.vpod.sh. Чтобы использовать снимок, собранный самостоятельно, передайте его напрямую вместо имени реестра:

// TypeScript: файл на диске (Node) или байты (где угодно)
await Sandbox.create({ snapshot: { path: "./dist/alpine-3.23.0-256mb.snap" } });
await Sandbox.create({ snapshot: { bytes, name: "alpine-3.23.0-256mb.snap" } });
# Python: VPOD_SNAPSHOT=/path/to/x.snap

В любом случае указывайте размер RAM в имени файла, поскольку эмулятор считывает его оттуда.

Сборка снимков

Проект использует предварительно собранные снимки Alpine из registry.vpod.sh, поэтому обычно это не требуется. Для локальной сборки:

./scripts/build-default-snapshot.sh   # dist/alpine-3.23.0-256mb.snap
./scripts/build-data-snapshot.sh      # вариант 512 МБ с numpy/pandas/scipy

[!TIP] Чтобы использовать локально собранный снимок в CLI, раскомментируйте строки в resolve_snapshot() в crates/vpod/src/main.rs.

Сборка снимков также может выполнять AOT-проход (scripts/aot-snapshot.sh <snapshot>), который трассирует репрезентативную рабочую нагрузку, транслирует горячие блоки и пересобирает эмулятор с ними. Это занимает время; заглушка из aot-stub.sh подходит для повседневной разработки — всё работает одинаково, просто медленнее.

Из Dockerfile (macOS и Linux)

Пользовательские снимки также можно собирать из Dockerfile. Сборщик использует CLI container от Apple на macOS и Docker Buildx на Linux. Свежая установка macOS требует однократной настройки среды выполнения, иначе сборка будет ждать сборщик, который никогда не запустится:

container system kernel set --recommended
container builder start

На Linux установите Docker с плагином Buildx и один раз зарегистрируйте эмуляцию riscv64, если хост ещё не настроен для кросс-платформенных сборок:

docker run --privileged --rm tonistiigi/binfmt --install riscv64
./scripts/build-custom-snapshot.sh -f Dockerfile -n my-image   # dist/my-image-256mb.snap
# опционально: --aot --trace-cmd '<горячая команда образа>' для встраивания AOT-блоков

Dockerfile собирается для linux/riscv64 (BuildKit выполняет шаги RUN под эмуляцией), его уплощённая корневая файловая система заменяет Alpine minirootfs, а остальной конвейер идентичен: vpod overlay, загрузка, --snapshot-save.

При экспорте сохраняется только файловая система. ENV, CMD и ENTRYPOINT из конфигурации образа отбрасываются, поэтому сохраняйте переменные окружения через /etc/profile.d/ в шаге RUN. Автоматический тёплый запуск Python применяется для образов на основе musl, в которых python3 находится в /usr/bin или /bin.

Pull requests

  • Держите PR сфокусированными: одно изменение на PR.
  • fmt, clippy и набор тестов должны проходить (CI проверяет все три).
  • Если вы затрагиваете пути выполнения или памяти эмулятора, опишите, как вы проверили корректность (минимум — набор тестов; для тонких изменений хорошей проверкой будет загрузка и реальная рабочая нагрузка в госте).

Лицензия

Этот проект лицензирован под Apache License 2.0. Подробности см. в файле LICENSE.

Категории