Real-time NATS
in the browser
A Caddy module that streams NATS JetStream straight to the browser over Server-Sent Events — with replay, multi-topic subscriptions, JWT auth, Prometheus metrics, and slow-client safety baked in.
xcaddy build --with github.com/ideaconnect/nuts
Live demonstration of JSON events streaming from NATS JetStream to the browser over Server-Sent Events.
Producers publish to JetStream · NUTS bridges · the browser subscribes with plain EventSource
Build with xcaddy
xcaddy build --with github.com/ideaconnect/nutsOr run with Docker
docker run -d -p 8080:8080 \
-e NATS_URL=nats://host.docker.internal:4222 \
-v ./Caddyfile:/app/Caddyfile:ro \
idcttech/nuts:latestEverything you need to stream
Real-time SSE streaming
Stream NATS messages to browsers instantly via the standard EventSource API.
JetStream persistence
Messages live in NATS JetStream — retention, storage, and replay stay in NATS.
Message replay
Resume from any point with last-id or the standard Last-Event-ID header.
Multiple topics
Subscribe to many NATS subjects in a single SSE connection via repeated ?topic= params.
Slow-client safety
Backpressure-aware — slow clients are disconnected cleanly so they can reconnect and replay.
JWT subscriber auth
HMAC-signed JWTs with per-topic subscribe claims, from header or cookie.
Prometheus metrics
Built-in nuts_* counters and gauges for full ops visibility.
NATS TLS / mTLS
Encrypted and mutually-authenticated connections between NUTS and your NATS cluster.
Caddy native
A first-class Caddy module — configure via Caddyfile or JSON and inherit Caddy's TLS & routing.
Streaming end-to-end with Docker
Spin up NATS, create a stream, run NUTS, open a page, and watch messages arrive in real time.
-
1
Create a Docker network and launch NATS
A shared network lets the containers talk by name. JetStream is enabled with
-js.docker network create nuts-demo docker run -d --name nats --network nuts-demo \ -p 4222:4222 nats:2.12-alpine -js -
2
Create a JetStream stream
Use
nats-boxas a one-shot CLI on the same network.docker run --rm --network nuts-demo natsio/nats-box:0.19.0 \ nats -s nats://nats:4222 stream add EVENTS \ --subjects "events.>" --storage file --retention limits \ --max-msgs 10000 --max-age 24h --discard old --defaults -
3
Write a Caddyfile and launch NUTS
Save this as
Caddyfilein your working directory::8080 { route /events* { nuts { nats_url nats://nats:4222 stream_name EVENTS topic_prefix events. } } }Then run NUTS, mounting that file:
docker run -d --name nuts --network nuts-demo \ -p 8080:8080 \ -v $(pwd)/Caddyfile:/app/Caddyfile:ro \ idcttech/nuts:latest -
4
Drop in a tiny HTML page
Save as
demo.htmland open it in a browser:<!doctype html> <html> <body> <h1>NUTS demo</h1> <ul id="messages"></ul> <script> const es = new EventSource( 'http://localhost:8080/events?topic=demo' ); es.addEventListener('message', (e) => { const data = JSON.parse(e.data); const li = document.createElement('li'); li.textContent = data.topic + ': ' + JSON.stringify(data.payload); document.getElementById('messages').appendChild(li); }); </script> </body> </html> -
5
Publish a few messages
Each
nats pubshows up live in the browser.docker run --rm --network nuts-demo natsio/nats-box:0.19.0 \ nats -s nats://nats:4222 pub events.demo '{"hello": "world"}' docker run --rm --network nuts-demo natsio/nats-box:0.19.0 \ nats -s nats://nats:4222 pub events.demo '{"order": 42, "status": "shipped"}'
NUTS is inspired by Mercure, built for teams already running NATS.
Ready to get started?
Install in seconds with xcaddy, or pull the Docker image.