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.
Drop it into your Caddyfile, point it at JetStream, and your frontend gets a durable, replayable event stream.
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:latestWhat's in the box
Real-time SSE streaming
Stream NATS messages to browsers instantly via EventSource
JetStream persistence
Messages stored in NATS JetStream — replay on reconnect
Message replay
Resume from any point with last-id or Last-Event-ID
Multiple topics
Subscribe to many NATS subjects in a single connection
Slow-client safety
Backpressure-aware — slow clients are disconnected cleanly so they can replay
JWT subscriber auth
HMAC-signed JWT with per-topic subscribe claims
Prometheus metrics
Built-in nuts_* counters and gauges for ops visibility
NATS TLS / mTLS
Encrypted and mutually-authenticated NATS connections
Caddy native
First-class Caddy module — configure via Caddyfile or JSON
5-Minute Quick Example
End-to-end with Docker: spin up NATS, create a stream, run NUTS, open a browser 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:latest \ 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:latest \ nats -s nats://nats:4222 pub events.demo '{"hello": "world"}' docker run --rm --network nuts-demo natsio/nats-box:latest \ nats -s nats://nats:4222 pub events.demo '{"order": 42, "status": "shipped"}'
NUTS is inspired by Mercure, built for teams already running NATS.