Skip to main content

Wire Envelope

Every message in the Atlas protocol is wrapped in a binary envelope. The envelope provides framing, versioning, and type discrimination for the MessagePack-encoded payload.

Envelope format

Byte(s)FieldValue
0-1Magic0xAC 0x01
2Version0x01
3TypeMessage type byte
4-7LengthPayload size (u32 big-endian)
8+PayloadMessagePack-encoded (up to 4 MB)
FieldOffsetSizeDescription
Magic02 bytes0xAC 0x01 — identifies Atlas protocol frames
Version21 byteProtocol version, currently 0x01
Type31 byteMessage type discriminator (see Messages)
Length44 bytesPayload length in bytes (big-endian u32)
Payload8variableMessagePack-encoded message body

Constants

pub const MAGIC: [u8; 2] = [0xAC, 0x01];
pub const PROTOCOL_VERSION: u8 = 1;
pub const MAX_PAYLOAD_SIZE: usize = 4 * 1024 * 1024; // 4 MB

Encoding

To encode a message:
  1. Serialize the message struct to MessagePack using rmp_serde::to_vec_named
  2. Check that the payload size does not exceed MAX_PAYLOAD_SIZE (4 MB)
  3. Write the 8-byte header: magic (2) + version (1) + type byte (1) + length (4 big-endian)
  4. Append the MessagePack payload
pub fn encode<T: Serialize>(
    message_type: MessageType,
    value: &T,
) -> Result<Vec<u8>, WireError> {
    let payload = rmp_serde::to_vec_named(value)?;
    if payload.len() > MAX_PAYLOAD_SIZE {
        return Err(WireError::PayloadTooLarge(payload.len()));
    }
    let mut buffer = BytesMut::with_capacity(8 + payload.len());
    buffer.extend_from_slice(&MAGIC);
    buffer.put_u8(PROTOCOL_VERSION);
    buffer.put_u8(message_type as u8);
    buffer.put_u32(payload.len() as u32);
    buffer.extend_from_slice(&payload);
    Ok(buffer.to_vec())
}

Decoding

To decode an envelope:
  1. Verify at least 8 bytes are available
  2. Check magic bytes match 0xAC 0x01
  3. Check protocol version is 0x01
  4. Parse the message type byte
  5. Read the 4-byte length and verify it does not exceed MAX_PAYLOAD_SIZE
  6. Verify total frame size matches 8 + length
  7. Deserialize the payload with rmp_serde::from_slice

Wire errors

ErrorCondition
BadMagicFirst 2 bytes are not 0xAC 0x01
UnsupportedVersionVersion byte is not 0x01
TruncatedLess than 8 bytes received
UnknownMessageTypeType byte does not map to a known message
PayloadTooLargePayload exceeds 4 MB
LengthMismatchActual frame size does not match header length
CodecMessagePack serialization/deserialization failure
The 4 MB payload limit applies to a single message. MessagePack is a binary format, so payloads are typically compact. The limit prevents memory exhaustion from malformed or malicious frames.

Serialization

Atlas uses MessagePack (rmp_serde) with named fields (to_vec_named / from_slice). This means field names are included in the serialized output, providing forward compatibility when new optional fields are added. Key properties:
  • Binary format (more compact than JSON)
  • Named fields (self-describing, unlike positional MessagePack)
  • No code generation required (no protobuf, no schema files)
  • Round-trip compatible with serde Serialize/Deserialize traits