The engineering log

Everything that has landed.

Newest first. This is the full record behind the claims on the home page, including the audits that found our own bugs. Findings are named rather than hidden, and corrected when they turn out to be wrong.

The first feature release on the 1.0 line. Editions: edition "2027" promotes two warnings to errors (W0005 container read-modify-write to E0309, W0003 SSRF-bypassable string comparison to E0332), proving the mechanism handles multiple semantic changes per edition; the default edition stays backward-compatible; --edition= or KERN_EDITION selects; an unknown edition is refused. Package management: kern mod / kern get delegate to the native kern-pkg-port manager (built once to a cached binary, exec'd, never linked into the compiler). Deterministic resolution by Go's Minimal Version Selection; kern.sum (h1:<sha256-hex>, trust-on-first-use then immutable) enforced on top of kern.lock and the Ed25519 kern.trust signature path. Compiled-regex handle API: compile / is_match_compiled / find*_compiled / replace*_compiled over a 256-slot handle table (compile once, match many), plus batch ops. Stdlib breadth: std.flag, std.csv, std.tabwriter, std.base32, std.glob, std.mime, std.strconv, std.template (with conditionals), std.xml (structured tree), std.crc, std.mail; std.time gained fixed-offset time zones, RFC 3339 parse, Duration and a drift-compensating Ticker. M:N scheduler: cooperative scheduler with per-coroutine preemption, coroutine-aware HTTP context, and in-process zstd OCI layer read with a no-subprocess-escape invariant (SEAL-4 Step 2). file_io restored to Go parity: capability-confined filesystem resolution now caches the confined-root fd with per-call revalidation (2.68x slower to 0.94x). v1.0.0 compat corpus: 12 frozen programs exercising core 1.0.0 features. 2,073 tests (1,521 conformance + 85 compat + 467 negative). 432 stdlib modules (309 core + 123 in 10 bundled packages), 4,846 public functions. 59K-line self-hosted compiler (LLVM + native ARM64), 67K-line C runtime (42,207-line audited TCB).

A security release on the 1.0 line. Capability bypass fixes: E0404 waived for a library's call to an authority builtin and for a Cap-taking declaration of a gated name; E0407 switched off by a user function named __cap_trusted_src or by compiler-source trust inferred from paths; the checker's own bookkeeping writable by declaring a function of the same name. Ambient authority closed: nine host-state functions without a capability now take Cap<Container>; the microVM runner's entry hook (std.runtime.vm_dispatch) and the netlink builders are import-restricted (E0408). Container state hardened: the state root was world-readable (0755, now 0700); a container's env or image could plant a field in state.json (now escaped); path traversal out of the container and network state root is closed. std.ai removed (gave package code stdlib provenance; import kern_ai.* instead). In-kernel virtio datapath: vhost-net rings through ioeventfd and irqfd; the runner process is on neither the data path nor the interrupt path. VM runner self-containment: user/mount/pid/IPC/UTS/net namespaces, a per-VM subordinate uid, chroot to an empty directory, all before KVM_CREATE_VM. Installer release tag signature verification: SSH-signed tag check with a pinned key; a non-v* ref requires KERN_ALLOW_UNSIGNED=1. Go-parity wave: a safe-by-construction formatter (kern fmt) with comment preservation and a round-trip regression gate; 12 new stdlib modules (std.csv, std.flag, std.tabwriter, std.base32, std.glob, std.mime, std.strconv, std.template, std.crc, std.mail, std.xml, time.parse_iso); generic return recovery for Container<T>; generic typed channel/mutex API in std.sync (TypedChannel<T>, Mutex<T>, RwLock<T>); per-instance CPU pressure through /v1/utilisation (UR-106); a frozen compat corpus enforcing the 1.x compatibility promise; a normative spec with formal-proof limits surfaced; the edition "2027" mechanism (W0005 to E0309, W0003 to E0332). TCB 42,174. 2,274 tests (1,601 conformance + 206 compat + 467 negative). 431 stdlib modules (308 core + 123 in 10 bundled packages), 4,824 public functions. 61K-line self-hosted compiler (LLVM + native ARM64), 66K-line C runtime.

38 commits on the 1.0 line (v1.0.1 was a one-day patch carrying E0331, the trait-bound fix and the shared macOS container VM). The standard library split into a 290-module core and ten bundled packages (kern_cloud, kern_vm, kern_security, kern_net_services, kern_ble, kern_ops, kern_auth, kern_otel, kern_ai, kern_mcp), each with its own kern.toml and README, declared by package name (from kern_vm.vfio import ...). 19 contentless stubs removed, 100% docstrings, conformance tests for 12 previously untested modules. Seccomp allowlist (UR-89): process containers default to an allowlist BPF filter built from the Docker-default syscall base (runtime/kern_ns.c, per arch through __NR_*); the old denylist stays as denylist per instance. IO class and cgroup io.max (UR-88): InstanceSpec.io_class is standard, high or unlimited, with host-configurable bandwidth and IOPS bounds applied on the whole block device. unlimited requires an operator mark (kern cloud tenant set <id> --io-unlimited). Per-instance disk and network byte counters (UR-93): four cumulative counters (disk read/write, net rx/tx) from the cgroup's io.stat and the instance's tap; a counter that cannot be read is -1, never 0. Control plane HA (UR-96): hostd dials a list of planes and fails over between them; routes.set is replayable with a parity gate between commit sites and replay arms (UR-103). VM runner containment: a per-VM subordinate uid, its own user/mount/pid/net namespaces and an empty root before the guest image exists. VM datapath: vhost-net rings through ioeventfd and irqfd, a block device thread. Unicode safety (CWE-176/180): kern_uni_first_rune rejects overlong, surrogate and invalid UTF-8 sequences. Field encryption at rest: std.fieldcrypt. std.ai added as a stdlib entry. crypto_gen_token requires Cap<Random>. String cache safety: per-thread strcache cleared in epoch_end and arena_reset. Compiler registers the return signature for kern_str_replace (W0001). TCB raised to 41,786 for the strcache fix. Toolchain: clang 21/22 blessed, dnf in the error hint.

The first stable release, tagged 2026-10-03. The language implementation is 1.0-grade: it self-hosts, the full suite is green (1,423 conformance + 403 negative, 1,826 tests at 100% pass; both gates CI-blocking), the concurrency model is race-free (thread-safe GC, ThreadSanitizer-verified, ASan/UBSan-gated) and the build is reproducible byte-for-byte. The surface is frozen. docs/stability.md enumerates what 1.0 promises: the grammar, type system, taint rules and capability model, every pub fn in the stdlib index, the CLI and its --json schemas, the kern.toml keys. A removal takes a full deprecation cycle: @deprecated in a minor release, warning W0004 at every call site for the rest of the major line, removal only in 2.0, and kern fix performs the drop-in migrations mechanically. The systems surface a hypervisor builds on (std.cap_vm, std.cap_unsafe, std.arena, std.dma, std.irq) is locked in docs/systems-surface.lock, and a changed line without a recorded reason is a red gate. A release is a signed tag that builds from source. Tags are signed with a key whose public half is pinned in the repository; the release job refuses to publish from a tag that does not verify; artefacts (the C runtime bundle and the Windows launcher) are signed offline with a second pinned key. No compiler binary is published by any route, because for a compiler a downloaded binary is exactly the Trusting-Trust threat the toolchain exists to remove; the Homebrew tap is the repository itself, so there is no second copy of the formula to fall behind. The project's headline goal is verifiable trust, and that programme is at ten of eleven steps. Deterministic compiler IR (Step 3), the reproducible end-to-end binary (Step 4), the independent bootstrap path (Step 5) and the diverse double-compilation gate (Step 6) are per-commit CI gates. kern audit-caps makes the audit surface the capability grants, not the code (Step 8). The verifiable supply chain (Step 10) is closed on all four sub-claims. Capability enforcement, the lexer bisimulation and flow-analysis soundness are machine-checked in Rocq with zero admits (Step 11), re-checked per commit. What 1.0 does not claim, named rather than left to be found: the proofs are about models. The lexer model is now measured against the shipped lexer on every file in the tree, and the taint model is extracted and judged beside kern check on generated programs (300 of 300 agree); for the capability model the evidence that the compiler implements it is still the probe grid and the negative suite, not a mechanised link. Step 12, the independent external audit, was removed from the plan on 2026-09-19 and is taken up when ready. Performance is not at Go's on every benchmark cell; the per-cell matrix and ceilings are in docs/design/performance-to-go-parity.md. The live TRUST.md, ROADMAP.md and docs/threat-model.md track exactly what is and is not verified. The flagship privacy claim, PersonalData<T> to AI is a compile error, extends to every sink: display, file, database and HTTP, not just the LLM call.

Around the 1.0.0 tag the type system tightened in four places, two of them found by the project's own formal models. Two of the four are in the tag (the clock capability and per-type generics); the other two, E0331 and the trait-bound fix, landed on main on 2026-10-04, the day after, together with the PersonalData runtime fixes, and ship in 1.0.1. Implicit flow is E0331. The shipped checker accepted if secret: out = "a" followed by print(out), for two reasons each sufficient on its own: pd == q typed as a plain bool (the operator dropped the tag), and there was no program counter. The machine-checked taint model, extracted to OCaml and run beside kern check on 300 generated programs, disagreed on 83 of them, every one an implicit flow. Now an operator's result carries every tag of its operands, if/elif/else, while, match arms and a for over a tagged iterable run under a program counter, and under a tagged pc a write to an untagged variable, a call to an effectful function, a spawn, an untagged return or a break out of an untagged loop is refused. Measured across all 2,352 files in the tree: seven new diagnostics, all real; 300 of 300 and 2,000 of 2,000 generated programs now agree. The clock is authority. time_now_ms, time_now_seconds and time_sleep_ms take Cap<Time>; the ambient now_ms / now_seconds / sleep_ms are confined to std.cap_time and are E0404 anywhere else, exactly as entropy already was. Trait bounds were dead code. The call-site check looked for a one-letter type name; since a 2026-09-10 rename a generic parameter's type is a marker, so the guard was false for every call and a bounded generic accepted any argument. It hid because the two gates that would have caught it ran only in make verify-release, which nothing ran automatically, so v1.0.0 was tagged with both red. Both are release-gates steps now, and check-release-checklist-in-ci fails when a verify-release member is run by nothing in CI. Generics are compiled once per type when they compare their parameter. A comparison on two erased generic operands used to segfault for an inline-packed int, so it had been refused as E0330 and the stdlib carried _int twins; the typechecker now records which type each parameter takes at every call and emits one specialisation per binding. any_eq, all_eq, count_eq, list_contains and list_count are generic, their _int twins deprecated with replace: so kern fix does the rename. The JSON parser left C: 221 code lines out of the protocol class, the largest single risk the threat model names. The C parser was declared to return a map and returned whatever the top level happened to be, so json_decode("[1,2,3]") parsed and the first map operation walked a list as a map, exit 139, reachable from the network through the service dispatcher. The C fuzz harness had run 97 million executions under AddressSanitizer and was clean, because it called the function and never used the result as its declared type promised. The Kern decoder is total and strict; the harness is deleted rather than left calling a stub. With it, the TCB is now split by kind of trust (substrate, protocol, platform, binding, migratable, asm) and the split is a gate, so a new runtime file cannot be added without saying what kind of trust it carries. Packages cross repositories. kern-pkg publish packs and uploads from the file, update records a real SHA-256, install checks the checksum, the pinned signature and that the archive names the locked package before moving it into place, and the compiler resolves a dotted import into kern_modules/<name>/src/. None of it had worked before (publish truncated the tarball at its first NUL byte); check-pkg-roundtrip runs it end to end against a loopback registry. Also in this cut: two builds of one program are byte-identical (the pid left the link name, a macOS link without -g drops its dead debug map; gate 64); a signed JWT whose payload was a JSON array never expired, found by writing the test for its documented error code; every bare error string in the stdlib is now a coded error and the ceiling is zero; kern desktop is the Docker Desktop equivalent served from the one binary on loopback with three independent controls; a rootless container maps the user's subordinate id range and a non-root host agent holds a container to its disk through a privilege-separated mount helper; and the cloud control plane gained four-eyes approval on destructive actions, durable volumes, snapshots, revocable SSH keys and a web console into a running instance.

rc3 closes every finding from the rc2 security scan (H-1 through H-9, M-1 through M-15) and adds three CI gates that did not exist: AddressSanitizer, UndefinedBehaviorSanitizer and ThreadSanitizer. The installer builds from source. No prebuilt binary: the one-liner clones the release tag, builds the C runtime, verifies the published seed IR, compiles the compiler with the adopter's own clang, and proves the result by compiling and running a program. The trust gates run on the adopter's machine. Container host sealed (H-1): seccomp filters compose monotonically, so the host seal now installs on top of the workload seal; the premise "a container host cannot be sealed" was wrong. Handle lifetime (H-3/H-5): Channel, Mutex, RwLock and thread-pool handles are now GC objects with trace and destroy hooks; 500,000 channels created and dropped left 163 MB resident and added 2 ms to every collection, now 22 MB flat. chan_select race (H-4): two selectors woken by one send both returned the same index; one took the value, the other hung for ever. A reservation protocol settles it. pd_anonymize deleted (M-3): it returned the first byte plus asterisks, not anonymisation; pseudonymise(key, data) is the named primitive for a stable, join-preserving surrogate. Mutex<T> and RwLock<T> value slot was unguarded (H-2): the type whose purpose is mutual exclusion provided none for the value it holds, and E0309's own fix-it text recommended the pattern. A second lock guards the value slot now, and ThreadSanitizer over the concurrency pins is the evidence, not a passing test. The fuzzer found an out-of-bounds write in the GC collector on its first real run; the harnesses had been running a unit test.

A parallel bug hunt found nine expression positions where the typechecker never called check_expr at all. The most critical: a while condition bypassed the entire type and security model, so while db_query(...) compiled without Cap<Db> and while personal_data leaked without consent; f(x = __builtin_cap_fs_root()) could forge a capability past E0402. Eight more positions followed: default parameters, struct and union field defaults, assignment target indices, spawn lambda bodies and match patterns. All are now enforced, and make check-enforcement-positions gates 26 positions per commit. In the same wave, three trust-critical properties became machine-checked in Rocq with zero admits: capability enforcement (every ambient authority in the model is a provable error), the lexer bisimulation (both directions, including the comment token class) and flow-analysis soundness (tainted data reaching a sink is provably detected). All three re-check per commit via make check-formal-proofs. The honest remainder: these proofs cover the rules, and nothing yet proves src/typechecker.kern implements them; the correspondence grid is empirical, not formal. Separately, a full cloud control plane landed: multi-tenant with HA standby failover, SSO/OIDC, org-scoped tenants, WAF policies, an S3-compatible object store, load balancers, cordon/drain/fence, metered usage from the tamper-evident chain, scheduled workloads, capacity admission, and a typed client behind kern cloud. VM capabilities extended to GPU passthrough (VFIO behind an IOMMU, guest PCI bus, interrupt emulation proven in two halves), live migration (snapshot and restore an SMP guest, transport it, resume), and virtio-rng. Five adversarial security criticals from the rc2 scan were closed. The GC nursery shipped earlier was found to lose reachable objects and is now refused; the other performance work (list append, sort, string leaves) stands.

Four threads of work landed in the same week. Multi-vCPU guests. The microVM monitor gained SMP: microvm_create_smp spawns a VM with n vCPUs, each its own KVM vCPU on its own host thread, with per-vCPU CPUID topology and an Intel MP 1.4 floating-pointer table the monitor writes so the guest kernel discovers every core. Block-image volumes mount into the guest under the same capability that gates the VM itself, with a copy-on-write rootfs so tenants share a base image without writing to it, and an ext4 image writer, written in pure Kern, that produces the disk from a directory. Scoped Cap<Fs>. fs_scope(fs, root) confines a filesystem capability to a single directory: every path is resolved component by component via openat(O_DIRECTORY|O_NOFOLLOW), so an absolute path, .. or a symlink pointing outside is Err, never a read or write outside. The root is pinned by device and inode, so renaming the directory and planting an impostor at the same path is refused. Scopes nest. Generational GC. A nursery collects short-lived values without scanning the full heap, auto-GC is on by default, and a write barrier promotes nursery values stored into old-gen collections. Alongside it: HTTP thread pool, str length cache, GC lock elision, zero-copy str.split, in-place list append (the compiler lowers xs = xs + [e] to an append), and single-pass code emission, taking bootstrap from 35 seconds to 14. C-layer fail-closed sweep. Two systematic passes across every C leaf in HTTP, TLS, sync, event log, KMS, netlink, Redis, MQTT and WebSocket, converting every "succeed on error" path to a fail-closed return. A static-analyser sweep, an init_globals-per-TU fix and a W0003 SSRF lint round out the hardening.

Four modules landed that turn kern from a language you deploy onto a cloud into one you can build a cloud out of, and they share a design decision worth naming. net.s3_server answers the S3 API that net.s3 calls, with buckets and keys validated against traversal and objects stored flat so a request can never escape the root. net.lb is the backend-selection brain: round-robin, least-connections and weighted strategies over an immutable pool, where marking a backend unhealthy gates it out and an all-down pool fails fast rather than dialling a dead host. sys.volume is a block-volume catalog with the safety invariants written into the state machine: a volume is attached to at most one instance, an attached volume cannot be deleted, a snapshot records its parent, and a base volume with live snapshots is protected. sys.fleet is the self-healing loop, diffing the fleet you asked for against the fleet you have and returning the ordered actions to close the gap, including restarting an instance whose heartbeat has gone stale. The shared decision: every one of these cores is pure and deterministic. Selection returns the advanced pool rather than mutating one, an illegal volume transition comes back as data rather than crashing a live host, and reconciliation is a function from desired and observed state to a list of actions. That means the logic is unit-testable without a socket, a disk or a running fleet, which is the difference between a control plane you can reason about and one you can only watch.

The isolation story went from a proof of concept to a multi-tenant platform in one wave. The decision recorded in docs/design/microvm-kern.md was to write the monitor from scratch in Kern rather than orchestrate Firecracker or Cloud Hypervisor, because a multi-tenant isolation boundary that is someone else's binary is where sovereignty stops. The guest. Cap<Vm> is a capability kind only the entry module can mint, so an ambient KVM call is E0404. The engine enters 64-bit long mode, implements the Linux boot protocol and boots an unmodified Ubuntu kernel, which logs over a 16550 UART emulated in Kern; emulating the transmit and receive interrupts made that console interactive, so a shell answers uname over a live session. A virtio-blk device mounts real ext4 and the guest boots straight from /dev/vda with no initramfs, running the distribution's own /sbin/init as PID 1. A tap-backed virtio-net NIC gives it a network, and vm_publish_port DNATs a host port to a guest service over nftables, verified end to end. The platform. Tenants run as one OS process each, the same isolation boundary Firecracker and Fly.io use, so a tenant cannot corrupt a co-tenant through shared in-process state; each gets its own cgroup, Landlock scope and seccomp jailer. Authority is split by capability rather than by convention: spawning and stopping need Cap<Process>, confinement and stats need Cap<Fs>, and only the runner itself mints Cap<Vm>. Every lifecycle transition, created, booted, spawned, stopped, reaped, lands in the same SHA-256-chained event log the compliance trail uses, and microvm_audit_verify checks it independently. The hardening shipped with it, not after. No guest-supplied value forms a host pointer without first being proven to lie inside the guest's RAM; the bounds check was rearranged so a descriptor address near INT64_MAX cannot overflow-trap and abort the monitor; disk DMA is gated on the backing file's real size so a guest cannot write past it; and kill now rejects a non-positive pid, because a failed spawn's -1 reaching a terminate path would have signalled every process on the host. An adversarial test proves host memory just past the guest region survives a hostile write.

An adopter put it plainly: the documentation is what actually gets read, so a document promising a capability the code does not deliver is a false contract. A doc-versus-code audit was run across the README, the agent-facing llms.txt, the internal guide and every stdlib docstring, and it surfaced one critical security bug alongside a batch of guarantees that were narrower in the code than on the page. The critical one is worth stating without softening, because it lands on a module this site listed as shipped: webauthn_verify_assertion returned valid after checking the challenge, the relying-party hash, the user-presence flag and the signature counter, and never verified the signature at all; the imported verifier was dead code. Any request carrying the right relying-party hash and challenge would authenticate as any user, with an arbitrary signature and an unrelated public key. That is a total authentication bypass in a module advertised as phishing-resistant FIDO2. The root cause is structural rather than careless: a WebAuthn signature covers authenticatorData concatenated with the SHA-256 of the client data, a binary message a NUL-terminated Kern string cannot carry, and the existing verify primitive measures its message with strlen, so no primitive in the language could have verified it correctly. The fix was to make the function fail closed, returning invalid with an explicit reason so it can never authenticate without a real signature check, and to flip the conformance test that had been asserting an all-zero signature was valid so it now pins the bypass shut. A binary-safe verifier leaf (ES256 through mbedTLS P-256, EdDSA through libsodium) is scoped as the follow-up; until it lands, passkey verification is stated as unavailable rather than quietly broken. The rest of the sweep narrowed claims to what the code does: cert_is_self_signed was documented as checking for a known CA while returning subject-equals-issuer, an inverted trust decision; the mTLS module's “peer certificate verification” and “enforced minimum TLS version” are policy checks over fields TLS has already verified, with the minimum version recorded rather than enforced; the SIEM module's plain SHA-256 chain was described as tamper-evident when an unkeyed chain is re-forgeable by whoever writes the log, so its header now points at the keyed chain; hostname validation was documented as an SSRF control when the real control is the resolver guard; and the tracked-nonce helper is bookkeeping, not the cipher's own nonce, so it does not prevent nonce reuse. In llms.txt the flagship SQL-literal rule had been documented under the wrong error code entirely, which means code-generating models were being taught the wrong code for the headline feature; the generator is corrected. A parallel crypto audit found the matching pair: pseudonymise was an unkeyed hash of personal data, and an unkeyed hash of a name, an e-mail or a national ID is dictionary-reversible, so it did not pseudonymise in the GDPR Art. 4(5) sense at all. It is now keyed HMAC-SHA256 with a domain-separated subkey and a version tag, and an empty key is refused rather than silently producing a reversible value. The same pass corrected four places, including this site, that described the event-log chain as BLAKE2b when the code has used keyed HMAC-SHA256 for some time. None of this is comfortable to publish. It is also the only version of a trust claim worth anything: the register names what was wrong, including in the documentation, and the corrections ship with the tests that pin them.

Compile throughput was profiled end to end and three root causes fell out, each fixed without a rewrite. The strlen tax: Kern strings are NUL-terminated, so every length query was O(n), and a compiler slices and bounds-checks the same string constantly. The GC object header already carried a spare uint32 that nothing read or wrote; it is now a memoized strlen, so the first query on a string computes and caches and every later one is O(1). Self-bootstrap user-CPU went from 133.8 s to 37 to 46 s, about 3x, with no layout change and no producer changes. The GC hash: the collector's address-to-liveness table folded address bits with XOR-shifts, which collapses to addr>>4 for the near-constant strides malloc returns, textbook primary clustering. Measured over 44 million inserts, average probe was 14.97 and a single insert probed 123,898 slots. Fibonacci hashing diffuses every input bit into the high word: average probe 0.45, worst case 57. Wall time is unchanged, so this is a tail-latency fix, and it matters most for a long-running Kern server allocating under KERN_AUTO_GC. The prelude: every build force-injected all 146 replaceable-stdlib modules and emitted every function whether the program called it or not, so hello.kern shipped 255 function definitions and 52,002 IR lines for clang to optimise and the linker to throw away. Emission now runs a reference fixpoint over the resolved @kern_ symbols, so unreferenced prelude functions are never emitted at all: 25 definitions, 6,411 IR lines, build user-CPU 1.97 s to 0.40 s. The compiler's own bootstrap deliberately keeps the shake off, so the reproducibility chain is untouched, and the bootstrap fixpoint is still byte-identical generation over generation. The latest round went after collections. list_append is the allocation hot path under strings, parsers and every collection-heavy stdlib function, and profiling a tight append loop showed the cost was not the store but the machinery around it: a mutex taken on every single element even when no second thread exists, a GC header probe per append, and a root slot pushed for a result the caller discards. A monotonic flag now records whether the process has ever spawned a thread, set on the parent side before every thread-creating primitive so no window exists where two threads both read “single-threaded”, and the lock is skipped until that flag flips, after which it re-arms forever. The GC roots moved inside the grow branch, because a no-grow append allocates nothing and therefore cannot trigger a collection. The compiler emits an inline fast path at the call site. Sorting got its own fix: a Kern quicksort was making a runtime call per comparison and per swap, where the values already sit inline in the list's own slots, so a native sort over those slots replaced it. Together, appending is about 4x quicker and sorting a million integers went from 731 ms to 118, with the sort step itself down to roughly 16 ms, which upstream calls Go-class. Verified under GC stress with a collection on every allocation, so nothing is left unrooted.

Kern is now addressable by code-generating models as a first-class consumer. kern check --json and kern build --json emit exactly one JSON object on stdout with the same diagnostic schema (severity, E-code, message, file, line, column, hint), covering the whole build path: import resolution, typecheck, IR streaming, the cross-link refusal and the clang link. kern fix closes the loop by applying the mechanical repairs an agent should not have to reason about, starting with the typechecker's own “Did you mean?” renames; it is safe by construction, re-verifying that the bad token is still exactly at the recorded position before writing and applying edits bottom-up so a same-line fix never shifts a pending column. A generated llms.txt bundle gives a model the whole language surface in one file, counts pulled from source. On the protocol side, three new modules implement Model Context Protocol server and client over JSON-RPC 2.0, and they close the injection hole that MCP normally leaves open: tool arguments arrive as Untrusted<str> and tool results return as Untrusted<str>, so a handler that feeds model-provided input into SQL, shell, HTTP or a file without validating it does not compile (E0303). The same rule now applies to the model itself: ai_complete, ai_api_call and ai_chat return Untrusted<str>, because LLM output is adversarial input by definition, and every wrapper above them propagates the taint. That is the compile-time axis; the runtime axis is a WAF prompt-injection detector covering 27 patterns in five families (instruction override, role hijack, system-prompt extraction, delimiter injection, jailbreak). The language surface grew to match: select over channels with recv, timeout and default arms, which was the largest concurrency gap against Go, plus else if, real atomic sub/and/or/compare-swap, string interpolation over any expression rather than bare identifiers, and main(args) receiving argv. And kern new api <name> now scaffolds a service that actually works: health and root routes on :8080, compiling to a roughly 115 KB binary that answers curl out of the box, on macOS and Linux.

Kern reaches the device tier without a C dependency. sys.hci drives a Bluetooth controller over HCI_CHANNEL_USER, claiming the adapter exclusively the way a from-scratch host stack does, which is what mesh needs because it wants precise control of scanning and advertising rather than sharing with the kernel. It is verified against a real controller: hci_local_version returned HCI 5.2 and matched hciconfig exactly, revision included. sys.ble adds active LE scanning (parsing advertising reports into address, RSSI, flags, local name, manufacturer data and service UUIDs, deduplicated and merged with scan responses) and the advertising bearer that mesh provisioning rides on. On top of that the BLE Mesh stack is complete: network layer with obfuscation, AES-CCM, replay protection, IV index, segmentation and relay; the access layer and a Generic OnOff model for real device messages; Secure Network Beacon; commissioning, revocation and configuration; Heartbeat publication and subscription for topology discovery; Friendship, so a Low Power Node can sleep while a Friend queues its messages; and the Proxy protocol with PDUs and SAR. Provisioning is P-256 ECDH written from scratch in Kern, deliberately on 16-bit limbs rather than 32, because Kern integer arithmetic is overflow-checked and traps, so a 32×32 limb product would abort. It is verified against authority rather than itself: point doubling matches the standard vector in both coordinates, a derived public key matches one generated by OpenSSL, and the shared secret matches openssl pkeyutl -derive. Scalar multiplication is constant-time, which closes the provisioning timing side channel, and an invalid-curve hole found during the work was fixed. The whole stack is pure Kern over the unsafe systems tier, no new C, TCB unchanged. Alongside it, sys.bluetooth gained a real RFCOMM implementation and sys.wifi stopped being a stub, which together retire the last stubs in the standard library.

The standard library grew from 303 modules to 354, and from 2,219 public functions to 2,982, in three deliberate directions. Forward-looking crypto and identity: ML-KEM-768 and ML-KEM-1024 (FIPS 203, up to Security Level 5) for post-quantum key encapsulation, mTLS with SPIFFE workload identity, a CBOR codec, OpenTelemetry across all three signals, and SLSA build provenance. The SPIFFE claim is stated precisely: svid_verify checks X.509-SVID fields (expiry, issuer, format), and signature and chain verification stay with the caller against the trust bundle. A WebAuthn module landed in the same wave and is covered separately below, because an audit of it is the more instructive story. A gap analysis against Go 1.25 produced seven modules where Kern was behind: std.context for request-scoped cancellation, deadlines and value propagation; std.unique for handle-based string interning; std.synctest with a fake clock and virtual time for deterministic concurrency tests; std.fips for FIPS 140-3 algorithm, key and TLS policy checking; net.csrf using Fetch Metadata rather than tokens; and data.json_stream for token-by-token and NDJSON processing. Both the CSRF and FIPS modules bridge into the SIEM event pipeline with CWE-117 log sanitization, so a policy violation is an alert, not a log line someone might read. Domain work landed too: DSP, geospatial, acoustic and DAS fiber-monitoring modules, a PDF writer with images, text metrics and alignment, and MIME multipart attachments for mail. Those arrived with adversarial tests that immediately found two real bugs of their own, an ifft that mutated its input and a swapped polynomial in the WGS84 to RD projection, both fixed.

Test coverage was being measured the flattering way. A new by-name measurement over the standard library asked a blunter question, which functions does no test ever reach, and answered 710 public functions. Working through that list in batches did what a coverage number never does on its own: it found code that had never worked. std.graph had never compiled. So had std.integrity, a documented security module that was, in practice, dead code. All four of a decoder family failed open on malformed input, as did jwt_payload and base64url_decode, which is the worst possible direction for an auth primitive. pg_ping, pg_is_connected and check_db reported the inverse of reality, so a health check called a dead database healthy. List == List was always true and read out of bounds while doing it (now E0318). Builtin dispatch hijacked any stdlib function whose name happened to match a C symbol. A private stdlib function poisoned that name everywhere it appeared. The test harness itself reported a perfect PASS when handed a non-executable compiler. Each is now fixed and pinned, and the tooling learned from it: W0002 warns on an import that names nothing the module exports, with a location, and the resolver's module-name guard stopped rejecting 27 legitimate files. This is the unglamorous half of a trust claim, and the register is kept in public: coverage is reported as two axes with the remaining gaps recorded as open validations, not gated exclusions.

A sustained hardening wave gave the runtime a ceiling wherever a remote peer chooses the cost. HTTP gained a total request deadline and a connection admission cap; JSON parsing caps nesting depth, which was a remote unauthenticated stack overflow; chunked decoding is linear rather than quadratic; Redis replies, OCI registry bodies and HTTP/2 bodies are all bounded; a Content-Length remote abort and a Zip Slip arbitrary write are closed; a TLS file-descriptor use-after-free is refcounted; a 32-bit size wrap in gRPC decode is fixed; three TLS WANT_READ busy-spins and a blocking connect inside the global pool mutex are gone; and the SSRF and egress allowlist now applies at every dialer rather than one. A separate pass drew on twenty CVEs from 2024 to 2026 plus the CWE Top 25 and added net.request_guard for per-request body, URI, header and query-parameter bounds, a per-key rate limiter with fail-closed overflow, and a path sanitizer that rejects URL-encoded traversal (%2e, %2f, %5c, %00) rather than only literal ... Two detector gaps were found and closed on a later sweep: csrf_origin_check used a substring test, so https://example.com.attacker.net passed the same-origin fallback, and the SSRF guard only recognised dotted-decimal IPv4, so 0177.0.0.1, 2130706433, 0x7f000001 and 127.1 all walked past a 127. prefix check; both now normalise and match exactly. The most consequential finding was not an exploit. An adopter audit found that std.compliance was twenty functions of pure string formatting with zero persistence: calling audit_entry() made a GDPR obligation feel discharged while the string vanished if you dropped it. It is now backed by the same tamper-evident chain the AI Act log uses, keyed-HMAC-SHA256-linked, fsync'd and append-only, with compliance_audit_verify returning true only if every record still links, which is the operator-verifiable proof a DORA Art. 17 or NIS2 Art. 23 examiner asks for. Editing one field in a written record breaks the chain and verification catches it. The rest of the compliance surface was audited and confirmed genuine, and audit_entry survives as an explicitly labelled formatter that persists nothing.

The July trust push turned the “real but manually verified” caveats into blocking CI jobs. reproducibility rebuilds the compiler and checks the IR and the binary byte-for-byte per commit; ddc compiles the compiler with two independent C toolchains and fails the build on any divergence, which is the Trusting-Trust check made routine. The independent seed path produces a working compiler without the shipped binary. Formal work extended past the proof-of-method: formal/caps/ proves well_typed_confinement, that a well-typed program which mints no kind-k capability can never reach an authority-k primitive, which is exactly the E0404 guarantee, plus that the functional checker matches the declarative typing relation. Supply chain: lockfile chain-integrity is a fail-closed SHA-256 chain, and kern-pkg now derives a dependency's actual capability set from kern audit-caps --json and rejects a package that escalates beyond its declared manifest. docs/VERIFICATION-DOSSIER.md ties every trust claim to the exact command that checks it, and scripts/kern_verified.sh grants the mark only to a toolchain that passes deterministic IR + reproducible binary + DDC + full conformance on the clean-room seed. Documentation joined the gate set too: make check-docs fails on any stdlib or test count that drifts from the source, and every fenced Kern example in the normative docs must typecheck (it found six real API-stale examples on first run and 18 stale numbers).

Secret is now a first-class compile-time taint tag alongside PersonalData<T> (E0302) and Untrusted<T> (E0303): a secret reaching any sink that does not declare a Secret parameter, a log line, print, a string concat into a DSN, is E0316. Comparing a secret with == is E0317, because byte-by-byte equality short-circuits and leaks how many leading bytes matched (CWE-208); the fix is secret_equals or secure_compare, which always inspect every byte. A whole-program pass now builds the call graph from every spawn/spawn_thread and rejects a scalar or struct module global written from a thread-reachable function as E0309, a compile-time data race. Integer +/-/* are overflow-checked by default (CWE-190): the compiler lowers them to LLVM overflow intrinsics and traps with the operands named; code that wants modular arithmetic says so with wrapping_add. The security review also closed two escapes: the raw __syscall intrinsic, which let a zero-capability program do arbitrary I/O, is now gated on Cap<Unsafe>; and a taint launder via tuple projection is closed in the typechecker. Both are macOS-verified and pinned by negative tests.

The data-confidentiality track is complete: all five network transports now do TLS. A reusable TLS client underlies SMTP (TLS and STARTTLS), NATS honours tls_required, Redis speaks rediss://, MQTT on 8883, and gRPC over grpcs:// with ALPN h2 was the last plaintext transport to close. One shared policy is applied at every mbedTLS config site, server and client: a TLS 1.2/1.3 floor that is asserted rather than inherited, and an explicit ECDHE-AEAD-only ciphersuite pin, so static RSA, RC4, 3DES and CBC-SHA1 are dropped by omission and forward secrecy is guaranteed. The Linux release binary is now built inside a pinned Debian trixie image against mbedTLS 3.x, so it negotiates TLS 1.3, and the TLS 1.2 configuration is recorded against the Dutch government's Forum Standaardisatie / NCSC guidance. Mail is reachable from Kern for the first time: net_smtp_send_mail is cap-gated on Cap<Net>, fails closed on a non-TLS channel, CR/LF-guards every address, dot-stuffs the payload, and batches many messages over one authenticated connection, with egress control and SIEM security monitoring on the mail path. net.siem_sink ships each SecurityEvent the moment it happens: CEF over syslog/UDP for ArcSight, QRadar, Splunk and Wazuh, or JSON over HTTPS for Splunk HEC, Elastic and Sentinel, fire-and-forget with no in-memory retention. Fleet-driven fixes landed alongside: the Postgres pool self-heals after a server restart, net.health readiness aggregation names which dependency is down, kctl gives daemonless day-2 container ops (ls, logs, exec, stats), and a communication-protocol security audit (about 75 findings across SSH, TLS, HTTP, WebSocket, messaging, DB and DNS) is landing in batches, each box-verified against real servers: the P0 stack buffer overflow in the MQTT packet builders (CWE-787); SSH host-key verification that fails closed on a changed key and refuses unknown hosts unless KERN_SSH_TOFU=1, plus a bounded connect timeout and keepalive floor under every TCP client; a Postgres sslmode look-alike-host bypass closed with exact loopback matching and verify-full when a CA root is configured, and savepoint names validated against SQL injection; a shared CR/LF field guard that stops NATS subject frame injection, an opt-in SSRF and DNS-rebinding guard screening the resolved address at every TCP connect (cloud-metadata and link-local first, strict adds RFC1918, CGNAT and ULA), WebSocket Origin and frame-length guards, HTTP request-line CR/LF rejection, NTP anti-spoofing, real HMAC sessions with constant-time verify, credential auth for NATS/MQTT/Redis that fails closed over TLS, and certificate pinning (KERN_TLS_PINS, SHA-256 of the peer DER, checked in the one shared handshake so every mbedTLS transport gets it). The remaining P2/P3 list is cleared and box-verified; the one item briefly recorded as open (MQTT-over-TLS receive) was root-caused to a buggy verification test and is marked fixed, which is how the register works: findings are named, not hidden, and corrected when wrong.

Five independent audits of the language itself were folded into one ranked register (docs/design/language-security-hardening-2026-08.md), which first records what holds up (the per-commit Trusting-Trust chain, the Coq-checked E0404 confinement, container isolation that exceeds a default Docker profile, complete overflow checking) and then the holes in the flagship claims, traced to four root causes and fixed at that level rather than per symptom. Containment: the seccomp seal denied socket/openat/execve by syscall number, but an io_uring ring could issue the same operations without the syscall entry; io_uring_setup/enter/register are now denied unconditionally under the seal and in containers. Cap<Net> egress narrowing was advisory; KERN_NET_ALLOW is now enforced at the C connect leaf, so even an RCE is bounded. Forwarder revocation was decorative (no use path); forwarder_use_* now fails closed, the alive flag is atomic, and a new compile error E0406 forbids an extracted capability from escaping its frame (return, field store, module global, spawn capture), so revocation gates the lifetime, not just the extraction event. Taint: enforcement was per-site code that each forgot a different tag; it is now one env-aware predicate routed through every boundary, string interpolation parts included, and it recurses into struct and union fields, so serialising a struct with a PersonalData field to a log or a DB row no longer launders the tag. Codegen: integer / and % trap on a zero divisor and INT64_MIN / -1 instead of LLVM undefined behaviour. Stdlib: fallible crypto returns Result<str> rather than an in-band "ERROR:" string or an empty key, Untrusted discharges only into structured sinks (argv, params, auto-escaped templates) with the string escapers gone, CSV cells are quoted against formula injection, and the audit and revocation-feed chains are keyed to the box anchor. Trust: a seed-faithfulness CI gate re-derives the seed IR with kern_v4 and byte-compares it to the manifest, stated as trusting kern_v4 (a consistently poisoned seed pair would still pass; that is what the independent bootstrap and DDC gates are for). Every fix is box-verified and pinned by a negative test; the register keeps the honest T-model status alongside: flow soundness remains test-backed rather than machine-checked, and the external audit is a business decision not yet taken.

Every Kern binary now seals itself, before user code runs, to a seccomp-BPF filter that permits only the syscalls its capability set grants, and that set is derived by the compiler from the program's own types. A program that never mints Cap<Net> gets a kernel that denies socket(); one that never mints Cap<Process> cannot execve. E0404 already made ambient authority a compile error; the sandbox makes the same guarantee kernel-enforced at runtime, so a later RCE cannot exceed what the type system permitted. Nothing to configure, installed with NO_NEW_PRIVS so a spawned process inherits the filter. Self-hosting survives the seal: the compiler seals itself to FS|PROCESS and still bootstraps the next generation, running clang under the inherited filter. Landlock completes the filesystem story: the seccomp gate decides whether a program may open files, KERN_SANDBOX_FS_RO/RW decide which paths, layered on the seal and a clean no-op on kernels without Landlock.

A Kern service's identity on the network is now the SHA-256 of its own reproducible binary: kern_attest_self_hash() measures the running executable, so peers recognise a service by a hash they can rebuild themselves, with no certificate authority. Ed25519-signed challenges prove liveness, capability tokens carry what a caller may ask, the compiler injects a per-call guard on RPC serve, and the server attests back in an X-Kern-Server-Attest header. The mesh is bidirectional: the token proves who is calling and what they may ask, the attestation proves which build is answering. Unset config means disabled, fully backward compatible. Forensic record-replay welds nondeterminism to the audit chain: KERN_RECORD captures every clock read and random draw to a SHA-256-chained trace, KERN_REPLAY re-executes the incident run with byte-identical inputs, and kern_replay_verify() detects a trace edited after the fact. Built for DORA/NIS2 incident reconstruction, and a debugging tool for the heisenbug class.

The container engine's full mode (host bridge, veth pairs, NAT, inter-container networking) now runs for a non-root launcher: install-caps.sh grants cap_net_admin,cap_sys_admin to the binary once, the Docker-daemon privilege model but daemonless, and every later run by any user gets full networking. True-rootless mode adds inbound port publishing, AppArmor userns profiles and cgroup delegation (box-validated on cgroups v2), and compose service DNS resolves container names in capability and rootless mode. On the TLS side, kern ships an ACME v2 client (net.acme) that generates the PKCS#10 CSR and drives the Let's Encrypt exchange, and serve_tls_managed(server, cert_path, key_path) serves HTTPS from the resulting files, with reload_tls_cert to pick up a renewal without dropping the listener. HTTPS without a reverse proxy in front of it; the renewal loop is yours to schedule.

http_static(prefix, dir) now actually serves files, over both plain HTTP and HTTPS (it was a no-op that recorded the mount but never read it). Content type is set by extension, index.html is served for directories, requests are GET/HEAD only, and a strict path-traversal guard rejects any .. with a 403 before a file is ever opened (verified it will not serve /etc/passwd); the TLS path was refactored around a write sink so the same implementation serves plaintext and TLS sockets correctly. persist(s) is the epoch-GC escape hatch: under request-scoped GC (KERN_HTTP_EPOCH_GC) every object allocated during an HTTP request is reclaimed at request end, so a request-derived key stored into a long-lived global map (a rate-limiter's client-IP table, a cache) would dangle; persist copies the bytes onto the permanent heap so they survive, implemented as a thread-local allocator redirect that leaves epoch and collection semantics untouched.

Two adopter-requested tools turn claims into checks. A one-line .kern-version file pins the toolchain: kern build / run / check compare it to the running compiler and print warning: kern-version-mismatch when a compiler upgrade or a stale binary drifts from what the project expects (warning-only, so it never breaks a build; CI that wants hard enforcement greps for the line). kern audit-docker <file> walks a program's full resolved import graph and reports whether it takes any Docker dependency (an import of sys.docker, which shells out to the docker CLI, or a /var/run/docker.sock reference): a clean program prints audit-docker: PASS, a Docker dependency is FLAGGED and the command exits non-zero so CI can gate on it. “Docker-free” becomes auditable: a Kern binary is native and links no Docker client.

The OWASP injection family is eliminated at the level of the grammar, not patched at runtime. Five @<kind>_literal annotations (@sql_literal, @shell_literal, @url_literal, @regex_literal, @template_literal) require the decorated argument to be a string literal at every call site, so a query or command built from user input does not type-check. A Url<Trusted, Untrusted> type-state closes SSRF for URLs built at runtime: parse_url yields Url<Untrusted>, and only trust_url(raw, allowed_hosts) promotes it after a host-allowlist check. @url_literal also rejects any literal that does not start with https://. The promise is concrete: an AST shape an attacker would need to land the bug does not exist in valid Kern source.

Injection prevention stops a query built from user input from type-checking. The typechecker now goes one step further and validates the SQL itself against your declared schema, before the program runs. A query against a table that does not exist, or a SELECT / INSERT naming a column that is not in that table, is a compile error (E0311): not a 500 in production at 2am. The whole class of “typo in a column name” and “migration drifted from the code” bugs moves from runtime to kern build. Combined with the @sql_literal rule, a Kern SQL statement is checked for both injection safety and schema correctness at the same compile step.

The container subsystem used to shell out to a host Docker or Podman. It is now a complete OCI engine compiled into the language. It pulls and runs real images (debian:bookworm-slim, alpine:3.19) with nothing on the host but the Kern binary, a Linux kernel 5.15 or newer, and libc. No docker, podman, runc, crun, skopeo, iptables, nft or iproute2 required. HTTPS and tar extraction run in-tree (vendored mbedTLS plus a statically linked libarchive). Network setup is hand-rolled netlink (RTM_NEWLINK / NEWADDR / NEWROUTE), and outbound NAT installs an inet kern_nat nftables table directly over NFNETLINK_NFT. oci_pull_image_verified() checks a cosign-compatible Ed25519 signature and pins the manifest digest. A build gate (make check-container-no-shellout) fails if popen or system appears in the container runtime sources, and a CI leg apt-purges every shellout binary before running the suite under an strace execve tripwire.

The container engine now extends beyond Linux namespaces. On macOS, Kern boots a full Linux kernel via Apple's Virtualization.framework: ARM64 with GIC emulation, virtiofs file sharing, and virtio-console. Multi-container lifecycle works on macOS: cgroups v2, NAT networking, log capture, and port forwarding, all running inside the VM. On Linux, containers run directly via namespaces as before. Production deploys still need only the Kern binary + Linux kernel 5.15+ + libc, but development on macOS now runs real Linux containers without Docker Desktop.

All authority-bearing entry points: network, filesystem, process, FFI, environment access: now require explicit Cap<T> capabilities. Only the entry module can mint root capabilities; every other module must receive them as arguments. Compile error E0404 enforces this unconditionally. The entire conformance suite threads capabilities. Combined with the five @literal injection rules, kern's security model covers both ambient authority and injection attacks at compile time: two of the three classic attack surfaces (the third, memory safety, is mitigated by the GC and stack guards).

Generics (v0.7): selective monomorphization for stdlib hot paths: List<T>, Map<K,V>, Result<T>, Option<T>, Decimal: emitted as linkonce_odr typed wrappers so multiple compilation units share them without link conflicts; @monomorphize opt-in for user code; type-erased fallback. Async (v0.8): real spawn/await codegen, cooperative kern routines (16 KB guard-paged stack, configurable via KERN_ROUTINE_STACK_SIZE), routine-aware libuv IO (file/DNS/TCP), routine-aware libpq + mbedTLS, spawn_thread for CPU-bound work via the libuv worker pool. Two spawned sleep_ms(80) tasks complete in ≈82 ms. ABI-breaking decisions are now behind us, not ahead.

kern-pkg ported to Kern (full surface): init / build / run / test / add / remove / verify / install / publish / search / update with manifest [dependencies] / [dev-dependencies] parsing. The Python shim is gone. EU-sovereign cloud KMS providers: KERN_KMS_PROVIDER routes kms_encrypt / kms_decrypt to Scaleway (EU-native), Azure Key Vault (with AAD bearer cache), or an OVH stub; local AES-256-GCM remains the default. Chain-hashed AI audit log: every record carries prev_hash + record_hash (SHA-256 over the previous hash plus canonical body); kern_ai_audit_verify() replays the file and refuses appends if any link mismatches, and the kern ai-audit verify CLI runs that replay from the shell. This closes the EU AI Act traceability gate.