Back to blog

kern v0.6.0 — Decimal, durable event log, LLM resilience

The hardening release. Six items moved from the v1.0 list to shipped, including a real Decimal type that unblocks finance and government deployment, a per-provider circuit breaker on the LLM proxy, and an event log that survives crashes and concurrent writers.

871
Tests, 100% pass
83
Stdlib modules
23K
Compiler lines (Kern)
27K
C runtime lines

A note on the version

Earlier drafts of this changelog labelled the recent feature wave v1.0.0 and v1.1.0. That was premature. v1.0 is an API stability promise we are not yet ready to honour — generics are still type-erased, async codegen has synchronous fallback paths, the package manager is still Python, and the foundation is in formation rather than incorporated. Those entries have been consolidated into v0.6.0. No code was reverted. Only the version label was corrected.

See the ROADMAP for the explicit v1.0 exit criteria. Pre-1.0 versions are bumped on real progress, not to look further along than we are.

Decimal — exact base-10 arithmetic

IEEE-754 floats are not acceptable for currency, taxation, or regulatory reporting. So std.decimal now exposes a real Decimal struct on top of the runtime's arbitrary-precision integers. Parse from a string, format with a locale, round-trip through JSON, never lose a cent.

invoice.kern
import std.decimal as dec

fn main():
    subtotal = dec.parse("1234.56")?
    vat_rate = dec.parse("0.21")?

    vat   = subtotal.mul(vat_rate).round(2, dec.HALF_EVEN)
    total = subtotal.add(vat)

    # Locale-aware formatting
    print(total.format_currency("€", dec.DE_DE))
    # => "€ 1.493,82"

    # Lossless JSON round-trip via the safe-string form
    j = total.to_json_string()
    back = dec.from_json_string(j)?
    assert(total.cmp(back) == 0)

Six rounding modes — HALF_EVEN (banker's rounding), HALF_UP, HALF_DOWN, CEILING, FLOOR, DOWN, UP — plus locale formatting for en_US, de_DE, nl_NL, fr_FR, and currency formatting for €, $, £. The legacy fixed-point cents helpers are kept for backwards compatibility.

This unblocks finance and government deployment. std.compliance now requires Decimal rather than float for monetary fields — auditors stop asking why your tax calculation rounds differently on different machines.

LLM proxy and Qdrant — circuit breaker, retry, backoff

Calling someone else's network service has three failure modes that a one-shot HTTP call ignores: transient blips you should retry, persistent outages you should not hammer, and malformed responses that should be rejected before parsing. v0.6.0 wraps both kern_llm_api_call and kern_qdrant_request in the same resilience layer:

rag.kern
import std.ai.llm as llm
import std.ai.vector as vec

# Multi-provider — fall back across providers, but each call
# is independently circuit-broken, retried, and validated.

async fn answer(question: str) -> Result<str>:
    store = vec.qdrant("docs")
    hits  = await store.search(question, top_k: 5)?

    context = hits.map(|h| h.text).join("\n\n")

    model = llm.mistral("mistral-large")
    return await model.complete(
        prompt: "Context:\n${context}\n\nQuestion: ${question}"
    )?

Both calls above route through the same retry/backoff/circuit-breaker path. When Qdrant blips, your service waits 250 ms and tries again. When Mistral has a sustained outage, your service stops hammering it and reports the error in 3 seconds, not 30. EU AI Act audit logging is mandatory per call and runs after the resilient call succeeds — every successful inference lands in the audit log.

Event log — flock, fsync, chain re-verification

The BLAKE2b chained event log was the right primitive but the wrong durability story: no flock meant two processes could interleave appends, no fsync meant a crash could lose entries, and the chain was only verified by tooling, not by the writer itself. v0.6.0 closes all three:

audit.kern
import std.audit as audit

fn on_user_export(user_id: int, requester: str):
    log = audit.open("/var/log/kern/audit.jsonl")?
    # open() refuses if the BLAKE2b chain is broken

    log.append({
        "event":     "gdpr_export",
        "user_id":   user_id,
        "requester": requester,
        "article":   "GDPR Art. 15",
    })
    # flock held for the duration of the write,
    # fsync triggered every 16 appends

    log.close()  # always fsyncs

SSE production polish

Server-Sent Events that work on a developer laptop tend to fail on the way through a reverse proxy: idle connections get reaped, reconnecting clients can't tell the server what they last saw. v0.6.0 ships the two missing pieces:

net.tls — passive audit against BSI TR-02102-2

The new net.tls module classifies every TLS connection against the German Federal Office for Information Security's TR-02102-2 cipher-suite recommendations. TLS version validation, cipher suite classification (forward-secrecy, AEAD, BSI-approved), connection audit reports, and a strict policy mode that requires TLS 1.3 + AEAD + FS.

For a Dutch or German public-sector deployment, this is the difference between "we use TLS" and "we use TLS in a configuration the regulator has explicitly endorsed."

std.compliance — explicit article-level mapping

Compliance is not "encryption + audit log + good intentions." It is a specific set of articles in a specific set of regulations. std.compliance now maps every runtime operation — collect, store, process, transfer, delete, access, encrypt — to its specific article in GDPR Art. 5–44, DORA Art. 5/9/10/11, and NIS2 Art. 21/23. With consent validation, data classification (standard / sensitive / special-category), cross-border transfer checks (adequacy / SCCs), retention recommendations, and audit entries that name the article they satisfy.

Auditor-readable, not lawyer-readable.

Other stdlib additions

Compiler hardening

Why this is v0.6.0 and not v1.0.0

v1.0 is a contract: no breaking changes without a v2. That contract requires the known ABI-breaking decisions made before the promise, the flagship safety claims covered by enough tests that a regression is impossible to land silently, and production primitives hardened with retry, backoff, fsync, and reconnect — not one-shot best-effort. We aren't there yet.

The remaining gates:

Every item above is on the ROADMAP with a checkbox. None are aspirational; all are blockers.

By the numbers

Try v0.6.0

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

terminal
curl -fsSL https://kern-lang.eu/install.sh | bash
Get Started View on Codeberg