Back to blog

On main — generics, cooperative async, Secure<T>, native OCI

What's landed on main between v0.6.0 and v0.7 / v0.8: selective monomorphization for stdlib generics, real spawn/await codegen on cooperative kern routines, the Secure<T, State> type-state with EU-sovereign cloud KMS, a native OCI container runtime, and the kern-pkg port to Kern. The released line is still v0.6.0 — and that is intentional.

892
Tests, 100% pass
190 / 190
Negative tests reject
v0.6.0
Released line
v0.7 · v0.8
In flight on main

Why this is a status post, not a release

Earlier drafts of the changelog labelled work on main as v1.0.0 and v1.1.0. That label was retracted in April. v1.0 is an API stability promise we are not yet ready to honour: until generics and async ABI land, every async fn frame layout could move; until the foundation is legally incorporated, governance is informal; until the soak window passes, "no breaking changes" is wishful.

So: the released line is still v0.6.0. Everything below is real, on main, with tests, but it is not tagged. The order matters — generics first, async on a stable generic ABI second, KMS providers and container runtime on top. See ROADMAP.md for the explicit v1.0 exit criteria.

Generics — selective monomorphization for stdlib hot paths

Generics in v0.6.0 were type-erased to i8*. Fine for compiler simplicity, bad for throughput on List<Decimal> and Map<str, Decimal> — exactly the workloads finance and government deployments lean on. The v0.7 strategy committed to a hybrid: monomorphize what matters, box what doesn't, let users opt in.

Why selective: full monomorphization inflates binaries unacceptably for kern's container deployment model (scratch-image, single static binary). Full erasure leaves throughput on the table for the workloads that actually pay people. The hybrid means List<Decimal> in a tax engine doesn't pay a vtable round-trip per add, and a one-off List<User> in a CLI tool doesn't bloat the resulting binary.

This is the ABI-breaking decision that had to land before async. Async codegen heap-allocates an __async_ctx struct that contains the function's locals. If generics changed the struct layout afterwards, every async fn's frame would re-layout — doubling the migration cost. Land generics first; build async on top.

Async — cooperative routines with libuv-backed IO

v0.6.0 had async plumbing — libuv at startup, kern_async_spawn, kern_async_await — but the codegen was incomplete: spawn and await in some branches fell through to synchronous evaluation. v0.8 fixes that across five phases.

concurrent_io.kern
import std.async
import net.dns

# Two DNS lookups concurrently — each parks the routine
# during the resolver call, libuv wakes them up on completion.

async fn resolve_pair():
    a = spawn dns.resolve("codeberg.org")
    b = spawn dns.resolve("qdrant.tech")

    ip_a = await a
    ip_b = await b

    print("codeberg=${ip_a} qdrant=${ip_b}")
    # Wall-clock: ~max(rtt_a, rtt_b), not rtt_a + rtt_b.

What's not yet there: stackless coroutine lowering for the 1M-routines-at-1.5 GB target (today: 16 GB at 1M with the 16 KB stack — needs LLVM coroutine intrinsics or segmented stacks). HTTPS and a few stdlib paths still hold the routine. Channels and select integration. Those are the v0.8.0 finish line, not regressions.

Secure<T, State> — type-state IO governance

PersonalData<T> said "this string is personal data; don't pass it to LLMs without consent." Secure<T, State> says "this value is cryptographic material; don't pass it to a network sink in plaintext." It's the same idea applied to a different concern, and it composes with PersonalData.

secrets.kern
import std.secure as sec
import db.postgres as pg

# Token enters as Plain — fresh from a form post.
token: Secure<str, Plain> = sec.wrap(form.get("api_key"))

# Compile error: Plain must be encrypted before db_insert.
# pg.insert("tokens", { "raw": token })
#   ^ E0162: Secure<str, Plain> cannot flow into db_*

ct: Secure<str, Encrypted> = sec.kms_encrypt(token, key_id: "prod-tokens")?
pg.insert("tokens", { "ct": ct })  # OK

Native OCI container runtime

kern's deployment story has always been "static binary into a scratch image." That still works. What changed is that main can now pull, verify, and run OCI images directly from Kern, without shelling out to docker or podman:

For an EU institution that does not want a US-headquartered container runtime in the production critical path, this is the missing piece. The OCI client speaks the standard registry protocol — pulls from any conformant registry — but the runtime that actually executes the container is in main, with kern's audit and supply-chain story.

kern-pkg — substantially ported to Kern

kern-pkg in v0.6.0 was a Python shim. The asterisk on "self-hosted" bothered everyone. The port on main now covers the full cargo-equivalent surface, in Kern itself:

Ed25519 in-band signatures land first; HSM/PKCS#11 signing is its own subproject and is decoupled — that decoupling is what unblocked the port. Removing the Python shim also removes a CPython dependency from a build that otherwise has zero runtime dependencies on the host. For an EU institution evaluating whether the kern toolchain runs end-to-end on European infrastructure, "the package manager is written in Python" was a fair objection. It is on its way out.

Other things in flight

What this is not

This is not v0.7. It is not v0.8. It is not v1.0. It is what's on main between releases, written down so that institutions evaluating kern can see the trajectory and time their pilots accordingly.

The remaining gates before v0.7.0 and v0.8.0 ship as tagged releases:

Every item above is on the ROADMAP with a checkbox. Pre-1.0 versions are bumped on real progress, not to look further along than we are.

By the numbers

Track the work

v0.6.0 is the release line you can deploy today. main is where the next two milestones are taking shape.

Read the changelog See the roadmap