Architecture
Inside the AMQP 1.0 connector — the amqpmux shared front door, the connection/session/link model, address-to-pattern mapping, and the metadata envelope.
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 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.
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.
How AMQP 1.0 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.
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 beforeevents/, soevents-store/ordersnever collides with theevents/arm. The matching order isevents-store → queues → events → commands → queries → responses. - Leading slash is optional. At most one leading
/is stripped before matching, soqueues/ordersand/queues/ordersresolve identically. - Bare addresses (no recognized prefix) resolve by a JMS node-capability hint
(
queue→queues,topic→events) or fall back to the configuredDefaultPattern(queuesby 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 withamqp:not-allowed.- Channel charset is stricter than the array layer: non-empty, ≤255 chars, no trailing
., no whitespace, no*>;:. A violation returnsamqp:not-found.
See Address mapping for the master table and Addressing for narrative guidance.
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.
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
5672and TLS port5671are shared with the AMQP 0-9-1 connector. SettingCONNECTORS_AMQP10_PORTequal 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).
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).
| 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
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 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.
The metadata envelope and type markers
KubeMQ messages carry a JSON Metadata string. The connector serializes the full AMQP
message context into a single canonical envelope keyed by amqp10:
{
"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-emptyMetadata. body_sectiondiscriminates the body:"data"(binaryDatasection) or"value"(AmqpValuesection). AnAmqpSequencebody is rejected withamqp: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
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
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.
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
Address mapping
The master mapping table, longest-prefix matching, dynamic / anonymous termini, and channel validation.
Capabilities
Supported body sections, settle modes, advertised fields, and forced limits.
Queues
At-least-once enqueue and credit-driven destructive consume over AMQP 1.0.
Error conditions
The amqp:* error conditions the connector raises and what triggers each.
Was this page helpful?
Addressing
The AMQP 1.0 terminus address grammar — pattern/channel prefixes, channel validation, longest-prefix matching, and dynamic and anonymous nodes.
Commands
Native AMQP 1.0 request/reply over KubeMQ Commands — dynamic reply nodes, anonymous responders, correlation-id matching, and an executed/error signal.