Releases: cablehead/http-nu
Release v0.17.2
v0.17.2
Highlights
-
Embedded cross.stream store updated to 0.13.3. A re-registered (hot-replaced) service no longer loses
.append, and a module's exported function can now call the store builtins (.append,.cat,.last, ...), not just plain nushell. See the cross-stream 0.13.3 notes for detail. -
--watchno longer reload-loops on the store directory. The--storedirectory is now excluded from the recursive--watch, so a handler writing frames no longer triggers an endless reload cycle.
Raw commits
- feat: update embedded cross.stream to 0.13.3 (2026-06-09)
- docs: note Homebrew tap-trust before install (2026-06-08)
- fix: exclude --store dir from --watch to prevent reload loops (2026-06-07)
- docs: note --watch is recursive; keep --store out of the watched dir (2026-06-07)
- chore: bump to v0.17.2-dev (2026-06-07)
Release v0.17.1
v0.17.1
Highlights
-
to sseemits acommentfield as an SSE comment line. A record with acommentkey now renders a: textline ahead of anyevent/id/retry/datalines. SSE comments are ignored by the browser but keep idle connections alive through proxies, so a handler can ship{comment: "hb"}as a heartbeat without patching the DOM. Empty (Value::Nothing) comments are skipped, matching how the other fields behave. -
value_to_jsonno longer panics on types without a JSON analogue. Dates, durations, filesizes, binary, closures, and similar values now fall back to their string rendering instead of hitting atodo!(). A record carrying a timestamp or duration field can be returned straight to an HTTP/JSON response without crashing the handler. Thedata-line JSON path (value_to_bytes) inherits the same fallback. -
nu2048 example: the splash page credits Datastar with the animated rocket; changelog link fixed.
Raw commits
- fix: render unsupported value_to_json types as strings instead of panicking (2026-06-07)
- feat: emit an SSE comment line from a record's comment field (2026-06-07)
- feat(2048): splash credits Datastar with the animated rocket (2026-06-07)
- docs: fix nu2048 changelog link (trailing slash) (2026-06-07)
- chore: bump to v0.17.1-dev (2026-06-07)
Release v0.17.0
v0.17.0
Highlights
-
xs store: 0.12 -> 0.13.2. A breaking change, but only to the store's internal bookkeeping. The topics your own handlers append to and read are unaffected.
- xs now keeps its lifecycle records under a reserved
xs.prefix, named consistently across actors, services, and actions asxs.<kind>.<name>.<event>. Previously each kind used its own ad-hoc topic names in the same namespace as your data. - An old store still opens. To bring its
--servicesautomation back up, rewrite those lifecycle topics to the new names with the new binary (.export-> rewrite ->.import); seeexamples/2048/migrate-topics.nu. - Registration topics renamed:
*.register->xs.actor.*.create,*.spawn->xs.service.*.create,*.define->xs.action.*.create,*.nu->xs.module.*. - Exit reasons are now distinct topics (
fin.ok/fin.error/fin.term) rather than a field on the frame. - New handler builtins:
.import(restore frames) and.cas-post(write a CAS blob).
- xs now keeps its lifecycle records under a reserved
-
--servicesforhttp-nu eval. Eval mode can now start the same background services serve mode runs, so scripts can exercise actors, services, and actions without a live server. -
Nushell engine: 0.112.1 -> 0.113.1. Handlers gain
peek(sample a stream without acollect) and theidxfamily (in-memory file index with fuzzy lookup and content search). Minimum Rust is now 1.93.1; CI'ssetup-nutracks the bundled engine. -
Datastar SDK: 1.0.1 -> 1.0.2. The bundle http-nu serves under
--datastaradvances to/datastar@1.0.2.js(sourcemap comment stripped). Upstream changes:datastar-patch-elementsevents accept aviewTransitionSelectordata line naming the element to use for the view transition (defaults todocument);- in-flight requests are cancelled by matching method + URL, regardless of which element initiated them;
- checkboxes and radios bound with
data-bindnow sync on theinputevent for more immediate updates; - faster single-target element patches;
- fixes for
data-bindwith a modifier on an array-valued checkbox signal, and for retrying on 5xx responses.
-
Accurate
startup_ms. The startup timer now starts before handler eval, so the banner andstartedlog event report the real cold start.build.rsalso reruns onCargo.lockchanges to keepNU_VERSION/XS_VERSIONin sync. -
2048 example, substantially rebuilt. nu2048 is now a full event-sourced, multiplayer app and a worked example of the http-nu + xs primitives. Game state is a chain of
game.snapshot.<id>frames in the store, each pointing at its predecessor, so a move appends a snapshot and undo walks back up the chain. Three xs actors write and derive from that stream:- the snapshot actor is the single writer of the chain: per move it reads the head, computes the next state, and appends the new snapshot;
- the leaderboard actor watches every snapshot and maintains a derived
leaderboard.topview, resuming from a stored cursor rather than replaying all history; - the presence actor folds ephemeral per-tab pings into a
_presence.summaryframe, pruning stale tabs on eachxs.pulse. - SSE handlers follow these frames with
.last --follow/.cat --followand patch the browser over Datastar, with cookie sessions, a<game-board>web component, and a/designplayground.
Raw commits
- fix: start the startup timer before handler eval so startup_ms reflects real cold start (2026-06-06)
- build: rerun build.rs on Cargo.lock change so NU_VERSION/XS_VERSION track dep bumps (2026-06-06)
- docs: add how-to for refreshing the embedded datastar SDK (2026-06-06)
- deps: update datastar to v1.0.2, strip embedded sourcemap comment (2026-06-06)
- refactor: register store builtins via cross-stream's add_{core,read,write}_commands (2026-06-06)
- build: bump cross-stream to 0.13.2; strip-store imports via .import in one eval (2026-06-06)
- feat(2048): undo tally left of score (score stays put); undo evicts a game from the leaderboard (2026-06-05)
- feat(2048): live undo tally next to score on /play and /watch (2026-06-05)
- feat(2048): leaderboard ranks clean runs only (undo-assisted scores excluded) (2026-06-05)
- feat(2048): track undos count in snapshot meta; leaderboard reads it O(1) (2026-06-05)
- perf(2048): list-games/list-players/top-players use indexed -T "prefix.*" not full .cat scans (2026-06-05)
- perf(2048): /sse-wc reads head via --last 1 instead of replaying the chain (2026-06-05)
- fix(2048): emit trailing newline from strip-store.nu (2026-06-04)
- refactor(2048): xs 0.13 lifecycle topics, actor-owned snapshot rebuild (2026-06-04)
- build: upgrade to cross-stream 0.13.0 and nushell 0.113.1 (2026-06-04)
- fix(2048): widen /design via body max-width after column moved to body (2026-06-03)
- fix(2048): drop preview frame on the design markdown story (2026-06-03)
- fix(2048): render design markdown preview in so it matches /notes (2026-06-03)
- feat(2048): add 'why is this so addictive' notes page (2026-06-03)
- refactor(2048): render notes in semantic , drop .prose wrapper (2026-06-03)
- docs(adr): remove 0006 (moved to xs/docs/adr/0005-lifecycle-topics.md) (2026-05-30)
- docs(adr): reword 'load-bearing' to 'depend on' (2026-05-30)
- docs(adr): rename parse.error to invalid, strip em-dashes, reframe trade-off as property (2026-05-30)
- docs(adr): plainer wording, drop 'collapses' (2026-05-30)
- docs(adr): enumerate deficiencies, add lifecycle invariants (2026-05-30)
- docs(adr): xs.. namespacing, one-shot migration (2026-05-30)
- docs(adr): xs lifecycle topics + compaction algorithm (2026-05-30)
- examples/2048: add migrate-topics.nu for old->new topic shape (2026-05-28)
- examples/2048: prefix-shape game topics; reroute no-op ack via ephemeral snapshot (2026-05-28)
- examples/2048: add /sse-wc integration test (2026-05-28)
- examples/2048: add snapshot-actor integration test (2026-05-28)
- feat: add --services flag to eval subcommand (2026-05-28)
- examples/2048: plain-language test-sse.nu names, fix leaderboard resume comment (2026-05-28)
- examples/2048: fix leaderboard-actor crash on empty .last, guard the idiom (2026-05-27)
- examples/2048: snapshot-actor resumes from cursor so reloads don't drop moves (2026-05-27)
- examples/2048: dim the board while a move's response is pending (2026-05-27)
- docs: note to keep setup-nu in sync with the bundled nu version (2026-05-26)
- ci: run nu tests on 0.112.1 to match the bundled engine (2026-05-26)
- examples/2048: enlarge splash PLAY NOW button and callout text (2026-05-25)
- examples/2048: use Cirulli-continued palette in the board WC (2026-05-25)
- examples/2048: tile-palette gallery in /design, drop colors experiment (2026-05-25)
- examples/2048: thumb-ergonomic D-pad controls, mobile-friendly chrome (2026-05-24)
- examples/2048: snapshot-actor clears spawned/merged on undo (no re-animation) (2026-05-20)
- examples/2048: strip decorative parens around single-line let values (2026-05-20)
- examples/2048: drop dead try around .last; use get?.field? | default for chained access (2026-05-20)
- examples/2048: /sse/games --from instead of --new (race-free cursor) (2026-05-20)
- examples/2048: /sse/games uses .cat --follow --new instead of
.cat | lasthead probe (2026-05-20) - examples/2048: layout owns $presence seed; drop redundant per-route declarations (2026-05-20)
- examples/2048: hoist /watch signals to so site-header data-text can read $presence (2026-05-20)
- examples/2048: leaderboard-actor resumes from last_processed_id cursor (2026-05-20)
- examples/2048: cap actor register topics with --ttl last:1 (2026-05-20)
- examples/2048: presence-stream filters xs.threshold before .meta access (2026-05-20)
- examples/2048: fix
let board_stream = .cat --followhang in 3 SSE handlers + test-sse.nu regression guard (2026-05-20) - examples/2048: site-wide presence (
X herechip + per-game count, /presence/ping) (2026-05-20) - examples/2048: fold playedMs into board signal, owns overlay (2026-05-20)
- examples/2048: leaderboard link in header; #1 podium row, #2-5 two-up (2026-05-20)
- examples/2048: /leaderboard page reads
leaderboard.top, dimmed-card per row (2026-05-20) - examples/2048: leaderboard-actor maintains
leaderboard.top(last:5) (2026-05-20) - examples/2048: reserve over-slot so 'you win' stays in place (2026-05-19)
- examples/2048: per-tab splash topic, no more shared bus.splash.seek (2026-05-19)
- examples/2...
Release v0.16.0
v0.16.0
Highlights
-
.bus pub/.bus sub: ephemeral local pub/sub. A new in-process bus for UI events that don't belong in the durable event log..bus pub <topic>publishes a value;.bus sub [pattern]yields{topic, value}records and supports glob patterns liketab-abc.*. Always available; no flag required. Slow subscribers are disconnected on overflow so the SSE stream ends and the client reconnects fresh. Sits alongside the cross.stream--storefor a clean ephemeral / persistent split. -
.runfor evaluating user-submitted nushell scripts. Parses, compiles, and evaluates a script string against a clone of the engine state. Parse, compile, and runtime errors surface as distinct types with source-excerpt diagnostics. Each call's defs and lets stay scoped to the call. Useful for in-browser REPLs and similar; expose only on localhost or in trusted environments since the script has full process access. -
--datastarflag forhttp-nu eval. Sets$HTTP_NU.datastarso eval-mode scripts can branch on the same flag the serve mode uses. -
Datastar bumped to v1.0.1 (from RC.8); the bundled JS and CDN reference advance together. The startup banner only reports the datastar version when
--datastaris set. -
SSE cancel surfaces a stream error so clients reliably auto-retry instead of seeing a clean EOF.
-
2048 example demonstrates the
.busround-trip end-to-end with view-transition tile slides, plus atests-browser/infrastructure mirroring stacks.nu for chromium-driven end-to-end tests. -
README: documents the
hrefhelper for mount-aware URL construction, splits the Local Bus and Embedded cross.stream sections so each describes itself before comparing.
Raw commits
- docs: document .run command; drop two filler phrases (2026-05-06)
- feat: 2048 advertises og:image and twitter card; harden browser test (2026-05-06)
- feat: add 2048 example demonstrating .bus pub/sub with view transitions (2026-05-06)
- style: format .nu files with topiary (2026-05-05)
- feat: add .bus pub / .bus sub for ephemeral local pub/sub (2026-05-05)
- feat: render .run parse/compile errors with source excerpts (2026-05-04)
- feat: add .run command for sandboxed nushell pipeline evaluation (2026-05-04)
- feat: add --datastar flag to http-nu eval (2026-04-28)
- deps: update datastar to v1.0.1 (2026-04-27)
- fix: SSE cancel surfaces stream error so clients auto-retry (2026-04-27)
- fix: only show datastar version in startup banner when --datastar is set (2026-04-27)
- chore: bump to v0.15.1-dev (2026-04-13)
Release v0.15.0
v0.15.0
Highlights
-
Updated to Nushell 0.112.1 and cross.stream 0.12.0. Nushell removed the
--mergeflag frommetadata set. If you were usingmetadata set --merge {...}, switch to the closure form:metadata set { merge {'http.response': {status: 404}} }. The{ merge {...} }form that was already used in examples and docs continues to work unchanged. -
Security hardening for HTML output. Minijinja templates now autoescape by default. The
.mdcommand escapes code fence language attributes. HTML DSL attribute values are properly escaped. Theescape-htmlutility consistently escapes all five HTML-significant characters (& < > " ') everywhere. -
SSRF fix in reverse proxy. The
strip_prefixlogic now rejects URLs with authority components (e.g.//evil.com) that could confuse downstream routing.
Raw commits
- deps: update nushell to 0.112.1 and cross-stream to 0.12.0 (2026-04-13)
- refactor: unify escape-html to escape all 5 chars everywhere (2026-04-13)
- fix: consolidate HTML escaping, add attribute escaping to HTML DSL (2026-04-13)
- fix: escape code fence language in .md to prevent XSS (2026-04-08)
- fix: enable HTML autoescape in minijinja templates to prevent XSS (2026-04-08)
- fix: prevent SSRF via URL authority confusion in reverse proxy strip_prefix (2026-04-08)
- feat: add cargo-docs example (2026-04-07)
- docs: update v0.14.0 release notes (2026-04-04)
- fix: rename const to avoid collision when sourced from www/serve.nu (2026-04-04)
- fix: use path self for cwd-independent path resolution in templates example (2026-04-03)
- docs: rewrite v0.14.0 release notes (2026-04-04)
- chore: bump to v0.14.1-dev (2026-04-03)
Release v0.14.0
New release, http-nu :: v0.14.0
https://github.com/cablehead/http-nu/releases/tag/v0.14.0
Highlights
.mdsupports GFM: Tables,strikethrough,- [x]checklists, footnotes, and definition lists now render correctly.eval --store <path>:evalnow accepts a--storeflag to use.cat,.append,.casagainst a store directory directly.ICONIFY: Use any of 200k+ icons from Iconify:ICONIFY "lucide:copy",ICONIFY "mdi:home".- Clean SSE shutdown: SSE connections now close immediately on Ctrl+C instead of hanging for 10 seconds.
- Datastar SDK update:
from datastar-signalsnow handles DELETE like GET — signals come from query params, matching the updated ADR. Also addsSCRIPT-DATASTARto emit the client script tag (served from the binary, no CDN needed). - New examples: A blog example showing routing, layouts, and HTML composition, plus a reworked stor example that demonstrates in-memory SQLite by logging its own page views.
href: Mount-aware link helper.$req | href "/about"returns/blog/aboutwhen mounted at/blog.
Release v0.13.0
v0.13.0
Highlights
Updated to Nushell 0.111, cross.stream 0.11, and Datastar v1.0.0-RC.8.
The $HTTP_NU const is now available in handler scripts, providing access to runtime context (version, dev mode, store path, etc.) without environment variables. An examples hub at examples/ showcases available demos with live links.
SSE streams with brotli compression now correctly abort on hot reload, fixing a race where compressed SSE connections could hang during --watch reloads.
The curl hint URL now respects X-Forwarded-Proto when running behind a reverse proxy.
Changelog
- chore: bump Datastar to v1.0.0-RC.8 (2026-03-02)
- feat: add getting started tutorial and unified copy buttons for www site (2026-03-02)
- fix: compose SSE reload-abort with brotli compression (2026-02-26)
- fix: use X-Forwarded-Proto for curl hint URL behind reverse proxy (2026-02-26)
- feat: add curl hint for streaming /time endpoint in basic example (2026-02-26)
- docs: link to live examples from README and examples/README (2026-02-26)
- feat: $HTTP_NU const + examples hub (#42) (2026-02-26)
- examples: add live mermaid editor with Datastar + web component (2026-02-23)
Release v0.12.0
v0.12.0
Highlights
Templates can now load from the store. .mj --topic "page.html" renders a template stored in an xs topic, and {% extends %} / {% include %} references resolve as topic names from the same store. Previously you could load template content via --inline, but if that template referenced other templates they'd resolve from the filesystem -- there was no way for stream-sourced templates to reference each other.
.mj and .mj compile now take exactly one of three mutually exclusive modes:
- File --
{% extends %}/{% include %}resolve from the template's directory on disk - Inline (
--inline) -- self-contained, no resolution - Topic (
--topic, requires--store) -- resolves template names as store topics
examples/templates/ demonstrates all three modes with visually distinct template variants so you can tell which source served the page.
Scripts can use path self for relative paths. Handler scripts no longer need to hardcode paths relative to the working directory:
const script_dir = path self | path dirname
let slides = open ($script_dir | path join data.json)
let page = .mj compile ($script_dir | path join page.html)http-nu eval works the same way, so you can unit test handler endpoints from anywhere:
# test.nu
use std/assert
const script_dir = path self | path dirname
let handler = source ($script_dir | path join serve.nu)
let response = do $handler {method: GET, path: "/", headers: {}}
assert ($response | str contains "<h1>State in the Right Place</h1>")http-nu eval test.nuThe Nushell standard library (std/assert, std/log, etc.) is now available in scripts and eval.
Breaking changes
- feat!: inline mode no longer attaches a filesystem loader --
{% include "local.html" %}in an--inlinetemplate previously resolved from the working directory; use file mode instead
Changelog
- feat!: .mj --topic for store-backed templates (#40) (2026-02-18)
- feat: support path self in file-based scripts (2026-02-18)
- feat: load nushell stdlib into engine (2026-02-19)
- examples: add unit test for tao handler (2026-02-19)
- examples: use path self for relative paths in tao (2026-02-18)
- examples: The Tao of Datastar (#39) (2026-02-18)
- docs: document unit testing endpoints with eval (2026-02-19)
Release v0.11.0
v0.11.0
Highlights
The server can now run entirely from the stream. --topic loads the handler closure from an xs topic instead of a file, and with -w it hot-reloads whenever the topic is updated:
http-nu :3001 --store ./store --topic serve -wIf the topic doesn't exist yet, a placeholder page serves a 503 with instructions on how to get started. Append a closure and the server picks it up:
'{|req| "hello, world"}' | .append serveTopic-sourced handlers get access to VFS modules stored on the stream -- any *.nu topic appended to the store is available via standard use imports, same as with actors and services in xs.
Cookies have first-class support. use http-nu/http gives you cookie parse, cookie set, and cookie delete with secure defaults (HttpOnly, SameSite=Lax, Secure). Multiple cookies accumulate naturally through the pipeline:
use http-nu/http *
{|req|
let cookies = $req | cookie parse
"logged in"
| cookie set "session" "abc123" --max-age 86400
| cookie set "theme" "dark"
}The --dev flag tells the cookie module to omit the Secure attribute, so cookies work over plain HTTP during local development.
Datastar's JS bundle is now embedded in the binary. --datastar serves it at a versioned path with immutable cache headers -- no CDN dependency, zero runtime compression (brotli is pre-built at compile time):
use http-nu/datastar *
SCRIPT {type: "module" src: $DATASTAR_JS_PATH}--expose opens the xs API on an additional address, useful for separating the public-facing HTTP server from the stream management interface. Accepts TCP addresses or iroh:// for peer-to-peer QUIC.
Breaking changes
- feat!: update cross-stream to 0.10.0 -- aligns with the xs processor rename (handlers/generators/commands are now actors/services/actions) and the 2-parameter actor closure shape
Changelog
- feat: add
--topicflag to load handler from xs store - feat: add cookie module (
cookie parse,cookie set,cookie delete) with secure defaults - feat: add
--devflag for development mode - feat: embed Datastar JS bundle, serve via
--datastar - feat: add
--exposeflag to expose xs API on additional address - feat: display startup options and dependency versions in preamble
- feat: VFS modules from the stream available to topic-sourced handlers
- feat: add cross.stream-powered branding
- refactor: bundle handler params into AppConfig struct
- refactor: align with xs processor rename (actor/service/action)
- fix: update actor closure signature to match xs 2-param requirement
- fix: add duration literal support to nushell syntax grammar
Release v0.10.2
v0.10.2
Highlights
- Empty response bodies now default to 204 No Content
- Datastar SDK naming aligned with spec (breaking for datastar users)
- Shutdown message now gets a timestamp like other log lines
- File watcher uses trailing-edge debounce for more reliable reloads
- Bumped cross-stream to 0.9.3
Raw commits
- chore: bump cross-stream to 0.9.3 (2026-01-29)
- feat: timestamp the shutdown message separately from the closing tag (2026-01-29)
- feat: default to 204 No Content for empty response body (2026-01-27)
- refactor(datastar)!: align SDK naming with spec (2026-01-26)
- fix: use trailing-edge debounce for file watcher (2026-01-23)
- docs: update release.md for versioned artifact naming (2026-01-22)