# Architecture (/connectors/amqp/concepts/architecture)



The KubeMQ **AMQP 1.0 connector** is an embedded, wire-protocol bridge inside
kubemq-server. It speaks the AMQP 1.0 dialect on plain port **5672** (TCP / SASL) and TLS
port **5671**. The connector is &#x2A;*opt-in (disabled by default)** — enable it with
`CONNECTORS_AMQP10_ENABLE=true` (Docker) or `spec.amqp10.enabled: true` (Kubernetes). Any
standard AMQP 1.0 client connects to it with only a connection-string change — no code
rewrite, no library swap, no KubeMQ SDK.

Unlike the RabbitMQ (AMQP 0-9-1) connector — which bridges onto exactly one KubeMQ
primitive (the Queue) — the AMQP 1.0 connector bridges onto **all five** KubeMQ patterns:
Queues, Events, Events-Store, Commands, and Queries. The leading segment of the node
address selects which pattern a link is bound to. That single fact drives the whole mental
model.

<Callout type="info">
  AMQP 1.0 is a peer-to-peer link protocol — there are **no exchanges, no bindings, no
  routing keys, and no publisher-confirms** here. Those are AMQP 0-9-1 concepts. In AMQP 1.0
  a *link* is attached to a *node* (an address), message flow is governed by *credit*, and
  delivery is resolved by *delivery state* (accepted / released / modified / rejected). If
  you are migrating from 0-9-1 or ActiveMQ, see
  [Migrating from ActiveMQ](/connectors/how-to/migration/from-activemq).
</Callout>

## How AMQP 1.0 maps to KubeMQ [#how-amqp-10-maps-to-kubemq]

The connector binds a link to a KubeMQ pattern by the **leading segment of the node
address**. The grammar is `[/]<pattern>/<channel>` — the prefix selects the pattern and
the remainder is the KubeMQ channel.

<Mermaid
  chart="`
graph LR
ADDR[&#x22;Node address<br/>&lt;pattern&gt;/&lt;channel&gt;&#x22;]
Q[&#x22;queues/&#x22;]
E[&#x22;events/&#x22;]
ES[&#x22;events-store/&#x22;]
C[&#x22;commands/&#x22;]
QY[&#x22;queries/&#x22;]
BROKER[&#x22;Message Broker&#x22;]

ADDR --> Q
ADDR --> ES
ADDR --> E
ADDR --> C
ADDR --> QY

Q -- &#x22;Queues&#x22; --> BROKER
E -- &#x22;Events&#x22; --> BROKER
ES -- &#x22;Events-Store&#x22; --> BROKER
C -- &#x22;Commands (RPC)&#x22; --> BROKER
QY -- &#x22;Queries (RPC)&#x22; --> BROKER

class ADDR client
class Q,E,ES,C,QY connector
class BROKER broker
`"
/>

*The address prefix selects the KubeMQ pattern; `events-store/` is matched before `events/` so it never collides.*

| Address prefix      | KubeMQ pattern     | Produce (client sender → target)                                               | Consume (client receiver ← source)                                            |
| ------------------- | ------------------ | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
| `queues/<ch>`       | **Queues**         | at-least-once enqueue; the server dispositions `accepted` per send             | credit-driven destructive consume; `accept` / `release` / `modify` / `reject` |
| `events/<ch>`       | **Events**         | pre-settled fan-out (at-most-once)                                             | standing-credit fan-out; **0-credit → silent drop**                           |
| `events-store/<ch>` | **Events-Store**   | persisted append                                                               | durable replay/resume; start positions via `x-opt-kubemq-start`               |
| `commands/<ch>`     | **Commands (RPC)** | request + dynamic reply node; reply carries `x-opt-kubemq-executed` / `-error` | responder consumes, replies to `/responses/<RequestID>`                       |
| `queries/<ch>`      | **Queries (RPC)**  | request + dynamic reply node; reply = body + metadata only                     | responder consumes, replies to `/responses/<RequestID>`                       |

Resolution rules:

* **Longest-prefix wins.** `events-store/` is matched before `events/`, so
  `events-store/orders` never collides with the `events/` arm. The matching order is
  `events-store → queues → events → commands → queries → responses`.
* **Leading slash is optional.** At most one leading `/` is stripped before matching, so
  `queues/orders` and `/queues/orders` resolve identically.
* **Bare addresses** (no recognized prefix) resolve by a JMS node-capability hint
  (`queue` → `queues`, `topic` → `events`) or fall back to the configured `DefaultPattern`
  (`queues` by default). Best practice is to always emit the explicit prefix.
* **`/responses/<RequestID>`** is the write-only RPC reply path. A receiver attach on it is
  refused with `amqp:not-allowed`.
* **Channel charset** is stricter than the array layer: non-empty, ≤255 chars, no trailing
  `.`, no whitespace, no `*` `>` `;` `:`. A violation returns `amqp:not-found`.

See [Address mapping](/connectors/amqp/reference/address-mapping) for the master table
and [Addressing](/connectors/amqp/concepts/addressing) for narrative guidance.

## The shared front door: `amqpmux` [#the-shared-front-door-amqpmux]

KubeMQ ships two embedded AMQP dialects — 0-9-1 (RabbitMQ) and 1.0 — and they **share the
same listeners**. A single mux per `(port, tlsPort)` group accepts every connection, reads
the **8-byte AMQP protocol header**, and dispatches the raw connection to the engine that
speaks the matching dialect. The mux never speaks AMQP itself; once it classifies a
connection it hands the connection plus the consumed header to the engine, which resumes
the protocol exactly where the header left off.

<Mermaid
  chart="`
graph LR
CLIENT[&#x22;AMQP client<br/>:5672 / :5671&#x22;]
MUX[&#x22;amqpmux<br/>read 8-byte header&#x22;]
E091[&#x22;AMQP 0-9-1 engine&#x22;]
E10[&#x22;AMQP 1.0 engine&#x22;]

CLIENT -- &#x22;AMQP + (id,maj,min,rev)&#x22; --> MUX
MUX -- &#x22;AMQP\\x00\\x00\\x09\\x01&#x22; --> E091
MUX -- &#x22;AMQP\\x00\\x01\\x00\\x00 (bare)&#x22; --> E10
MUX -- &#x22;AMQP\\x03\\x01\\x00\\x00 (SASL)&#x22; --> E10
MUX -- &#x22;AMQP\\x02\\x01\\x00\\x00 (TLS)&#x22; --> E10

class CLIENT client
class MUX connector
class E091,E10 broker
`"
/>

*The mux classifies each connection by its 8-byte protocol header and routes it to the matching dialect engine.*

The 8-byte header is `"AMQP"` followed by a 4-byte `(protocol-id, major, minor, revision)`
tuple. The mux recognizes:

| Header bytes           | Meaning               | Listener | Dispatched to                      |
| ---------------------- | --------------------- | -------- | ---------------------------------- |
| `AMQP\x00\x00\x09\x01` | AMQP 0-9-1            | any      | 0-9-1 engine                       |
| `AMQP\x00\x01\x00\x00` | AMQP 1.0 (bare)       | any      | 1.0 engine                         |
| `AMQP\x03\x01\x00\x00` | AMQP 1.0 (SASL layer) | any      | 1.0 engine                         |
| `AMQP\x02\x01\x00\x00` | AMQP 1.0 (TLS token)  | TLS only | 1.0 engine (after TLS termination) |

Key consequences:

* **Plain port `5672` and TLS port `5671` are shared with the AMQP 0-9-1 connector.**
  Setting `CONNECTORS_AMQP10_PORT` equal to the 0-9-1 port is intentionally accepted — the
  mux dedupes the bind, so the two dialects coexist on one listener.
* **Version negotiation:** if a client presents an AMQP 1.0-family header but no live 1.0
  engine is available, the mux writes back the 0-9-1 header and closes (and vice-versa). A
  client that cannot even send a header gets nothing back — the connection is closed
  silently.
* **The connector advertises no negotiated capabilities** (see
  [What the server advertises](#what-the-server-advertises)).

## Connection → Session → Link [#connection--session--link]

AMQP 1.0 is a three-level container model. The connector implements the **server side** of
each finite-state machine, so the peer roles invert relative to your client: a client
**sender** is a server **receiver** (you produce to a `target`), and a client **receiver**
is a server **sender** (you consume from a `source`).

<Mermaid
  chart="`
graph TB
CONN[&#x22;Connection<br/>one TCP conn, one OPEN<br/>container-id required, ≤256&#x22;]
SESS[&#x22;Session<br/>BEGIN — up to channel-max+1&#x22;]
LSEND[&#x22;Link: client SENDER → server RECEIVER<br/>target = &lt;pattern&gt;/&lt;channel&gt;&#x22;]
LRECV[&#x22;Link: client RECEIVER → server SENDER<br/>source = &lt;pattern&gt;/&lt;channel&gt;&#x22;]

CONN --> SESS
SESS --> LSEND
SESS --> LRECV

class CONN,SESS connector
class LSEND,LRECV client
`"
/>

| Level          | Client performative | Connector behavior                                                                                                                                                                                                                                                                                                                                              |
| -------------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Connection** | `OPEN`              | `container-id` is required and non-empty (empty → `amqp:invalid-field`), sanitized to `[a-zA-Z0-9_-]`, capped at 256 chars. It becomes the ClientID when there is no SASL identity and is **half the durable-subscription identity — so it must be stable across reconnects** for durable subscribers. The OPEN `hostname` (vhost) is accepted but **ignored**. |
| **Session**    | `BEGIN`             | The server advertises `channel-max = min(client, SessionMax-1)` (255 with defaults). A session window violation closes with `amqp:session:window-violation`; an unattached or in-use handle is `amqp:session:errant-link`.                                                                                                                                      |
| **Link**       | `ATTACH`            | The peer role inverts (sender ↔ receiver). The address resolves to a `(pattern, channel)` pair. Receivers grant credit via `FLOW`; the server never delivers without it. The receive settle mode is `first` (only).                                                                                                                                             |

## What the server advertises [#what-the-server-advertises]

On `OPEN` the connector sends back **only** `container-id` (`"KubeMQ"`), `max-frame-size`,
`channel-max`, and `idle-time-out`. It sets **no offered/desired connection or link
capabilities** — in particular &#x2A;*no `ANONYMOUS-RELAY`** and no `queue`/`topic` node
capabilities. Clients must not depend on capability negotiation.

This is why the **anonymous terminus** (a sender with a null target that routes per-message
by `properties.to`) is driven entirely by the *null target address*, not by an advertised
capability. It is also why Apache Qpid JMS cannot drive the anonymous-terminus path — it
has no API to force a raw null-target link, and there is no capability to trigger its
anonymous-producer path. See [Capabilities](/connectors/amqp/reference/capabilities).

## The metadata envelope and type markers [#the-metadata-envelope-and-type-markers]

KubeMQ messages carry a JSON `Metadata&#x60; string. The connector serializes the full AMQP
message context into a single canonical envelope keyed by &#x2A;*`amqp10`**:

```json
{
  "amqp10": {
    "props": { "...": "original-form AMQP properties (message_id, correlation_id, to, reply_to, subject, content_type, group_id, ttl, ...)" },
    "app": { "...": "application-properties, type-preserved" },
    "annotations": { "x-opt-...": "message-annotations" },
    "delivery_annotations": { "...": "opaque pass-through" },
    "footer": { "...": "opaque pass-through" },
    "body_section": "data"
  }
}
```

* The envelope is **always present** — even `{"amqp10":{}}` for an empty/property-less
  message — so every message carries non-empty `Metadata`.
* `body_section&#x60; discriminates the body: &#x2A;*`"data"`** (binary `Data&#x60; section) or
  &#x2A;*`"value"`** (`AmqpValue` section). An `AmqpSequence` body is **rejected** with
  `amqp:not-implemented`.
* Treat the envelope as **opaque** from a client's point of view. Set standard AMQP
  properties natively (message-id, correlation-id, content-type, ttl, group-id) and let the
  connector derive the `amqp10.*` tags; do not hand-build the envelope.

### Type markers [#type-markers]

JSON has no native unsigned, 64-bit, binary, or timestamp types, so the codec wraps AMQP
scalar values that would otherwise lose fidelity using a `$`-prefixed marker. Integers
within ±2^53 are emitted as plain JSON numbers; only out-of-range or type-ambiguous values
are wrapped, and egress restores the exact AMQP type the client sent.

| Marker                                            | AMQP type → JSON form       |
| ------------------------------------------------- | --------------------------- |
| `$int64`                                          | int64 beyond ±2^53 → string |
| `$u64`                                            | uint64 → string             |
| `$ts`                                             | timestamp → Unix seconds    |
| `$bin`                                            | binary → base64             |
| `$uuid`                                           | UUID → RFC-4122 string      |
| `$u8` / `$i8` / `$i16` / `$u16` / `$i32` / `$u32` | sized integers              |
| `$f32` / `$f64`                                   | 32- / 64-bit floats         |

Receiver-set link properties (on the consuming ATTACH, not the message) carry pattern
options: `x-opt-kubemq-group` (consumer group for events / events-store / queues) and
`x-opt-kubemq-start` (the events-store start position, `new-only` by default). Inert
sections are accepted but not acted on: message `priority`, `group-id` ordering, and
`footer` are pass-through only.

## Cross-protocol interop [#cross-protocol-interop]

Because every pattern is backed by a normal KubeMQ channel, a message sent over AMQP 1.0
to `queues/orders` is consumable by a gRPC or REST queue client on the same channel, and
vice-versa. The connector asserts this equivalence: `queues/<ch>` over AMQP 1.0 maps to the
bare channel `<ch>` over gRPC.

<Mermaid
  chart="`
graph LR
A10[&#x22;AMQP 1.0 client<br/>queues/orders&#x22;]
GRPCCLIENT[&#x22;gRPC / REST client<br/>channel: orders&#x22;]
BROKER[&#x22;Message Broker&#x22;]

A10 -- &#x22;produce&#x22; --> BROKER
BROKER -- &#x22;consume&#x22; --> GRPCCLIENT
GRPCCLIENT -- &#x22;produce&#x22; --> BROKER
BROKER -- &#x22;consume&#x22; --> A10

class A10,GRPCCLIENT client
class BROKER broker
`"
/>

*The same KubeMQ channel backs both connectors, so an AMQP 1.0 client and a gRPC/REST client interoperate transparently.*

The RabbitMQ (AMQP 0-9-1) connector uses a **different** namespace
(`amqp.<vhost>.<queue>`), so the two AMQP connectors do **not** share an address space. To
reach 0-9-1 queue data from AMQP 1.0, address it explicitly through the queues pattern:
`queues/amqp.<vhost>.<queue>` (a naming convention, not a real vhost — AMQP 1.0 has none).

## Related [#related]

<Cards>
  <Card title="Address mapping" href="/connectors/amqp/reference/address-mapping" description="The master mapping table, longest-prefix matching, dynamic / anonymous termini, and channel validation." />

  <Card title="Capabilities" href="/connectors/amqp/reference/capabilities" description="Supported body sections, settle modes, advertised fields, and forced limits." />

  <Card title="Queues" href="/connectors/amqp/concepts/queues" description="At-least-once enqueue and credit-driven destructive consume over AMQP 1.0." />

  <Card title="Error conditions" href="/connectors/amqp/reference/error-conditions" description="The amqp:* error conditions the connector raises and what triggers each." />
</Cards>
