KubeMQ
ConnectorsSTOMPReference

Destination Grammar

How a STOMP destination maps to a KubeMQ (pattern, channel) pair — primary prefixes and aliases, slash-to-dot joining, wildcards, and header-to-tag tables.

The single most important mental model for the KubeMQ STOMP connector: a STOMP destination is parsed into a (pattern, channel) pair, where the first path segment selects the KubeMQ messaging pattern and the remaining segments are .-joined into the KubeMQ channel name. Everything else on this page follows from that one rule.

This is the formal reference. For a task-oriented walkthrough see Destination Mapping.

The primary destination prefixes (lead with these)

A STOMP destination begins with one of six primary prefixes. These are the names you should write in application code:

STOMP destinationPatternKubeMQ channelDelivery model
/queue/orders/newQueuesorders.newat-least-once, competing consumers
/topic/a/b/cEventsa.b.cat-most-once, fan-out
/topic-store/auditEvents-Storeauditpersistent, replayable
/command/execCommands (RPC)execrequest / reply
/query/lookupQueries (RPC)lookuprequest / reply
/reply/r1reply (connection-local)r1RPC reply sink

The prefix map is case-sensitive. The /reply/ prefix is special — it is a connection-local RPC reply sink, never routed to the KubeMQ array (see Commands).

ActiveMQ / MQTT-style aliases (accepted, but do not lead with them)

Each pattern also accepts a single MQTT-style alias prefix. The aliases are accepted on ingress for compatibility, but egress always canonicalizes to the primary name — a subscriber that subscribed via /events/x receives MESSAGE frames stamped destination:/topic/x. Prefer the primary names everywhere.

PrimaryAlias
/queue//queues/
/topic//events/
/topic-store//store/
/command//commands/
/query//queries/

There is no /topic_store/, /eventstore/, /exchange/, or /amq/queue/ prefix.

The parse algorithm (formal grammar)

destination   = [ "/" ] prefix-or-segment *( "/" segment )
prefix        = "queue" / "queues" / "topic" / "events"
              / "topic-store" / "store" / "command" / "commands"
              / "query" / "queries" / "reply"
segment       = 1*( %x00-2E / %x30-FF )   ; any non-"/" bytes; non-empty

The connector resolves a destination as follows:

  1. Length gate first. The raw destination string is rejected if it exceeds 512 bytes — checked on the raw string, before the leading slash is stripped.
  2. Strip exactly one leading /. Both /queue/x and queue/x resolve identically; only the first slash is removed.
  3. Split on /. Any empty segment (a // or a trailing /) is rejected.
  4. First segment selects the pattern via the case-sensitive prefix map. A known prefix consumes the first segment; an unknown first segment falls through to the configured DefaultPattern (see below).
  5. Remaining segments are .-joined into the KubeMQ channel: /topic/a/b/c → channel a.b.c.

A known prefix with no trailing channel segment is rejected: /queue → empty channel; /queue/ → empty segment.

Slash → dot is the channel join

This is the core transformation. Every / after the prefix becomes a . in the KubeMQ channel name. The same channel is reachable from gRPC, MQTT, AMQP, and REST under its dotted name.

STOMP destinationKubeMQ channel
/queue/jobsjobs
/queue/jobs/emailjobs.email
/topic/orders/eu/westorders.eu.west

A literal . inside a destination segment is lossy. /topic/a.b and /topic/a/b both map to the same KubeMQ channel a.b, and egress always emits the slash form /topic/a/b. A destination that contains literal dots is therefore not round-trip safe. Prefer slashes; avoid literal dots in destination segments.

Wildcard subscriptions

Wildcards use the message broker's native wildcard syntax and are passed through untranslated — there is no MQTT-style + / #. They are subject to three hard constraints:

  • SUBSCRIBE-only — a wildcard in a SEND destination (any pattern) is rejected as invalid destination.
  • Events-only — wildcards are allowed only on the Events pattern (/topic/, /events/). Queues, Events-Store, Commands, Queries, and reply destinations reject them.
  • Two tokens, with the broker's native semanticsnot the MQTT + / #:
TokenMeaningPosition rule
*matches exactly one segmentany position
>matches one or more trailing segmentsmust be the final segment

Slash → dot applies to the matched filter too: /topic/a/*/c → channel a.*.c; /topic/orders/> → channel orders.>.

Egress delivers the CONCRETE matched channel, not the filter. A subscriber on /topic/orders/* that receives a message published to orders.eu gets a MESSAGE frame stamped destination:/topic/orders/eu — the concrete channel, never /topic/orders/*. Always read the destination header to learn what actually matched.

Pattern routing (where each SEND goes)

Once a destination resolves to a (pattern, channel) pair, the SEND routes to the matching KubeMQ array call:

PatternArray callNotes
Queuesarray.SendQueueMessageat-least-once
Eventsarray.SendEventsat-most-once
Events-Storearray.SendEventsStore (Store=true)persisted
Commandsarray.SendCommandRPC; reply on /reply/
Queriesarray.SendQueryRPC; reply on /reply/
reply(none)connection-local; never routed

A SEND to a /reply/ destination is rejected: invalid destination / cannot SEND to reply destinations.

DefaultPattern — bare (prefixless) destinations

A destination whose first segment is not a known prefix falls through to the connector-wide DefaultPattern:

CONNECTORS_STOMP_DEFAULT_PATTERNBare destination resolves to
events (default)Events; sensor/temp → channel sensor.temp
queuesQueues
storeEvents-Store
nonerejected — bare destinations require an explicit prefix (invalid destination)

There is no commands or queries default — a bare destination can never resolve to an RPC pattern.

Examples and applications should always use explicit prefixes. Relying on DefaultPattern couples your code to the connector's configuration; an operator who sets DefaultPattern=none would break every bare destination.

Header ⇄ tag reference tables

STOMP headers map onto KubeMQ message Tags. There are three classes of header; the tables below are the authoritative reference (see Destination Mapping for the prose explanation).

Standard headers ↔ reserved stomp.* tags (round-trip both directions)

Five standard STOMP headers round-trip through reserved stomp.* tags:

STOMP header (ingress & egress)KubeMQ tag key
content-typestomp.content-type
correlation-idstomp.correlation-id
reply-tostomp.reply-to
prioritystomp.priority
typestomp.type
  • Collision rule: stomp.* WINS over a same-named bare custom tag on egress; exactly one header results.
  • Custom headers pass through name-as-is as tags (first-wins on duplicate). Limits: ≤32 custom tags, ≤4096 bytes per value — a SEND that exceeds either is a fatal frame too large + close.

No content-type frame default. The frame codec never defaults content-type. A native (gRPC / REST / MQTT / AMQP) producer that sets no stomp.content-type (or plain content-type) tag yields a MESSAGE with no content-type header — the STOMP subscriber must assume binary / octet-stream. content-length is always present, so the body is still framed correctly.

Machinery headers — never become tags

These frame-machinery headers are stripped on ingress and never forwarded as KubeMQ tags:

destination   receipt   transaction   content-length
message-id    subscription   ack   id   timeout

Protected-on-ingress headers (spoofing guard)

Inbound header names starting with stomp. or equal to x-kubemq-metadata are silently stripped on ingress. A client cannot inject stomp.* tags directly — only through the canonical standard headers above.

Inbound header nameAction
stomp.* (any)stripped (logged at debug)
x-kubemq-metadatastripped (logged at debug)

x-kubemq-metadata is egress-only

STOMP ingress never sets the KubeMQ Metadata field — STOMP has no canonical envelope (unlike AMQP). On egress only, a non-empty native Metadata value surfaces as the x-kubemq-metadata header, prepended at the highest emit priority.

Egress representability guard

On delivery, a header whose name / value cannot be serialized on the negotiated protocol version is dropped (the connection stays alive; metric deliver_headers / dropped):

VersionDrops a header when…
1.0name contains :, CR, or LF; or value contains CR or LF
1.1name or value contains CR (1.1 has no CR escape)
1.2never — always representable

CR/LF in a header value is silently dropped to 1.0 / 1.1 subscribers. Use accept-version:1.2 and keep CR/LF out of header values; put structured / multi-line metadata in the body.

Destination errors (sanitized wire vocabulary)

All destination-class errors are sanitized before crossing the wire — internal error text never reaches the client. See Error Frames for the full ERROR vocabulary.

TriggerWire message
raw destination > 512 bytesinvalid destination
//, trailing /, or /queue/invalid destination
known prefix, no channel (/queue)invalid destination
bare destination with DefaultPattern=noneinvalid destination
wildcard on SEND, or on queues / store / RPCinvalid destination
>64 headers / >8 KiB block / >32 tags / >4 KiB valueframe too large

Was this page helpful?

On this page