NUTS – NATS to SSE for Caddy

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/nuts

Or run with Docker

docker run -d -p 8080:8080 \
  -e NATS_URL=nats://host.docker.internal:4222 \
  -v ./Caddyfile:/app/Caddyfile:ro \
  idcttech/nuts:latest

What'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. 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. 2

    Create a JetStream stream

    Use nats-box as 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. 3

    Write a Caddyfile and launch NUTS

    Save this as Caddyfile in 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. 4

    Drop in a tiny HTML page

    Save as demo.html and 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. 5

    Publish a few messages

    Each nats pub shows 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.

Ready to get started?

Install in seconds with xcaddy.

Read the Docs