# 3-RRC: Wire Encoding, Constants, and Numeric Assignments _Version: 0.1.1_ This document exists so that two people, writing two separate RRC implementations, do not accidentally invent two different protocols while both claiming success. 1-RRC explained what the system is. 2-RRC explained what messages exist and how sessions behave. This document locks down the numbers, field keys, and expectations that appear on the wire so that CBOR blobs mean the same thing everywhere. If you skip this document, your implementation will still work, but only with itself. That is not a victory. ## Canonical Encoding Rules All RRC messages are encoded as CBOR maps. The map keys are unsigned integers. String keys are not allowed. This is not because strings are evil, but because numeric keys are smaller, faster to parse, and harder to misspell. All integer values are encoded as unsigned integers unless explicitly stated otherwise. Binary values are encoded as CBOR byte strings. Human-readable text is encoded as CBOR text strings using UTF-8. Ordering of keys in the CBOR map does not matter. Receivers must not assume any specific ordering and must not reject messages solely because the sender arranged keys differently. Unknown keys must be ignored. This is not optional. If your implementation explodes because it saw a field it did not understand, that is your fault. ## Envelope Structure Every RRC message shares the same top-level envelope. This envelope is a CBOR map containing the following fields. ### Field 0 - Protocol Version This value identifies the RRC protocol version used to encode the message. For this specification, the value must be 1. Messages with other versions may be ignored or rejected. ### Field 1 - Message Type This is an unsigned integer identifying what kind of message this is. The numeric values for message types are defined later in this document. ### Field 2 - Message Identifier This is a byte string chosen by the sender. It must be unique within the scope of the sender’s current session. It does not need to be globally unique, cryptographically strong, or persistent across reconnects. It exists so implementations can correlate messages if they care to. ### Field 3 - Timestamp This is an unsigned integer representing milliseconds since the Unix epoch, according to the sender’s clock. It is advisory only. No one is expected to agree on time. ### Field 4 - Sender Identity This is a byte string containing the Reticulum identity hash of the sender. This field is mandatory even though the Link already authenticates the peer, because forwarded messages need an explicit source identifier. ### Field 5 - Room Name This is a text string identifying the room the message applies to. If a message does not apply to a room, this field may be omitted. ### Field 6 - Body This field contains the payload of the message. Its structure and meaning depend entirely on the message type. ### Field 7 - Nickname This is an optional text string containing a human-readable nickname for the sender. It is advisory only. It may be omitted, empty, ridiculous, or all three. The hub may ignore it, sanitize it, replace it, or drop it on the floor entirely. Clients may display it if present, or may choose to rely on identity hashes, locally cached labels, or whatever else they think is a good idea. If both sides send this field, they are not negotiating. They are making suggestions. No other top-level fields are defined by this document. Envelope field keys 0 through 39 are reserved for core protocol use. Extensions may define additional top-level fields using keys 40 through 63. Keys 64 and above are reserved for future specifications, which is a polite way of saying "don’t squat there unless you enjoy avoidable pain." ## Message Type Assignments Message type values are fixed. They are not suggestions. | Type | Name | | ---- | ------- | | 1 | HELLO | | 2 | WELCOME | | 10 | JOIN | | 11 | JOINED | | 12 | PART | | 13 | PARTED | | 20 | MSG | | 21 | NOTICE | | 30 | PING | | 31 | PONG | | 40 | ERROR | ### 0 - 9: Link Control Messages #### Type 0 - RESERVED Type 0 is reserved and must not be used. #### Type 1 - HELLO The `HELLO` message uses type 1. This message is sent by the client to announce itself after a Link is established. #### Type 2 - WELCOME The `WELCOME` message uses type 2. This message is sent by the hub to acknowledge and accept the session. ### 10 - 19: Room Membership Messages #### Type 10 - JOIN The `JOIN` message uses type 10. This message is sent by the client to request entry into a room. #### Type 11 - JOINED The `JOINED` message uses type 11. This message is sent by the hub to confirm room membership. #### Type 12 - PART The `PART` message uses type 12. This message is sent by the client to leave a room. #### Type 13 - PARTED The `PARTED` message uses type 13. This message is sent by the hub to confirm that a client has left a room. ### 20 - 29: Messages Intended for Rooms and People #### Type 20 - MSG The `MSG` message uses type 20. This message carries chat content intended for a room. #### Type 21 - NOTICE The `NOTICE` message uses type 21. This message carries informational or non-conversational content. ### 30 - 39: Link Management Messages #### Type 30 - PING The `PING` message uses type 30. This message checks whether the peer is still responsive. #### Type 31 - PONG The `PONG` message uses type 31. This message is the response to a `PING`. ### 40 and Up: Error and Status Messages #### Type 40 - ERROR The `ERROR` message uses type 40. This message reports a failure or refusal. Values from 0 to 63 are reserved for core protocol use. Values from 64 upward are available for extensions, experiments, and future specifications. If you collide with yourself there, that is your own adventure. ## Message Body Definitions The body field exists so message-specific data does not leak into the envelope and turn everything into a special case. For `HELLO`, the body is a CBOR map with unsigned integer keys. It may contain session-specific data used for exchanging version information or program capabilities, as well as human-facing metadata. All body fields are advisory and optional. Any human-facing metadata is considered an extension of the protocol and is outside the scope of this specification. `HELLO` body key assignments: | Key | Name | Type | Description | | --- | -------------- | ----------- | -------------------------------- | | 0 | Client Name | text string | Client software name (optional) | | 1 | Client Version | text string | Client version string (optional) | | 2 | Capabilities | map/array | Capability hints (optional) | For `WELCOME`, the body is also a CBOR map with unsigned integer keys. It may contain session-specific data used for exchanging version information or program capabilities, as well as human-facing metadata. All body fields are advisory and optional. Any human-facing metadata is considered an extension of the protocol and is outside the scope of this specification. `WELCOME` body key assignments: | Key | Name | Type | Description | | --- | ------------ | ----------- | --------------------------- | | 0 | Hub Name | text string | Human-friendly hub name | | 1 | Hub Version | text string | Hub version string (optional) | | 2 | Capabilities | map/array | Capability hints (optional) | Unknown body keys must be ignored. Keys 0 through 63 are reserved for core body fields defined by this document. Keys 64 and above may be used for extensions. For `JOIN`, the body is empty or omitted. The room name is taken from the envelope field. For `JOINED`, the body may contain a list of current members. If present, this list is advisory and not guaranteed to be complete or current. For `PART`, the body is empty or omitted. For `PARTED`, the body may be empty, omitted, or may contain a list of remaining members. If you are hoping for perfect presence, you are in the wrong document. For `MSG`, the body contains the message payload. The simplest and most common form is a text string. Implementations may choose to use structured payloads, but hubs must treat the body as opaque data and forward it unchanged. For `NOTICE`, the body follows the same rules as `MSG` but carries a different semantic meaning. For `PING` and `PONG`, the body may be omitted or may contain arbitrary data to allow round-trip correlation. Receivers must echo the body back unchanged in the corresponding response if it is present. For `ERROR`, the body should be a text string describing the error in plain language. It may optionally be a CBOR map if structured error information is desired, but clients must handle simple text at minimum. ## Room Name Normalization Room names are encoded as text strings. Hubs should normalize room names internally, typically by converting them to lowercase. The exact normalization method is an implementation detail, but the observable behavior should be case-insensitive room matching. Clients should not assume that room names preserve original casing. ## Identity Encoding The sender identity field contains the Reticulum identity hash encoded as a CBOR byte string. No additional structure is imposed by RRC. Clients and hubs must not reinterpret, truncate, or re-encode this value. It is passed through as-is and treated as opaque data. ## Error Handling on the Wire Malformed messages may be ignored. Messages with unknown message types must be ignored. Messages with missing required envelope fields may be ignored or may trigger an ERROR, at the receiver’s discretion. If a hub sends an ERROR and then closes the Link, that is considered valid behavior. Clients should not attempt to argue with a closed socket. ## Forward Compatibility Rules Implementations must follow three simple rules to remain compatible over time. They must ignore unknown envelope keys. They must ignore unknown message types. They must not repurpose existing numeric assignments. Breaking any of these rules means you are no longer speaking RRC, regardless of how confident you feel. ## Tabular Examples The following table summarizes the structure of RRC messages. | Field Number | Field Name | Description | | ------------ | ---------------- | --------------------------------------------------------------- | | 0 | Protocol Version | Unsigned integer identifying the protocol version | | 1 | Message Type | Unsigned integer identifying the message type | | 2 | Message ID | Byte string chosen by the sender | | 3 | Timestamp | Unsigned integer representing milliseconds since the Unix epoch | | 4 | Sender Identity | Byte string containing the Reticulum identity hash | | 5 | Room Name | Text string identifying the room (optional) | | 6 | Body | Message-specific payload (structure depends on message type) | | 7 | Nickname | Optional, advisory human-readable sender nickname | --- The following table summarizes the message type assignments (Field 1). | Type | Name | Description | | ---- | -------- | ---------------------------------------- | | 0 | RESERVED | Reserved; must not be used | | 1 | HELLO | Client announces itself to the hub | | 2 | WELCOME | Hub acknowledges and accepts the session | | 10 | JOIN | Client requests entry into a room | | 11 | JOINED | Hub confirms room membership | | 12 | PART | Client leaves a room | | 13 | PARTED | Hub confirms room departure | | 20 | MSG | Chat message intended for a room | | 21 | NOTICE | Informational message | | 30 | PING | Check whether the peer is responsive | | 31 | PONG | Response to a PING | | 40 | ERROR | Report a failure or refusal | --- The following table is an example of a client sending a chat message to a room. | Field Number | Field Name | Value | | ------------ | ---------------- | --------------------------- | | 0 | Protocol Version | 1 | | 1 | Message Type | 20 (MSG) | | 2 | Message ID | b"\x01\x02\x03\x04" | | 3 | Timestamp | 1700000000000 | | 4 | Sender Identity | b"\xAA\xBB\xCC\xDD\xEE\xFF" | | 5 | Room Name | "#general" | | 6 | Body | "Hello, world!" | | 7 | Nickname | "alice" |