# 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: ```bash : "${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`: ```bash 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 ```bash nats \ --server=wss://nats.tes.gd \ --user="$NATS_USER" --password="$NATS_PASSWORD" \ server info ``` ### With `nats.js` / `nats.ws` (Node/browser) ```js 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` ```python 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, `expose`d 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. Substitution happens at Docker Compose parse time (the config lives inline in `docker-compose.yml` under `configs.nats-conf.content`), so NATS itself sees a static 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): ```bash : "${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. The value is substituted into `configs.nats-conf.content` at compose parse time. To add a second user (e.g. a scoped app account), edit the `authorization.users` array in the `configs.nats-conf.content` block in `docker-compose.yml`, and set any new credential env vars 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`. The NATS config lives inline under `configs.nats-conf.content` (delivered to the container as `/etc/nats/nats-server.conf`) — no separate config file, no bind mount (Coolify's compose executor rewrites relative host paths into a persistent app dir and can't materialise a source file for them). | | `.gitignore` | Keeps `deploy.json` out of git. | | `deploy.json` (local only) | Coolify app config used by the `deploying-to-coolify-via-api` skill. |