All endpoints are versioned under /v1; operational probes live at the root.
Request and response bodies are JSON.
- slave serves the client-facing auth + WebSocket endpoints.
- master serves the backend publish + presence-query endpoints.
| Method | Path | Notes |
|---|---|---|
| GET | /healthz |
liveness — 200 ok while the process serves |
| GET | /readyz |
readiness — 200 ready when NATS is connected, 503 otherwise |
| GET | /version |
build version string |
| GET | /ui |
master only — single-page engineering console (global stats + per-app channels) |
Counts refresh on each presence sync (GUSHER_SCAN_INTERVAL, default 30s).
connections is exact; users is an approximate sum across nodes (a user
connected to multiple nodes is counted per node).
| Method | Path | Response |
|---|---|---|
| GET | /v1/stats |
{"apps":3,"connections":348,"users":312} — totals across all apps |
| GET | /v1/apps |
[{"app":"TEST","connections":120,"users":110}, ...] — per-app, sorted |
Body: {"jwt":"<JWT>"}. The JWT is verified locally with the RSA public key and
returned as the session token (stateless — no store).
{ "token": "<JWT>" }Upgrades to a WebSocket. Subscribe over the socket with
{"event":"gusher.subscribe","data":{"channel":"AA"}}.
| Method | Path | Response |
|---|---|---|
| GET | /v1/apps/{app}/channels |
["channel1","channel2", ...] |
| GET | /v1/apps/{app}/channels/count |
{"count":3} |
| GET | /v1/apps/{app}/channels/{channel}/users |
["user_id", ...] |
| GET | /v1/apps/{app}/channels/{channel}/users/count |
{"count":3} |
| GET | /v1/apps/{app}/users |
["user_id", ...] |
| GET | /v1/apps/{app}/users/count |
{"count":3} |
Body:
{ "event": "notify", "data": { "key": "value" } }data may be any JSON value (object, string, number). Response echoes the
delivered envelope: {"channel","event","data"}.
Body:
{ "channel_pattern": "^App", "event": "notify", "data": { "key": "value" } }channel_pattern is a regular expression matched against the app's live
channels. Response: {"total":1,"pattern":"^App"}.
Body is a JSON array of messages:
[
{ "channel": "public", "event": "notify", "data": "test" },
{ "channel": "public", "event": "notify", "data": { "username": "test" } }
]Response: {"total":2}.
Body: {"data": {"key":"value"}} → {"user_id":"...","data":...}.
Body: {"data": {"key":"value"}} → {"socket_id":"...","data":...}.
Body: {"channel":"aa"} → {"user_id":"...","data":"aa"}.
Body: {"channels":["gg","ff"]} → {"user_id":"...","data":["gg","ff"]}.
Body: {"jwt":"<JWT>"}.
{ "gusher": { "channels": [], "user_id": "", "app_key": "" } }- note1: a
channelsslice containing"*"lets the user subscribe to all channels. - note2:
*glob is supported, e.g.t*stmatchestest,app*matchesapple.