[0.9.8] — 2026-06-15
Added
-
NAT- and mobile-traversing distributed transport (§7.4). A complete
pubkey-addressed overlay where a peer is a public key, not an address —
it reaches that key over a direct peer-to-peer path when it can and a relay
when it must, and never drops to zero reachability. Built bottom-up:
net/securedgram.nu(WireGuard-style encrypted UDP over Noise + a session
AEAD with a sliding replay window, plus endpoint roaming so a peer survives
a network change),net/stun.nu(RFC 8489 server-reflexive discovery),
net/nat.nu(candidate gathering, NAT-type classification, UDP hole punch),
net/relay.nu(DERP-style opaque forwarding plus group multicast —
broadcast to your own group),net/rendezvous.nu(signaling-only directory),
net/transport.nu(the flat seam:transport_send/broadcast/recv,
direct-when-possible/relay-when-forced with promote/demote), and SWIM
membership (net/membership.nu) hardened by Lifeguard
(std/lifeguard.nu) with a failure-detector control loop
(net/failuredetector.nu). On top sit the sharding + replicated-state
layers: a consistent-hash ring (dist/ring.nu), state-based CRDTs
(dist/crdt.nu— PN-Counter, LWW-Register, OR-Set) and their gossip wiring
(dist/replicator.nu, anti-entropy scoped to a key's replica set). Documented
end to end indocs/DISTRIBUTED.md. -
The Crown — distributed computation (§7.5). Turns the distributed state
above into distributed work.dist/identity.nugives each peer a replica
id;dist/job.nuis the keystone — submit a task keyed byk, the ring owner
executes it via a registered handler, the result is recorded idempotently, and
a key that re-homes mid-flight is forwarded to the new owner so the job
still completes.dist/lease.nuadds fencing tokens (epoch monotonicity +
idempotency keys) so a side-effecting task fires at most once across a
split-ownership window. Liveness under load is handled by SWIM
self-refutation plus a heartbeat on a dedicated OS thread
(dist/heartbeat.nu), so a 100%-CPU node is not falsely evicted. The whole
story is verified by a deterministic chaos-simulation harness
(dist/sim.nu): a virtual clock + in-process message bus with seeded fault
injection (drop, latency/jitter→reorder, partition/heal) drives the real
stdlib logic, with scenarios for converge-under-loss, keystone-across-
partition, at-most-once-side-effect, and CPU-pinned-not-evicted — all
byte-reproducible goldens, ASan-clean. -
Push-To-Talk voice app —
pttvoice/. A distributed PTT voice app on the
overlay: captures the microphone, Opus-encodes it (48 kHz mono, 20 ms
frames via a libopus FFI binding), and pushes a talkspurt either to one peer
(unicast — p2p when punchable, relayed otherwise) or to the whole group
(multicast); a receiver decodes and plays it. ALSA capture/playback
(audio.nu), the codec (opus.nu), the voice wire frame (proto.nu), and
the app (ptt.nu) live in a self-contained folder. Verified live over a
loopback relay (group broadcast and peer unicast).build.shnow detects
libopus and ALSA (droppingstdlib/runtime.{opus,asound}sentinels)
andnurl.shauto-links-lopus/-lasoundwhen those FFI symbols appear. -
Playground “🎙️ PTT Chat” demo with channels. A new
/pptchattab: a
page with a microphone button and an embedded NURL→WebAssembly module
(nurlapi/static/pptchat.nu→pptchat.wasm) that reads the mic through the
audioFFI and paints a live VU meter + frequency spectrum, framing the
distributed voice tech. Channels: no id → the sharedpublicchannel;
+ Create channel mints a random id and navigates to/pptchat/<id>;
opening that URL joins the same channel (the URL is the invite, the future
shared-secret/QR), with a Copy-link button. -
Parallel sanitizer test suite.
compiler/tests/run_san_tests.shnow runs
AddressSanitizer/UBSan checks in parallel (NURL_SAN_JOBS, default = cores),
cutting the sanitized CI leg from minutes to under one — matching the already-
parallel functional runner. -
HTTP client cookie jar —
stdlib/ext/cookies.nu(critic B23). The
server side writesSet-Cookie(ext/http_auth.nu); this is the missing
client half.cookie_jar_setparses oneSet-Cookievalue (Domain,
Path, Expires, Max-Age, Secure — Max-Age wins over Expires, an
already-expired cookie deletes its stored match) defaulting Domain/Path
from the request host/path;cookie_jar_headerreturns theCookie:
value for a request, applying RFC 6265 domain matching (§5.1.3,
host-only vs subdomain), path matching (§5.1.4), Secure gating, and
expiry, longest-path-first. Pure string-in/string-out — decoupled from
the HTTP client types, so it round-trips a session over HTTP/1.1, h2,
or any header source.now(unix seconds) is passed explicitly for
deterministic expiry. Lock:compiler/tests/cookies_basic.nu
(host-only vs Domain, path ordering, Secure, Max-Age/Expires expiry,
replacement, Max-Age=0 deletion, malformed rejection); ASan+UBSan+LSan
clean. -
Benchmark harness —
stdlib/std/bench.nu+nurlpkg bench(critic
C4).std/bench.nutimes a no-arg closure over many iterations and
reports ns/op (via the monotonic clock) and allocations/op.
bench_run name iters bodyruns a short untimed warmup then a timed
loop;bench_auto name bodyauto-scales the iteration count until a
pass clears ~50 ms, for stable numbers on sub-microsecond operations.
bench_reportprints one line;bench_result_*accessors expose the
raw numbers. The allocation metric is backed by a new runtime hook,
nurl_alloc_count(a relaxed-atomic counter on every
nurl_alloc/nurl_zalloc— which is what stdlib vec/string/struct
blocks route through), snapshotted around the timed loop so warmup is
excluded.nurlpkg benchdiscoversbenches/*.nu, compiles each at
-O2, runs it, and streams its report (no goldens — wall time is
machine-dependent; a bench fails only on a compile error or nonzero
exit). Shipsbench/stdlib_hotpath.nu(string build / vec push / sort
micro-benches). Locks:compiler/tests/bench_basic.nu(deterministic
surface — report formatting, the alloc counter on a vec cycle vs a
no-op body, iteration bookkeeping) and
compiler/tests/nurlpkg_bench_smoke.sh(runner discovery / streaming /
summary / exit codes). -
nurlpkg test— user-facing test runner (critic C3). Ships the
compiler suite's per-test pattern as a tool:nurlpkg testdiscovers
tests/*.nu, compiles and runs each, and reportsPASS/FAILwith a
summary (exit 0 iff every test passes). A test passes on exit 0; if a
tests/outputs/<name>.txtgolden exists, the program's stdout must
match it byte-for-byte instead. Tests run in sorted order for
determinism. The build driver is./nurl.shby default, overridable
via$NURL_CC(a command taking<flags> <src> <outbin>) for an
installed toolchain. Smoke-tested by
compiler/tests/nurlpkg_test_smoke.sh(all four verdict paths +
all-pass/any-fail exit codes + the empty-tree message). -
REPL —
tools/repl(nurl repl) (critic C1). An interactive
read-eval-print loop on a process-per-eval model: top-level definitions
(@functions,&FFI,$imports, and:types / enums / globals)
accumulate into a persistent session, while every other line is spliced
into a freshmain, compiled with./nurl.sh -O0, and run — its stdout
is echoed back. A new definition is validated by a fastbuild/nurlc
frontend pass before it joins the session, so a typo never poisons later
evaluations. Line editing + history come fromstd/term.nu(the B10
work); on a non-tty (pipe / script) it falls back to plain buffered
reads. All REPL chrome — prompts, acks, errors,:help— goes to
stderr, so stdout carries only the evaluated program's output. Meta-
commands::help/:h,:quit/:q,:defs,:reset,:save FILE.
Multi-line definitions are read until brackets balance. Build with
./tools/repl/build.sh; smoke-tested bycompiler/tests/repl_smoke.sh
(definitions + globals persist across lines, a bad definition is
isolated, stdout stays clean). Note: process-per-eval re-initialises a
:global on every evaluation — definitions and pure functions persist,
but mutation does not accumulate across lines. -
Bitset —
stdlib/std/bitset.nu(critic B18, collections round-out).
A fixed-size bit array over 64-bit limbs:bitset_set/bitset_clear
/bitset_flip/bitset_test(all bounds-checked, so the unused high
bits of the last limb stay clear),bitset_set_all/bitset_clear_all,
a popcount-backedbitset_count,bitset_any/bitset_all/
bitset_none, the in-place combinersbitset_and_with/bitset_or_with
/bitset_xor_with,bitset_clone, and an ascendingbitset_each_set.
Storage is a flatnurl_zallocword buffer peeked/poked by limb. NURL
has no native XOR or NOT operator, so the module uses the exact,
carry-free identitiesa ^ b = (a|b) - (a&b)and~m = -1 - m. Lock:
compiler/tests/bitset_basic.nu— cross-limb set/clear/flip, out-of-
range no-ops, popcount,all()on a full set, the three combiners with
an XOR-identity bit check, and clone independence; ASan+UBSan+LSan clean. -
LRU cache —
stdlib/std/lru.nu(critic B18, collections round-out).
A fixed-capacityLruCache [V]over string keys, backed by a HashMap
(key → slot) plus an intrusive doubly-linked recency list over
preallocated slot arrays with a free list — solru_get/lru_put/
lru_contains/lru_removeare all O(1) and a cache at capacity does
no further allocation.lru_getmoves the key to MRU;lru_peekdoes
not;lru_putreturns the displaced value (replaced or evicted), owned;
lru_eachwalks MRU→LRU;lru_free_withdrops each value on teardown
(the owned-element discipline the deque/heap work established). The map's
hash/eq closures are non-capturing, so they allocate nothing per call.
Lock:compiler/tests/lru_basic.nu— eviction order, get-as-touch
survival, peek-without-reorder, replace-returns-old, remove, the MRU→LRU
walk, and the owned-Stringfree_withpath; ASan+UBSan+LSan clean. With
the B-tree and bitset, this closes critic B18. -
B-tree ordered map —
stdlib/std/btree.nu(critic B18). A
BTree [K V]with O(log n) insert / lookup / delete, replacing the
array-shift backing for large ordered maps. Classic CLRS proactive
split-on-descent and borrow/merge-on-descent (minimum degree 8, so up
to 15 keys per node) keep the tree balanced; each node also caches its
subtree size, which makesbtree_key_at/btree_val_atorder-
statistic queries (the i-th smallest key) O(log n) too. API:
btree_new/btree_len/btree_is_empty/btree_get/
btree_contains/btree_set/btree_remove/btree_min_key/
btree_max_key/btree_key_at/btree_val_at/btree_each/
btree_free/btree_free_with. Nodes are raw 6-slot blocks with
typed-pointer access (the same generic-container pattern as
std/set.nu). Lock:compiler/tests/btree_basic.nu— a 2000-key
scrambled fill with replace, remove-every-third churn, order-statistic
and ascending-iteration checks, and full drain; ASan+UBSan+LSan clean. -
Terminal control —
stdlib/std/term.nu(critic B10). The
prerequisite for a REPL and TUI examples: POSIX termios raw mode
(term_raw_enable/term_raw_disable, withstruct termiossized
vianurl_native_sizeofso the platform layout never leaks into NURL),
term_is_tty, a full set of byte-exact ANSI builders (ansi_reset/
ansi_sgr/ansi_fg/ansi_bg/ansi_clear/ansi_clear_line/
ansi_cursor_to/ansi_cursor_up/down/right/left), and a
minimal raw-mode line editor (term_read_line) with printable insert,
backspace, ←/→, ↑/↓ history, Ctrl-A/E/K, and a clean not-a-tty None
fallback. AddsTCSANOW/TCSAFLUSHto the runtime's
nurl_native_constanttable (POSIX-only; Win32/WASI return None from
raw mode while the ANSI builders still work — Windows Terminal speaks
VT). Lock:compiler/tests/term_basic.nu— the tty-independent surface
(None on a file fd, byte-exact ANSI hex); ASan+UBSan+LSan clean. -
ZIP archives —
stdlib/ext/zip.nu(critic B15). A reader and
writer for the ZIP format over the existing zlib FFI. Writer:zip_new
/zip_add(raw-deflate, windowBits −15, falling back to store when
deflate would not shrink the entry) /zip_add_stored/zip_finish,
emitting local headers, the central directory, and the EOCD with a
fixed DOS timestamp so archives are byte-deterministic. Reader:
zip_open(backward EOCD scan over the comment window, with zip64
rejected as unsupported) /zip_count/zip_name_at/zip_size_at
/zip_extract/zip_extract_name/zip_close, every extraction
CRC-32-validated. Lock:compiler/tests/zip_basic.nu— build (deflate- store + a compressible 5000-byte entry), re-open, CRC-checked extract
of each entry, by-name extraction, missing-name and junk-archive
rejection; cross-checked against systemunzip -t; ASan+UBSan+LSan
clean.
- store + a compressible 5000-byte entry), re-open, CRC-checked extract
-
SMTP client —
stdlib/ext/smtp.nu(critic B17). A mail-submission
client over the runtime's client-side TCP/TLS connect:smtp_connect/
smtp_connect_tls, EHLO capability discovery (smtp_ehlo/
smtp_has_cap),smtp_starttls(RFC 3207 — upgrades the live
plaintext fd to TLS and re-EHLOs),smtp_auth_plain/smtp_auth_login
(RFC 4954), thesmtp_mail_from/smtp_rcpt_to/smtp_data
envelope (DATA dot-stuffs and terminates per RFC 5321 §4.5.2),
smtp_quit/smtp_close, plus a minimal RFC 5322 MIME builder
(mime_build),smtp_dotstuff, andsmtp_date_now. STARTTLS needs to
upgrade an already-open fd, which the existing connect primitives could
not do, so this addsnurl_tcp_starttlsto the runtime — the
client-handshake half ofnurl_tcp_connect_tlsapplied in place. Lock:
compiler/tests/smtp_basic.nu— the offline surface (multiline reply
scanner/parser, AUTH PLAIN/LOGIN base64 tokens, dot-stuffing, MIME),
ASan+UBSan+LSan clean;examples/smtp_send.nudemonstrates the live
STARTTLS submission flow. -
Unix domain sockets —
stdlib/std/unixsock.nu(critic B9). The
local-IPC sibling ofstd/net.nu's TCP, same API shape but a
filesystem path instead of host:port — for Postgres-over-socket,
systemd-style services, container control planes.unix_listen/
unix_accept/unix_connect/unix_socketpair/unix_read_chunk
/unix_write_all/unix_write_str/unix_close_conn/
unix_close_listener(which unlinks the socket file). Pure libc FFI
(blocking SOCK_STREAM);unix_listenunlinks any stale path before
binding. AddsAF_UNIX/SOCK_STREAM/EADDRINUSEto the runtime's
nurl_native_constanttable (POSIX-only; the module degrades to a
cleanUnixSocketerror on Win32/WASI). Lock:
compiler/tests/unixsock.nu— a deterministicsocketpairround-trip
(both directions + EOF-after-close) plus a thread-driven
listen/accept/connect echo gated onNURL_NET_TESTS; ASan+UBSan+LSan
clean on both paths. -
CBOR (RFC 8949) —
stdlib/ext/cbor.nu(critic B16). The
IETF-standard binary serialization (COSE / WebAuthn / CTAP), sibling of
MessagePack, over the sharedJsonvalue:cbor_encode Json → !( Vec u ) CborErrandcbor_decode ( Vec u ) → !Json CborErr. Encode
is canonical-ish — integers and lengths use the shortest head, floats
are float64 — so equal documents serialize to equal bytes. Decode is
liberal: every definite-length head, signed/unsigned integers at all
widths, and float16 / float32 / float64 (the half-float decoder
handles zero / subnormal / normal / inf / NaN). Byte strings, tags,
indefinite-length items, and exotic simple values are rejected as
CborUnsupported;undefined(0xf7) →JNull. Lock:
compiler/tests/cbor.nu— Json round-trip with canonical-byte check,
the RFC 8949 Appendix A decode vectors (integer boundaries, negatives,
nested array/map, all three float widths), and every documented
rejection; ASan+UBSan+LSan clean (error paths free the partial tree). -
Arbitrary-precision fixed-point decimal —
stdlib/std/decimal.nu
(critic B14, the last ROADMAP numeric gap). ADecimalis aBigInt
coefficient × 10^-scale, so it is exact —0.1 + 0.2is0.3, not
the binary-float0.30000000000000004— and never overflows. Exact
dec_add/dec_sub/dec_mul;dec_div a b scalewith an explicit
result scale and banker's rounding (round half-to-even, the
financial default); scale-agnosticdec_cmp;dec_round/dec_rescale
/dec_normalize;dec_from_string("-12.340",".5","42") and
dec_to_string. Builds on thebigintdiv/rem from PR #100. Lock:
compiler/tests/decimal.nu(exact0.1+0.2==0.3, the full
half-to-even rounding table incl. negatives, division + div-by-zero,
normalize, cross-scale compare; ASan+UBSan+LSan clean — every
intermediateBigIntfreed). -
Playground: rendered stdlib API reference at
/stdlib-docs
(nurlapi). Thenurldoclibrary is now wired into the playground
server:/stdlib-docsis an auto-generated index of every stdlib
module (grouped bycore/std/ext/hal), and/stdlib-docs/<path>
renders one module's signatures + doc comments through
nurldoc_render→ the existingmd_to_html+ dark-theme doc chrome
(the same presentation as the README/spec pages). Append.mdfor the
raw Markdown. Closes the loop the C2 nurldoc PR opened — the "broad
stdlib" is now browsable, not just greppable. Listed in the OpenAPI
spec; live-verified end-to-end (index + module HTML + raw.md,
ASan-clean). -
nurldoc— Markdown API-doc generator (critic C2). The stdlib's
//-header + doc-comment discipline (90+ modules) was unrenderable;
nurldocextracts each module's header block, top-level declaration
signatures (functions trimmed at their{body; types/enums/consts
keep their full definition), and the doc comment above each, into
Markdown. The render logic is an importable library
(stdlib/ext/nurldoc.nu,nurldoc_render content title → String,
brace-depth-aware so:locals inside bodies are never picked up);
tools/nurldoc/main.nuis a thin CLI —nurldoc <file.nu>to stdout,
ornurldoc <src-dir> <out-dir>to walk the tree withfs_globand
write one.mdper module. Lock:compiler/tests/nurldoc.nu. -
HTTP-date / RFC 2822 date parsing —
stdlib/std/time.nu(critic
B13). The server formatted HTTP dates (time_format_http) but could
not parse them;http_date_parsenow accepts all three forms RFC 7231
§7.1.1.1 requires — IMF-fixdate (Sun, 06 Nov 1994 08:49:37 GMT),
obsolete RFC 850 (Sunday, 06-Nov-94 08:49:37 GMT, 2-digit year via
the POSIX <70 pivot), and asctime (Sun Nov 6 08:49:37 1994) — for
If-Modified-Since/If-Unmodified-Since/ cookieExpires.
rfc2822_parsehandles the emailDate:form with numeric±HHMM
zones (Mon, 02 Jan 2006 15:04:05 -0700). Both return UTC seconds in
the!i ParseErrshape (pair withtime_from_unix), matching
time_parse_iso. Lock:compiler/tests/http_date.nu— the three
RFC 7231 spellings agree on the spec's own example (784111777), the
RFC 2822-0700case equals Go's canonical reference instant, plus
round-trip and rejects. -
JWT bearer-auth middleware —
stdlib/ext/http_jwt.nu(B5
follow-through).with_jwt_hs256 secret inner/with_jwt_eddsa pubkey innerwrap a claims-aware handler
(( @ HttpResponse HttpRequest Json )) and return the standard
( @ HttpResponse HttpRequest )middleware shape, so they compose
withwith_access_log/with_cors_default/router_handle. A
request runs the handler only with a validAuthorization: Bearer
token; the verified payload claims are passed straight in (no
re-parse, no header injection / spoofing surface, borrowed + freed by
the middleware). Missing / invalid / expired / not-yet-valid tokens
get a 401 with an RFC 6750WWW-Authenticate: Bearerchallenge whose
error=/error_description=names the failure. Kept in its own
module so the baseext/http_auth.nustays free of the OpenSSL
dependencyext/jwt.nupulls in. Lock:compiler/tests/http_jwt.nu
(valid/expired/tampered/wrong-key/missing × HS256 + EdDSA;
ASan+UBSan+LSan clean). -
Filesystem niceties + glob —
stdlib/std/fs.nu(critic B6 + B7).
fs_rename(libcrename),fs_copy_file(64 KiB-chunk streaming, so
large files copy in bounded memory),fs_tempfile(libcmkstemp→
unique 0600 file, returns the path).fs_globexpands shell patterns
against the tree:*/?/[...](with[a-z]ranges and
[!...]/[^...]negation) within a segment,**as a whole segment
for recursive descent, the leading-dot rule (*never matches a
dotfile; an explicit.does), absolute or relative patterns. Pure
NURL overdir_list. Lock:compiler/tests/fs_glob.nu(every pattern
class + rename/copy/tempfile against a built temp tree). -
One URL parser —
stdlib/std/url.nu(critic B12). RFC 3986
scheme://[userinfo@]host[:port][/path][?query][#fragment]into an
ownedUrl, with bracketed-IPv6 hosts,url_default_port/
url_port_or_default(http/https/ws/wss/ftp/redis/postgres),
url_request_target(path?query for the request line), a percent
codec (url_percent_encode/_decode), andurl_query_decode→
Vec[UrlParam](form-urlencoded+→space,%xx). Lives instd/
with core-only deps soext/layers on it without a cycle. Locked
against RFC 3986 component-split vectors in
compiler/tests/url_parse.nu. -
JSON Web Tokens —
stdlib/ext/jwt.nu(critic B5). HS256 (HMAC-
SHA256) and EdDSA (Ed25519) sign + verify over the existing crypto
block and base64url.jwt_hs256_sign/verify,jwt_eddsa_sign/verify,
a…_verify_atcore taking an explicit epochnow(deterministic;
the wrapper uses the system clock), andjwt_decode_unverified.
Validatesexp/nbftime claims; the HS256 signature comparison is
constant-time (std/subtle.nu). The HS256 path reproduces the
canonical jwt.io reference token exactly and EdDSA is goldened against
the RFC 8032 test key (Ed25519 signatures are deterministic) in
compiler/tests/jwt_basic.nu. Addsb64_url_encode_vec/
b64_url_decode_vectostd/encode.nufor binary, unpadded
base64url (the signature segment).
Changed
ext/websocket.nuandext/http2_client.nudelegate URL parsing to
std/url.nu(critic B12 consolidation). Both hand-rolled
scheme/host/port/path splitting; their__ws_parse_url/
__h2_parse_urlare now thin wrappers overurl_parsethat enforce
the ws/wss and http/https schemes and map to the existingWsUrl/
H2Urltypes — same public API, one parser underneath. The new parser
also correctly stops the authority at?/#(the old scanners folded
a query into the host when no path was present) and keeps the query in
the request target.
Fixed
-
CRDT replica ids must be globally stable (
dist/crdt.nu,
dist/replicator.nu,dist/identity.nu). The chaos-simulation harness
exposed silent state corruption:PNCounterstored increments in a dense
vector merged by position, whileidentity_ofhanded out ids in local
first-seen order, so every node called itself replica 0 — two distinct
replicas collided into one slot and the merge tookmax(1,1)=1instead of
summing to 2, converging to the wrong value with no error. Fixed deeply:
identity_stable_id(pubkey)derives a globally consistent id from the pubkey
(FNV-1a/64, no coordination);PNCounteris now sparse and keyed by
replica id (merge aligns by identity, not position); the wire carries the
id per slot andpncounter_encodeemits slots in canonical ascending-id
order so equal states encode identically (required by the digest anti-entropy). -
Struct-pointer field access mis-resolved when the field name shadows a
variable (compiler/nurlc.nu,gen_field).. p fieldon a struct
pointer resolvedfieldas a same-named local integer and emitted a pointer
array-index instead of a field load — a silent miscompile onlyclangcaught.
A struct field now always wins over a same-named variable on the field-load
path (the field-store= . p name valarray-index form is unchanged and
intentional). Lock:compiler/tests/ptr_field_name_shadow.nu. -
Closure literals rejected parenthesised compound param/return types
(compiler/nurlc.nu,gen_backslash_expr).\ ( Vec u ) p → ( Vec u ) { … }
failed with “undefined identifier 'u'” because\ (was only treated as a
closure when the next token was@; a compound type head fell through to the
try-expression path.\ (is now a closure whenever the next token introduces
a type (incl. the builtinVec). Lock:compiler/tests/closure_compound_param.nu. -
@ ?Enum { T Variant }emitted invalid IR (compiler/nurlc.nu,
gen_agg_lit). ConstructingSome(variant)of an option whose
payload is a no-payload (C-style) enum inserted the variant's bare
i64 tag into the option's%Enumaggregate slot, which clang
rejected (insertvalue operand and field disagree in type). The
Result form! T Ealways worked because its payload slot is i64;
only the option payload field carries the full%Enumtype. The
coercion now wraps the tag withinsertvalue %Enum zeroinitializer, i64 tag, 0. Found while writingext/jwt.nu. Lock:
compiler/tests/option_enum_payload.nu. -
Cryptography block — AEAD, signatures, key exchange, KDFs
(stdlib/ext/crypto.nu, critic B1–B3). Binds libcrypto's EVP layer
through pure-NURL& `openssl` @FFI (no C bridge, same sentinel
pattern as TLS/libpq → "install libssl-dev" at compile time):
AES-256-GCM and ChaCha20-Poly1305 one-shot AEAD (tag appended;
CryptoVerifyon tampered input); Ed25519 keygen/sign/verify and
X25519 keygen/derive via the EVP_PKEY raw-key API; HKDF-SHA256,
PBKDF2-SHA256/512, and scrypt. Every primitive is locked against its
published vector (NIST GCM, RFC 8439, RFC 8032 §7.1, RFC 7748 §6.1,
RFC 5869 A.1, RFC 7914 §12) incompiler/tests/crypto_evp.nu. -
std/subtle.nu— constant-time comparisons (critic B4).
constant_time_eq/_eq_n/_eq_vecfor secret material,
promoted from the private bearer-token loop inext/mcp_registry.nu
(which now calls it). Length-leaking but content-timing-invariant,
matchinghmac.compare_digest/ Gocrypto/subtle.
Fixed
-
ext/crypto.nuHKDF: binary salt was silently zeroed before the
module shipped —nurl_str_getis a NUL-bounded C-string read, so
hex-encoding aVec usalt through a# scast truncated at the
first0x00byte. Switched the binary→hex helper to the*u+. p k
indexed load (the idiomencode.nu's__b64_emitalready uses). The
RFC 5869 test vector caught it; documented as a reuse hazard in
critic.mdB3. -
nurlc --lintdetects unused imports (compiler/nurlc.nu). A
top-file$import none of whose defined symbols (functions, FFI
externs, types, constants — generic templates included) is referenced
by the top file itself now warnsunused import: no symbol from '<path>' is referenced in this file. References count from calls,
identifier reads, and type positions; uses recorded while re-parsing
generic template bodies during the instantiation flush are attributed
to the template's defining file, so stdlib internals never mask a top
file's dead import. Pure aggregator files (only$directives, no
decls of their own — e.g.stdlib/ext/http_full.nu) are exempt both
as importer and as import target: re-exporting is their purpose. The
LSP server (v0.6.0) now surfaces these straight fromnurlc --lint
and drops its former text-heuristic duplicate (~190 LOC removed) —
one source of truth for the diagnostic. -
Unknown callees are compile errors now (
compiler/nurlc.nu). A
call to a name with no registered return type — not an@-fn, FFI
extern, builtin, impl method, or local closure — previously fell
through as an assumed-i64call to an undeclared symbol: invalid IR
that clang rejected far from the source, or worse, code that linked
by accident when the defining file happened to be imported later in
the unit and the return type happened to be i64. Now dies at the
call site:call to unknown function 'X' — … add the missing '$' import … or check the spelling.Same treatment for a generic call
whose template is nowhere in the import closure (was: opaque
expected '->' but found end of inputinside the synthetic
<generic …>source). En route this surfaced — and forced fixing —
nine runtime builtins that were header-declared but missing from the
compiler's symbol table (nurl_peek,nurl_init,nurl_memset,
nurl_vec_drop,nurl_argc,nurl_argv_count,nurl_read_int,
puts,printf), all silently riding the i64 default.
Fixed
- 180 stale
$imports removed tree-wide (88 stdlib, 92
tests/examples/tools). Several masked real latent bugs, now fixed
with explicit imports:stdlib/ext/csv.nuusedopt_unwrap_or
without importingstdlib/core/option.nu(rode onsort.nu's own
stale import),stdlib/ext/http2_hpack.nucalled
h2_default_header_table_sizewithout importing
stdlib/ext/http2_frame.nu, andstdlib/std/bufio.nucalled
nurl_file_open/nurl_file_closewithout importing
stdlib/std/fs.nu— each compiled only when every consumer
happened to import the missing file first.stdlib/std/set.nuno
longer importshashmap.nufor its callers' convenience; import it
alongside (the stockhash_*/eq_*helpers live there).