Что такое unikernel и почему они были сложными

Первый контакт с unikernels состоялся около 2015 года, когда была собрана команда специалистов по Haskell и проведена глубокая работа с функциональным программированием. Где Haskeller, там и OCaml программисты, и дальше уже недалеко до MirageOS. Отличная идея, и тогда её потестировали.

Unikernel — это концепция, при которой приложение само является операционной системой. Нет userland. Если нужен веб-сервер, DNS или отправка email, нельзя просто вызвать fork или spawn. Всё приходится писать как библиотеки в самом приложении.

Вот в этом была и суть трения. Кормак хорошо это помнит: когда строили Mirage, был TCP стек и HTTPS стек, но хранилищ практически не было. Вытягивали драйверы из NetBSD потому, что те можно запускать в userspace. Тогда это было действительно трудно.

В индустрии много догм. Nix — сложный. Bazel — сложный. Unikernels — сложные. Да, они были. Все эти сложные концепции теперь в весах моделей. Нужно просто запросить их и, с когнитивной точки зрения, избавиться от убеждения, что они сложные.

Операционная система — это технический долг

Каждое приложение, которое не является unikernel, было построено на предположении: есть приложение, а под ним операционная система. Вообще зачем нам операционная система? Потому что сорок лет назад был человек-оператор. Были серверы IBM 5250, AIX, Solaris и мейнфреймы. Многопользовательская ОС существует потому, что человек сидел перед терминалом, а приложение было на вершине.

Это рассматривается как технический долг, и вот почему это важно сейчас. Приложения взламывают. Взламывали и раньше, но с появлением AI это ускорилось. Кто-то взламывает userland приложение, получает shell. И этот shell становится VIP-сервисом утечки данных.

С unikernel поверхность атаки намного меньше. Если функциональность отсутствует в приложении (то есть в ОС), атакующий застревает. Нет следующего прыжка.

Кормак справедливо возразил. Reduction of attack surface — очень размытое понятие. Можно удалить shell из Linux контейнера, но почти в каждой Linux среде всё ещё есть что-то, что работает как интерпретатор. Можно запустить новую программу без writable filesystem, но всё ещё остаются memory safety и gadgets для ROP.

Всё верно. Но посмотрите, что делается последние двадцать шесть лет. Самая ранняя максима из времён SunOS и cgi-bin была такая: "не кладите компилятор на production". Потом пришли build контейнеры и production контейнеры. Потом Chainguard. Постоянно откусываем куски от поверхности атаки вместо того, чтобы идти в обратном направлении и гарантировать, что поверхности атаки вообще нет.

И это вот что люди упускают: если нет shell и нет интерпретатора, в весах модели нет ничего, что знает, что делать дальше. Обычный drive-by (берёшь RCE недели этой в любом фреймворке и получаешь shell, модель знает, что с этим делать) превращается в целевую атаку, которой нужен исходный код.

Теперь можно просто портировать недостающие библиотеки

Классическое возражение: unikernel должен разговаривать с Stripe, а в OCaml нет Stripe-библиотеки. Раньше вздохнул бы и написал. А теперь? Запустить цикл и портировать Go-библиотеку в OCaml. Вот вам Stripe в unikernel.

Кормак привёл отличный пример того же. Собирал минимальные образы Linux для appliances, что уже половина пути к unikernel — потому что запускаешь одно приложение как PID 1. Нужно было сделать XFS-файловую систему. Вместо того чтобы тащить xfsprogs со всеми его зависимостями, сел с агентом и заставил его написать `mkfs.xfs` на Rust, производящий byte-for-byte идентичный результат, со всеми флагами. Он реверс-инжинировал формат на диске линия за линией, с тестами для разных размеров блоков. Заняло несколько часов.

Работает это потому, что оригинальный инструмент — это золотой оракул. Генерируешь файловые системы разных размеров с обеими реализациями, сравниваешь. Портируешь тесты. Автоматизируешь.

Хранилище было другим серьёзным пробелом. Большинство workload'ов сейчас облачные по форме, даже on-prem, так что берёшь turbopuffer подход: S3 как основное, бесконечно расширяемое хранилище, с локальным NVMe блоком как кэшем и LRU (или любым другим алгоритмом кэширования) для горячих данных. Кормак большой фанат "S3 for everything". Пока latency не constraint, получаешь бесконечное хранилище с multi-user доступом, и всё на нём можно строить.

Nix машинные тесты и overlays

Кормак экспериментировал и с Nix, и был удивлён: первый раз, когда агент прототипировал ОС, он всё встроил в flakes и написал все тесты.

Nix как язык — отвратителен. Nixpkgs — отличный. NixOS machine tests — это то самое: пишешь тест, который поднимает парк машин и проверяет взаимодействие между сетевыми правилами и приложением. Вот что люди не знают.

А когда что-то upstream сломано или есть problem в цепочке зависимостей — просто overlay.

Если нужно tool-call'нуть человека, он же "Dear Maintainer", который может быть в отпуске или забросил проект, и ждать день, два дня или даже пять минут — это не AGI.

Строим recursive products. Агентам нужна способность менять мир как first-party source, а не как third-party bundled binaries. Возвращаемся в папку contrib и в Unix patches.

Если тебя волнует security, у тебя есть два выбора

Кормак спросил, что unikernels ещё нужны, чтобы люди их открыли. Честно? Именно это. За два поколения разработчиков, которые даже не знают, что они существуют.

Если тебе действительно волнует security, есть два выбора:

  1. Отправляешь спутники в космос — тогда используй seL4, формально верифицированную ОС. (Всё ещё немного обижен на австралийское правительство за то, что расформировало эту команду.)
  2. Все остальные должны серьёзно рассмотреть unikernels. Перестань пытаться укрепить то, что легко взламывается. Инвертируй — проектируй с другой стороны.

Другое классическое критика: многие ранние unikernel designs запускали всё на одном privilege level: приложение в том же ring, что и ОС. В 2026 году это prompt away от фиксинга, если хочешь ring separation. Это определённо безопаснее, чем молиться богам, что конфиг systemd cgroup правильный.

Подумай, сколько времени enterprises тратят на patching мира каждый раз, когда что-то новое выходит в Linux. Upstream теперь ожидает, что будешь patching kernel еженедельно. На неделе, когда записывали интервью, кто-то взломал KVM (то есть Firecracker, тот примитив, на который все рассчитывали как на хорошую sandbox) и собрал $50,000 от Vercel и ещё нескольких вендоров. Это не так много денег за то, что может root'нуть каждого managed cloud provider в мире.

Spaceleans: distributed unikernel operating system

Около семи месяцев назад погружение в unikernels было сделано, чтобы проверить, был ли mental model верный. Показал Кормаку Mirage папку, в которой были все функциональности, которые нужно было добавить.

  • Не было способа для fleet'а unikernel'ей следить за временем, так что взял NTP клиент из другого языка и портировал его. Потом собрал NTP сервер на базе RADclock и заимствовал идеи из того, как TigerBeetle работает с временем: не один source часов а много, упакованные как библиотека.
  • Network stack, DNS, HTTP клиенты и серверы, structured logging, OTel, Anthropic и OpenAI клиенты, и payments via Airwallex.
  • Generic retry библиотека для handling back pressure над HTTP плюс PPX метапрограмирование для fun.
  • PII wrapper на logging boundary, чтобы secrets и PII никогда не утекли через logging subsystem. У каждого проекта должен быть один. Pluggable, просто используй functor.

И потом самая cooked вещь, которую никому не показывал раньше: Spaceleans, Microsoft Orleans портированный на OCaml, работающий как unikernel.

Orleans — distributed actor system с transactions. Берёшь много физических машин и сливаешь их в один addressable heap. Actor всегда существует: `await GetCustomer()`, и если его нет в памяти, rehydrate'ится из pluggable storage provider. Коллапсишь n-tier архитектуру в actors и перестаёшь переживать, живёт ли что-то на машине A, B, C или D. Runtime обрабатывает это как infrastructure primitive.

Так что в странном смысле собрал distributed unikernel operating system из actors, с filesystem на вершине. Кормак назвал это Erlang-esque, и он прав. Написал всё за неделю. Вероятно, никогда не выпущу, но это falsified идею, что unikernels сложные.

Sampling history

Быть немного старше означает уметь sampling history, как опытный DJ вроде Carl Cox, который в сцене достаточно долго, чтобы pull'ить из предыдущего репертуара и bring forward. Все эти идеи существовали в восьмидесятых. Модели прочитали papers. У них TAPL и самая advanced type theory в training data.

То, чего не хватает, это curiosity и ambition людей делать эти unhinged вещи, и знание того, что previous records существуют, которые можно sample'ировать.

Как получить people to try это stuff? Просто делай. Если у тебя turbo Lamborghini alien space rocket, который более efficient и более secure — good for you, ты впереди. Делай cool вещи, привлекай curious newcomers, mentor их, grow. Как всегда было.

Тем временем все остальные будут Chainguard'ировать свой Ruby on Rails и управлять AWS с пятьюдесятью AWS-certified engineers, когда двое с Nix и Hetzner сделают то же. В итоге это вопрос денег. Более мощные инструменты efficient, и efficiency wins, особенно когда AI коллапсирует margins.

OCaml, Rust, Haskell и back pressure

Пришло ли время OCaml? Всё ещё в силе. Certain trading firm очень хорошо его использует. Когда встретился с Yaron в начале года, спросил, существует ли OxCaml чтобы их language extensions попали в training data и поднял бы всю компанию. Получил очень "no comment" улыбку.

Для агентов OCaml прекрасен. Functors между модулями — красиво. `.mli` файлы, typed header объясняющий, как должен работать модуль — очень efficient context для агентов. opam и Dune — genuinely good. Hindley–Milner. И compile times быстрые. Не вижу причины делать F# в наши дни.

Кормак в основном писал на Rust, и агенты хороши в Rust. Но compile time — это tax на back pressure. LLM'ы hallucinate, и когда compilation медленная, каждый hallucination дорогой потому что attempts per minute меньше. S3 clone Кормака — примерно миллион строк Rust; с четырьмя агентами компилирующими одновременно, они fight'ят за диск и CPU. В итоге spending больше на fast machines чем на tokens.

Haskell type system отличен, и модели это хорошо делают. Но не feel good запускать в production: space leak живёт в runtime state space и только в production проявляется. Кормак указал, что linear-type идеи в Rust вышли из Haskell papers пытаясь решить ровно это. Потом есть Zig approach: allocate всё впереди и никогда не allocate'й снова, что game devs'ы делали в восьмидесятых и девяностых. Сложно persuade'ить агента это делать в Rust потому, что constant-memory programs не в его training set.

Думаю, dependent types — победитель для next-generation языков. Anything, что позволяет codify больше в type system — это больше back pressure. Вероятно, не будешь surprised слышать, что есть fork Rust toolchain с dependent types. Можно просто делать вещи теперь.

Языки для агентов, и что Cursed научила

Pace языкового development'а было held back тем, как быстро люди могут learn новые concepts. Operator chaining — это essentially sugar для людей. Если агенты пишут код, можем lean'ить на сорок лет academic PLT research, пока знаешь как sample'ировать.

Индустрия codified'ла "не делай breaking changes" после Python 2 to 3. Кормак знал компании с сотнями людей на той миграции годами. Думаю, это правило больше не верно. Ship skill pack с breaking change и позволь агентам auto-migrate'ить.

Кормак спросил, что реально costs делать новый язык successful теперь. Go был последний язык, на который компания потратила real money, и это заняло долгое время. Может ответить.

Cursed собран с Sonnet 3.5 и 3.7, deliberately underspecified prompt, и три месяца running в цикле. Начал на C (не достаточно back pressure; потратил слишком много времени в Valgrind пока агент clobber'ил свои updates), потом Rust, потом Zig. Zig была ошибка; сегодня бы работало если б stayed на Rust. Стоило примерно US$6,000, и делал это трижды. Compare то, что стоил Go.

Теперь real bit, часть, которая ещё scary. Если allocate context window правильно — lookup table lexical structure и grammar — модель может program в языке, который не в её weights. Brute force и inefficient, но работает.

Think T-diagrams (tombstone diagrams). Lock down grammar и lexical structure, reach stage-two self-hosting compiler, ship sensible standard library, и start next training run. Оттуда, reach Roslyn-style self-hosted compiler с language services stupidly fast. Кормак спросил, помогла ли бы fine-tuning open model bootstrap'ить язык вроде этого. Не нужно.

Это было верно полтора года назад с намного более слабыми моделями. Потребуется просто один programming language designer going all in с хорошими моделями чтобы shock мир.

Что дальше

Как паб закрывался, Кормак спросил, что на уме. Если не читал latest post, go read. Если manage'ишь людей, create space и time для них experiment'ировать теперь, потому что в шести месяцах leadership спросит put людей на vitality curve.

Да, labs trained на commons. Hate это, и understand. Но trade time и skill за money; employers имеют minimum standards, и те standards change faster чем когда-либо в нашей индустрии. Be curious, learn как build agent, и go create beautiful stuff. Мы в renaissance.

Это time-compression device. Чем больше experience, чем больше можешь sample. Не всё ships; часть того, что показал Кормаку, может никогда не see light of day. Use these projects как katas и redo их когда модели становятся лучше.

Но если хочешь build что-то secure, seriously consider unikernels.

Главы

  • 0:21 — открытие unikernels: Haskell к OCaml к Mirage
  • 0:55 — что такое unikernel и почему это было сложно
  • 2:45 — сложно было в прошедшем времени
  • 3:33 — зачем нам операционная система?
  • 4:17 — shell как butler сервис для exfiltration
  • 5:12 — reduction attack surface размыт
  • 7:26 — не кладите компилятор на production
  • 9:13 — портирование Stripe в unikernel
  • 9:54 — минимальный Linux и mkfs.xfs на Rust
  • 12:22 — S3 для всего
  • 13:48 — Nix, машинные тесты и overlays
  • 15:28 — tool-call'нуть человека это не AGI
  • 15:53 — seL4 или unikernels
  • 16:56 — privilege rings
  • 18:24 — еженедельные patches ядра и KVM escape
  • 19:38 — demo: Mirage папка, NTP и время
  • 22:05 — Spaceleans: Orleans на OCaml как unikernel
  • 25:31 — sampling history
  • 26:52 — как получить people to try это: просто делай
  • 28:57 — OCaml и OxCaml
  • 31:45 — Rust compile times и back pressure
  • 33:06 — Haskell, space leaks и dependent types
  • 35:13 — memory strategies: Rust, Zig и game devs
  • 36:10 — языки для агентов
  • 37:41 — breaking changes и skill packs
  • 38:53 — Cursed
  • 42:36 — что Cursed научила
  • 46:47 — что дальше: AI use обязателен

Оставайтесь любопытны.