NATS Server — API Surface#
API types#
NATS Server exposes functionality through five distinct surfaces:
- NATS Wire Protocol — the primary, native TCP protocol (text-based)
- JetStream Subject API — pub/sub-based control plane over
$JS.API.*subjects - HTTP Monitoring API — read-only observability endpoints on a separate port
- CLI —
nats-serverbinary with stdlibflag-based flags - Embedded Library API — the
serverpackage is publicly exported for in-process embedding
Additionally, two protocol bridges are served on top of the core:
- MQTT 3.1.1 bridge — clients speaking MQTT are mapped to NATS accounts/subjects
- WebSocket bridge — standard NATS protocol tunneled over WebSocket
There is no gRPC API and no REST control plane (monitoring is read-only).
NATS Wire Protocol (primary API)#
- Transport: Raw TCP (or TLS). Optionally WebSocket upgrade.
- Format: Line-oriented text protocol. Each operation is a keyword followed by optional args, then
\r\n, then optional payload. - Connection: Clients connect to port 4222 by default, receive
INFO {...}JSON blob, then sendCONNECT {...}. - Core operations:
| Client → Server | Description |
|---|---|
CONNECT {opts} | Authenticate and negotiate protocol version |
PUB <subject> [reply] <#bytes>\r\n<payload> | Publish a message |
HPUB <subject> [reply] <#hdr bytes> <#total bytes>\r\n<headers>\r\n<payload> | Publish with headers (NATS v2.2+) |
SUB <subject> [queue] <sid> | Subscribe to a subject |
UNSUB <sid> [max_msgs] | Unsubscribe |
PING | Keepalive ping |
PONG | Keepalive pong |
| Server → Client | Description |
|---|---|
INFO {json} | Server info on connect (also sent on topology changes) |
MSG <subject> <sid> [reply] <#bytes>\r\n<payload> | Deliver a message |
HMSG <subject> <sid> [reply] <#hdr bytes> <#total bytes>\r\n<headers>\r\n<payload> | Deliver with headers |
PING | Server-initiated keepalive |
+OK | Acknowledgment (verbose mode only) |
-ERR <msg> | Error response |
- Route protocol (cluster peers): same connection type (
client) butkind=ROUTER. Adds route-specific extensions (RS— route subscribe,RM— route message, etc.). - Gateway protocol (super-cluster):
kind=GATEWAY. UsesCONNECTwith gateway token + account-aware message forwarding. - Leaf node protocol:
kind=LEAF. JWT-based auth, selective subject import/export. - Protocol parsing:
server/parser.go— a hand-written zero-allocation state machine, no external parser library.
JetStream Subject API#
The JetStream control and data plane is exposed entirely through NATS subjects. Clients send request-reply messages to $JS.API.* subjects; the server’s internal subscriptions route these to handler functions in server/jetstream_api.go.
All subjects live under $JS.API.* (or $JS.<domain>.API.* in multi-tenant domain configurations). Subject constants are defined as exported const values in server/jetstream_api.go, making them part of the public API.
Account-level#
| Subject | Description |
|---|---|
$JS.API.INFO | JetStream account info and limits |
$JS.API.ACCOUNT.PURGE.<account> | Purge all JetStream data for an account |
Stream management#
| Subject | Description |
|---|---|
$JS.API.STREAM.CREATE.<name> | Create a stream |
$JS.API.STREAM.UPDATE.<name> | Update a stream’s configuration |
$JS.API.STREAM.INFO.<name> | Get stream info |
$JS.API.STREAM.NAMES | List stream names |
$JS.API.STREAM.LIST | List streams with full detail |
$JS.API.STREAM.DELETE.<name> | Delete a stream |
$JS.API.STREAM.PURGE.<name> | Purge messages from a stream |
$JS.API.STREAM.SNAPSHOT.<name> | Initiate a stream snapshot |
$JS.API.STREAM.RESTORE.<name> | Restore a stream from snapshot |
$JS.API.STREAM.MSG.GET.<name> | Get a message by sequence number |
$JS.API.STREAM.MSG.DELETE.<name> | Delete a specific message by sequence |
$JS.API.DIRECT.GET.<name> | Direct (non-API) fast message get by seq |
$JS.API.DIRECT.GET.<name>.> | Direct fast get by last matching subject |
Consumer management#
| Subject | Description |
|---|---|
$JS.API.CONSUMER.CREATE.<stream> | Create an ephemeral consumer |
$JS.API.CONSUMER.CREATE.<stream>.<consumer>.<filter> | Create consumer with filter |
$JS.API.CONSUMER.DURABLE.CREATE.<stream>.<name> | Create a durable consumer |
$JS.API.CONSUMER.INFO.<stream>.<consumer> | Get consumer info |
$JS.API.CONSUMER.NAMES.<stream> | List consumer names |
$JS.API.CONSUMER.LIST.<stream> | List consumers with full detail |
$JS.API.CONSUMER.DELETE.<stream>.<consumer> | Delete a consumer |
$JS.API.CONSUMER.PAUSE.<stream>.<consumer> | Pause or unpause a consumer |
$JS.API.CONSUMER.UNPIN.<stream>.<consumer> | Unpin pinned subscription |
$JS.API.CONSUMER.RESET.<stream>.<consumer> | Reset consumer to new starting sequence |
$JS.API.CONSUMER.MSG.NEXT.<stream>.<consumer> | Pull next message(s) from consumer |
$JS.API.CONSUMER.LEADER.STEPDOWN.<stream>.<consumer> | Consumer leader stepdown |
Cluster administration (operator-level)#
| Subject | Description |
|---|---|
$JS.API.META.LEADER.STEPDOWN | Step down the JetStream meta-leader |
$JS.API.SERVER.REMOVE | Remove a server from the JetStream meta group |
$JS.API.STREAM.PEER.REMOVE.<stream> | Remove a peer from a replicated stream |
$JS.API.STREAM.LEADER.STEPDOWN.<stream> | Step down a stream’s Raft leader |
Advisory events (server → subscribers)#
NATS publishes advisory events on $JS.EVENT.ADVISORY.* subjects. These are fire-and-forget; subscribers observe cluster state changes:
| Subject prefix | Event |
|---|---|
$JS.EVENT.ADVISORY.STREAM.CREATED | Stream created |
$JS.EVENT.ADVISORY.STREAM.UPDATED | Stream updated |
$JS.EVENT.ADVISORY.STREAM.DELETED | Stream deleted |
$JS.EVENT.ADVISORY.CONSUMER.CREATED | Consumer created |
$JS.EVENT.ADVISORY.CONSUMER.DELETED | Consumer deleted |
$JS.EVENT.ADVISORY.CONSUMER.PAUSE | Consumer paused/unpaused |
$JS.EVENT.ADVISORY.CONSUMER.PINNED / UNPINNED | Consumer pin state |
$JS.EVENT.ADVISORY.STREAM.LEADER_ELECTED | Stream leader election |
$JS.EVENT.ADVISORY.STREAM.QUORUM_LOST | Stream lost quorum |
$JS.EVENT.ADVISORY.DOMAIN.LEADER_ELECTED | Meta-leader election |
$JS.EVENT.ADVISORY.SERVER.OUT_OF_STORAGE | Server storage exhausted |
$JS.EVENT.ADVISORY.SERVER.REMOVED | Server removed from cluster |
$JS.EVENT.ADVISORY.API.LIMIT_REACHED | JetStream API rate limit |
$JS.EVENT.ADVISORY.API | JetStream API audit log (every API call) |
Design note: Exposing the entire JetStream API through NATS subjects rather than a separate REST or gRPC endpoint is an intentional architectural choice. It means JetStream API calls get TLS, account isolation, and authorization from the core NATS protocol automatically — no separate auth layer needed.
HTTP Monitoring API (read-only)#
NATS exposes an HTTP monitoring server on a separate port (default: 8222, configured via -m/-ms flags or http_port/https_port). All endpoints are read-only — there is no HTTP control plane. This is intentional: management is done through the JetStream subject API or CLI signals.
Route registration in server/server.go:3141-3171 using stdlib net/http ServeMux:
| Path | Handler | Description |
|---|---|---|
/ | HandleRoot | Server info summary |
/varz | HandleVarz | Server metrics and configuration |
/connz | HandleConnz | Active connection details (paginated, sortable) |
/routez | HandleRoutez | Cluster route connection details |
/gatewayz | HandleGatewayz | Super-cluster gateway details |
/leafz | HandleLeafz | Leaf node connection details |
/subsz | HandleSubsz | Subscription count and detail |
/stacksz | HandleStacksz | Goroutine stacks (debug) |
/accountz | HandleAccountz | Account info |
/accstatz | HandleAccountStatz | Per-account statistics |
/jsz | HandleJsz | JetStream statistics |
/healthz | HandleHealthz | Health check (for load balancers) |
/ipqueuesz | HandleIPQueuesz | Internal IP queue stats |
/raftz | HandleRaftz | Raft group status |
/debug/vars | expvar.Handler() | Go expvar (standard process metrics) |
- Router: stdlib
http.ServeMux(no third-party router) - Authentication: Optional HTTP basic auth or token auth (configurable in
Options.HTTPBasicAuth,Options.HTTPUsername,Options.HTTPPassword) - Response format: JSON (all
HandleXXXmethods marshal Go structs to JSON) - CORS/TLS: Monitoring port can be TLS-protected (
https_port); no CORS headers - External access:
(s *Server) HTTPHandler() http.Handlerallows embedding the monitoring handler into an external HTTP server
CLI#
- Framework: stdlib
flag.FlagSet(no Cobra, no urfave/cli) - Binary:
nats-server— single binary, no subcommands - Command structure: All configuration is done via flags or a config file; there is no subcommand tree
Key flags#
| Flag | Description |
|---|---|
-a, --addr | Bind address (default 0.0.0.0) |
-p, --port | Client listen port (default 4222) |
-c, --config | Configuration file path |
-t | Test configuration and exit |
-m, --http_port | Monitoring HTTP port |
-ms, --https_port | Monitoring HTTPS port |
-js, --jetstream | Enable JetStream |
-sd, --store_dir | JetStream storage directory |
--routes | Cluster peer URLs to solicit |
--cluster | Cluster listen URL |
--tls, --tlscert, --tlskey, --tlsverify, --tlscacert | TLS options |
--user, --pass, --auth | Simple authentication |
-sl, --signal | Send signal to running server (ldm, stop, quit, term, reopen, reload) |
--profile | pprof HTTP port |
-D, -V, -DV, -VV, -DVV | Debug/trace verbosity levels |
- Signal handling: The
-slflag sends OS signals to a running server.reloadtriggersSIGHUP→server.Reload().ldmtriggers lame-duck mode (graceful drain).stop/quit/termterminate the process. - Config file: Custom
.confformat (HCL-like) parsed byconf/package. Supports a superset of what flags expose: TLS, auth, clustering, JetStream, MQTT, leaf nodes, operator JWT chains, imports/exports, subject mappings, rate limits, etc.
Embedded Library API#
The server package is fully public and intended for in-process embedding (used extensively in the test suite and by projects like nats.go test helpers).
Constructor functions#
// Primary constructors
func NewServer(opts *Options) (*Server, error)
func NewServerFromConfig(opts *Options) (*Server, error) // alias
func New(opts *Options) *Server // panic on error variant
// Options loading
func ConfigureOptions(fs *flag.FlagSet, args []string, ...) (*Options, error)
func ProcessConfigFile(configFile string) (*Options, error)
func MergeOptions(fileOpts, flagOpts *Options) *OptionsLifecycle methods (*Server)#
func (s *Server) Start()
func (s *Server) Shutdown()
func (s *Server) WaitForShutdown()
func (s *Server) Reload() error
func (s *Server) ReloadOptions(newOpts *Options) error
func (s *Server) LameDuckShutdown()
func (s *Server) ReadyForConnections(dur time.Duration) boolInspection methods (*Server)#
func (s *Server) ID() string
func (s *Server) Name() string
func (s *Server) ClusterName() string
func (s *Server) ClientURL() string
func (s *Server) WebsocketURL() string
func (s *Server) Addr() net.Addr
func (s *Server) MonitorAddr() *net.TCPAddr
func (s *Server) ClusterAddr() *net.TCPAddr
func (s *Server) Running() bool
func (s *Server) NumClients() int
func (s *Server) NumRoutes() int
func (s *Server) NumLeafNodes() int
func (s *Server) NumSubscriptions() uint32
func (s *Server) NumActiveAccounts() int32
func (s *Server) ActivePeers() []string
func (s *Server) HTTPHandler() http.HandlerAccount management (*Server)#
func (s *Server) RegisterAccount(name string) (*Account, error)
func (s *Server) LookupOrRegisterAccount(name string) (*Account, bool)
func (s *Server) LookupAccount(name string) (*Account, error)
func (s *Server) SetSystemAccount(accName string) error
func (s *Server) SystemAccount() *Account
func (s *Server) GlobalAccount() *Account
func (s *Server) SetDefaultSystemAccount() errorJetStream (*Server)#
func (s *Server) EnableJetStream(config *JetStreamConfig) errorIn-process connection#
func (s *Server) InProcessConn() (net.Conn, error)Returns a net.Conn that bypasses TCP, allowing in-process clients to connect without network overhead. Used heavily in testing.
API style#
- No functional options for server construction — configuration is through the
Optionsstruct (a flat struct with ~200 fields) - No builder pattern —
Optionsis set up directly by field assignment or loaded from config - Backward compatibility: No explicit versioning strategy visible in the Go API. The module path is
github.com/nats-io/nats-server/v2, establishing a v2 module boundary. Breaking changes are managed through the module major version.
Protocol bridges#
MQTT 3.1.1#
- Listen port: Configurable (
mqtt.portin config, orOptions.MQTT.Port) - Protocol: Full MQTT 3.1.1 client support (
server/mqtt.go, ~6000 lines) - Mapping: MQTT topics → NATS subjects (slash-to-dot conversion). QoS 0 → fire-and-forget publish. QoS 1 → JetStream-backed delivery with ACK.
- Session persistence: MQTT persistent sessions stored in JetStream streams
- Auth: MQTT username/password mapped to NATS auth credentials
WebSocket#
- Listen port: Configurable (
websocket.portorOptions.Websocket.Port) - Protocol: Standard NATS text protocol tunneled over WebSocket (
server/websocket.go) - TLS: Independent TLS config from the main client port
- Compression: Per-message deflate compression support
- Limitation: Full NATS feature parity (headers, JetStream, etc.) — not a subset protocol
Summary of API surface characteristics#
| Dimension | Detail |
|---|---|
| Primary protocol | NATS wire protocol (custom text, TCP) |
| Management API | NATS subjects ($JS.API.*) — no separate REST control plane |
| Observability | HTTP (read-only, stdlib mux) |
| CLI | stdlib flag (no subcommands, signal-based control) |
| Library | server package exported for embedding |
| Bridges | MQTT 3.1.1, WebSocket |
| gRPC | None |
| Plugin system | None — extensions via account imports/exports |
The most architecturally notable aspect of NATS’s API surface is that the management and data planes are unified through the same NATS protocol. There is no secondary HTTP REST API for JetStream control — operators and clients both speak NATS subjects. This means the auth, TLS, and account isolation of the core protocol automatically applies to the management API, eliminating an entire class of API design problems at the cost of requiring a NATS client (rather than curl) to administer JetStream.