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.
| Byte(s) | Field | Value |
|---|
0-1 | Magic | 0xAC 0x01 |
2 | Version | 0x01 |
3 | Type | Message type byte |
4-7 | Length | Payload size (u32 big-endian) |
8+ | Payload | MessagePack-encoded (up to 4 MB) |
| Field | Offset | Size | Description |
|---|
| Magic | 0 | 2 bytes | 0xAC 0x01 — identifies Atlas protocol frames |
| Version | 2 | 1 byte | Protocol version, currently 0x01 |
| Type | 3 | 1 byte | Message type discriminator (see Messages) |
| Length | 4 | 4 bytes | Payload length in bytes (big-endian u32) |
| Payload | 8 | variable | MessagePack-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:
- Serialize the message struct to MessagePack using
rmp_serde::to_vec_named
- Check that the payload size does not exceed
MAX_PAYLOAD_SIZE (4 MB)
- Write the 8-byte header: magic (2) + version (1) + type byte (1) + length (4 big-endian)
- 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:
- Verify at least 8 bytes are available
- Check magic bytes match
0xAC 0x01
- Check protocol version is
0x01
- Parse the message type byte
- Read the 4-byte length and verify it does not exceed
MAX_PAYLOAD_SIZE
- Verify total frame size matches
8 + length
- Deserialize the payload with
rmp_serde::from_slice
Wire errors
| Error | Condition |
|---|
BadMagic | First 2 bytes are not 0xAC 0x01 |
UnsupportedVersion | Version byte is not 0x01 |
Truncated | Less than 8 bytes received |
UnknownMessageType | Type byte does not map to a known message |
PayloadTooLarge | Payload exceeds 4 MB |
LengthMismatch | Actual frame size does not match header length |
Codec | MessagePack 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