Docs Menu

Specs

spec/01-messaging.md · shown exactly as it appears in the repository

01: Messaging

Status: Draft, v0.1. License: This text is CC-BY-4.0; the field names, enum values, source code and other machine-readable schema elements defined here are also MPL-2.0, see LICENSE-DOCS and LICENSE.

Overview

Every exchange between two connected parties, whether establishing the connection, extending it to a new domain, granting a subscription, or sharing content, travels as a message: a single envelope addressed to a sendKey. This chapter defines that envelope and how an implementation routes an incoming message to the code that handles it, not what any particular message means. Each other chapter defines that for the message types it owns.

The envelope

A message has two parts: an envelope, sent in the clear, and a payload, always encrypted before it leaves the sender (see 07-security.md for the cryptography). The payload is opaque to the router: only the two connected parties can read it.

The envelope carries:

Field Purpose
version Envelope format version.
messageId Unique identifier for this message, used for deduplication (below).
sendKey The key this message is addressed to.
domain Which of the connection's domains this message belongs to.

The payload carries type, operation, and messageContent, covered next.

Message types and operations

type identifies which generic category a message belongs to, and MUST be one of a protocol-defined set: connection, subscription, content, or command. operation identifies what's being done within that type (e.g. subscribe, cxnEstablished, create), and is likewise protocol-defined, not something a domain invents. This is different from a domain's own content types (see 05-content-model.md), which are domain-defined.

Planned extension (non-normative preview): both sets are closed in the current protocol version; a future version may open operation, and possibly type, to domain extension.

messageContent carries the data for that type/operation pair: an array of content items, shaped by whichever chapter owns the type, or by the domain's own schema for content messages.

Deduplication

messageId MUST be unique per message. An implementation MUST treat a message whose messageId has already been seen for that sendKey as already delivered, not reprocess it. This is a generic, envelope-level guard against at-least-once redelivery, independent of the message's type/operation/messageContent. In the reference implementation this check happens at the receiving agent, which keeps a bounded, per-sendKey window of recently-seen messageIds; the router doesn't check messageId itself, it relays the resulting 409 back to the sender.

It's a cheaper safety net, not the only one; content messages carry a stronger ordering guarantee built on shareSequence and version (see 06-propagation.md).

Handler dispatch

An implementation MUST register exactly one handler per type before processing messages of that type; dispatching a message whose type has no registered handler MUST fail, not be silently dropped. A handler has the shape (context, connection, operation, messageContent) => ...; by the time it's invoked, the connection the message's sendKey resolves to has already been found and passed in. The handler, not the dispatch mechanism, interprets operation and decides what to do with messageContent.

Exception: the first handshake message (connection / cxnEstablished, see 02-connections.md) is handled specially, before type-based dispatch: no connection exists yet to resolve it against.