Binary Serialization Protocol

August 10, 2026 · View on GitHub

The HiveMind binary serialization protocol (hivemind_bus_client/serialization.py) encodes HiveMessage objects into compact bitstrings for efficient WebSocket transport. This is the reference implementation. Other language clients must produce identical wire bytes.

Protocol Version

Current version: 1 (PROTOCOL_VERSION, serialization.py).

Decoding a bitstring with an unsupported version raises UnsupportedProtocolVersion (exceptions.py).

Wire Format (v1)

All fields are packed MSB-first. The bitstring is left-padded with 0 bits to reach a byte boundary. The first 1 bit marks the start of meaningful data.

┌─────────────┬───────────┬─────────────────────┬──────────┬──────────┬──────────────────┬────────────────┬──────────┐
│ Padding 0s  │ Start (1) │ Versioned (1 bit)   │ [Proto   │ Type     │ Compressed       │ Meta-length    │ Metadata │
│ (0+ bits)   │           │ 0=unversioned       │  Version │ (5 bits) │ (1 bit)          │ (8 bits = N    │ (N bytes)│
│             │           │ 1=versioned         │  8 bits] │          │ 0=raw, 1=zlib    │  bytes)        │          │
└─────────────┴───────────┴─────────────────────┴──────────┴──────────┴──────────────────┴────────────────┴──────────┘
                                                                       ┌────────────────────────────────────────────┐
                                                                       │ If Type == BINARY:                         │
                                                                       │   BinarySubtype (4 bits)                   │
                                                                       │ Payload (remaining bits)                   │
                                                                       └────────────────────────────────────────────┘

Field Details

FieldBitsDescriptionSource
Padding0+Leading 0 bits for byte alignment_get_bitstring_v1, serialization.py
Start marker1Always 1. Decoder skips 0s until it finds thisserialization.py
Versioned flag11 = next 8 bits are a protocol version, 0 = assume current versionserialization.py
Protocol version8 (optional)Only present when versioned=1. Integer protocol versionserialization.py
Message type5Index into _INT2TYPE mapping (12 assigned codes: 0-10 and 12; 11 is retired)serialization.py, _INT2TYPE, serialization.py
Compressed11 = payload is zlib-compressedserialization.py
Metadata length8Number of bytes of metadata that follow`serialization.py$
\text{Metadata}\text{N} \times 8\text{JSON}-\text{encoded} \text{dict}, \text{optionally} \text{zlib}-\text{compressed}$serialization.py`
Binary subtype4 (conditional)Only for BINARY type. Maps to HiveMindBinaryPayloadTypeserialization.py
PayloadremainingJSON string (text types) or raw bytes (BINARY type)serialization.py

Type Map (_INT2TYPE, serialization.py)

IntegerHiveMessageType
0HANDSHAKE
1BUS
2SHARED_BUS
3BROADCAST
4PROPAGATE
5ESCALATE
6HELLO
7QUERY
8CASCADE
9PING
10RENDEZVOUS
12BINARY

Code 11 was THIRDPRTY, which is removed. Do not reuse it: a new type takes the next free code from 13.

Code 11 and codes 13-31 are unassigned. Per HIVEMIND-WIRE-1 §4.2 a receiver MUST reject a frame carrying an unassigned message-type code as malformed: decode_bitstring() raises ValueError for any code not in this map instead of silently coercing it to a type. The three client receive loops (client.py, async_client.py, http_client.py) catch that error, log it, and drop the frame rather than crashing.

Spec-vs-impl note. This numbering is the wire reality retained for compatibility with deployed encoders and the HiveMind-js peer; it does not match the aspirational table in HIVEMIND-WIRE-1 §4.2 (which makes QUERY/CASCADE/RENDEZVOUS text-only wrapper types). Reconciling the two is a wire-breaking change reserved for a coordinated spec revision; only the unassigned-code rejection is enforced here.

Binary Payload Subtypes (HiveMindBinaryPayloadType, message.py)

IntegerSubtypeDescription
0UNDEFINEDNo info about binary contents
1RAW_AUDIORaw audio data
2NUMPY_IMAGEImage as numpy array
3FILEGeneric file transfer
4STT_AUDIO_TRANSCRIBEAudio for STT transcription
5STT_AUDIO_HANDLEAudio for STT + immediate handling
6TTS_AUDIOSynthesized TTS audio

Public API

get_bitstring(), serialization.py

Encode a HiveMessage to a BitArray.

from hivemind_bus_client.serialization import get_bitstring
from hivemind_bus_client.message import HiveMessageType

bitstr = get_bitstring(
    hive_type=HiveMessageType.BUS,
    payload=message,          # Message, HiveMessage, str, dict, or bytes
    compressed=None,          # None=auto (pick smaller), True/False=force
    hivemeta=None,            # optional dict of metadata
    binary_type=...,          # HiveMindBinaryPayloadType (for BINARY only)
    proto_version=1,          # protocol version
    versioned=False,          # embed version in bitstring
)
# Returns: BitArray, call .bytes for raw bytes

When compressed=None, both compressed and uncompressed encodings are generated and the smaller one is returned (serialization.py).

decode_bitstring(), serialization.py

Decode raw bytes back to a HiveMessage.

from hivemind_bus_client.serialization import decode_bitstring

hive_msg = decode_bitstring(raw_bytes)  # returns HiveMessage

BINARY_ENCODABLE_TYPES, serialization.py

The frozen set of message types that have a 5-bit wire code. INTERCOM is not in it: it has never had a code, so it can only travel as a text frame. _get_bitstring_v1 raises ValueError for a type outside this set instead of relabelling the frame, which is what it used to do. Senders check the set before they pick a framing.

mycroft2bitstring(), serialization.py

Convenience wrapper: encode an OVOS Message as a BUS-type bitstring, using msg.context as metadata.

from hivemind_bus_client.serialization import mycroft2bitstring

bitstr = mycroft2bitstring(msg, compressed=False)

Auto-Compression

get_bitstring(compressed=None) encodes the payload both ways and returns whichever is smaller. The compressed bit in the wire format tells the decoder which path to take. For small payloads, uncompressed is often smaller. For payloads over ~200 bytes, zlib compression typically wins.

Cross-Language Implementors

To implement a compatible encoder/decoder:

  1. Pack fields in the exact order and bit widths above
  2. Use zlib (RFC 1950) for compression
  3. JSON-encode metadata and non-binary payloads as UTF-8 before compression
  4. Left-pad with 0 bits to byte-align, then prepend a 1 start marker
  5. For BINARY messages, the 4-bit subtype appears before the raw payload bytes
  6. Test against the Python reference implementation using the cross-platform vectors that HiveMind-js/test/generate_vectors.py produces. tests/test_serialization.py loads them and skips when the file is absent

← Message Types · Home · Binary Handlers →