Skip to main content

Rate limit

Deny or suspend a connection that sends or receives more messages or bytes than allowed within an interval. Each connection gets its own allowance.

Rate limits do not slow traffic down. NATS request-reply is timing sensitive, and a throttled client fails in confusing ways. Instead, a connection that exhausts its allowance is disconnected or suspended with the rule's message attached, which the application can observe and handle.

Type: message

Variants:

Each variant combines a direction, a unit, and an action:

  • Direction — ingress counts messages the client sends toward the backend (to_backend). egress counts messages the backend delivers to the client (from_backend).
  • Unit — messages counts protocol messages. bytes counts payload bytes.
  • Action — deny closes the connection. suspend keeps it open but stops forwarding traffic.
ActionDirectionUnitID
denyingressmessagescom.synadia.protect.builtins.v1.deny.ratelimit.connection.ingress.messages.message
denyingressbytescom.synadia.protect.builtins.v1.deny.ratelimit.connection.ingress.bytes.message
denyegressmessagescom.synadia.protect.builtins.v1.deny.ratelimit.connection.egress.messages.message
denyegressbytescom.synadia.protect.builtins.v1.deny.ratelimit.connection.egress.bytes.message
suspendingressmessagescom.synadia.protect.builtins.v1.suspend.ratelimit.connection.ingress.messages.message
suspendingressbytescom.synadia.protect.builtins.v1.suspend.ratelimit.connection.ingress.bytes.message
suspendegressmessagescom.synadia.protect.builtins.v1.suspend.ratelimit.connection.egress.messages.message
suspendegressbytescom.synadia.protect.builtins.v1.suspend.ratelimit.connection.egress.bytes.message

Configuration​

An object with an interval and a cap. Message variants take messages, byte variants take bytes:

activations:
com.synadia.protect.builtins.v1.deny.ratelimit.connection.ingress.messages.message: true
com.synadia.protect.builtins.v1.suspend.ratelimit.connection.egress.bytes.message: true

configurations:
com.synadia.protect.builtins.v1.deny.ratelimit.connection.ingress.messages.message:
interval: 1s # disconnect a client that publishes more than 5000 messages per second
messages: 5000
com.synadia.protect.builtins.v1.suspend.ratelimit.connection.egress.bytes.message:
interval: 10s # suspend a client that receives more than 50 MB in 10 seconds
bytes: 50mb
FieldTypeRequiredDescription
intervaldurationyeswindow duration, such as 1s, 500ms, or 5m. A bare number is treated as seconds
messagesintegeryesmaximum messages per interval (message variants only). Must be greater than zero
bytesstringyesmaximum payload bytes per interval (byte variants only), such as 100kb, 1mb, or a plain number of bytes. kb, mb and gb are multiples of 1,000; use kib, mib or gib for multiples of 1,024. Must be greater than zero

Egress variants only run on a port that evaluates backend traffic, so the egress rule above needs that port's default_rule_direction set to both or from_backend in the rules configuration. With the default, to_backend, it never trips.

Configuration is validated when the bundle is installed. An invalid value is rejected before it reaches a gateway.

Behavior​

The cap is a token bucket that refills continuously. A limit of 5000 messages per 1s gives the connection 5000 tokens, refilling at 5000 per second. Each message spends one token; a byte limit spends one token per payload byte. A connection that has been quiet can send its whole allowance at once, then continue at the steady rate. A connection that stays above the steady rate long enough drains the bucket and trips the rule.

  • Limits are per connection, not per account or subject. Ten clients each get their own allowance.
  • Egress limits act on the receiving client. A subscriber on a busy subject can trip an egress limit because of what the backend sends it. Use egress caps to bound what a leaf or client is allowed to consume.
  • Bytes means payload bytes. Subjects, headers, and protocol framing are not counted.
  • A single message larger than the byte cap always trips the rule. Set byte caps above the largest expected message.
  • Existing connections keep their original limits. Changing the configuration and reinstalling the bundle affects new connections only. Reconnect a client to pick up new values.
  • Suspended connections stay parked for the gateway's suspend timeout, five minutes by default. The client still receives PING responses, so it believes it is connected. Requests time out and subscriptions go quiet until the timeout releases it. Once suspended, the rule engine stops evaluating that connection's traffic. The timeout is set with default_suspend_timeout in the rules configuration and applies to every port.
warning

Activate one action per direction and unit. Enabling both the deny and suspend variant for the same direction and unit makes them share one allowance and charge every message twice.

note

Bundle evaluation does not simulate rate limits, so rate limit rules never trip when evaluating a bundle against scenarios. Verify them against a running gateway with a trace or the audit log.

Choosing deny or suspend​

deny is the right default. The client gets a clean disconnect with the rule's message attached, reconnects, and starts with a fresh allowance.

suspend is for cases where a misbehaving client should be held off the backend for a while rather than immediately retrying. From the client's side, a suspended connection looks healthy but silent.

Custom rules​

The built-ins wrap two functions available to any expr message rule. They are not available in Rego, JavaScript, or WASM rules.

FunctionReturnsDescription
connectionMessageRateExceeded(interval, messages)booltrue when the connection has exceeded messages per interval
connectionBytesRateExceeded(interval, bytes)booltrue when the connection has exceeded bytes of payload per interval

Direction comes from the rule's direction condition, so the same function serves ingress and egress rules. Combine it with facts and conditions to scope the limit, for example to leaf connections only, or to messages on a given subject prefix:

name: leaf_ingress_limit
description: Disconnect leafnodes that publish too fast on telemetry subjects.

facts:
- connection_kind: leaf

conditions:
- rule_type: message
- direction: to_backend
- subject_match: 'telemetry.>'

default: allow

rules:
- success: deny
message: ingress message rate exceeded
expression: |
connectionMessageRateExceeded("1s", 1000)

Schema (messages)​

{
"type": "object",
"properties": {
"interval": { "type": "string" },
"messages": { "type": "integer", "minimum": 1 }
},
"required": ["interval", "messages"],
"additionalProperties": false
}

Schema (bytes)​

{
"type": "object",
"properties": {
"interval": { "type": "string" },
"bytes": { "type": "string" }
},
"required": ["interval", "bytes"],
"additionalProperties": false
}