Skip to content

mq9: RobustMQ's Communication Layer Designed for AI Agents

Starting From a Real Problem

People have Slack, email, and messaging apps. I send something, you read it when you have time — no need to be online at the same time. We take this for granted, because the asynchronous communication infrastructure for humans is already very mature.

What about Agent-to-Agent communication?

Agents are not services — they are ephemeral, they die when a task completes, and they may exist for only a few seconds. Agent A sends a message to Agent B; B is offline, and the message is gone. There is no standard mechanism to guarantee that "a message I sent will be received once the other party comes online." Every team building multi-Agent systems uses their own temporary workaround — HTTP callbacks, Redis pub/sub, polling a database, or a homegrown task queue. These work, but they are all workarounds.

Going further: if two Agents run on different machines owned by different people, there is currently no way to let them communicate directly. Every Agent system is an island.

This is the problem mq9 wants to solve.


What mq9 Is

mq9 is a message broker designed for AI Agents. Not a protocol, not an SDK, not a framework — it is a deployable service. One docker run command, and Agents have asynchronous communication capabilities.

mq9 is RobustMQ's fifth native protocol layer, alongside MQTT, Kafka, NATS, and AMQP, built on top of RobustMQ's unified storage architecture. Not a plugin, not an external integration — a native capability.

Deploy one RobustMQ, and all of mq9's capabilities are ready. IoT devices send data via MQTT, analytics systems consume via Kafka, Agents collaborate via mq9 — the same broker, the same storage, zero bridging, zero duplication.

bash
docker run -d -p 4222:4222 robustmq/robustmq:latest

After this one line, Agents can create mailboxes and send messages.

mq9 only solves the communication problem — how to reliably deliver messages. What the message contains, how it is used, and what the business semantics are — mq9 does not care. Message content is a byte array: not parsed, not validated, not restricted.


Core Concept: Mailbox

mq9 has a single core abstraction: Mailbox (MAILBOX).

Why a mailbox? Because Agents are not services. Services have fixed addresses, run long-term, and are always reachable. Agents are ephemeral — born for a task, gone when it's done, with a lifecycle that may be only a few seconds. Assigning a permanent address to a temporary process is a mismatch.

mq9's approach: communication addresses are bound to tasks, not to Agent identity. An Agent applies for a mailbox for each task, receives a mail_id, and that is its communication address for this task. When the task ends, the mailbox expires and is cleaned up automatically. No deregistration, no manual deletion, no residual state to clean up.

One Agent can hold multiple mailboxes simultaneously — one for receiving task assignments, one for reporting status, one for receiving approval results — completely isolated, with no interference.

mail_id unguessability is the security boundary. mail_id is a system-generated unguessable string. Knowing the mail_id lets you send messages and subscribe — just like knowing an email address lets you send email. Without it, you can't interact with the mailbox. No token, no ACL.

Mailboxes come in two kinds:

Private mailboxPublic mailbox
mail_idSystem-generated, unguessableUser-defined, meaningful name
DiscoverabilityPrivate — only Agents who know the mail_idAuto-registered to PUBLIC.LIST, discoverable by anyone
Use casesPoint-to-point messaging, task result deliveryTask queues, public channels, capability announcements

A public mailbox's mail_id is its address — choose a meaningful name like task.queue or analytics.result rather than a UUID, making it easier for other Agents to discover and understand.


Three Operations, Six Commands

mq9's entire interface is three operations: create a mailbox, send a message, subscribe to a mailbox.

Create a Mailbox

bash
# Private mailbox
nats req '$mq9.AI.MAILBOX.CREATE' '{"ttl":3600}'
{"mail_id":"mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag","is_new":true}

# Public mailbox, auto-registered to PUBLIC.LIST
nats req '$mq9.AI.MAILBOX.CREATE' '{"ttl":3600, "public":true, "name":"task.queue", "desc":"Task queue"}'
{"mail_id": "task.queue"}

ttl is the mailbox lifetime in seconds. Auto-destroyed on expiry, all messages cleaned up with it.

Send a Message

Knowing the mail_id is all that's needed — no authorization required. Messages are sent by priority:

bash
nats pub '$mq9.AI.MAILBOX.MSG.mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag'          'payload'  # Normal (default, no suffix)
nats pub '$mq9.AI.MAILBOX.MSG.mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag.urgent'   'payload'  # Urgent
nats pub '$mq9.AI.MAILBOX.MSG.mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag.critical' 'payload'  # Critical, processed first

Three priority levels:

LevelSemanticsTypical use
criticalHighest priority, processed firstAbort signals, emergency commands, security events
urgentUrgent, time-sensitiveTask interrupts, time-sensitive instructions
normalRoutine communication (default, no suffix)Task dispatch, result delivery, approval requests

The storage layer guarantees priority ordering. An edge device that has been offline for 8 hours reconnects and receives the urgent shutdown command first, then the routine configuration update. Consumers need not sort themselves.

Subscribe to a Mailbox

bash
nats sub '$mq9.AI.MAILBOX.MSG.mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag'           # Normal (default) only
nats sub '$mq9.AI.MAILBOX.MSG.mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag.*'         # all priorities (critical + urgent + normal)
nats sub '$mq9.AI.MAILBOX.MSG.mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag.critical'  # Critical only

Subscribing delivers all non-expired messages in full — immediately on subscribe, then real-time for new arrivals. Agents that reconnect do not miss messages. No separate pull interface needed.

No read/unread, no consume offset, no QUERY command. Each message carries a unique msg_id; clients handle deduplication themselves.

Prohibited operations:

bash
nats sub '$mq9.AI.MAILBOX.MSG.*'  # Rejected by broker
nats sub '$mq9.AI.MAILBOX.MSG.#'  # Rejected by broker

Subscriptions must specify an exact mail_id. Wildcard subscriptions across all mailboxes are not permitted.


Public Mailbox Discovery

$mq9.AI.PUBLIC.LIST is a system-managed address maintained by the broker. Does not accept user writes. Never expires.

Public mailboxes are automatically registered on creation and removed when their TTL expires. No manual registry maintenance.

bash
nats sub '$mq9.AI.PUBLIC.LIST'

Subscribing delivers all current public mailboxes immediately, then streams additions and removals in real time. Subscribe once at startup and continuously sense which public channels exist in the network.


Why Existing Solutions Fall Short

HTTP: Requires both parties to be online simultaneously. Agents go offline frequently; HTTP's assumption does not hold. Adding retry loops and backoff is patching asynchronous behavior onto a synchronous protocol — it only gets more complex.

Redis pub/sub: No persistence. If the other party is offline, the message is gone. Adding Redis Streams provides persistence, but introduces consumer groups and offset management — not worth it just to send an Agent message.

Kafka: Designed for high-throughput data pipelines. Topics need pre-creation, partitions need planning, consumer groups need management. Agents are ephemeral and disposable; Kafka's resource model assumes long-lived, carefully-maintained infrastructure. Fundamentally mismatched.

Homegrown task queues: Every team starts from scratch, producing incompatible solutions that cannot interoperate.

These solutions share a common assumption: the other party is online. The most fundamental characteristic of Agents — frequent online/offline transitions — is a problem that all existing solutions work around rather than directly solving.

mq9 does not work around it. Store-first delivery — messages are written to storage on arrival; persistence is the default behavior, not an option.


Why NATS Protocol

mq9 is compatible with the NATS protocol. All NATS client SDKs — Go, Python, Rust, JavaScript, Java, .NET — are mq9 clients out of the box, with zero ecosystem cost.

There are three reasons for choosing NATS:

First, Agents naturally understand NATS. LLMs have been exposed to extensive NATS code and documentation during training. Pub/sub, subject addressing, queue groups — these concepts are already in the model's prior knowledge. mq9 does not invent new concepts; it defines semantic conventions on top of what Agents already understand, with zero adoption cost.

Second, NATS's subject model is naturally suited for mailbox addressing. $mq9.AI.MAILBOX.MSG.mail-d7a5072lko83gp7amga0-d7a5072lko83gp7amgag.critical — one string, five layers of information: mq9 protocol, AI scenario, message type, mailbox address, priority. The bare subject (no suffix) carries normal-priority messages; wildcard subscriptions let Agents flexibly choose their granularity of interest.

Third, NATS is lightweight. The protocol itself is text-based, simple to parse, with low connection overhead — well-suited for large numbers of ephemeral Agents going online and offline frequently.

mq9 and NATS

To be clear: mq9 is not NATS, and not a plugin for NATS. mq9 is an independent broker that is compatible with the NATS protocol.

mq9 occupies a middle ground between Core pub/sub and JetStream in the NATS ecosystem:

  • Core NATS is too lightweight — pure in-memory forwarding, no persistence; if the other party is offline, the message is lost. Cannot implement a mailbox.
  • JetStream is too heavy — streams, consumers, offsets, replay — a full Kafka-equivalent semantic stack. Not needed just to send an Agent message.
  • mq9 adds persistence, priority, and TTL auto-management on top of pub/sub, without introducing streams, consumer groups, or offsets. Just enough, no more.

Eight Scenarios

Three operations combined — what can they solve?

  1. Sub-Agent completes a task, notifies the main Agent asynchronously. Sub-Agent sends the result to the main Agent's mailbox. The main Agent does not need to block waiting; it picks it up when ready.
  2. Main Agent tracks all sub-Agent status. Workers create public mailboxes and report status periodically. The main Agent discovers them via PUBLIC.LIST. TTL expiry automatically signals a Worker's death.
  3. Broadcast tasks; multiple Workers compete to consume. Create a public mailbox as a task queue; Workers use a queue group to compete; the winner sends results back to the main Agent's mailbox. Offline Workers that come back online can still receive non-expired tasks.
  4. Agent detects an anomaly, broadcasts an alert. Create a public mailbox and publish anomaly events; subscribers respond on their own. Offline handlers that come back online can still receive non-expired alerts.
  5. Cloud sends commands to an offline edge Agent. Send to the edge Agent's mailbox; the message is persisted and waits. Once the edge device reconnects, it receives messages by priority — critical before urgent before normal.
  6. Human-in-the-loop workflow. Agent sends an approval request to an approver's mailbox; the approver replies to the Agent's mailbox. Humans and Agents use the same protocol.
  7. Agent A queries offline Agent B. Send a request to B's mailbox with reply_to; B processes it and replies upon coming online. Asynchronous request-reply; nothing is lost if offline.
  8. Agent registers capabilities; other Agents discover it. Create a public mailbox to declare capabilities; auto-registered to PUBLIC.LIST. Subscribers detect changes in real time.

mq9 and RobustMQ

mq9 is not a standalone product — it is part of RobustMQ.

RobustMQ is a unified messaging engine built in Rust — single binary, zero external dependencies, native support for MQTT, Kafka, NATS, and AMQP, with a unified storage layer underneath. One message written once, consumed by any protocol. mq9 is the fifth protocol layer grown from this foundation.

For users, mq9 is just a broker. Deploy one RobustMQ, and IoT devices send data via MQTT, analytics systems consume via Kafka, Agents collaborate via mq9 — the same broker, the same storage, zero bridging, zero duplication.


What mq9 Is Not

  • Not a better NATS. mq9 does not compete with NATS for general messaging scenarios. NATS protocol compatibility is for zero ecosystem cost.
  • Not a data pipeline. It does not replace Kafka for high-throughput stream processing.
  • Not service-to-service RPC. It does not replace HTTP/gRPC for always-online service communication.
  • Not an orchestration framework. mq9 moves messages; it does not make decisions. Orchestration logic belongs to the layer above.

Our Judgment

Agents lack an asynchronous communication mechanism — this problem is real. The protocol layer (A2A) and the infrastructure layer (NATS/Kafka) have each made efforts, but the combination of "Agent-native semantics + native store-and-forward + lightweight out-of-the-box + self-deployable" does not exist today. This is the gap mq9 fills.

mq9 is still being built. The protocol is iterating, features are being completed. This is not a finished product — it is a direction that is growing.

If you need to run multiple Agents, you need mq9.

🎉 既然都登录了 GitHub,不如顺手给我们点个 Star 吧!⭐ 你的支持是我们最大的动力 🚀