
Runtime de conteneurs et sandbox rootless qui lance des images OCI appliquées par le noyau en quelques millisecondes, sans démon, avec des profils de ressources, des listes d'autorisation seccomp et la prise en charge de compose pour le code non fiable et généré par l'IA.
kern : un runtime sandbox rootless et de ressources virtuelles, rapide, pour toute charge de travail, y compris le code non fiable et généré par IA.
Un vrai conteneur, appliqué par le noyau, en ~3.5 ms, à partir d'un binaire de 1.52 Mo et sans démon.
0 RAM au repos · pas de démon, pas de socket, rien à démarrer · un binaire statique, libc comme seule dépendance Rust
# install the release binary (static, 1.52 MB, checksum-verified by the script)
curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh
# a throwaway shell in a real OCI image: rootless, kernel-enforced, a few ms
kern box dev --image alpine -it -- sh
Pas de Windows natif : utilisez WSL2. Installer.
Un seul binaire qui gère les ressources, dont l'isolation est la première. C'est pourquoi il n'y a pas de ligne unique pour kern dans un tableau de comparaison : c'est à la fois un runtime de conteneurs, un sandbox, un découpeur de ressources et un lanceur de stacks, en 1.52 Mo et sans démon.
pull, build depuis un Dockerfile, commit, push,
save/load. Une box issue d'une image démarre en ~3.5 ms.--security-profile untrusted, constitue l'ensemble durci complet.vcpu:), mémoire, disque (vdisk:)
et périphériques (vgpio:), déclarés une fois dans un kern.toml et attachés par nom. kern run
applique les mêmes limites à un processus sur l'hôte, sans aucun sandbox. docs/RESOURCES.mdTout son arbre de dépendances Rust se résume à libc : les manifests JSON et OCI sont analysés à la main,
et pull délègue à curl et tar, déjà présents sur la machine, plutôt que de lier une pile TLS.
(1.52 Mo est la compilation release optimisée en taille ; un simple cargo install depuis les sources
fait 1.91 Mo.)
Pas un hyperviseur. La frontière est le noyau Linux, donc un bug d'élévation de privilèges du noyau est une évasion. Docker et Podman partagent cette condition, c'est pourquoi gVisor et Firecracker existent.
Lu avec le slogan, c'est une ligne vue des deux côtés : le code non fiable et généré par IA est ce pour quoi kern est FAIT, parce que vous choisissez de l'exécuter et d'assumer le rayon d'impact (appels d'outils d'agents, jobs CI, étapes de build, cellules de code). Ce pour quoi il n'est pas fait, c'est le code hostile d'inconnus, multi-tenant, sur un noyau à partir duquel vous servez d'autres locataires. kern démarre toujours en rootless, là où Docker le propose en option.
Pas exempt du compromis userns. Son isolation repose sur un espace de noms utilisateur non privilégié, une source fertile de bugs d'élévation de privilèges locaux (LPE) du noyau. SECURITY.md le déclare avant toute affirmation.
Pas un mur autour de ce que vous montez. -v $HOME:/host donne à la box votre répertoire
personnel : un montage est une décision de confiance que vous prenez, pas une frontière que kern
applique. --net host et --privileged sont des exclusions explicites par leur nom. (Le seul chemin
que kern refuse de lier est son propre registre de runtime.)
Pas une réimplémentation de Docker Engine. Il parle les formats de Docker, pas son API : pas de réseaux overlay, pas de plugins, pas de Swarm. Matrice : docs/DOCKER-COMPAT.md.
Pas un runtime Kubernetes. Pas de CRI. Utilisez containerd ou CRI-O.
Pas de tranches GPU fournies. Prévu sur la feuille de route, sans aucun code GPU dans cette édition : il n'y a donc encore rien à valider ni à attaquer.
Ce qu'il ne sait pas ou ne fait pas encore est dans OPEN_ITEMS.md plutôt que de vous laisser le découvrir.
kern nécessite un noyau Linux avec des espaces de noms utilisateur non privilégiés et cgroup v2. Il fonctionne sur Linux, WSL2 et les cartes ARM (Raspberry Pi · Jetson · Arduino UNO Q) ; il n'existe pas de build Windows natif, utilisez WSL2 (kern fournit une rootfs WSL pré-configurée).
Le plus rapide est le binaire de release : un fichier statique, pas d'outillage, et le script vérifie son SHA256 avant de l'installer.
curl -fsSL https://raw.githubusercontent.com/getkern/kern/main/install.sh | sh
Il choisit x86_64 ou aarch64 pour vous, installe dans ~/.local/bin (/usr/local/bin en tant que
root, ou KERN_INSTALL_DIR), et refuse d'installer un téléchargement dont la somme de contrôle ne
correspond pas. Vérifier à la main tient en deux lignes :
curl -fsSLO https://github.com/getkern/kern/releases/latest/download/kern-x86_64-unknown-linux-musl.tar.gz{,.sha256}
sha256sum -c kern-x86_64-unknown-linux-musl.tar.gz.sha256 && tar xzf kern-x86_64-unknown-linux-musl.tar.gz
Depuis les sources est l'autre voie, et tout l'arbre de dépendances est une seule crate (libc),
donc c'est court : clone, build et installation ont pris 36 s sur un desktop (i7-14700KF), plus sur
une petite carte ARM.
# if you do not have Rust yet
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install --git https://github.com/getkern/kern getkern --locked
Cela place kern dans ~/.cargo/bin, que rustup ajoute à votre PATH (ouvrez un nouveau shell, ou
source "$HOME/.cargo/env", si kern n'est pas trouvé).
La release fournit aussi un binaire aarch64, un shim Windows .exe et une rootfs WSL pré-configurée,
chacun avec son propre .sha256 ; le tag est signé GPG et horodaté de manière indépendante
(provenance/).
kern doctor vous dit si les boxes pourront s'exécuter ici avant d'essayer. Cartes, WSL2 et la version
longue : docs/INSTALL.md. Questions courantes (Docker, bubblewrap, youki, E2B, Windows,
le modèle de menace) : docs/FAQ.md.
kern box dev --image alpine -it -- sh # a throwaway shell in a real OCI image
kern run --memory 256M --cpus 0.5 -- ./crunch # cap a process, no sandbox
kern box svc --image nginx:alpine -d -p 8080:80 \ # a service: published, restarted, health-checked
--restart --health-cmd 'wget -qO- localhost:80' -- nginx -g 'daemon off;'
kern ps # what is running, with PORTS and HEALTH
kern exec svc -it -- sh # shell into it
kern stop svc # its signal, its grace, then the code it exited with
kern top # live TUI: boxes, CPU/RAM, profiles, volumes
kern compose stack.toml up # a multi-box stack (examples/) or a compose.yml
kern compose stack.toml down # and take it down again
Code non fiable, un seul drapeau pour l'ensemble :
kern box job --image python:3.12-slim --security-profile untrusted --memory 256m \
-v ./job:/w -- python3 /w/x.py
--security-profile untrusted est l'allowlist seccomp + --cap-drop ALL + --read-only dans un
seul drapeau opt-in (vous pouvez les épeler à la main si vous préférez) ; ajoutez --require-limits
pour refuser de démarrer si les limites mémoire/pids ne sont pas réellement appliquées. Pas de réseau
sauf si vous le demandez, capacités dangereuses supprimées, seccomp toujours actif. Quatre-vingt-dix
exemples exécutables, chacun faisant une seule chose : examples/.
Chaque verbe de lecture répond aussi en JSON, donc rien n'a à analyser un tableau :
kern ps --json | jq '.[] | select(.health == "unhealthy") | .name'
kern volume ls --json # ps · images · stats · inspect · builds · pod ls · config list · diff
kern parle docker-compose.yml. Pointez-le vers la stack que vous avez déjà et kern compose up
l'exécute sans démon et sans Docker Desktop, à l'identique sur Linux, WSL2 et les cartes ARM.
# compose.yaml - a real stack, unchanged
services:
db:
image: postgres:alpine
environment: { POSTGRES_PASSWORD: secret, POSTGRES_DB: app }
web:
image: adminer
ports: ["8080:8080"]
depends_on: [db]
kern compose compose.yaml up
Les deux images officielles démarrent, web atteint db par nom de service, et le port est publié sur
l'hôte. À chaud (images en cache), la couche web répond en ~0.3 s, et la stack ne coûte que ce que
postgres et adminer utilisent réellement (~66 Mo ici) avec zéro démon en plus, là où Docker Desktop
est une VM en arrière-plan avant même votre premier conteneur.
Les images officielles qui passent à un utilisateur non-root (postgres, redis, ...) nécessitent uidmap
et une ligne /etc/subuid, et les pulls d'images sortants nécessitent pasta ; les deux tiennent en un
apt install sur une machine de dev, et kern doctor nomme celui qui manque. C'est la boucle de dev
locale, pas un orchestrateur de production : pas de Swarm, pas de réseaux overlay.
Exécutez du code généré par un agent ou un LLM depuis votre propre programme avec
kern-sandbox, un wrapper mince et sans dépendance autour du binaire
kern. Chaque appel s'exécute dans une box isolée neuve : réseau coupé, limites mémoire et pid,
capacités supprimées, sortie bornée, et un timeout que le binding applique lui-même.
pip install kern-sandbox # PyPI · needs the `kern` binary above, on PATH or $KERN_BIN
npm install kern-sandbox # npm · same
from kern_sandbox import run_code
r = run_code("import platform; print(platform.python_version())")
print(r.stdout) # ran in a fresh box; a timeout / OOM / blocked escape is data on r.fault
Sandbox
conserve un espace de travail entre les appels et un kernel() chaud garde un interpréteur pour les
cellules de moins d'une milliseconde (isolation plus faible, par choix).display() et les figures
matplotlib reviennent capturées, comme une cellule de notebook.kern-mcp) : un serveur stdio sans dépendance qui donne à Claude Desktop,
Cursor ou tout client MCP un interpréteur de code local. Pointez le client vers lui :{ "mcpServers": { "kern": { "command": "kern-mcp" } } }
Outils : run_code (python/bash/node), write_file, read_file, list_files. Chaque appel est une
box neuve réseau coupé ; les fichiers persistent entre les appels dans un espace de travail sur disque.
Commande de configuration, image et autres options : bindings/python/README.md.
API complète, Python et Node : bindings/python/README.md · bindings/node/README.md.
Une tranche est déclarée une fois dans ~/.config/kern/kern.toml et attachée par nom, à une box
sandboxée ou à un processus nu, avec le même jeton.
Trois sortes : vcpu: (CPU et mémoire), vdisk: (un disque scratch plafonné en taille) et vgpio:
(nœuds de périphériques). Deux d'entre elles, et les ancres dont elles sont découpées :
[[cpu]] # the host budget a slice is carved from
id = "cpu:0"
cores = 8.0
[[vcpu]] # 1.5 cores and 512 MiB -> attach as vcpu:heavy
name = "heavy"
backend = "cpu:0"
cpus = 1.5
memory = "512m"
[[gpio]] # a controller anchor
id = "gpio:0"
[[vgpio]] # exactly one device node -> attach as vgpio:sensor
name = "sensor"
backend = "gpio:0"
i2c = ["/dev/i2c-1"]
kern validate ~/.config/kern/kern.toml # check it before anything runs
kern box train --image alpine vcpu:heavy vdisk:scratch -- ./train.sh
kern run vcpu:heavy -- ./train.sh # the same slice, no sandbox
kern box iot --image alpine vgpio:sensor -- ls /dev
Les profils se composent : plusieurs s'attachent à une même box, et un drapeau explicite l'emporte sur
la valeur du profil. Chaque clé s'écrit comme son drapeau CLI, donc cpus correspond à --cpus et
memory à --memory. Un backend nommant un pool non déclaré est refusé à la lecture de la config, pas
au lancement de la box. docs/RESOURCES.md contient le schéma champ par champ.
Un vdisk: est un tmpfs adossé à la RAM quand kern s'exécute en rootless, quoi que dise son backend,
et une image ext4-on-loop avec un vrai quota quand il s'exécute en privilégié. kern indique lequel vous
avez, par profil, plutôt que de vous laisser le supposer, et le plafond de taille est appliqué dans les
deux cas.
vgpio: est granulaire au niveau du chip, pas par ligne. Demander des pins lie tout
/dev/gpiochipN, et ce périphérique caractère expose chaque ligne de ce contrôleur. pins = [17] ne
restreint pas la box à la ligne 17 : le noyau n'a pas de frontière de montage par ligne, donc la liste
de pins est une métadonnée de coopération plutôt qu'une frontière. Nommer un nœud de périphérique,
comme le fait i2c ci-dessus, accorde ce nœud et rien d'autre.
Intel i7-14700KF, Linux 7.0.0, le binaire de release, un script que vous pouvez exécuter vous-même :
python3 examples/benchmark.py. Vos résultats différeront selon votre CPU, votre noyau et votre système
de fichiers.
Trois mille d'un coup prennent ~2.2 s, et une box vivante coûte ~0.3 Mo de mémoire.
Deux notes honnêtes. Personne ne gagne vraiment la latence d'un seul coup : le plancher pour
unshare + exec est de 1 à 2 ms, donc tout le haut du tableau se trouve dans son propre bruit, et
bubblewrap est un lanceur sans images, sans capacités ni cycle de vie. L'écart qui compte, c'est face
aux moteurs, deux ordres de grandeur au-dessus.
Méthode, détail par phase, chiffres sur cartes et toutes les mises en garde : BENCHMARKS.md.
Espaces de noms, un pivot_root, 16 capacités dangereuses supprimées avant l'exec, une allowlist
seccomp toujours active par défaut (le filtre par défaut de moby moins les 35 syscalls d'évasion de
kern, qui restent tués durement ; un syscall hors de l'ensemble validé renvoie ENOSYS, et la denylist
plus large est l'opt-out via KERN_SECCOMP=denylist), des limites cgroup v2 (--require-limits refuse
de démarrer si elles ne s'appliquent pas), et un /dev refusant par défaut. Là où une frontière est
coopérative plutôt qu'appliquée par le noyau, SECURITY.md le dit et nomme le contournement.
Vous n'êtes pas obligé de le croire sur parole : pentest/ contient quatre suites adversariales qui vérifient ces frontières contre le noyau plutôt que contre les propres rapports de kern, et elles s'exécutent sans compte de registre ni réseau.
sh pentest/run-with-local-registry.sh ./target/release/kern pentest/pentest-ports.sh
Signalez une vulnérabilité en privé via GitHub Security Advisories ou [email protected].
Le cœur est terminé. Tout ce qui précède fonctionne aujourd'hui : 840 tests Rust, 78 Python et
61 Node, clippy-clean, cargo-deny-clean, sur du matériel réel : Linux, WSL2, Raspberry Pi 5, Jetson
Orin Nano, Arduino UNO Q. v0.7.0 est la première release publiée. La surface CLI et config peut
encore changer, toujours signalé dans CHANGELOG.md.
Les issues et pull requests sont les bienvenues. CONTRIBUTING.md décrit le workflow et les critères d'admission ; les contributions sont couvertes par le CLA.
Alex, @realexhub. Les commits proviennent de @getkerndev, l'identité de commit du projet.
Les commits ne sont pas signés ; le TAG de release, lui, l'est. C'est ce qu'il faut vérifier :
git verify-tag v0.7.0 avec la clé dans provenance/, dont l'empreinte est dans
SECURITY.md.
Apache-2.0. Voir LICENSE et TRADEMARK.md.
kern compose <file> up accepte un kern-compose.toml
(tables [box.NAME], avec les profils de ressources ci-dessus) ou le docker-compose.yml que vous avez déjà,
lu tel quel. Une stack correspond à un pod, les services se joignent par nom.ps, logs, exec, stats, inspect, wait, top (une TUI en direct),
doctor, plus un SDK Python et Node et un serveur MCP pour les agents.| kern | Docker | Podman |
|---|
| Démon | non | oui (dockerd + containerd) | non |
| Rootless | oui, toujours | opt-in | oui |
| Démarrage à froid, box nue | ~2.3 ms | ~297 ms | ~293 ms |
| Démarrage à froid, depuis une image OCI | ~3.5 ms | ~297 ms | ~293 ms |
| Arrêt d'un service (init gère SIGTERM) | ~1.9 ms | ~310 ms | ~380 ms |
| Mémoire résidente, rien ne tourne | 0 | 154 à 160 Mo | 0 |
| Empreinte | un binaire de 1.52 Mo | pile de démons | installation multi-binaires |
| Images OCI, pull / build / push | oui | oui | oui |
docker-compose.yml | oui, lu tel quel | oui | partiel |
| Réseaux overlay, Swarm, CRI | non | oui | partiel |
| GPU | sur la feuille de route | oui | oui |
| kern | bubblewrap | runc | podman | docker |
|---|
| Démarrage à froid (box nue) | ~2.3 ms | ~2.3 ms | ~18.6 ms | ~293 ms | ~297 ms |
| 200 boxes en parallèle | ~0.11 s | ~0.16 s | ~0.35 s | ~44.8 s | ~16.2 s |
| docs/INSTALL.md | installation sur Linux, WSL2 et cartes ARM, depuis les sources |
| docs/DOCKER-COMPAT.md | ce qui fonctionne de Docker, ce qui ne fonctionne pas, et où cela diffère |
| docs/RESOURCES.md · docs/CONFIG.md · docs/STORAGE.md · docs/EGRESS.md | le modèle à deux verbes, le schéma kern.toml, les volumes et l'egress |
| docs/THREAT_MODEL.md · SECURITY.md · OPEN_ITEMS.md | le modèle de menace (structuré, puis par mécanisme), et les lacunes connues |
| BENCHMARKS.md · EDGE.md | mesures, et fonctionnement sur un Pi, Jetson ou UNO Q |
| examples/ · blog/ | quatre-vingt-dix scripts exécutables, et des articles plus longs |
| bindings/python/README.md · bindings/node/README.md | le SDK kern-sandbox : intégrez kern en Python ou Node |