AMQP Protocol
Kuestenlogik.Bowire.Protocol.Amqp (sibling repo) covers both major AMQP wires in one plugin id — the actual wire is picked from the URL scheme:
| URL scheme | Wire | Library | Discovery |
|---|---|---|---|
amqp:// / amqps:// |
AMQP 0.9.1 | RabbitMQ.Client | RabbitMQ Management HTTP API |
amqp1:// / amqps1:// |
AMQP 1.0 | AMQPNetLite | Artemis management over AMQP; Service Bus ATOM feed; synthetic Broker otherwise |
Package: Kuestenlogik.Bowire.Protocol.Amqp (sibling repo, not bundled with the CLI)
Setup
bowire plugin install Kuestenlogik.Bowire.Protocol.Amqp
Standalone — AMQP 0.9.1 (RabbitMQ)
bowire --url amqp://localhost:5672
Standalone — AMQP 1.0 (ActiveMQ / Solace / Azure SB)
bowire --url amqp1://localhost:5672
Embedded
app.MapBowire(options =>
{
options.ServerUrls.Add("amqp://localhost:5672");
});
Discovery
AMQP 0.9.1 — AmqpDiscovery hits the RabbitMQ Management HTTP API (/api/queues, /api/exchanges) on port 15672 by default. Each queue surfaces as a service with publish (Unary) and consume (ServerStreaming); each exchange surfaces as a service with publish per binding key.
AMQP 1.0 — the spec defines no discovery, but the two brokers Bowire meets most each answer a question about themselves, and neither needs a credential the connection does not already carry.
ActiveMQ Artemis answers management requests over the AMQP connection itself: a message to
activemq.managementnaming a resource and an operation, replied to on a temporary queue. Discovery asks it forlistAddressesandlistQueues. Each address becomes a service withsend; each queue bound to it becomes areceive:<queue>on that service, addressed in Artemis' fully-qualified form (address::queue) so a multicast address with several subscriptions can be told apart. A queue named after its address — the ordinary anycast case — is the barereceive. The broker's own plumbing ($sys.*,activemq.*, temporary addresses) is hidden unlessshowInternalServicesis on.Azure Service Bus serves the namespace's ATOM management feed over HTTPS, signed with the shared-access key already in the URL (
amqps1://<keyName>:<key>@<ns>.servicebus.windows.net). Queues become services withsend+receive; topics become services withsendand onereceive:<subscription>per subscription, addressedtopic/Subscriptions/name.The feed's location is derived, not assumed:
https://<host>/for a live namespace, andhttp://<host>:<port>/when the connection is plaintext and?_mgmtPort=names a port. That second form is what reaches Microsoft's Service Bus emulator, which serves the same feed over HTTP on 5300 — a port no part of the AMQP URL implies. An emulator also needs?_amqp10Discovery=servicebus, becauseautorecognises Service Bus by hostname and127.0.0.1is not one:amqp1://RootManageSharedAccessKey:SAS_KEY_VALUE@127.0.0.1:5672?_amqp10Discovery=servicebus&_mgmtPort=5300Anything else — Solace, Qpid, a bespoke 1.0 endpoint — keeps the synthetic
Brokerservice with genericsend+receive, and the target address rides on theaddressmetadata key or the URL path, exactly as before.
Which one to ask comes from the amqp10Discovery setting (per-connection: ?_amqp10Discovery=…). auto, the default, reads the host — a *.servicebus.* name is Service Bus — and otherwise tries Artemis. Nothing answering is not an error: discovery falls back to the synthetic service rather than failing the connection, so a broker that is neither behaves as it always did.
Invocation
Consume returns a structured envelope per message:
{
"exchange": "...",
"routingKey": "...",
"contentType": "application/json",
"messageId": "...",
"encoding": "...",
"payload": "..."
}
Security
Both wires honour the shared __bowireMtls__ + __bowireAmqpSasl__ marker keys for client cert + SASL auth respectively.
Settings
managementApiPort(number, default15672) — RabbitMQ Management API port; per-URL override via?_mgmtPort=…discoveryTimeoutSeconds(number, default5) — also bounds the Artemis management round-trip and the Service Bus feedamqp10Discovery(select, defaultauto) —auto/artemis/servicebus/none; which broker's management surface anamqp1://endpoint is asked. Per-connection override:?_amqp10Discovery=<value>receiveTimeoutSeconds(number, default30)
Mock replay
AmqpMockEmitter re-publishes recorded messages on a bowire mock server. Recordings with protocol: "amqp" step kind get routed here, honouring loop / replay-speed.
Coverage
First sibling plugin to clear stable. Live Testcontainers integration suites under [Trait("Category","Docker")] cover all three brokers: RabbitMQ for 0.9.1 (protocol + mock-emitter publish loop), ActiveMQ Artemis for 1.0 (management discovery, multicast fan-out to each discovered queue, anycast round-trip), and Microsoft's Service Bus emulator for the Service Bus flavour (the ATOM feed, a queue's methods, a topic's per-subscription methods, and the address each one resolves to).
What the emulator does not prove is the SAS signature — it accepts any Authorization header it is handed — so that stays covered by an independently computed vector in the unit suite. A live namespace is a paid resource and is not in CI.
The emulator needs SQL Server beside it, a 2.3 GB pull where the other two images are under 600 MB, so it carries a second trait: dotnet test --filter-not-trait "Broker=ServiceBusEmulator" drops it without dropping the rest of the Docker suite.
See: Recording, Mock Server.