Binary Protocol

AWS SSM binary message format (116-byte header).

Table of contents

  1. Message Format
    1. Field Specifications
  2. Usage
    1. Creating Messages
    2. Serialization
    3. Deserialization
  3. Payload Types
    1. Control Flags (PayloadType::Flag)
  4. Message Flags
  5. SHA-256 Digest
  6. Performance
  7. Reference

Message Format

Messages use a 120-byte total header (4-byte HL field + 116-byte fixed header):

| HL(4) | MessageType(32) | Ver(4) | CD(8) | Seq(8) | Flags(8) |
| MessageId(16) | PayloadDigest(32) | PayType(4) | PayLen(4) |
| Payload(variable) |

Field Specifications

Field Size Type Description
HeaderLength 4 bytes u32 Always 116 (excludes HL field)
MessageType 32 bytes UTF-8 Space-padded string
SchemaVersion 4 bytes u32 Protocol version (1)
CreatedDate 8 bytes u64 Unix timestamp (milliseconds)
SequenceNumber 8 bytes i64 Message sequence number
Flags 8 bytes u64 Bit flags (SYN=1, FIN=2)
MessageId 16 bytes UUID Binary UUID (no hyphens)
PayloadDigest 32 bytes [u8; 32] SHA-256 hash
PayloadType 4 bytes u32 Enum 1-12
PayloadLength 4 bytes u32 Payload byte count
Payload variable bytes Raw payload data

Byte Order: Big-endian (network byte order)


Usage

Creating Messages

use aws_ssm_bridge::binary_protocol::{ClientMessage, PayloadType};
use aws_ssm_bridge::protocol::MessageType;
use bytes::Bytes;

let payload = Bytes::from("ls -la\n");
let message = ClientMessage::new(
    MessageType::InputStreamData,
    1,  // sequence_number
    PayloadType::Output,
    payload,
);
// SHA-256 digest is automatically computed
// UUID and timestamp are automatically generated

Serialization

let binary_data = message.serialize()?;
websocket.send(binary_data).await?;

Deserialization

use bytes::Bytes;

let received = websocket.recv().await?;
let message = ClientMessage::deserialize(Bytes::from(received))?;

// Validation is automatic:
// - Header length check
// - Payload length vs actual payload
// - SHA-256 digest verification

Payload Types

All 12 AWS payload types are supported:

pub enum PayloadType {
    Output = 1,              // stdout
    Error = 2,               // stderr
    Size = 3,                // terminal resize
    Parameter = 4,           // session parameters
    HandshakeRequest = 5,    // handshake from agent
    HandshakeResponse = 6,   // handshake from client
    HandshakeComplete = 7,   // handshake done
    EncChallengeRequest = 8, // encryption challenge
    EncChallengeResponse = 9,// encryption response
    Flag = 10,               // control flags
    StdErr = 11,             // explicit stderr
    ExitCode = 12,           // process exit code
}

Control Flags (PayloadType::Flag)

pub enum PayloadTypeFlag {
    DisconnectToPort = 1,    // TCP connection closed
    TerminateSession = 2,    // End session
    ConnectToPortError = 3,  // Port unavailable
}

Message Flags

Bit flags for stream boundaries:

pub mod flags {
    pub const SYN: u64 = 1 << 0;  // First message
    pub const FIN: u64 = 1 << 1;  // Last message
}

message.flags = flags::SYN;              // First message
message.flags = flags::FIN;              // Last message
message.flags = flags::SYN | flags::FIN; // Single-message stream

SHA-256 Digest

The payload digest is automatically computed during message creation and automatically validated during deserialization.

// Automatic on creation
let message = ClientMessage::new(...);
// message.payload_digest = SHA256(payload)

// Automatic on deserialization
let parsed = ClientMessage::deserialize(data)?;
// Returns error if digest doesn't match

Performance

SHA-256 computation (using aws-lc-rs):

Payload Time Throughput
1 KB ~5 µs 200+ MiB/s
16 KB ~50 µs 300+ MiB/s

The library uses bytes::Bytes for zero-copy buffer management.


Reference


Copyright © 2026. Distributed under the MIT license.