Unikernels waren lastig. Trefwoord: waren.
Justin Cormack, die destijds aan MirageOS werkte, belde me vanuit de pub om te bespreken waarom unikernels een comeback maken. De wrijving die ze voorheen lastig maakte is verdwenen; het traditionele besturingssysteem is inmiddels 'design debt'. Als je geeft om beveiliging, zou je er serieus over na moeten denken.
Justin Cormack, die in het verleden aan MirageOS en Unikernel Systems heeft gewerkt, merkt hetzelfde op als ik: mensen ontdekken unikernels opnieuw. Voor zijn nieuwsbrief start hij een serie gesprekken over dit onderwerp, en ik mocht de eerste zijn. Vijf minuten na zijn e-mail zat ik met een pull glas bier in mijn hand in gesprek met hem. We doken diep in Mirage, Orleans, Haskell, Nix, Cursed en nog veel meer.
Hieronder volgt de kern van dat gesprek.
Wat is een unikernel en waarom waren ze lastig?
Ik kwam voor het eerst in aanraking met unikernels rond 2015. Ik had een team van Haskellers samengesteld en was diep in de functionele programmering gedoken. Waar Haskellers zijn, zijn ook OCaml-programmeurs, en via die weg kom je bij MirageOS. Een geweldig idee; ik heb er destijds mee geëxperimenteerd.
Een unikernel is het idee dat jouw applicatie het besturingssysteem is. Er is geen 'userland'. Als je een webserver, DNS of e-mailfunctionaliteit wilt, kun je geen processen forken of spawnen. Je moet deze functionaliteiten als bibliotheken in je applicatie schrijven.
Dat was precies de wrijving. Justin herinnert zich dit goed: toen ze Mirage bouwden, hadden ze een TCP-stack en een HTTPS-stack, maar voor opslag was er bijna niets. Ze moesten drivers uit NetBSD trekken omdat die in userspace konden draaien. Dat was destijds erg zwaar.
Er heerst veel dogma in onze industrie. Nix is lastig. Bazel is lastig. Unikernels zijn lastig. Ja, dat waren ze. Maar deze complexe concepten zitten nu in de modelgewichten van AI. Je hoeft er alleen maar om te vragen via een prompt en het dogma dat het 'lastig' is, cognitief los te laten.
Het besturingssysteem als design debt
Elke applicatie die geen unikernel is, is gebouwd op de aanname dat er een applicatie is met daaronder een besturingssysteem. Waarom hebben we überhaupt een besturingssysteem? Omdat er veertig jaar geleden een menselijke operator was. Ik heb gewerkt met IBM 5250, AIX, Solaris en mainframes. Het multi-user besturingssysteem bestaat omdat er een persoon voor het systeem zat, en we de applicatie daar bovenop hebben geplaatst.
Ik beschouw dit als design debt, en dit is waarom dat nu relevant is. Applicaties worden 'gepopped'. Dat gebeurde al vóór AI. Iemand breekt door de userland-applicatie heen en krijgt toegang tot een shell. Die shell fungeert vervolgens als een VIP-butlerservice voor data-exfiltratie.
Bij een unikernel is het aanvalsoppervlak (attack surface) veel kleiner. Als de functionaliteit niet in de applicatie (het besturingssysteem) zit, is de aanvaller kansloos. Er is geen volgende stap mogelijk.
Justin uitte hier kritiek, en terecht. De reductie van het aanvalsoppervlak is een vaag begrip. Je kunt de shell uit een Linux-container verwijderen, maar bijna elke Linux-omgeving heeft nog steeds iets dat effectief een interpreter is. Je kunt een nieuw programma uitvoeren zonder een beschrijfbaar bestandssysteem. Je hebt nog steeds te maken met geheugenveiligheid en gadgets.
Dat is waar, maar kijk naar wat we de afgelopen zesentwintig jaar hebben gedaan. De oudste wijsheid die ik me herinner uit de dagen van SunOS en cgi-bin was: "zet de compiler niet op productie." Daarna kwamen build-containers en productie-containers, en later Chainguard. We blijven het aanvalsoppervlak telkens een klein beetje inkorten, in plaats van de tegenovergestelde weg in te slaan en te zorgen dat er simpelweg geen aanvalsoppervlak is.
En dit is het punt dat mensen missen: als er geen shell en geen interpreter is, is er niets in de modelgewichten van een AI die weet wat de volgende stap is. Dat verandert een 'drive-by' aanval (waarbij je de RCE van de week van je framework gebruikt om een shell te krijgen, waarna de AI-modellen weten wat ze met die shelltoegang moeten doen) in een gerichte aanval waarvoor de aanvaller daadwerkelijk over jouw broncode moet beschikken.
Het porten van ontbrekende bibliotheken
Het klassieke bezwaar is: je unikernel moet communiceren met Stripe, maar OCaml heeft geen Stripe-bibliotheek. Vóór AI zou je zuchten en het zelf schrijven. Nu? Je draait een loop om de Go-bibliotheek naar OCaml te porten. En voilà: Stripe in een unikernel.
Justin had een vergelijkbaar voorbeeld. Hij bouwde minimale Linux OS-images voor appliances, wat eigenlijk halverwege een unikernel is, aangezien je slechts één applicatie draait als PID 1. Hij had een XFS-bestandssysteem nodig. In plaats van xfsprogs en alle bijbehorende ballast mee te slepen, gebruikte hij een agent om mkfs.xfs in Rust te schrijven. Dit produceerde een byte-voor-byte identiek resultaat, waarbij elke flag correct werd geïnterpreteerd. De agent reverse-engineerde de on-disk formaten één voor één, met tests over verschillende blokgroottes. Dit kostte slechts een paar uur.
Dit werkt omdat de originele tool fungeert als een 'golden oracle'. Je genereert bestandssystemen van verschillende groottes met beide implementaties en vergelijkt ze (diff). Je poort de tests over en automatiseert het proces. Het porten van software is inmiddels triviaal geworden.
Opslag was het andere grote gat. De meeste workloads zijn tegenwoordig 'cloud-shaped', zelfs on-prem. Neem daarom de turbopuffer-benadering: S3 als primaire, oneindig uitbreidbare opslag, met een lokale NVMe block cache en een LRU-algoritme voor de 'hot bits'. Justin is ook een groot voorstander van "S3 voor alles". Zolang latentie geen beperkende factor is, krijg je oneindige opslag met multi-user toegang.
Nix machine tests en overlays
Justin experimenteerde ook met Nix en was verrast dat een agent, bij het prototypen van een OS, alle tests direct in flakes bouwde.
De taal Nix is matig, maar Nixpkgs is geweldig. NixOS machine tests zijn fantastisch: je schrijft een test die een vloot machines opstart en de interactie tussen je netwerkregels en je applicatie test. Dit is een functie die veel mensen niet kennen.
Wanneer er upstream iets kapot is, of er een probleem is in de supply chain van je afhankelijkheden, is dat simpelweg een kwestie van een overlay. Je kunt de wereld patchten. Als je een mens moet aanroepen (de bekende "Dear Maintainer"), die misschien op vakantie is of het project heeft verlaten, en je moet dagen wachten, dan is dat geen AGI.
We bouwen hier recursieve producten. Agents hebben de mogelijkheid nodig om de wereld te modificeren als first-party source, niet als third-party bundled binaries. We keren terug naar de contrib-folder en Unix-patches.
Beveiliging: twee keuzes
Justin vroeg wat unikernels nog nodig hebben om ontdekt te worden. Eerlijk gezegd is het dit: we hebben twee generaties ontwikkelaars opgevoed die niet eens weten dat unikernels bestaan.
Als je echt geeft om beveiliging, zijn er eigenlijk twee keuzes:
- Je stuurt satellieten de ruimte in, en dan moet je waarschijnlijk seL4 gebruiken, een formeel geverifieerd besturingssysteem.
- Iedereen anders zou serieus naar unikernels moeten kijken. Stop met het proberen te harden van iets dat inherent hackbaar is. Draai het om en ontwerp vanuit de andere richting.
Een andere klassieke kritiek is dat veel vroege unikernel-ontwerpen alles op één privilege-niveau draaiden: je applicatie in dezelfde ring als het OS. In 2026 is dat met een prompt op te lossen als je ring-scheiding wilt. Het is in ieder geval veiliger dan hopen dat je systemd cgroup-configuratie correct is.
Denk aan hoeveel tijd bedrijven besteden aan het patchen van de wereld telkens wanneer er iets nieuws in Linux verschijnt. Upstream verwacht nu dat je wekelijks je kernel patcht. In de week dat we dit gesprek opnamen, had iemand KVM gepopt (essentieel Firecracker, de core-primitive die we allemaal als goede sandboxing beschouwden) en $50.000 verzilverd van Vercel en enkele andere vendors. Dat is weinig geld voor iets dat elke managed cloudprovider ter wereld zou kunnen rooten.
Spaceleans: een gedistribueerd unikernel-besturingssysteem
Ongeveer zeven maanden geleden ben ik diep in unikernels gedoken om te controleren of mijn mentale model correct was. Ik liet Justin mijn Mirage-folder zien, die alle functionaliteit bevat die ik moest toevoegen.
Omdat er geen manier was voor een unikernel-vloot om de tijd bij te houden, portte ik een NTP-client uit een andere taal. Vervolgens bouwde ik een NTP-server gebaseerd op RADclock en leende ik ideeën van hoe TigerBeetle tijd afhandelt: niet één klokbron, maar vele, verpakt als een bibliotheek.
Ik voegde toe:
- Een netwerkstack, DNS, HTTP-clients en servers.
- Gestructureerde logging, OTel, Anthropic- en OpenAI-clients.
- Betalingen via Airwallex.
- Een generieke retry-bibliotheek voor back pressure over HTTP, plus wat PPX-metaprogrammering.
- Een PII-wrapper op de logging-boundary, zodat geheimen en persoonsgegevens nooit lekken via het logging-subsystem.
En dan het meest extreme project: Spaceleans, Microsoft Orleans geport naar OCaml, draaiend als een unikernel.
Orleans is een gedistribueerd actor-systeem met transacties. Je voegt veel fysieke machines samen tot één adresseerbare heap. Een actor bestaat altijd: await GetCustomer(), en als deze niet in het geheugen zit, wordt hij gerehydrateerd vanuit een pluggable storage provider. Je collapse je n-tier architectuur tot actors en stopt met je zorgen over of iets op machine A, B, C of D leeft. De runtime handelt dit af als een infrastructuur-primitief.
In zekere zin bouwde ik een gedistribueerd unikernel-besturingssysteem uit actors, met een bestandssysteem erbovenop. Justin noemde het "Erlang-esque", en hij heeft gelijk. Ik deed dit alles in een week. Ik zal het waarschijnlijk nooit releasen, maar het falsifieerde het idee dat unikernels lastig zijn.
De geschiedenis 'samplen'
Als je wat ouder bent, kun je de geschiedenis 'samplen', zoals een ervaren DJ als Carl Cox, die lang genoeg in de scene zit om putten uit oud repertoire naar voren te halen. Al deze ideeën bestonden al in de jaren tachtig. De modellen hebben de papers gelezen. Ze hebben TAPL en de meest geavanceerde typetheorie in hun trainingsdata.
Wat ontbreekt, is de nieuwsgierigheid en ambitie van mensen om deze onconventionele dingen te doen, en de kennis dat er eerdere records bestaan die gesampled kunnen worden.
Hoe krijgen we mensen dit te proberen? Door het gewoon te doen. Als je een turbo Lamborghini alien-ruimteraket hebt die efficiënter en veiliger is, dan heb je een voorsprong. Doe coole dingen, trek nieuwsgierige nieuwkomers aan, mentor ze en groei. Zo is het altijd gegaan.
Ondertussen zullen anderen proberen hun Ruby on Rails-applicatie te "Chainguarden" en AWS beheren met vijftig AWS-gecertificeerde engineers, terwijl twee mensen met Nix en Hetzner hetzelfde zouden kunnen doen. Uiteindelijk komt het neer op geld. Krachtigere tools zijn efficiënter, en efficiëntie wint, zeker nu AI de marges doet instorten.
OCaml, Rust, Haskell en back pressure
Is de tijd van OCaml teruggekeerd? Het is nog steeds sterk. Een bepaald handelsbedrijf gebruikt het zeer effectief. Toen ik Yaron aan het begin van het jaar sprak, vroeg ik hem of OxCaml bestaat, zodat hun taalextensies in de trainingsdata terechtkomen en het hele bedrijf naar een hoger niveau tillen. Ik kreeg een glimlach die deed denken aan "no comment".
Voor agents is OCaml prachtig. Functors tussen modules zijn elegant. De .mli-bestanden (typed headers die uitleggen hoe een module werkt) zijn zeer efficiënte context voor agents. opam en Dune zijn legitiem goed. Hindley–Milner. En de compileertijden zijn snel. Ik zie tegenwoordig geen reden meer om F# te gebruiken.
Justin schrijft voornamelijk in Rust, en agents zijn goed in Rust. Maar compileertijd is de 'belasting' op back pressure. LLM's hallucineren, en wanneer compilatie traag is, is elke hallucinatie duur omdat je minder pogingen per minuut kunt doen. Justins S3-kloon bestaat uit ongeveer een miljoen regels Rust; met vier agents die tegelijk compileren, vechten ze om disk en CPU. Je geeft uiteindelijk meer uit aan snelle machines dan aan tokens.
Het typesysteem van Haskell is geweldig en de modellen beheersen het goed. Maar ik voel me niet prettig om het in productie te draaien: een space leak leeft in de runtime state space en verschijnt pas in productie. Justin wees erop dat de lineaire-type ideeën in Rust voortkwamen uit Haskell-papers die precies dat probeerden op te lossen. Dan is er nog de aanpak van Zig: alles vooraf alloceren en nooit meer alloceren, wat game-developers in de jaren tachtig en negentig deden. Het is echter lastig om een agent te overtuigen dit in Rust te doen, omdat programma's met constant geheugengebruik niet in zijn trainingsset zitten.
Ik denk dat dependent types de winnaar zijn voor de volgende generatie talen. Alles wat je in staat stelt om meer te codificeren in het typesysteem, zorgt voor meer back pressure. Je zult niet verrast zijn te horen dat ik een fork heb van de Rust-toolchain met dependent types. Je kunt dit soort dingen nu gewoon doen.
Talen voor agents en de lessen van 'Cursed'
Het tempo van taalontwikkeling is vertraagd door hoe snel mensen nieuwe concepten kunnen leren. Operator chaining is in essentie slechts 'sugar' voor mensen. Als agents de code schrijven, kunnen we leunen op veertig jaar academisch PLT-onderzoek, zolang je maar weet hoe je dat moet samplen.
De industrie heeft na de overstap van Python 2 naar 3 de regel "maak geen breaking changes" gecodificeerd. Justin kende bedrijven met honderden mensen die jarenlang bezig waren met die migratie. Ik denk dat die regel niet langer opgaat. Ship een skill pack met de breaking change en laat agents de migratie automatisch uitvoeren.
Justin vroeg wat het tegenwoordig kost om een nieuwe taal succesvol te maken. Go was de laatste taal waarin een bedrijf echt veel geld heeft gestoken, en dat duurde lang. Daar kan ik antwoord op geven.
Ik heb Claude drie maanden in een loop gedraaid, en het creëerde een GenZ-programmeertaal genaamd Cursed. Het is de enige gecompileerde taal waarin je kunt coderen met termen als sus, slay en vibes.
Cursed is gebouwd met Sonnet 3.5 en 3.7, een bewust ondergespecificeerde prompt, en drie maanden draaien in een loop. Ik begon in C (niet genoeg back pressure; ik verspilde te veel tijd in Valgrind omdat de agent zijn eigen updates overschreef), daarna Rust, en toen Zig. Zig was een fout; het zou vandaag werken als ik bij Rust was gebleven. Het kostte ongeveer US$ 6.000, en ik heb het drie keer gedaan. Vergelijk dat met de kosten van Go.
Nu het echt interessante deel, het deel dat me nog steeds beangstigt: als je het context-venster correct allocateert — een lookup-tabel van de lexicale structuur en grammatica — kan het model programmeren in een taal die niet in zijn gewichten zit. Het is brute force en inefficiënt, maar het werkt.
Denk aan T-diagrammen (tombstone diagrams). Leg je grammatica en lexicale structuur vast, bereik een stage-two self-hosting compiler, ship een zinvolle standaardbibliotheek, en start de volgende trainingsrun. Vanaf dat punt kun je extreem snel een Roslyn-stijl self-hosted compiler met taalservices bereiken. Justin vroeg of het fine-tunen van een open model zou helpen om een taal als deze te bootstrappen. Dat is niet nodig.
Dat was al waar anderhalf jaar geleden met veel zwakkere modellen. Het kost slechts één programmeertaal-ontwerper die vol inzet op de goede modellen om de wereld te choqueren.
Wat nu?
Toen de pub sloot, vroeg Justin wat er in mijn hoofd speelde. Als je mensen aanstuurt, creëer dan nu de ruimte en tijd voor hen om te experimenteren. Want binnen zes maanden zal het management vragen om mensen op een vitality curve te zetten.
Als je team te druk is met hun 'normale baan' om met AI te experimenteren, bereid je ze voor om vervangen te worden. AI-gebruik is nu een voorwaarde voor inzetbaarheid.
Ja, de labs hebben getraind op de commons. Ik haat dat, en ik begrijp het. Maar je ruilt tijd en vaardigheid voor geld; werkgevers hebben minimumnormen, en die normen zijn sneller veranderd dan ooit tevoren in onze industrie. Wees nieuwsgierig, leer hoe je een agent bouwt, en ga prachtige dingen creëren. We bevinden ons in een renaissance.
Het is een tijdcompressie-apparaat. Hoe meer ervaring je hebt, hoe meer je kunt samplen. Niet alles wordt gepubliceerd; sommige dingen die ik aan Justin liet zien zullen nooit het daglicht zien. Ik gebruik deze projecten als kata's en doe ze opnieuw wanneer de modellen beter worden.
Maar als je iets veiligs wilt bouwen, overweeg dan serieus unikernels.
Groetjes,