kern: rootless-контейнер за 3.5 мс без демона

Логотип kern

kern — быстрая, rootless-песочница и среда выполнения виртуальных ресурсов. Запускает любую рабочую нагрузку в настоящем контейнере, включая вызовы инструментов агента и AI-генерированный код.

Настоящий, аппаратно-изолированный контейнер примерно за 3,5 мс — из одного статического бинарника, без демона.

Терминал: команда kern box запускает контейнер на базе Alpine и печатает приветствие, время старта — 3,5 мс

0 байт RAM в покое · никакого демона, никакого сокета, ничего не нужно запускать · один статический бинарник, единственная Rust-зависимость — libc

# установить бинарник релиза (статический, скрипт проверяет контрольную сумму)
curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh

# одноразовая оболочка в настоящем OCI-образе: rootless, изоляция на уровне ядра, несколько мс
kern box dev --image alpine -it -- sh
# Windows: тот же бинарник под WSL2, скрипт настраивает WSL2 автоматически
irm https://raw.githubusercontent.com/getkern/kern/main/install.ps1 | iex
# macOS, два шага: сначала Linux-VM, затем kern внутри неё
brew install colima && colima start && colima ssh
curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh   # внутри VM

Работает напрямую на Linux и ARM-платах, на Windows — через WSL2, на Mac — через colima, Lima или OrbStack: один и тот же бинарник и тот же CLI под ядром Linux. Установка.


Что такое kern

Один бинарник, управляющий ресурсами, первый из которых — изоляция. Именно поэтому kern не укладывается в одну строку сравнительной таблицы: это одновременно среда выполнения контейнеров, песочница, распределитель ресурсов и запускатель стеков — всё в одном статическом бинарнике без демона.

  • Настоящий контейнер. Полноценные OCI-образы: pull, build из Dockerfile, commit, push, save/load. Запуск box из образа занимает ~3,4 мс.

  • Изоляция AI-агента — один контейнер на каждый вызов инструмента. Команда, которую агент только что решил выполнить; фрагмент кода, только что написанный моделью; ячейка notebook; шаг CI. kern запускает box, выполняет задачу, удаляет контейнер — настолько быстро, что изоляция на уровне каждого вызова становится поведением по умолчанию. Сеть отключена, если явно не запрошена; ограничения памяти и PID применяет ядро; возможности (capabilities) сброшены; seccomp работает в режиме запрета по умолчанию; таймаут применяется извне.

    Типизированные ошибки, а не копание в трассировке стека. Таймаут, принудительное завершение по OOM, заблокированный системный вызов, отсутствующая команда — каждое из этих событий возвращается вместе со стандартным выводом и кодом завершения. Можно ветвиться по результату и продолжать работу.

    Один бинарник, никакого демона. Подключается из Python, Node, LangChain или любого MCP-клиента ниже. А также чем kern не является — потому что граница определяется ядром Linux.

  • Rootless — всегда. Пространства имён пользователя (user), PID, монтирования (mount), сети (network), UTS и IPC; корневая файловая система монтируется через overlay или в режиме только для чтения с pivot_root; allowlist seccomp, запрещающий всё по умолчанию; ограничения cgroup v2. Флаг --security-profile untrusted — это весь усиленный набор в одном параметре.

  • Профили ресурсов, не только изоляция. CPU (vcpu:), память, диск (vdisk:) и устройства (vgpio:) объявляются один раз в kern.toml и подключаются по имени. kern run применяет те же ограничения к процессу на хосте без какой-либо песочницы, плюс --landlock-rw <path> — для ограничения записи этого процесса средствами LSM ядра. docs/RESOURCES.md

  • Стеки — в собственном формате kern или в уже имеющемся. kern compose <file> up принимает stack.toml (таблицы [box.NAME] с описанными выше профилями ресурсов) или docker-compose.yml без какого-либо конвертирования. Один стек — один pod, сервисы обращаются друг к другу по имени.

  • Инструменты вокруг них. ps, logs, exec, stats, inspect, wait, top (живой TUI-интерфейс), doctor. Привязка для Python также интегрируется в LangChain двумя способами: как инструмент для выполнения кода и как политика выполнения для его shell-middleware.

Всё дерево Rust-зависимостей kern — это libc: JSON и OCI-манифесты разбираются вручную, а pull вызывает уже установленные на машине curl и tar, не встраивая TLS-стек.

Установка

Бинарник

Контейнер строится на возможностях ядра Linux, поэтому kern работает там, где есть ядро Linux: на Linux и ARM-платах (Raspberry Pi · Jetson · Arduino UNO Q) напрямую; на Windows через WSL2 с готовым rootfs и установщиком, который настраивает WSL2 автоматически; на Mac внутри Linux-VM (colima, Lima, OrbStack, UTM), где это обычный Linux-kern: тот же бинарник, тот же CLI, то же поведение, что и на CI-сервере.

curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh

Один статический файл, никакого тулчейна. Скрипт выбирает x86_64 или aarch64, устанавливает бинарник в ~/.local/bin и отказывается от загрузки, если SHA256 не совпадает. Для сборки из исходников всё дерево зависимостей — один крейт:

cargo install --git https://github.com/getkern/kern getkern --locked

Debian и Ubuntu, Fedora, семейство RHEL (Rocky, CentOS Stream), openSUSE, WSL2 и ARM-платы: любой дистрибутив с неприви-легированными пространствами имён пользователя и cgroup v2. Каждый релиз проходит полный набор тестов на всех перечисленных платформах — в реальных VM и на платах — перед публикацией.

docs/INSTALL.md содержит остальное: ручную проверку контрольной суммы, переменную KERN_INSTALL_DIR, пошаговую установку на Windows и Mac, а также описание того, что делают ограничения ресурсов в VM по умолчанию.

SDK — вызов kern из Python или Node

kern-sandbox (PyPI, npm) требует установленного бинарника выше, поэтому сначала установите его:

pip install kern-sandbox
npm  install kern-sandbox

Оба пакета — только обёртка: они находят kern в PATH или по пути, указанному в $KERN_BIN. Релизы пакетов и бинарника не синхронизированы, поэтому при неожиданном поведении стоит проверить версии:

kern --version
python3 -c "import kern_sandbox; print(kern_sandbox.__version__)"

Для чего это предназначено — описано ниже; changelog указывает, в каком релизе появилось каждое исправление.

Быстрый старт

kern box dev --image alpine -it -- sh                  # оболочка в настоящем OCI-образе
kern box svc --image nginx:alpine -d -p 8080:80        # сервис с публикацией порта
kern box job --image python:3.12-slim --security-profile untrusted -- python3 /w/x.py
kern compose stack.toml up                             # целый стек одной командой

--security-profile untrusted объединяет allowlist seccomp, --cap-drop ALL и --read-only в одном флаге. kern ps и kern top показывают запущенное; каждый глагол для просмотра и инспектирования поддерживает --json, так что не нужно разбирать табличный вывод. По одному запускаемому примеру для каждого действия kern: examples/.

Запуск кода агента: Python, Node, LangChain, MCP, pi

Привязки (bindings) — это то, как ваша программа вызывает kern. Код внутри box может быть написан на любом языке, поскольку box — это OCI-образ: Python, Node, Go и Rust запускаются одной и той же командой с разным --image, и то же верно для всего, что поставляется в виде образа.

Агенту нужно где-то выполнять код, который только что написала модель. kern-sandbox — это то самое место: тонкая обёртка без зависимостей поверх бинарника kern, вызываемая из вашей программы. Устанавливается выше; API описан в bindings/python/ и bindings/node/.

from kern_sandbox import run_code

r = run_code("import platform; print(platform.python_version())")
print(r.stdout)          # выполнено в свежем box; таймаут / OOM / заблокированный побег — данные в r.fault

Каждый вызов — это свежий изолированный box: сеть отключена, ограничены память и PID, возможности сброшены, вывод ограничен по размеру, таймаут применяет сама привязка. Таймаут, завершение по OOM или заблокированный системный вызов возвращаются как типизированное поле fault в результате, а не выбрасываются как исключение. code_stderr в этом результате содержит stderr без собственных пометок kern — именно то, что нужно помещать в контекстное окно.

Пакет также включает kern-mcp — stdio-сервер без зависимостей, который даёт Claude Desktop или Cursor локальный интерпретатор кода. Поскольку сервер работает через stdio, одна та же строка конфигурации направляет клиент к box на другой машине.

{ "mcpServers": { "kern": { "command": "kern-mcp" } } }

Остальное находится на соответствующих страницах и здесь не дублируется: полный API, расширенный захват результатов, предварительный прогрев (prewarming) и интеграция с LangChain — в bindings/python/README.md и bindings/node/README.md; все переменные KERN_MCP_* и удалённое использование — в docs/MCP.md; для агента кодирования pi — kern-pi, маршрутизирующий его вызовы bash, read, write, edit, ls, grep и find в box, с README, разграничивающим, где граница задаётся ядром, а где это просто проверка пути, — в integrations/pi/.

Запуск целого стека: ваш docker-compose.yml без изменений

Один файл, одна команда. kern читает собственный формат, а также ваш существующий docker-compose.yml — без какого-либо конвертирования.

# stack.toml - одна таблица на сервис, ключи совпадают с флагами `kern box`
[box.db]
image = "postgres:alpine"
env   = ["POSTGRES_PASSWORD=secret", "POSTGRES_DB=app"]

[box.web]
image      = "adminer"
ports      = ["8080:8080"]
depends_on = ["db"]
kern compose stack.toml up          # или укажите путь к вашему compose.yaml
kern compose stack.toml watch       # пересборка + перезапуск сервиса при изменении его build context
kern compose stack.toml port web 80 # хостовый адрес, обслуживающий этот порт, из запущенного box

Оба официальных образа запускаются, web обращается к db по имени сервиса, порт опубликован. В compose-файле также можно указывать специфичные для kern параметры через пространство имён расширений спецификации (x-kern-vcpu, x-kern-security-profile) — и файл по-прежнему без проблем запустится в любой другой среде, поскольку спецификация предписывает любой среде выполнения игнорировать поля x-. Опечатка в таком поле не отбрасывается молча, а сообщается явно.

Стек использует одно сетевое пространство имён, если файл в него укладывается — именно это и даёт скорость: сервисы обращаются друг к другу по 127.0.0.1 без посредников. Если файл не укладывается, kern назначает каждому сервису собственное пространство имён и сообщает, чем это обходится: лишним ретрансляционным прыжком между узлами. Два случая не укладываются: два сервиса, слушающие на одном и том же порту контейнера, и секция networks:, не оставляющая сервисам ничего общего. --pod принудительно задаёт одно пространство имён и явно отказывается выполнять такой файл вместо того, чтобы молча снять изоляцию; --no-pod принудительно задаёт отдельное пространство имён для каждого сервиса.

Официальные образы, переключающиеся на непривилегированного пользователя, требуют uidmap и строки в /etc/subuid, а для внешних загрузок нужен pasta; kern doctor явно укажет на отсутствующее. Это инструмент для локального цикла разработки, а не production-оркестратор. docs/DOCKER-COMPAT.md

Профили ресурсов

Слайс (slice) объявляется один раз в ~/.config/kern/kern.toml и подключается по имени — как к изолированному box, так и к обычному процессу, одним и тем же токеном. Три вида: vcpu: (CPU и память), vdisk: (временный диск с ограничением размера) и vgpio: (узлы устройств).

[[cpu]]                     # бюджет хоста, из которого выделяется слайс
id    = "cpu:0"
cores = 8.0

[[vcpu]]                    # 1.5 ядра и 512 МиБ  ->  подключается как  vcpu:heavy
name    = "heavy"
backend = "cpu:0"
cpus    = 1.5
memory  = "512m"
kern validate ~/.config/kern/kern.toml       # проверить конфигурацию до запуска
kern box train --image alpine vcpu:heavy vdisk:scratch -- ./train.sh
kern run vcpu:heavy -- ./train.sh            # тот же слайс, без песочницы

Профили компонуются; явный флаг переопределяет значение из профиля; каждый ключ совпадает с соответствующим флагом CLI. Если backend ссылается на необъявленный пул, ошибка возникает при чтении конфигурации, а не при запуске box.

Две вещи, которые kern сообщает явно, а не оставляет на ваши догадки. vdisk: при rootless-запуске — это RAM-backed tmpfs, что бы ни говорил backend, а при привилегированном запуске — образ ext4 на loop-устройстве с настоящей квотой; kern сообщает, что именно вы получили, и ограничение размера действует в обоих случаях. И vgpio: работает на уровне чипа, а не отдельных линий: запрос pins привязывает весь /dev/gpiochipN, открывая каждую линию этого контроллера, так что pins = [17] — это кооперативные метаданные, а не граница безопасности. Указание узла устройства даёт доступ только к этому узлу и ничему больше. docs/RESOURCES.md

Стоимость контейнера и чего в kern нет

Все три столбца измерены на одном хосте, с одной рабочей нагрузкой, в один день: Intel i7-14700KF под управлением Linux 7.0.0; методология — в BENCHMARKS.md.

kern Docker Podman

Демон

нет

есть (dockerd + containerd)

нет

Rootless

да, всегда

опционально

да

Холодный старт, пустой box

~2,4 мс

~288 мс

~297 мс

Холодный старт, из OCI-образа

~3,4 мс

~288 мс

~297 мс

Остановка сервиса (init обрабатывает SIGTERM)

~2,3 мс

~162 мс

~194 мс

Резидентная память в простое

0

154–160 МБ

0

Размер

один статический бинарник

стек демонов

мульти-бинарная установка

OCI-образы: pull / build / push

да

да

да

docker-compose.yml

да, читается как есть (как организована сеть)

да

частично

Overlay-сети, Swarm, CRI

нет

да

частично

Проброс GPU в контейнер

нет

да

да

Производительность

Intel i7-14700KF, Linux 7.0.0, бинарник релиза, чередующиеся пакеты тестов на простаивающей машине.

kern bubblewrap runc podman docker

Холодный старт (пустой box)

~2,4 мс

~2,6 мс

~13,1 мс

~297 мс

~288 мс

200 box параллельно

~0,11 с

~0,13 с

~0,29 с

~43,1 с

~16,7 с

kern опережает bubblewrap примерно на 9%, и этот разрыв достаточно мал, чтобы его можно было подтвердить только при строгой методологии: BENCHMARKS.md содержит описание чередования, 240 пакетов тестов, результаты на платах aarch64, объяснение того, почему используется бинарник релиза, а не локальная сборка, и все оговорки. Значимый разрыв — это разрыв с движками-конкурентами: два порядка величины.

Безопасность

Пространства имён, pivot_root, 16 опасных возможностей (capabilities), сброшенных перед exec, всегда включённый seccomp-allowlist (стандартный фильтр moby минус 35 системных вызовов-путей к побегу, которые kern жёстко блокирует; всё вне проверенного набора возвращает ENOSYS), ограничения cgroup v2, без которых --require-limits отказывается запускаться, и /dev в режиме запрета по умолчанию. Там, где граница является кооперативной, а не обеспечивается ядром, SECURITY.md явно об этом говорит и называет способ обхода.

Не нужно верить на слово. pentest/ содержит пять наборов состязательных тестов, которые проверяют эти границы на уровне ядра, а не по отчётам самого kern, — без аккаунта в реестре и без сети:

sh pentest/run-with-local-registry.sh ./target/release/kern pentest/pentest-ports.sh

Сообщить об уязвимости конфиденциально можно через GitHub Security Advisories или по адресу hello@getkern.dev.

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

Документ Содержание

docs/INSTALL.md

установка на Linux, WSL2 и ARM-платах, из исходников

docs/MCP.md

MCP-сервер: инструменты, все переменные KERN_MCP_*, запуск через ssh или WSL с размещением песочницы на другой машине

docs/DOCKER-COMPAT.md

что из Docker работает, что нет, и в чём отличия

docs/RESOURCES.md · docs/CONFIG.md · docs/EGRESS.md

двухглагольная модель с томами и vdisk, схема kern.toml, исходящий трафик

docs/THREAT_MODEL.md · SECURITY.md · docs/GPU-CLAIMS.md

модель угроз (структурированная, затем по механизмам) и почему ограничение VRAM в пространстве пользователя не является границей безопасности

ROADMAP.md

чего сегодня не хватает или не измерено, и что может появиться

BENCHMARKS.md · EDGE.md

замеры производительности и запуск на Pi, Jetson или UNO Q

examples/ · blog/

92 запускаемых скрипта и развёрнутые статьи

bindings/python/README.md · bindings/node/README.md

SDK kern-sandbox: встраивание kern в Python или Node

Статус

Ядро готово, CLI заморожен. 1217 тестов на Rust, 458 на Python и 93 на Node, без предупреждений clippy и cargo-deny, на Linux, WSL2, Raspberry Pi 5, Jetson Orin Nano и Arduino UNO Q.

Скрипт, написанный под текущий CLI, продолжит работать: ни один глагол, флаг или поле --json не меняет смысл внутри патч-релиза. Что изменилось в каждом из них — в changelog.

Чем kern не является

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

  • Не свободен от компромиссов пространств имён пользователя (userns). Изоляция строится на неприви-легированных пространствах имён пользователя — плодотворном источнике LPE-уязвимостей ядра. SECURITY.md говорит об этом прямо, прежде чем делать какие-либо утверждения.

  • Не защищает то, что вы монтируете внутрь. -v $HOME:/host передаёт в box вашу домашнюю директорию. --net host и --privileged — это явные отказы от изоляции, указываемые по имени.

  • Не переработка Docker Engine. Речь о форматах, а не об API: нет overlay-сетей, нет плагинов, нет Swarm. docs/DOCKER-COMPAT.md

  • Не среда выполнения Kubernetes. Нет CRI. Используйте containerd или CRI-O.

  • Не управляет GPU-слайсами. Есть в дорожной карте. kern doctor сообщает, что даст ограничение VRAM для каждого GPU; на потребительском железе это кооперативная квота, а НЕ граница от вредоносного кода. Ничто не перехватывает вызовы драйвера и ничто не ограничивает GPU.

Известные пробелы: ROADMAP.md#known-gaps-and-what-would-settle-them.

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

Issues и pull request’ы приветствуются. CONTRIBUTING.md описывает рабочий процесс и требования; на вклады распространяется CLA.

Мейнтейнер

Алессандро Полито (Alessandro Polito), @realexhub, Италия. В ранних коммитах фигурирует @getkerndev — аккаунт, с которого был опубликован проект.

Лицензия

Apache-2.0. См. LICENSE и TRADEMARK.md.

© 2026 meganuke