This roadmap describes where NURL is today and where it is going. It is forward-looking and deliberately concise — the full, reverse-chronological record of what shipped when lives in CHANGELOG.md. Anything marked done here has a regression test in compiler/tests/ and is covered by the bootstrap fixed point.
_Last reviewed: 2026-10-27 · Current release: 0.79.1 · Language: **Grammar v2.11** (spec/grammar.ebnf)._
NURL is a small systems language with a regular prefix-arity grammar, a self-hosted compiler written in NURL, and an LLVM backend. The compiler bootstraps to a byte-identical fixed point on its own source (stage1 ≡ stage2). The only build dependency is clang / LLVM 15+.
The path to 1.0 is hardening, documentation precision, and external validation rather than new language surface — see Toward 1.0 below.
What is solid today:
Sum types (|) and product types (structs), generics over structs and functions (incl. generics over option/result types, closure types and trait objects), pattern matching with match guards, or-patterns, and N-ary payloads, trait bounds on type parameters ([A: Ord]), **compile-time constant folding** (const_eval_int), a full numeric type set (i = i64 and u = byte/u8, plus sized i8/i16/i32, u16/u32/u64, f = f64 and f32), tail-call optimization, and variadic FFI (the printf family callable directly). Since 0.40.0 the language also spells the shapes a fast numeric kernel is written in: v128, a first-class by-value SIMD vector type over ~31 nurl_v128_* primitives (§4.1b) that lowers to SSE2 / NEON, and to wasm simd128 where the module is built with it (a scalarised loop otherwise), with no CPUID probe and no fallback path, and **wide arithmetic** — nurl_umulhi (the high half of a 64×64 multiply) plus nurl_addc / nurl_subb / nurl_mac for carry chains the backend recognises. 0.46.0 adds the two that need a runtime decision: v256 (64-, 32- and 16-bit lanes) and the **simd declaration prefix** (grammar v2.6, §3.3b) — nurlc emits one clone of the function per instruction-set tier and dispatches on CPUID once per process, so a kernel written once runs AVX2 where it exists and baseline where it does not, with no #ifdef and no separate build. 0.49.0 adds the second declaration prefix, inline (grammar v2.7, §3.3c): LLVM's alwaysinline, for the case where the cost model is structurally wrong — a helper whose arguments are constants at every call site, scored on its whole body, called from a caller large enough to push the decision the other way. 0.71.0 adds the third, unsafe (grammar v2.8, §3.3d): the only functions whose body may touch raw memory. 0.73.0 adds raw (grammar v2.9, §3.3e): a function whose soundness rests on its caller, callable only from unsafe code, and a struct whose fields carry such an invariant. The grammar decision for prefix-arity (no grouping delimiter) is formally locked, and since 0.37.0 the n-ary &/| arity trap it makes possible is a **hard error by default** (--no-strict-arity demotes it to a warning) — the shape compiled to working, wrong code before.
Single-owner memory with compiler-inserted auto-drop at scope exit — no tracing GC, no hidden boxing. The **ownership rules are always on** (--strict-borrowck adds audit checks) and reject use-after-move, alias double-free, escaping closure captures, interprocedural/return escape, loop-carried double-frees, a release after storing into an owner, and iterator invalidation as hard errors without changing generated code.
Since 0.71.0 every program accepted without an unsafe function of its own is memory-safe and leak-free (docs/MEMORY.md §6.2; 0.72.0 closed 0.71.0's one hole, a Slice of a Vec — views are values — and the 37 its review found are closed since, each a probe in tools/fuzz/holes/ CI holds); Rc cycles are collected and thread-shared cycles are rejected at compile time.
Since 0.44.0 no rule depends on definition order: every check that consults a per-function summary parks what it cannot answer and resolves it after the module, so where a helper is written can no longer change a verdict.
Since 0.53.0 the coverage is measured from the other direction: an inverse-oracle fuzzer (tools/fuzz/genreject.py) writes one ownership violation into every context the language offers and demands the diagnostic that violation deserves, not merely a failed compile. Its first sweep found the two contexts where a definite double free still compiled clean and segfaulted at run time — a ?? arm and a closure body, the latter because the checker was switched off wholesale inside one — and both are now analysed.
Since 0.55.0 a closure body's two exits agree: one that falls off its end reclaims what it bound, where only one that returned through ^ did, and --no-borrowck is a flag the corpus runs rather than one nothing had ever exercised. 0.57.0 closed the other direction across that same boundary: the checker knew what a closure body frees, but nothing tied a handle the enclosing frame freed to a later call of a closure holding it, so every spelling of that use-after-free compiled clean. **Invoking a closure is now a read of everything it captured.**
A stackful M:N work-stealing async runtime with **no async/await colouring** — ordinary code runs unchanged under the scheduler — plus threads/mutex/cond, typed channels, and Go-style ?? channel select. Since 0.43.0, Send / Sync marker traits (stdlib/core/marker.nu) are derived over a type's whole graph and checked wherever a value crosses a thread boundary — thread_spawn, spawn, chan_send, and an Arc's payload — so an Rc or a Cell reaching a worker is a compile error however it is spelled, and % NotSend / % Send mark the cases a structural derivation cannot see either way. Since 0.71.0 a thread closure moves its captures and only share handles (Channel, Mutex, Arc, Locked) cross threads; since 0.73.0 an Arc's payload is frozen once made and state threads change lives in a Locked [T] whose own lock its operations hold, so data races are inside the guarantee with no lock counted; marker assertions are the trusted part (docs/MEMORY.md §3, §6.5). Since 0.75.0 threads share an Atomic word, an RwLocked [T] whose read guards many threads hold at once and lend frozen (% Frozen) views, and a PFile read and written at offsets; locked_await waits on a Locked value until a condition holds. Since 0.58.0 the Linux I/O path has no reactor thread: an idle worker becomes the poller and blocks in epoll_wait itself, so a ready fiber resumes on that worker with no queue hop, no cross-thread wake and no migration (the Go netpoller / tokio driver shape). Sockets register once per fd lifetime, edge-triggered, and readiness that arrives while the fiber computes is banked so the next wait skips the park entirely. The surplus a saturated poller cannot keep spills into a fair global FIFO taken in batches, which is what keeps the tail flat under load. Elsewhere a reactor thread polls: poll(2) on the other POSIX systems, WSAPoll on Windows (busy workers polling there too is agora #137). The contract — a wait may only follow an EAGAIN — is in docs/ASYNC.md.
A broad pure-NURL stdlib (see the inventory below) spanning collections, hashing, serialization, a full HTTP/1.1+2 + WebSocket stack, database clients, distributed systems (p2p overlay, CRDTs), MCP, and the Anthropic Claude API.
Linux x86_64 (primary, CI-tested), Windows x86_64 (CI-tested: bootstrap fixed point + the Windows golden corpus on every push and PR, plus a Linux-side mingw-w64 cross-link gate, since the Windows runner's two toolchains are both UCRT and the cross one is not), macOS ARM64 (CI-tested on Apple Silicon: bootstrap fixed point + the full corpus against the same goldens as Linux, on every push and PR; needs Homebrew LLVM, no prebuilt toolchain) and macOS x86_64 (cross-compiled Mach-O, no CI), wasm32-wasi, static Linux ARM64 / RISC-V64 (musl), and bootable unikernel images — a NURL program as its own kernel on x86_64, AArch64 and RISC-V64, no host OS and no libc. Tier definitions: docs/PLATFORMS.md.
nurlc (compiler), nurlfmt (canonical formatter), nurl-lsp (language server), nurlpkg (package manager + test/bench runner), nurldoc (API-doc generator), tools/repl, DWARF debug info (--g), a VS Code extension, and nurlapi — a compiler-as-a-service container that powers the public playground and MCP endpoint.
A high-level map of what exists. Dates and per-feature detail are in CHANGELOG.md.
compiler/nurlc.nu) with a deterministic, byte-identical
bootstrap; stage-0 links the committed nurlc_lastgood.ll snapshot (no Python in the toolchain).
spec/). v2.x added:
visibility (pub) enforcement across functions, types, consts, and enum variants; trait bounds; match guards + or-patterns; const folding; channel select; dynamic trait objects (%Trait + ( dyn Trait v ), v2.3); break / continue as reserved identifiers (v2.4); the v128 SIMD lane type (v2.5); the simd CPU-dispatch prefix on @ declarations (v2.6); the inline always-inline prefix (v2.7); the unsafe raw-memory prefix (v2.8); the **raw caller-vouched prefix (v2.9); FFI parameter markers** (in, sync, nocapture) and a foreach over a field (v2.10); unsafe % Send / unsafe % Sync and the v512 lane type of the AVX-512 VAES crypto tier (v2.11); and locked the prefix-arity grouping decision.
conversions. Sized integer/float types with **signedness carried in the type representation itself** (u/u16/u32/u64 distinct from the signed types end to end — no flag side-channels); explicit # casts with correct sext/zext/trunc/fpext/fptrunc. Since 0.58.0 every value boundary — return and every argument path, including generic instantiations, trait-method dispatch, closure/fn-pointer invocation and %Trait dynamic dispatch — ends in a **total-agreement backstop**: after the sanctioned coercions the value's lowered type must equal the declared one, so a pair nobody enumerated is a diagnostic rather than a call clang assembles and the callee reads as garbage (docs/spec.md §4.1).
emission: declarations may follow their impls, associated types and defaults are order-independent, and explicit/default methods share argument-type and inout/sink signature registration.
monomorphs, including behind */? prefixes), generic nesting (Channel[A], Vec[Thread]), generics over ?T / !T E, and closure types and trait objects as type arguments (( Vec ( @ v i ) ), ( HashMap i ( @ i i ) ), ( Vec %Show ), 0.74.0).
String, Vec, owning structs, closure
environments, handle enums (Json, TomlValue) and library handles (HashMap, Set, Deque, BTree, Box, Rc, Arc) are dropped by the compiler through per-binding drop flags, on panic paths too; containers drop their elements; recursive Drop for boxed enum/struct payloads, % Drop user destructors, move/borrow analysis (incl. interprocedural and loop-carried escape detection, consume after store). Since 0.68.0 the whole test corpus runs leak-clean under LeakSanitizer as a CI gate, --lint reports release calls the model made redundant ([redundant-free]), and the panic journal belongs to the fiber, so recover extents of interleaving fibers stay apart. Since 0.69.0 opaque library state is self-releasing handles and a raw pointer or closure outliving the value it borrows is a compile error; since 0.73.0 nothing a program keeps is released by hand — no package, example or bench kernel frees or claims memory itself, and the standard library's remaining such calls sit inside the owners that implement ownership. Since 0.70.0 ownership of a returned value is decided per call (a function that lends on one path and gives on another no longer leaks or double-frees), a borrowed value of a type that cannot be copied is never silently duplicated (a compile error says where to clone or move), and --debug builds at the default -O2. Since 0.71.0 the rules are a guarantee — every program accepted without an unsafe function of its own is memory-safe and leak-free (MEMORY.md §6.2; every hole found, through probe h388, is closed and held by a probe; the rules for single-threaded code are proven sound in Lean 4, formal/ phases A1–A2, 0.74.0) — Rc cycles are collected by a cycle collector, Weak and ArcWeak exist, and a cycle of thread-shared handles is a compile error. Model, guarantee and trusted base: docs/MEMORY.md.
identifiers, call-arity mismatches, unbalanced braces / stray top-level tokens, and visibility violations are hard errors with source locations — nothing malformed reaches the backend silently. Since 0.39.0 that claim is measured corpus-wide: a mutation probe (one realistic mistake injected into each of the ~790 test programs, ~5 400 mutants) produces zero compiler hangs, zero crashes, and zero broken programs reaching the LLVM verifier or linker; every return path is type-checked (implicit fall-off and closure tails included), and errors inside generic/trait re-parses point at the template's real file:line with the instantiation named.
CI**: check_diag_coverage.sh reports which of the compiler's 364 die/warn sites a test has ever made it print (~85% provably, with the 18 never-fired ones baselined so a new silent diagnostic fails the build); check_diag_anchor.sh gates that every baselined diagnostic points at the mistake rather than at the token after it; diag_mutate.py injects one realistic error into a working program and reads the answer. Between them they have found messages that were false, messages that were unreachable, and programs the compiler accepted and miscompiled.
construct reached by a path that skips a check the compiler already performs in another spelling? Round after round it says yes, and the ledger of what was closed and what remains open is docs/dev/V1_HARDENING.md — silent reinterprets, invented error payloads, calls that jump into a constant, IR only llvm-as rejects. Two oracles keep it honest: a token-deletion mutation sweep (tools/fuzz/mutate_delete.py --clang) that asks clang whether a surviving mutant's module is even valid, and tools/tree_sweep.sh, which requires byte-identical diagnostics over every tracked first-party .nu file so a new rejection rejects nothing that was valid.
the program reaches is not compiled at all — about 40% fewer compile instructions across the corpus. Only the functions main can reach the .ll (--no-dce to emit everything). Reachability is computed over the finished IR, so closures, monomorphs, drop glue and dyn vtable thunks need no special casing — worth 30–40% of the clang step on a stdlib-heavy program. What survives is then emitted as several independent modules (--split=N) that the driver lowers concurrently and links with ThinLTO, taking the clang step on the compiler's own 3.2 MB of IR from 11.3 s to 2.0 s — at a measured 3.4% of the built program's own speed (before 0.73.0 started copying small functions into every part that calls them, so they inline across parts), which is why nurl.sh splits your program and build.sh does not split the compiler it installs (NURL_SPLIT=0 opts out). Since 0.40.0 that link is also cached — per-module ThinLTO backend codegen, the pre-link object keyed by content hash of the emitted IR, and the driver's toolchain probes, all under ~/.cache/nurl — so an empty program links in 99 ms instead of 305 and a warm rebuild costs less than the equivalent C compile (NURL_CACHE=0 opts out; NURL_LTO=full links one full-LTO module, which on Linux inlines less than the default: the runtime is ThinLTO bitcode). Both passes: docs/BUILDING.md.
nurlc --g) with ptype/print over structs.
Organised as core/ (language essentials), std/ (general-purpose), and ext/ (external-format / network / service bindings). All pure NURL except a small C runtime (stdlib/runtime.c) for the bootstrap surface and a few platform-specific shims.
string, vec, option, result, errors, char, slice,
pair, box, cell, mem, io (write_bytes, read_all_stdin_bytes), symtab, posix.
hashmap, set, deque, heap,
ordmap, btree, lru, bitset, iter, sort, cmp, bytes, bufio, fmt, int, float, bigint (arbitrary-precision integers), decimal (exact fixed-point).
async, thread (incl. Locked [T],
shared state threads change, and locked_await), atomic (Atomic), rwlock (RwLocked [T], frozen read guards), par (par_map_index), channel, arc, rc, arena, mmap (a read-only file mapping lent as a checked view), signal, panic/recover, process, unixsock (local IPC), log (text + JSON), time (incl. local time via the platform's zone, timezone/DST, HTTP/RFC 2822 dates), args (CLI parser), term (POSIX termios, ANSI), sysinfo (uname fields, host name, processor count).
mlkem (ML-KEM-512/768/1024, FIPS 203),
mldsa (ML-DSA-44/65/87 & HashML-DSA, FIPS 204), slhdsa (SLH-DSA, FIPS 205, SHAKE family), hash_sha3 (SHA-3 & SHAKE128/256, FIPS 202). Classical & symmetric: x25519, ed25519, ecdsa_p256 (P-256 + P-384 constant-time), rsa (PKCS#1 v1.5 + PSS), aes_gcm, chacha20poly1305, hash (SHA-1/224/256/384/512, SHA-512/224, SHA-512/256, MD5, HMAC), hash_blake3, hash_xxh64 (XXH64), hkdf, pbkdf2, scrypt, encode (hex, base64, base32), random (OS CSPRNG rand_bytes), rng (xoshiro256**).
x509 (DER parser + chain/host validation, ML-DSA & SLH-DSA OIDs), x509_gen (self-signed P-256 & ML-DSA certificates, PKCS#8), csr (PKCS#10 RFC 2986 parser, generator, self-signature verifier, PEM round-trip, CA issuance from CSR), pkey (PEM private-key loading: SEC1/PKCS#8 P-256, RSA, and ML-DSA). A complete private CA built on them — classical or post-quantum, with enrollment, CSR signing, revocation and CRLs — is the pki-server registry package.zstd (pure-NURL RFC 8878 Zstandard decompressor & compressor up to level 19 optimal parser, no libzstd), deflate (RFC 1951 + table-driven CRC-32).fs (incl. streaming, stat/lstat/fstat + statvfs,
glob matching, readlink, file_sync, dir_sync, file_truncate, set_permissions, set_times), pfile (positional reads and writes shared by threads), path (typed), net (TCP/TLS), udp, dns, dos.
json, toml, csv, msgpack, cbor, xml, yaml,
protobuf (checked Protocol Buffers wire readers/writers, docs/stdlib/protobuf.md), serde, regex (a Pike VM, with capture groups and back references), unicat (Unicode general categories for tokenizers).
auth, JWT bearer-auth with HS256/EdDSA/ES256, cookies, forms, multipart, router, middleware, access log + Prometheus metrics, DoS caps, graceful shutdown (server_drain: idle keep-alive closed, in-flight finished with Connection: close / HTTP/2+3 GOAWAY, deadline), per-request timeouts, panic recovery), HTTP client (with cookie jar), Post-Quantum TLS 1.3 (client & server: X25519MLKEM768 hybrid & pure ML-KEM key exchange, ML-DSA certificate support, client SNI + ALPN; per-hostname server certificates, mTLS and live cert reload are not yet supported on the pure-NURL server, docs/NETWORKING.md), HTTP/2 (RFC 9113 + HPACK, server and client — served by every HttpServer / HttpApp listener, ALPN h2 over TLS and prior knowledge on cleartext, h2spec-gated in CI), HTTP/3 over a pure-NURL QUIC (RFC 9000/9001/9002 + 9114 + QPACK, served beside every HttpApp TLS listener with Alt-Svc, h3spec-gated in CI), WebSocket (RFC 6455, **server and client, with permessage-deflate** compression — RFC 7692), reverse proxy with binary-safe streaming. Since 0.46.0 the serve path's scaling mode is fiber-per-connection (http_app_async n): TLS record I/O parks on the reactor instead of blocking a worker pthread, and the TLS handshake runs on the connection's own fiber, so handshakes overlap rather than serialise on the accept loop. The stack has had a dedicated security-hardening pass (path-traversal, SSRF, request-smuggling, HTTP/2 CONTINUATION-flood + stream-accounting, and clean cross-thread listener shutdown) with regression tests, and its serve path is peer-benchmarked against Rust hyper and Node (bench/HTTP_RESULTS.md: within 2.5% of hyper's plaintext throughput from c=10 up and well ahead of it at c=1, an HTTP request served in 2 syscalls).
sqlite (production-hardened), mqtt 5.0 client,
smtp (mail submission). Postgres and Redis clients live in the registry packages psql and redis (pure NURL — no libpq, no hiredis), and nurldb is a multi-threaded relational database in safe NURL.
mcp (+ server — the one server framework, on
which every MCP server in the tree is built since 0.60.0, with its wire frozen by compiler/tests/mcp_server_contract.nu; client, http, session, stdio, search, since 0.40.0 tasks — the io.modelcontextprotocol/tasks extension, so a long-running tools/call returns a pollable task handle instead of holding a JSON-RPC response open — and since 0.61.0 auth, the OAuth resource-server half of the MCP Authorization spec: RFC 9728 protected resource metadata and the 401 challenge that points at it) and anthropic (Claude Messages API incl. streaming SSE + tool-use deltas).
semver, manifest, lockfile, resolver,
registry_index, pkg_fetch, pkg_publish (the nurlpkg backend).
compress (pure zlib/zstd/gzip), zip (archives), tar, uuid (v4/v7),
credentials, env.
NAT traversal, DERP relay, SWIM membership, state-based CRDTs (PN-Counter, LWW-Register, OR-Set), gossip replicator, consistent-hash ring, distributed computation (Crown).
ARM64 (CI-tested on Apple Silicon; needs Homebrew LLVM, no prebuilt toolchain), macOS x86_64 (cross-compiled, not CI-tested).
wasm32-wasi (WASI SDK), including the compiler itself running
in the browser playground, and **whole neural networks running client-side in the browser** — the pure-NURL ONNX runtime compiled to wasm, executing on the CPU (precompiled kernels) or on the visitor's GPU via WebGPU (the CUDA-C kernels translated to WGSL compute shaders; the gpu package's third backend). Live YOLOE segmentation and tiny-YOLOv2 detection run in a tab with no server inference (packages/yoloe-demo, the playground objdet demo).
packages/nwasm) that
decodes and executes real wasm32-wasi modules (full int/float instruction set, linear/bulk memory, tables + call_indirect, threads + atomics, sockets, WASI + --dir file ops), with no external runtime — and with two JIT tiers on top over guard-page linear memory: a **register-allocating JIT** (tier 8, the default on x86-64) and the template JIT it falls back to. Over bench/wasmbench.sh's corpus it runs every one of the 60 modules faster than the reference Cranelift JIT's wall clock (nwasm 2.4.0, 2026-10-09: geometric mean 0.58× for the NURL modules) — and the control columns say this is not a NURL-shaped fast path: the same corpus emitted by clang and rustc runs at 0.62× and 0.56×. The compiler self-hosts on wasm: nurlc compiled to wasm32-wasi recompiles nurlc.nu to byte-identical IR, both under the external reference wasmtime and under this pure-NURL runtime — and identically under its interpreter and both JIT tiers.
C906) validated on-device.
libc, no interpreter — on three architectures: x86_64 (QEMU microvm, and the same PVH image boots under Firecracker and cloud-hypervisor), AArch64 and RISC-V64 (QEMU virt; on AArch64 a flat Image wrapper covers Firecracker/cloud-hypervisor). Since 0.40.0 the x86_64 image also carries a Multiboot2 header beside its PVH note and packages into a hybrid BIOS+UEFI disk, so it **boots on real PC hardware off a USB stick** — with screen output (EGA text or a UEFI framebuffer) where a microvm had a serial port, and a PIT-calibrated TSC where no firmware states the frequency. Per-arch QEMU gates run the hosted corpus against the same goldens (19–20 each, including the SIMD corpus), with virtio net/rng/blk drivers, TLS handshakes in the guest, native fiber switches on all three ISAs, and CI booting every architecture on every commit. Since 0.48.0 a guest can also keep state: --disk links in a virtio-blk driver and a read/write FAT12/16/32 filesystem, so the ordinary std/fs.nu calls reach a volume any host can read — checked by fsck.vfat and by an independent reader, across reboots. The same release closed the endpoint milestone (a guest that boots straight into a swarm compute appliance, answering expression and wasm tasks) and ran the compiler itself inside the machine: nurlc.wasm compiles in the guest to output byte-identical to native, and takes the bootstrap fixed point there. The playground builds and boots these images (POST /build_unikernel + target dropdown), and agents do the same over MCP (nurl_build_unikernel).
packages/nurlbox
is a busybox-shaped multi-call executable: 100 of the everyday UNIX utilities, sh among them, in pure NURL over the shipped stdlib with nothing to link beyond libc. It builds for Linux, macOS, Windows and wasm32-wasi, and boots as its own kernel on the unikernel target. 348 differential cases hold it to the original: the same command line through nurlbox and through the system busybox, agreeing on stdout, on the bytes, and on the exit status.
nurlfmt canonical formatter (idempotent, IR-preserving), nurl-lsp
(completion, references, unused-symbol lint), nurlpkg package manager, DWARF debugging, VS Code extension, and the nurlapi compiler-as-a-service container (playground + cross-compile endpoints + public MCP server).
targets in examples/, Milk-V Duo programs in duo/, and a Push-To-Talk distributed voice app (pttvoice/).
nurllama + gguf):
pull a GGUF model from HuggingFace (resumable, content-addressed store), then run, chat, or serve an ollama-compatible API that existing clients speak unchanged. A hostile-input GGUF parser, a tokenizer read from the model's own metadata, and a llama forward pass whose matvec kernels decode quantised blocks inside the matmul — Q4_0…Q8_0 and the K-quants (Q4_K/Q5_K/Q6_K) — so weights never expand to f32 on the device, and the same kernel sources run on the CPU backend byte-identically. Verified against independent references at every layer: token IDs vs a SentencePiece implementation, logits and greedy text vs a numpy forward pass, dequantisation bit-identical to an independent decoder. Architectures span llama / qwen2 / gemma3 / phi3 and, since 0.21.0, diffusion language models: LLaDA2.x, a Mixture-of-Experts llada2 model that converts from its Hugging Face checkpoint (nurllama convert, streaming a model larger than RAM to GGUF at constant memory) and generates by block denoising — parallel commits with token editing, not left-to-right — its logits matched to a numpy forward and its decoded ids to a reference-faithful loop.
whisper + audio + safetensor +
tokenizer): a WAV goes in and text comes out — *word for word what Hugging Face transformers produces from the same model*, on whisper-tiny and on distil-large-v3, on the GPU or the CPU. Every stage is verified against an independent implementation rather than against our own understanding of it: the resampler against scipy, the log-mel against HF's own feature extractor (r = 1.00000000, on both 80 and 128 bands), the 400-point FFT against numpy (Bluestein, because 400 is not a power of two and padding it computes a different transform), the weights bit-exact, the vocabulary against HF's tokenizer, the encoder against HF's encoder, and the transcription against HF's. Whisper is not the llama shape — LayerNorm rather than RMSNorm, error-function GELU rather than tanh, two conv1d layers, cross-attention, and the ecosystem's first non-causal attention.
f5tts + audio + safetensor +
torchpt + gpukit): a reference recording and its transcript go in and the same voice comes out saying something it never said — F5-TTS, the flow-matching model, from the checkpoints Hugging Face ships, with no Python and no ONNX export. A 22-block diffusion transformer with adaLN-zero modulation, conditional flow matching with classifier-free guidance and sway sampling, and the vocos vocoder. Verified against the reference with fixed inputs at every stage: the text front-end id for id, the text encoder at 2.6e-6, the whole forward at 2.3e-6, 32 guided ODE steps at 7.4e-6, the waveform at 2.9e-4. What the model is not is the whole program — the duration is guessed linearly, the reference is loudness-normalised and scaled back, and long text is chunked and cross-faded, none of which is learned and all of which changes what comes out. The service answers wav, mp3 or raw PCM; the MP3 encoder is audio's own (MPEG-1/2/2.5 Layer III, all nine sample rates, no ffmpeg and no codec library), because a program that can only hand back WAV cannot answer a request for an mp3.
reg.nurl-lang.org,
nurlpkg install): GPU compute (gpu — CUDA driver + NVRTC, a CPU fallback backend, a static-kernel backend, and a WebGPU / WGSL backend; gpukit, tensor), pure-NURL vision and ML (image PNG/JPEG codecs, onnx runtime, objdet, yoloe, iforest, anomaly, and mlp — the first trainable package, a deterministic sklearn-faithful MLP regressor), local LLMs (nurllama, gguf, tokenizer, safetensor), speech recognition and synthesis (whisper, f5tts, audio — WAV, resampling, mel spectrograms and MP3 encoding), agent coordination (agora — channels, mail, a task board and notes over one SQLite file, as MCP and REST), distributed compute (swarm, swarm-mcp), web (template HTML templating, http, http-client — one client facade over HTTP/1.1, HTTP/2 and HTTP/3 — gRPC, a native client and server over that HTTP/2, and oauth, an OpenID Connect relying party and resource-server guard in pure NURL), PKI (pki-server — a private CA with a classical or post-quantum signing key), database clients (psql, redis — pure NURL), a relational database (nurldb — SQL, MVCC, a write-ahead log and B+tree indexes, multi-threaded, no unsafe function), and application scaffolding (cli, cas, wasmbuilder, nurl-mcp). Installed tools carry runtime data via the manifest's [install] assets mechanism (staged into <prefix>/share/<name>/).
packages/registry): a
self-hostable server speaking the exact nurlpkg wire protocol — bearer-authenticated publish with server-side checksums, name ownership and version immutability, yank/revoke, search, and a server-rendered catalog that renders each package's README straight from its published tarball. SQLite as the single source of truth; built on the http, template and md2html packages and proven end-to-end against the real client. (The public reg.nurl-lang.org cutover from the Cloudflare Worker is pending deployment.)
published tarball with a project Ed25519 key; nurlpkg pins the public key and verifies the detached minisign signature — using a pure-NURL BLAKE2b + minisign implementation (std/hash_blake2b, std/minisign) — before unpacking, mandatory and fail-closed. Release archives are signed too; the installers always verify the checksum fail-closed and verify the minisign signature against a pinned key when minisign is available. Since 0.41.0 the packer also excludes compiler output by extension, so a given revision packs to the same bytes from any clean checkout — a checksum identifies the source, not the machine that happened to build it.
The remaining work to declare a stable 1.0 is mostly precision and proof, not new language features.
ownership rules reject, with no implied Rust-equivalence (docs/MEMORY.md, docs/LIMITATIONS.md). *(Resolved in 0.71.0: safe programs are guaranteed memory-safe and leak-free, MEMORY.md §6; the one Slice exception was closed in 0.72.0, below.)*
interprocedural escape analysis and *T raw-pointer flows are on the roadmap or out of scope by design. *(Resolved: interprocedural escape and return-escape implemented; in 0.71.0 raw pointers, casts, raw-memory primitives and user FFI are confined to unsafe functions, spec §3.3d, nurlc --unsafe-report.)*
arm-local fall-through bindings, allocations inside a recover scope). (Resolved: leaks fixed.)
Slice hole — a Slice built from a Vec
(slice_from_vec, slice_sub, slice_from_raw, protobuf's ProtoReader) was not tracked as a view of it, the one known exception to the 0.71.0 guarantee (hole probe tools/fuzz/holes/h32). *(Resolved in 0.72.0: views are values in the borrow walk — tracked through structs, Options, containers, closures, globals and function results — with 109 more probes (h33–h141) closed on the way: sealed representations, exclusive calls, closure effects, accessors and field paths, checked allocation sizes, temporaries, ownership summaries, the raw foreign surface, null strings, method calls asked of their impls (static and dyn), strings that own their buffer, and raw strings in values (MEMORY.md §2.13); docs/HARDENING_PLAN.md.)*
compiler accepted and that faulted under the sanitizers: raw strings handed to string_adopt or a sink s, views leaving through a helper's return value or into a container reached indirectly, mem_forget / mem_take / rcbox callable from safe code, destructors, closure calls with a fresh string, and trust by /stdlib/ path. All closed, each at its root, and held by a probe in tools/fuzz/holes/ (h142–h179; h179 a view stored into a field of a struct that outlives it, found on the way). In safe code only a declared sink takes a raw string over and its callee releases it — a caller hands over its own string or a copy — so nothing is released by hand.
review's (0.73.0), h274–h287 (0.74.0): ten found by modelling closures for the proof below — a closure that changes a capture, run where something else holds a view of it — and four by making closure types generic arguments (an option or result of a closure that owned nothing, a closure read out of a field); and h288–h298 (0.75.0), found building a multi-threaded database (nurldb): frozen views, guards in structs and argument temporaries on the panic path, closures replaced through inout; and h299–h315 (after 0.76.0), found probing what the library's doc comments still asked of callers: closures that outlived what they read — a closure a call returned over its argument, the library's iterators, a by-value capture whose binding was given a new value — and lends returned through a join (lru_get), h311–h315; h316–h321 (0.77.0–0.78.0); and h322–h370 (0.79.0): fields that cannot be copied, field borrows, a call's temporaries on the panic path, the MCP task store, and, making threads sound, a trait object's Send, a handle's copies left behind at a crossing (checked at run time), the journal's drop flags, a dyn method's effects and a borrowed inout argument replaced; h371–h377 (0.79.1): an assignment's value dropped by the ? statement it ended, a binding given its own value through a join, and closure envs a panic skipped; and h378–h383: a view of a call's temporary argument kept past its statement (rejected), and a UdpSocket used after udp_close or never closed (a handle with a drop now); and h384–h388: a closure returned over a parameter in a spelling its function's summary missed (bound first, joined, given a new value, over a view). None is open.
formal/) —
phases A1 (the core calculus) and A2 (structs, enums, closures and trait objects, panics, the drop journal, leak freedom) are proven in Lean 4 as of 0.74.0 (A2.memory_safe, A2.no_leak_end). Open: A3 (threads, data-race freedom), B (the standard library's unsafe base), C (nurlc implements the rules, by certificate) and D (code generation).
These convert a hypothesis into measured results.
characters) for NURL vs Python/Rust/JS across 8 matched programs (bench/TOKEN_EFFICIENCY.md). *Result: the raw token-count claim did not survive measurement — on today's tokenisers NURL is ~1.7× Python's tokens (median), losing to out-of-distribution glyph fragmentation. The claim was retired; the defensible arguments are grammar regularity and first-pass compile success.*
correctness, NURL vs Python/Rust, across four models (Sonnet 4.6 / Opus 4.8 / Haiku 4.5 / mercury-2 diffusion), primed with NURL's one-page reference since the training corpus contains zero NURL (bench/genacc/, results in bench/genacc/RESULTS.md). *Result: from a single page a model reaches the Python/Rust ballpark on several tasks but not parity first-try; failures are out-of-distribution habits (imports, then grouping-parens, then mutability) that targeted primer cues fix in turn. The follow-up measured the agentic half of the claim: with ONE round of compiler-diagnostic feedback (bench/genacc/repair.py — the model sees only its program and the compiler's stderr, never the expected output), Sonnet and Opus reach **exact Python/Rust parity — 100% compile, 100% correct** — Haiku reaches 100% compile, and the diffusion model quadruples its score. The diagnostic-first compiler is the load-bearing artifact, and its value is now measured, not asserted.*
project copy — "an agent can drive the toolchain over MCP" is a tooling win, not evidence the language is better for LLMs.
maintainer and publish a short governance note (license stays MIT OR Apache-2.0).
Not blocking 1.0; ordered roughly by likely value.
no_std-styleembedded profile. The RISC-V / ARM64 static cross-compiles already prove the shape; these extend it.
stdlib/runtime.c is
split into stdlib/runtime_core.c (bootstrap core) and stdlib/runtime_ffi.c (stdlib FFI shims), stitched by a thin aggregator so the single runtime.o build is unchanged. The core compiles standalone and defines the symbol set the no_std profile links.
Ideas under consideration, no committed timeline.
suggestions, leaning on the regular grammar and local error semantics.
the simple IR, but are not on the near-term path.
Deliberate exclusions, to set expectations:
model; rc/arc are opt-in library types.
async/await function colouring. Concurrency is via stackful fibers;ordinary code is scheduler-agnostic.
whole point.
(#).
recover — only
explicit panic is recoverable; faults remain process aborts.
compiler/tests/ and survives
the bootstrap fixed point; details in CHANGELOG.md.
docs/spec.md (normative) and
spec/grammar.ebnf (authoritative grammar).