# kern v1.0.0-rc2, reproducible across two machines

> Cut to drive the tag-release pipeline end to end, so registry artifacts are built by CI, not by hand.

Source: https://kern-lang.eu/blog/v100-rc2-release

July 14, 2026

# kern v1.0.0-rc2, reproducible across two machines

`1.0.0-rc2` is the second release candidate, cut to drive the tag-release pipeline end to end so registry artifacts are built by CI rather than hand-uploaded, and to carry the fixes accumulated on `main` since rc1 into a coherently-versioned build. It also moves the trust story: reproducibility is no longer single-machine. The compiler IR is now byte-for-byte identical across two machines and two operating systems. And three practical features land: static file serving that actually works, a safe escape hatch for long-lived state, and tooling that turns two adopter claims into checks.

## At a glance

Tests, 100% pass

1,014

Machines and OSes: compiler IR byte-identical (manual)

2 × 2

Stdlib modules, 1,961 functions

272

Data races (ThreadSanitizer-verified)

0

## What "1.0.0-rc2" is, exactly

rc2 is a small, deliberate step, not a feature wave. It was cut for two reasons.

- **To exercise the release pipeline end to end.** The tag-release jobs (bootstrap-verify plus the release build) now produce the registry artifacts, so what adopters download is built by CI from a tagged commit rather than hand-uploaded. rc2 is the first tag to run that path in anger.
- **To carry the fixes accumulated on `main` since rc1** into one coherently-versioned build: a compiler fix where an untyped struct-return bind failed to propagate its type through a for-loop, a codegen fix where spawning a void-returning function emitted invalid IR, and the new features below. VERSION, `src/main.kern`, and the README all read `1.0.0-rc2`.

The honest-framing rule from rc1 still holds: the language implementation is 1.0-grade (it self-hosts, the full suite is green, the concurrency model is race-free, the build is reproducible), and the verifiable-trust goal is mid-roadmap. What changed in rc2 is one of the trust rows.

## Reproducibility moves past single-machine

The single biggest caveat on rc1 was the word _single-machine_: everything was evidenced on the one box that built it. rc2 breaks that for the compiler's intermediate representation.

- **Two machines, two operating systems, byte-identical IR.** `check_reproducible.sh --ir` emits all 102 `.ll` files and they are byte-for-byte identical across two independent bootstraps: on the Mac (via `kern_v4`) and on a Linux box (via `kern_seed`), verified 2026-07-13\. Cross-OS determinism holds too: the macOS seed and the Linux re-emission agree. This is Step 3 of the trust roadmap, no longer local to one host.
- **End-to-end binary, reproducible on the box.** On the Linux machine the gen2 and gen3 binaries are byte-identical, which is Step 4 evidenced there.

The honest boundary is precise, and worth stating rather than blurring: the IR is reproducible cross-machine and cross-OS; the whole binary is reproducible on one box; and none of it is yet a per-commit CI gate or externally audited. The live `TRUST.md` dashboard marks each of these rows as manual, not gated. The direction of travel is what matters: trust is becoming a property a second machine re-derives, not a promise the author makes.

## Static files that actually serve, over HTTP and HTTPS

`http_static(url_prefix, dir)` used to be a no-op: it recorded the mount and never read it, so every static path 404'd. In rc2 it serves files, and it does so on both the plain-HTTP worker and the TLS server.

- **It serves real files.** Content type is set by extension (html, css, js, json, svg, png, fonts, wasm and more), `index.html` is served for the mount root and for directories, and only `GET` and `HEAD` are answered. Dynamic routes coexist with the static mount.
- **Path traversal is refused before a file is opened.** Any request containing `..` is rejected with `403` before the filesystem is touched at all. It was verified that the guard will not serve `/etc/passwd`.
- **The same code serves plaintext and TLS.** The implementation was refactored around a write _sink_: the plain worker passes a raw file-descriptor sink, the HTTPS worker passes a TLS sink, so static files no longer get written as plaintext onto a TLS socket (which is what made the HTTPS worker 404 every static path before).

## A safe escape hatch for long-lived state: `persist`

Kern can run HTTP requests under an epoch garbage collector (`KERN_HTTP_EPOCH_GC`): every object allocated during a request is reclaimed in one shot at the end of the request, which is fast and leak-proof for request-scoped work. The sharp edge is global state. If you take a request-derived value, say a client IP, and store it into a long-lived global map (a rate-limiter's table, a cache), the arena pages it lived in are `munmap`'d at request end and the key dangles, which is a hard segfault, not a subtle bug.

`persist(s: str) -> str` is the fix: it returns a permanent-heap copy of the string, safe to store in global state even when called inside a request epoch. It is implemented as a thread-local redirect in the GC allocator that copies the bytes onto the permanent heap without disturbing epoch or collection semantics, verified with a C escape reproducer and `test_persist.kern`. It is the smallest primitive that makes an in-process rate-limiter or cache correct under epoch GC, chosen over a narrower keyed-limiter built into the runtime.

## Adopter tooling: version pinning and a Docker-independence attestation

Two features came straight from adopters, and both turn a claim into a check you can run.

- **`.kern-version` toolchain pinning.** Drop a one-line file (for example `1.0.0-rc2`) next to the source you build or in the working directory. `kern build`, `run`, and `check` compare it to the running compiler and print `warning: kern-version-mismatch` when they differ, so a compiler upgrade or a stale binary surfaces immediately. It is warning-only and never breaks a build; a CI job that wants hard enforcement greps for the warning.
- **`kern audit-docker <file>`.** It 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" stops being a README sentence and becomes an auditable fact: a Kern binary is native and links no Docker client.

The standard library continued to grow alongside these: it is now 272 modules and 1,961 public functions across `std`, `data`, `net`, `db`, and `sys`.

## Where verifiable trust stands: Steps 2 to 7 of 12

The trust program is a 12-step roadmap, and being honest about a release means being honest about which steps are done and how far. rc2's progress lands in the reproducibility rows, which are no longer confined to the build host:

- **Step 1: the Trusted Computing Base is written down.** Exhaustively, in `TRUST.md`: every component a Kern binary must trust, and which step verifies it.
- **Step 2: the C runtime TCB is shrunk to an auditable core.** The trusted part is named and minimal by construction; new functionality defaults to capability-bounded Kern, outside the TCB.
- **Step 3: deterministic compiler output.** Now byte-identical across two machines and two operating systems (macOS and Linux), verified by hand, not confined to the build host.
- **Step 4: reproducible end-to-end binary.** gen2 and gen3 binaries byte-identical on the Linux box; not yet a per-commit CI gate.
- **Steps 5 and 6: an independent bootstrap path and a diverse double-compilation gate.** In progress: the second machine now runs its own `kern_seed` bootstrap, which is the raw material for both.
- **Step 7: a capability-secure core.** All authority-bearing entry points require an explicit `Cap<T>`; ambient authority is a compile error (E0404).

## What stands between rc2 and 1.0.0 final

The remaining five steps are exactly the difference between "trust we can evidence here, on two machines" and "trust a third party can re-derive on a per-commit basis." None are aspirational; each has a Done-when clause on the roadmap.

- **Step 8: capability provenance and audit tooling.** Make a binary's actual grants inspectable and checkable.
- **Step 9: sound, whole-program flow analysis.** The soundness backing for the privacy and capability claims.
- **Step 10: a verifiable supply chain.**
- **Step 11: formal verification of the trust-critical path.** Coq was chosen as the proof assistant (INRIA, EU-native, the CompCert track record); the lexer track is scaffolded.
- **Step 12: an independent audit and the verification dossier.** Plus turning CI on, so every claim above is re-checked on a machine that is not the author's.

Every item is on the [ROADMAP](https://codeberg.org/PUAGroup/kern-lang/src/branch/main/ROADMAP.md) with a checkbox and a Done-when clause. A trust claim with no check is not done.

## By the numbers

- **1,014 tests** at 100% pass, 787 conformance and 227 negative; both gates CI-blocking
- **Compiler IR byte-identical across two machines and two OSes** (macOS and Linux, manual); end-to-end binary byte-identical on the Linux box
- **Steps 2 to 7 of 12** trust-roadmap steps done, with reproducibility now cross-machine; Steps 8 to 12 remain
- **0 data races**: the GC is thread-safe, ThreadSanitizer-verified
- **`http_static`** serves files over HTTP and HTTPS, with a strict path-traversal guard
- **`persist()`**: the epoch-GC escape hatch for long-lived global state
- **`.kern-version` pinning** and **`kern audit-docker`** attestation
- **272 stdlib modules**, 1,961 public functions, across `std`, `data`, `net`, `db`, `sys`
- **36,192 lines** of Kern in the self-hosted compiler, 42K lines in the C runtime
- **EUPL-1.2**, hosted on Codeberg, governed from the Netherlands

## Try v1.0.0-rc2

Install on Linux or macOS. Single binary, no runtime dependencies.

`curl -fsSL https://kern-lang.eu/install.sh | bash`

[View on Codeberg ](https://codeberg.org/PUAGroup/kern-lang)
