Files
NATS_Expert/CLAUDE.md
T

4.7 KiB

NATS Expert

This project deploys a NATS server behind Coolify, reachable over WebSockets only. NATS' native TCP port (4222) is not exposed to the outside — the WS endpoint is the sole entry point for both applications and the managing agent.

Deployment target

  • Coolify project: Volcanic MFEs (p4fts5a0kv3tahzjtbtfequk)
  • Server: localhost (d0c4owww4kwo0ow08wws4ss8)
  • Build pack: dockercompose
  • Compose file: /docker-compose.yml
  • Public domain: https://nats.tes.gd → routed by Traefik to nats:8080 (WebSocket listener)
  • TLS: terminated at Traefik. NATS itself runs with no_tls: true on the WS listener.

Coolify's non-secret app config (project/server UUIDs, name, domain) lives in deploy.json at the repo root and is gitignored — it is regenerated locally when needed by the deploying-to-coolify-via-api skill flow.

Connection details (for agents managing this NATS)

Both are populated from the two env vars set on the Coolify application:

  • NATS_USER — admin username (default: admin)
  • NATS_PASSWORD — admin password (generated at deploy time, stored in the Coolify app's env vars)

The current live values are printed by:

: "${COOLIFY_URL:?}" "${COOLIFY_KEY:?}"
APP_UUID=$(jq -r '.app.uuid' deploy.json)   # from local deploy.json
curl -sS -H "Authorization: Bearer $COOLIFY_KEY" \
  "$COOLIFY_URL/api/v1/applications/$APP_UUID/envs" \
  | jq -r '.[] | select(.is_preview==false) | "\(.key)=\(.value)"'

If deploy.json is not present, list applications under the Volcanic MFEs project and find the one named nats:

curl -sS -H "Authorization: Bearer $COOLIFY_KEY" \
  "$COOLIFY_URL/api/v1/projects/p4fts5a0kv3tahzjtbtfequk" \
  | jq '.applications[] | {uuid, name}'

WebSocket URL

wss://nats.tes.gd

The NATS server accepts standard NATS-over-WebSocket framing (RFC 6455 with the nats subprotocol). No custom path; connect to the root URL.

With nats CLI

nats \
  --server=wss://nats.tes.gd \
  --user="$NATS_USER" --password="$NATS_PASSWORD" \
  server info

With nats.js / nats.ws (Node/browser)

import { connect } from 'nats.ws'; // browser
// import { connect } from 'nats';  // node with ws

const nc = await connect({
  servers: 'wss://nats.tes.gd',
  user: process.env.NATS_USER,
  pass: process.env.NATS_PASSWORD,
});

With nats-py

import asyncio, os
from nats.aio.client import Client as NATS

async def main():
    nc = NATS()
    await nc.connect(
        servers=['wss://nats.tes.gd'],
        user=os.environ['NATS_USER'],
        password=os.environ['NATS_PASSWORD'],
    )
    await nc.publish('hello', b'world')
    await nc.drain()

asyncio.run(main())

What's inside the server

  • Listeners
    • 4222 — native NATS protocol, exposed only inside the compose network. Not reachable from outside.
    • 8080 — WebSocket listener, no_tls: true. Traefik terminates TLS and forwards ws://nats:8080 from the public wss://nats.tes.gd.
    • 8222 — HTTP monitoring (/healthz, /varz, /jsz), internal only. Used by the compose healthcheck.
  • JetStream: enabled, persisted to the named volume nats-data mounted at /data. Limits: 256MB memory / 4GB file. Bump max_file_store in nats-server.conf if you need more.
  • Auth: a single user, whose name and password come from the NATS_USER / NATS_PASSWORD env vars via NATS' native $VAR substitution in the config. No accounts, no operator/JWT mode.

Managing / redeploying

Config or compose changes → commit and push to main, then trigger a Coolify deploy (fire-and-forget):

: "${COOLIFY_URL:?}" "${COOLIFY_KEY:?}"
APP_UUID=$(jq -r '.app.uuid' deploy.json)   # or look it up as shown above
curl -sS -X POST "$COOLIFY_URL/api/v1/deploy?uuid=$APP_UUID" \
  -H "Authorization: Bearer $COOLIFY_KEY"
# → { "deployments": [ { "deployment_uuid": "…" } ] }   done, don't poll

Rotating the password: PATCH the NATS_PASSWORD env on the Coolify app, then redeploy. Do not edit nats-server.conf — auth is env-driven.

To add a second user (e.g. a scoped app account), edit the authorization.users array in nats-server.conf, wire the new credential env vars into docker-compose.yml, and set them in Coolify.

Files in this repo

File Purpose
docker-compose.yml Single nats service, exposes 4222/8080/8222 internally; only 8080 is routed publicly via SERVICE_FQDN_NATS_8080 + Coolify's docker_compose_domains.
nats-server.conf NATS config: JetStream, WebSocket listener (no_tls), env-driven auth.
.gitignore Keeps deploy.json out of git.
deploy.json (local only) Coolify app config used by the deploying-to-coolify-via-api skill.