Skip to main content

Message Flow

This page details how messages move through NSB at the protocol level โ€” the RabbitMQ queue sequences, the full operations table with channel assignment, and the status codes returned in each response.

Queue Naming Convention (RabbitMQ Backend)โ€‹

nsb.{channel}.{client_id}
โ”œโ”€โ”€ nsb.ctrl.{client_id} # Control channel
โ”œโ”€โ”€ nsb.send.{client_id} # Outgoing payload channel
โ”œโ”€โ”€ nsb.recv.{client_id} # Incoming payload channel
โ”œโ”€โ”€ nsb.config.request # Daemon configuration request queue
โ””โ”€โ”€ nsb.config.response.{client_id} # Client-specific config response

RabbitMQ Message Flowโ€‹

Client Initializationโ€‹

1. Client creates NSBAppClient or NSBSimClient
2. Client sends INIT to nsb.config.request
3. Daemon responds to nsb.config.response.{client_id}
4. Client receives config and is ready

Message Sendingโ€‹

1. App Client calls send(dest_id, payload)
2. Message published with routing_key = nsb.recv.{dest_id}
3. RabbitMQ routes directly to destination's RECV queue
4. No daemon mediation required

Message Fetching (Simulator)โ€‹

1. SimClient calls fetch()
2. Consumes directly from nsb.recv.{simulator_id}
3. Calls post() to return processed message

In the RabbitMQ backend, once a client is registered, the broker handles all routing natively โ€” the daemon is only involved during the INIT handshake. Contrast this with the socket backend, where the daemon mediates every message. See Socket Backend vs RabbitMQ Backend for the full comparison.

Operations Tableโ€‹

Every nsbm message carries a Manifest with an Operation, Originator, and OpCode. This table shows which channel each operation travels on and its direction:

OperationOriginatorChannelDirectionDescription
PINGAny clientctrlโ†”Liveness check; daemon echoes SUCCESS or FAILURE
INITAny clientctrlโ†”Registration handshake; client sends IntroDetails; daemon responds with ConfigParams
SENDAppClientsendโ†’ DaemonSubmit payload to daemon TX buffer; no acknowledgement
FETCHSimClientrecvโ†”Poll for outbound payload; daemon responds with MESSAGE or NO_MESSAGE (PULL mode)
POSTSimClientsendโ†’ DaemonDeliver arrived payload to daemon RX buffer; no acknowledgement
RECEIVEAppClientrecvโ†”Poll for delivered payload; daemon responds with MESSAGE or NO_MESSAGE (PULL mode)
FORWARDDaemonrecvDaemon โ†’Proactively push payload to client (PUSH mode only)
EXITAny clientctrlโ†’ DaemonGraceful shutdown signal; no response

Notice that SEND and POST both travel on the send sub-channel (client โ†’ daemon, no response expected), while FETCH and RECEIVE both travel on recv (bidirectional poll/response). This symmetry is intentional โ€” see Deep Architecture โ†’ Multi-Channel Architecture for why.

Status Codes Tableโ€‹

The OpCode field on a response tells the client how to interpret the message:

CodeUsed InMeaning
SUCCESSPING, INIT, RECEIVE, FETCHOperation succeeded
FAILUREPING, INITOperation failed
MESSAGESEND, POST, FETCH, RECEIVE, FORWARDMessage carries a payload or key
NO_MESSAGEFETCH, RECEIVENo payload found for requested node

For the complete field-by-field schema (including CLIENT_REQUEST, DAEMON_RESPONSE, IMPLICIT_TARGET, and EXPLICIT_TARGET โ€” used for routing context rather than final status) see Protobuf Schema and Operations Reference.

Go Deeperโ€‹