# Capabilities (/connectors/stomp/reference/capabilities)



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](/connectors/stomp/reference/destination-grammar).

## Protocol versions [#protocol-versions]

| Version | Supported         | Notes                                                                                                |
| ------- | ----------------- | ---------------------------------------------------------------------------------------------------- |
| **1.2** | Yes (recommended) | full escaping including CR; `id`-based ACK token; `id` required on SUBSCRIBE                         |
| **1.1** | Yes               | escapes `:` / LF / `\` but **not** CR; `message-id` + `subscription` ACK; `id` required on SUBSCRIBE |
| **1.0** | Yes               | no 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.

<Callout type="info">
  **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](/connectors/stomp/how-to/protocol-versions) for the
  full per-version feature matrix.
</Callout>

## Supported client commands [#supported-client-commands]

All 11 client → server STOMP commands are recognized:

| Command                      | Supported         | Behavior                                                            |
| ---------------------------- | ----------------- | ------------------------------------------------------------------- |
| `CONNECT` / `STOMP`          | Yes               | handshake; must be the first frame within 30 s                      |
| `SEND`                       | Yes               | routes by destination prefix to the matching pattern                |
| `SUBSCRIBE`                  | Yes               | events / store / queues / `/reply/`; **not** `/command` or `/query` |
| `UNSUBSCRIBE`                | Yes               | by `id` (1.1 / 1.2); by `destination` on 1.0                        |
| `ACK`                        | Yes               | queue acknowledgement; no-op on events subscriptions                |
| `NACK`                       | Yes               | queue negative-ack / requeue; honored on 1.0 too (lenient)          |
| `DISCONNECT`                 | Yes               | graceful close; `RECEIPT` flushed before socket close               |
| `BEGIN` / `COMMIT` / `ABORT` | **Hard-rejected** | `ERROR "transactions not supported"` + close                        |

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

## Acknowledgement modes (queues) [#acknowledgement-modes-queues]

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

| `ack` mode            | Client action                                   | Semantics                                                                 |
| --------------------- | ----------------------------------------------- | ------------------------------------------------------------------------- |
| `auto` (default)      | none                                            | fire-and-forget; reserve → enqueue → immediate ack                        |
| `client-individual`   | ACK / NACK one message                          | resolves exactly that one delivery — **the recommended reliable default** |
| `client` (cumulative) | ACK a message + all earlier on the subscription | grouped 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](/connectors/stomp/how-to/ack-modes-and-receipts) for
per-version ACK / NACK token correlation and receipt rules.

## Events-Store replay (`/topic-store/` only) [#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-from`   | `start-value`                             | Replays from                   |
| -------------- | ----------------------------------------- | ------------------------------ |
| absent / `new` | must NOT be present                       | new messages only (default)    |
| `first`        | must NOT be present                       | the earliest stored message    |
| `last`         | must NOT be present                       | the most recent stored message |
| `sequence`     | required, numeric ≥ 0                     | the given sequence number      |
| `time`         | required, RFC3339 **or** unix-seconds ≥ 0 | the given timestamp            |
| `time-delta`   | required, 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](/connectors/stomp/how-to/events-store).

## Hard-rejected features [#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**.

| Feature                                | Behavior                                                                                                                       |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **STOMP transactions**                 | `BEGIN` / `COMMIT` / `ABORT`, **or any frame carrying a `transaction` header**, → `ERROR "transactions not supported"` + close |
| **Selectors**                          | a `selector` header on SUBSCRIBE → `ERROR "selectors not supported"` + close (even without a transaction)                      |
| **SUBSCRIBE to `/command` / `/query`** | → `ERROR "cannot subscribe to RPC destinations"` + close — STOMP is RPC-**requester-only**                                     |
| **SEND to `/reply/`**                  | → `ERROR "invalid destination"` + close — `/reply/` is a connection-local sink                                                 |

<Callout type="warn">
  **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](/connectors/stomp/how-to/commands) and
  [Queries](/connectors/stomp/how-to/queries).
</Callout>

## V1 non-goals (not implemented) [#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:

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

## Hard limits [#hard-limits]

### Frame-codec constants (not tunable — no env var) [#frame-codec-constants-not-tunable--no-env-var]

These five are package constants in the frame codec:

| Limit                      | Value                                                | Error on violation        |
| -------------------------- | ---------------------------------------------------- | ------------------------- |
| Max headers / frame        | **64**                                               | `frame too large`         |
| Max header-block bytes     | **8192** (8 KiB; command + headers share the budget) | `frame too large`         |
| Max destination length     | **512** bytes                                        | `invalid destination`     |
| Max custom tags (SEND)     | **32**                                               | `frame too large` + close |
| Max tag-value bytes (SEND) | **4096**                                             | `frame too large` + close |

### The one tunable codec limit [#the-one-tunable-codec-limit]

| Limit             | Default                   | Env var                          |
| ----------------- | ------------------------- | -------------------------------- |
| **Max body size** | **104857600** (\~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](/connectors/stomp/concepts/configuration).

### Connection / flow-control limits [#connection--flow-control-limits]

| Limit                                  | Value                      | Notes                                                                                                                                                         |
| -------------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MaxConnections`                       | **1000** (`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 inflight             | **64**                     | not tunable                                                                                                                                                   |
| Out-queue depth                        | **256**                    | not tunable                                                                                                                                                   |
| Per-subscription events deliver buffer | **100**                    | tunable via `CONNECTORS_STOMP_SUB_BUFF_SIZE` (1..10000)                                                                                                       |

### content-length and binary safety [#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](/connectors/stomp/reference/destination-grammar) for the
header ⇄ tag mapping.

## Reliability [#reliability]

| Pattern                   | Guarantee                                                                                                                                     |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **Queues**                | **at-least-once** — duplicates tolerated, never lost; NAck / requeue on full output, sweeper requeue on ack-timeout, disconnect NAcks pending |
| **Events / Events-Store** | **at-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-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                                                                                                                                                                                                                                                                                 |
| -- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1  | **RPC 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. |
| 2  | **Ack timeout = 30 s → requeue (not disconnect); NO client-side DLQ** — redelivery surfaces only as `redelivered:true`. See [Acknowledgement modes (queues)](#acknowledgement-modes-queues).                                                                                           |
| 3  | **Queues at-least-once; events / store at-most-once** — never exactly-once.                                                                                                                                                                                                            |
| 4  | **No `content-type` frame default** — no content-type tag means no `content-type` header (the subscriber assumes binary).                                                                                                                                                              |
| 5  | **CR/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.                                                                                                                                          |
| 6  | **Wildcards are events-only + subscribe-only**, using the message broker's native syntax (`*` = one segment, `>` = final tail); egress delivers the CONCRETE channel — &#x2A;*no MQTT `+` / `#`**.                                                                                     |
| 7  | **A 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).                                                                                                                                                                |
| 9  | **Cross-protocol interop is gRPC / array-proven only** — phrase it as "via the shared KubeMQ array, the same path gRPC uses".                                                                                                                                                          |
| 10 | **SUBSCRIBE to `/command` / `/query` is rejected; transactions & selectors are hard-rejected (ERROR + close)** — STOMP is RPC-requester-only.                                                                                                                                          |

## Related [#related]

<Cards>
  <Card title="Destination Grammar" href="/connectors/stomp/reference/destination-grammar" description="The destination → (pattern, channel) grammar and the header ⇄ tag tables." />

  <Card title="Error Frames" href="/connectors/stomp/reference/error-frames" description="The complete ERROR-frame vocabulary behind each rejection above." />

  <Card title="Connections & Observability" href="/connectors/stomp/reference/connections-endpoint" description="The management API, Prometheus metrics, dashboard, and audit taxonomy." />

  <Card title="Migrating from STOMP" href="/connectors/stomp/scenarios/migration-from-stomp" description="What to drop when migrating an existing STOMP application to KubeMQ." />
</Cards>
