Wire Contract

Last updated:

BabelQueue is, at its core, one contract: a strict, language-neutral definition of the bytes that travel on the queue. Every SDK — PHP, Python, Go, Node.js, Java and .NET — produces and consumes exactly this shape, so a message written in one language is read natively in another, over the broker you already run.

This section is the source of truth for that contract. If an SDK, an example, or a marketing page ever disagrees with these pages, the contract wins.

Producer to broker to consumer Three boxes from left to right, a producer, a broker and a consumer, joined by arrows. The caption below gives the full description. producer Any SDK encodes the envelope as UTF-8 JSON broker Redis · RabbitMQ SQS · Kafka · Pulsar Azure SB · Artemis consumer Any SDK decodes the envelope routes on the URN 1 3 2 1 Publish: the SDK builds the envelope; job is a URN, trace_id is minted or reused. 2 Store: the broker carries the same bytes, no PHP serialize() and no per-language format. 3 Consume: any SDK reads the same envelope and forwards trace_id on every hop.
A message crosses languages without translation. (1) A producer, written with any SDK, encodes the canonical JSON envelope and publishes it through a broker transport. (2) The broker (Redis, RabbitMQ, SQS, Kafka, Pulsar, Azure Service Bus or Artemis) stores it untouched, with no language-specific serialization. (3) A consumer in any SDK language (Go, Python, Node.js, Java, .NET or PHP) decodes it, routes on the job URN to a handler and forwards the trace_id unchanged. Which SDK can use which broker is listed on each SDK's page; not every pairing exists.

What the contract covers

  • The envelope — the JSON document on the wire: job, trace_id, data, meta, attempts, plus the cross-language data rules (encoding, numbers, time) and the producer/consumer rules every SDK obeys.
  • URN naming — how a message is identified by a stable urn:babel:<context>:<event> string instead of a class name, so any language can route on it.

The three things every message carries

  1. Identity — what the message is: the job field, a URN, never a class name.
  2. Correlation — how to trace it across services: a required trace_id.
  3. Payload + bookkeeping — the business data, descriptive meta, and the transport attempts counter.
Anatomy of the BabelQueue envelope A JSON envelope with its five required top-level fields drawn as labelled boxes. The caption below gives the full description. JSON envelope on the wire job "urn:babel:orders:created" Identity a URN, never a class name trace_id "7b3f9c2a-e41d-4f88-9b2a-1c0d5e6f7a8b" Correlation forwarded unchanged on every hop data { "order_id": 1042, … } Payload pure JSON, no language-specific types meta idqueuelangschema_version: 1created_at Producer metadata set once, immutable schema_version 1 is frozen attempts 0 Transport state the only field that changes in flight
Anatomy of the envelope (schema_version: 1). Five required top-level fields: job is the message identity, a URN and never a class name; trace_id is the correlation id, forwarded unchanged on every hop; data is the pure JSON business payload; meta is producer metadata set once and immutable (id, queue, lang, schema_version, created_at); attempts is the transport retry counter and the only field that changes during the message's life.

Frozen at schema_version: 1

The envelope is frozen: within schema_version: 1, the existing envelope fields, their types and their meanings do not change — no field is renamed, removed or retyped, and the required fields keep their meaning. A new field may be added only as an optional, ignorable field and only through an architecture decision record and an explicit version decision. A consumer that does not understand an unknown field MUST still process the message correctly and MUST preserve that field when it re-emits the message. Renaming, removing or retyping a field requires a bump of meta.schema_version. Consumers MUST reject or quarantine any schema_version they do not understand.

This stability is the whole point — a v1 producer stays readable by every v1 consumer, in any language, indefinitely.

Cross-SDK parity

Every SDK ships against a shared suite of golden envelope fixtures: two SDKs producing the same logical message emit byte-comparable envelopes (modulo the intrinsically per-message/per-language fields meta.id, trace_id, meta.lang and meta.created_at). Parity is enforced in every SDK’s CI, not just asserted here.

Continue to The envelope.