KubeMQ
ConnectorsSTOMPReference

Capabilities

What the KubeMQ STOMP connector supports and rejects — protocol versions, client commands, ack modes, hard-rejected features, and reliability guarantees.

This reference defines exactly what the embedded KubeMQ STOMP connector supports, what it hard-rejects, and the hard limits it enforces. The connector is an embedded STOMP 1.0 / 1.1 / 1.2 server inside kubemq-server with its own raw-TCP / TLS listeners and a hand-rolled frame codec. It bridges STOMP onto KubeMQ's five native patterns by destination prefix — see Destination Grammar.

Protocol versions

VersionSupportedNotes
1.2Yes (recommended)full escaping including CR; id-based ACK token; id required on SUBSCRIBE
1.1Yesescapes : / LF / \ but not CR; message-id + subscription ACK; id required on SUBSCRIBE
1.0Yesno header escaping; auto sub-id; ACK by message-id; ActiveMQ-style leniency

accept-version picks the highest common version. The list is comma-separated, order-independent, and whitespace-tolerant; unknown tokens are ignored. An absent or empty accept-version negotiates 1.0. When there is no common version the connector emits an ERROR carrying version:1.0,1.1,1.2 and closes the connection.

Recommended default: accept-version:1.2. It is the only version that can represent CR in a header value — and the only one that never silently drops a CR/LF-bearing header to the subscriber. See Protocol Versions for the full per-version feature matrix.

Supported client commands

All 11 client → server STOMP commands are recognized:

CommandSupportedBehavior
CONNECT / STOMPYeshandshake; must be the first frame within 30 s
SENDYesroutes by destination prefix to the matching pattern
SUBSCRIBEYesevents / store / queues / /reply/; not /command or /query
UNSUBSCRIBEYesby id (1.1 / 1.2); by destination on 1.0
ACKYesqueue acknowledgement; no-op on events subscriptions
NACKYesqueue negative-ack / requeue; honored on 1.0 too (lenient)
DISCONNECTYesgraceful close; RECEIPT flushed before socket close
BEGIN / COMMIT / ABORTHard-rejectedERROR "transactions not supported" + close

Server → client frames are CONNECTED, MESSAGE, RECEIPT, and ERROR. A body is allowed only on SEND (client) and on MESSAGE / ERROR (server) — a body on any other command is a malformed frame.

Acknowledgement modes (queues)

Three ack modes apply to a SUBSCRIBE on a /queue/ destination:

ack modeClient actionSemantics
auto (default)nonefire-and-forget; reserve → enqueue → immediate ack
client-individualACK / NACK one messageresolves exactly that one delivery — the recommended reliable default
client (cumulative)ACK a message + all earlier on the subscriptiongrouped per downstream transaction
  • Ack timeout = 30 s. A 1 s sweeper NAcks / requeues the delivery; the client is not disconnected, and a late ACK after expiry is silently ignored.
  • No client-side DLQ. Redelivery surfaces only via the redelivered:true MESSAGE header; maxReceiveCount / DLQ is queue-channel config on the broker side, not a STOMP feature.

See ACK Modes & Receipts for per-version ACK / NACK token correlation and receipt rules.

Events-Store replay (/topic-store/ only)

A SUBSCRIBE to a /topic-store/ destination accepts start-from + start-value headers. Replay headers are ignored for plain /topic/ Events.

start-fromstart-valueReplays from
absent / newmust NOT be presentnew messages only (default)
firstmust NOT be presentthe earliest stored message
lastmust NOT be presentthe most recent stored message
sequencerequired, numeric ≥ 0the given sequence number
timerequired, RFC3339 or unix-seconds ≥ 0the given timestamp
time-deltarequired, numeric > 0 (seconds)now minus N seconds

Bad combinations — a start-value present for new / first / last, or a missing / invalid value for sequence / time / time-delta — produce ERROR "invalid subscription" + close. See Events-Store.

Hard-rejected features

These are documented exclusions. Each is refused deterministically with an ERROR frame followed by an immediate socket close — no example ever uses them.

FeatureBehavior
STOMP transactionsBEGIN / COMMIT / ABORT, or any frame carrying a transaction header, → ERROR "transactions not supported" + close
Selectorsa selector header on SUBSCRIBE → ERROR "selectors not supported" + close (even without a transaction)
SUBSCRIBE to /command / /queryERROR "cannot subscribe to RPC destinations" + close — STOMP is RPC-requester-only
SEND to /reply/ERROR "invalid destination" + close — /reply/ is a connection-local sink

Transactions and selectors are documented exclusions, never examples. STOMP is the RPC requester; the responder lives on the gRPC side (via the kubemq-go/v2 SDK). A SUBSCRIBE to /command or /query is hard-rejected. See Commands and Queries.

V1 non-goals (not implemented)

These are not refused at the frame layer (where applicable) but are simply not implemented in V1. They are listed so nothing is silently omitted:

FeatureStatus
STOMP-over-WebSocketnot in V1 — the connector is raw TCP / TLS only. This is why JS examples use stompit (raw TCP), not @stomp/stompjs (WebSocket-only).
Durable subscriptionsnot supported — use Events-Store /topic-store/ + start-from replay for persistence
Temp queues / temp topicsnot supported
vhost semanticsthe host header is accepted and ignored (no vhost isolation)
Per-operation token re-validationauth is connect-time only; token expiry does not drop a live connection
Client-side DLQ / redelivery-limit knobsredelivery surfaces only as redelivered:true; DLQ is broker config

Hard limits

Frame-codec constants (not tunable — no env var)

These five are package constants in the frame codec:

LimitValueError on violation
Max headers / frame64frame too large
Max header-block bytes8192 (8 KiB; command + headers share the budget)frame too large
Max destination length512 bytesinvalid destination
Max custom tags (SEND)32frame too large + close
Max tag-value bytes (SEND)4096frame too large + close

The one tunable codec limit

LimitDefaultEnv var
Max body size104857600 (~100 MiB)CONNECTORS_STOMP_MAX_BODY_SIZE

A body larger than MaxBodySize produces frame too large. This is the only config-tunable codec limit — the other five are constants. See Configuration.

Connection / flow-control limits

LimitValueNotes
MaxConnections1000 (0 = unlimited)counted at accept — a raw socket that never CONNECTs still consumes a slot; an over-limit ERROR "connection limit reached" is deferred to the handshake
Per-channel queue inflight64not tunable
Out-queue depth256not tunable
Per-subscription events deliver buffer100tunable via CONNECTORS_STOMP_SUB_BUFF_SIZE (1..10000)

content-length and binary safety

content-length, when present, is authoritative and binary-safe — the reader reads exactly N bytes then requires a NUL terminator (the only way to send a body with embedded NULs). The writer auto-stamps content-length on every bodied MESSAGE / ERROR. There is no content-type default at the frame layer, so a producer that sets no content-type tag yields a MESSAGE with no content-type header — the subscriber must assume binary. See Destination Grammar for the header ⇄ tag mapping.

Reliability

PatternGuarantee
Queuesat-least-once — duplicates tolerated, never lost; NAck / requeue on full output, sweeper requeue on ack-timeout, disconnect NAcks pending
Events / Events-Storeat-most-once — a full subscriber output buffer drops THAT delivery for THAT subscriber; the connection stays alive

Never exactly-once. No example or guide should promise it.

The ten STOMP gotchas

The behaviors most likely to surprise a STOMP migrant. Each is surfaced as a callout in the page it applies to:

#Gotcha
1RPC failures are a MESSAGE + stomp-error header, NOT an ERROR frame — the connection stays OPEN. A logical error carries the responder's body + tags alongside stomp-error; only a transport error / timeout or nil response is empty-bodied — never assume an empty body.
2Ack timeout = 30 s → requeue (not disconnect); NO client-side DLQ — redelivery surfaces only as redelivered:true. See Acknowledgement modes (queues).
3Queues at-least-once; events / store at-most-once — never exactly-once.
4No content-type frame default — no content-type tag means no content-type header (the subscriber assumes binary).
5CR/LF in a header value is silently dropped to 1.0 / 1.1 subscribers — use accept-version:1.2; structured metadata belongs in the body.
6Wildcards are events-only + subscribe-only, using the message broker's native syntax (* = one segment, > = final tail); egress delivers the CONCRETE channel — no MQTT + / #.
7A literal . in a destination segment is lossy/topic/a.b and /topic/a/b collide; egress emits the slash form.
8@stomp/stompjs is WebSocket-only and cannot drive the raw-TCP V1 connector — JS examples use stompit (raw TCP).
9Cross-protocol interop is gRPC / array-proven only — phrase it as "via the shared KubeMQ array, the same path gRPC uses".
10SUBSCRIBE to /command / /query is rejected; transactions & selectors are hard-rejected (ERROR + close) — STOMP is RPC-requester-only.

Was this page helpful?

On this page