Leafnode
Leafnode connections extend NATS clusters by creating lightweight, unidirectional connections from edge servers to a central cluster. The leafnode protocol is optimized for hub-and-spoke topologies, enabling edge deployments while maintaining low overhead. Unlike cluster routes, leafnodes create asymmetric connections where the edge initiates and maintains the connection to the hub.
Overview
The following table lists the leafnode protocol messages. Leafnode protocol operations are case insensitive, thus LS+ foo\r\n and ls+ foo\r\n are equivalent.
| Message | Sent By | Description |
|---|---|---|
INFO | All Servers | Sent after initial connection with leafnode-specific extensions |
CONNECT | Leafnode | Sent to establish and authenticate a leafnode connection |
LS+ | All Servers | Subscribes to a subject, optionally with queue group and weight |
LS- | All Servers | Unsubscribes from a subject, optionally with queue group |
LMSG | All Servers | Delivers a message with subject, optional reply, and queue groups |
LDS | Hub Server | Loop detection subjects to prevent routing loops |
PING | All Servers | PING keep-alive message |
PONG | All Servers | PONG keep-alive response |
-ERR | All Servers | Indicates a protocol error. May cause the leafnode to disconnect. |
INFO
The INFO message for leafnodes includes server capabilities and configuration needed for connection establishment. Both the hub and leafnode exchange INFO messages upon connection.
Syntax
INFO {<key>:<value>,...}
Leafnode-specific fields include:
server_id: The unique identifier of the NATS serverversion: The version of the NATS serverproto: Protocol version (1 for current leafnode protocol)go: The version of golang the NATS server was built withhost: The host specified in the leafnode configurationport: The port number for leafnode connectionsheaders: Whether headers are supported (true/false)jetstream: Whether JetStream is enabledcompression: Compression mode (off,accept,s2_auto,s2_fast,s2_better,s2_uncompressed)cluster: Name of the cluster (if part of a cluster)domain: JetStream domain namexkey: Server's public xkey for encryptionauth_required: If set, authentication is requiredtls_required: If set, TLS is requiredtls_verify: If set, TLS certificate verification is requiredmax_payload: Maximum payload size acceptedclient_id: Client ID for compression negotiationnonce: Nonce for security operationsconnect_urls: Alternative servers to connect to
Example
INFO {"server_id":"NCXU7YJOG5XAU","version":"2.10.0","proto":1,"go":"go1.21","host":"localhost","port":7422,"headers":true,"max_payload":1048576,"jetstream":true,"compression":"s2_auto","cluster":"hub-cluster","domain":"hub","connect_urls":["localhost:7422"]}
CONNECT
The CONNECT message is sent by the leafnode to authenticate and establish the connection parameters. It includes leafnode-specific configuration and capabilities.
Syntax
CONNECT {<key>:<value>,...}
Valid options include:
version: The version of the leafnode serverserver_id: The unique identifier of the leafnodename: Server name of the leafnodedomain: JetStream domain of the leafnodecluster: Cluster name of the leafnode (if clustered)hub: Whether this is a hub connection (true/false)headers: Whether headers are supportedjetstream: Whether JetStream is enabled on the leafnodecompression: Requested compression moderemote_account: Account to bind the leafnode connection toproto: Protocol versionnkey: Public NKey for authenticationjwt: JWT for authenticationsig: Signature for nkey authenticationuser: Username for basic authenticationpass: Password for basic authenticationauth_token: Token for token authenticationtls_required: Whether TLS is required
Example
CONNECT {"version":"2.10.0","server_id":"NATS123","name":"edge-1","cluster":"edge","hub":false,"headers":true,"jetstream":false,"compression":"s2_auto","remote_account":"EDGE_ACCOUNT","proto":1}\r\n
LS+
LS+ registers interest in a subject on the leafnode connection, optionally with a queue group and weight for distributed queues.
Syntax
Simple Subscription
LS+ <subject>\r\n
Queue Subscription
LS+ <subject> <queue_group>\r\n
Weighted Queue Subscription
LS+ <subject> <queue_group> <weight>\r\n
where:
subject: The subject to subscribe to (supports wildcards)queue_group: Optional queue group nameweight: Optional weight for queue distribution (default 1)
Examples
LS+ orders.new\r\n
LS+ orders.> fulfillment\r\n
LS+ orders.> fulfillment 4\r\n
Origin Cluster Support
With LNOCU (Leaf Node Origin Cluster Unsub) support, LS+ can include origin cluster information to prevent loops:
LS+ <subject> <queue_group> <weight> <origin_cluster>\r\n
LS-
LS- removes interest in a subject on the leafnode connection, optionally for a specific queue group.
Syntax
Simple Unsubscription
LS- <subject>\r\n
Queue Unsubscription
LS- <subject> <queue_group>\r\n
where:
subject: The subject to unsubscribe fromqueue_group: Optional queue group name
Examples
LS- orders.old\r\n
LS- orders.cancelled fulfillment\r\n
Origin Cluster Support
With LNOCU support, LS- can include origin cluster information:
LS- <subject> <queue_group> <origin_cluster>\r\n
LMSG
The LMSG protocol message delivers a message through the leafnode connection. It supports reply subjects, queue group targeting, and headers.
Syntax
Simple Message
LMSG <subject> <size>\r\n[payload]\r\n
Message with Reply
LMSG <subject> + <reply> <size>\r\n[payload]\r\n
Message with Queue Groups
LMSG <subject> | <queue_group1> [queue_group2...] <size>\r\n[payload]\r\n
Message with Reply and Queue Groups
LMSG <subject> + <reply> <queue_group1> [queue_group2...] <size>\r\n[payload]\r\n
Message with Headers
LMSG <subject> [+ reply] [| queue_groups...] <header_size> <total_size>\r\n[headers]\r\n[payload]\r\n
where:
subject: The subject to deliver the message to+: Indicator that a reply subject followsreply: Optional reply subject|: Indicator that queue groups followqueue_group: Target queue groups for deliveryheader_size: Size of headers in bytes (for messages with headers)total_size: Total size of headers plus payloadsize: Size of the payload in bytes (for messages without headers)headers: NATS headers in standard formatpayload: The message payload
Examples
LMSG orders.new 11\r\nHello World\r\n
LMSG orders.new + _INBOX.123 11\r\nHello World\r\n
LMSG orders.new | fulfillment 11\r\nHello World\r\n
LMSG orders.new + _INBOX.123 fulfillment shipping 11\r\nHello World\r\n
LMSG orders.new 22 33\r\nNATS/1.0\r\nFoo: Bar\r\n\r\nHello World\r\n
LDS
Loop Detection Subjects (LDS) are special subjects used to detect and prevent routing loops in complex leafnode topologies.
Format
$LDS.<unique_id>
where:
$LDS.: Fixed prefix for loop detection subjectsunique_id: Unique identifier generated per account
Purpose
When a server receives its own LDS subject through a leafnode connection, it detects a routing loop and takes corrective action:
- Logs the loop detection
- Closes the connection
- Implements a 30-second reconnection delay
- Prevents message loops that could cause infinite routing
Example
$LDS.5XZq8bFpWH1234567890abcdef
Compression
Leafnodes support S2 compression to reduce bandwidth usage, especially valuable for WAN connections.
Compression Modes
off: No compressionaccept: Accept compressed connections but don't initiates2_auto: Automatically select compression based on RTTs2_fast: S2 fast compression (lower CPU, less compression)s2_better: S2 better compression (higher CPU, more compression)s2_uncompressed: S2 framing without compression
Negotiation
Compression is negotiated through the INFO protocol exchange:
- Both sides advertise compression capabilities in INFO
- Leafnode proposes compression mode in CONNECT
- Hub accepts or modifies the compression mode
- S2 compression begins after successful negotiation
Auto Mode
In s2_auto mode, compression is enabled based on RTT measurements:
- Enabled if RTT > threshold (typically for WAN connections)
- Disabled for low-latency LAN connections
- Dynamically adjusts based on connection characteristics
WebSocket Support
Leafnodes can connect over WebSocket for firewall traversal and browser compatibility.
WebSocket Path
ws://server:port/leafnode
Features
- Standard leafnode protocol over WebSocket frames
- Optional per-message-deflate compression
- Frame masking (can be disabled for performance)
- Same authentication and protocol messages
Example Connection URL
ws://hub.example.com:443/leafnode
Permission and Security
Account Binding
Leafnodes can be bound to specific accounts using remote_account in CONNECT:
- Messages are scoped to the bound account
- Prevents cross-account message leakage
- Enables multi-tenant edge deployments
Permission Violations
When permissions are violated:
- Connection is closed with
-ERR 'Permissions Violation' - 30-second reconnection delay is enforced
- Violations are logged for security auditing
Authentication Methods
Leafnodes support multiple authentication methods:
- NKey with signature
- JWT with NKey
- Username/Password
- Auth Token
- TLS client certificates
PING/PONG
PING and PONG implement keep-alive between leafnode and hub. Servers send PING messages at configurable intervals. Failure to respond with PONG results in connection termination.
Syntax
PING\r\n
PONG\r\n
-ERR
The -ERR message indicates a protocol, authorization, or runtime error. Most errors result in the leafnode connection being closed.
Syntax
-ERR <error message>
Common Leafnode Errors
-ERR 'Permissions Violation': Subject/publish permission denied-ERR 'Loop Detected': Routing loop detected via LDS-ERR 'Authorization Violation': Authentication/authorization failure-ERR 'Maximum Payload Exceeded': Message exceeds max_payload-ERR 'Invalid Subject': Malformed subject in protocol message-ERR 'Leafnode Not Allowed': Server not configured for leafnodes