Expand description
§AATP — AI Agent Transport Protocol
A tiny, dependency-free binary framing protocol for LLM agents — a compact binary
alternative to text-based RPC that is structurally cheaper to parse (no tokenizing, no
per-field allocation) and memory-safe by construction (#![forbid(unsafe_code)],
no_std-compatible). A frame is a fixed shape:
[ Magic "AATP" (4) ][ Version (1) ][ Type (1) ][ Length (4, LE) ][ Payload (N) ][ CRC32C (4, LE) ]Zero-copy / zero-allocation. decode borrows the payload straight out of
the input buffer (&[u8], no heap copy); encode writes into a caller-owned
&mut [u8] (no allocation). Transmute-casting a byte slice to a repr(C) struct
would need unsafe; this crate forbids it and reads each field in place with
u32::from_le_bytes behind explicit bounds guards instead — the safe form of
zero-copy.
Hardened. Every field is bounds-checked with slice::get (a range read that
fails closed rather than panics); a truncated frame, a bad magic, an unsupported
version, an over-large declared length, or a corrupt payload each return a
distinct Error — never a panic, never an out-of-bounds read.
The CRC32C detects accidental corruption, not a malicious sender: an attacker can recompute a valid checksum for any payload, so it is integrity-against-noise, not authentication. Pair AATP with a MAC/AEAD if you need to trust the peer.
Modules§
- codec
- Ergonomic, bounds-checked cursors for AATP payloads — the safe alternative to
hand-indexing bytes.
Writerappends typed fields into a caller-owned buffer;Readerpulls them back, borrowing strings and byte slices zero-copy. Both fail closed (never panic, never over-read). Theaatp_message!macro composes these into a whole-struct codec, so a message type getsencode/decodefor free — the derive-style ergonomics of the field, with zero dependencies. - dict
- Adaptive payload compression via a compile-time dictionary of the tokens
agents repeat — tool names, roles, stock phrases. Each dictionary entry gets a
one-byte code, so a 12-byte
"orchestrator"collapses to two bytes on the wire. - message
- A complex agent-message payload for AATP — the shape an LLM agent actually
ships: a message id, a role byte, a tool/agent name, free-text content, and a
token count. Encoded as a compact little-endian binary record with
length-prefixed strings, and decoded zero-copy:
nameandcontentare borrowed&strslices validated in place, no allocation. - shadow
- A reversible XOR keystream layer — obfuscation, NOT encryption.
- state
- Protocol state machine — the layer that validates a sequence of frames, not
just each frame. A session walks
Handshake → Active → Closed; anything out of order (a data frame before the handshake, traffic after close) is rejected, so two agents can never drift out of sync.
Macros§
- aatp_
message - Define an agent-message type and generate its zero-copy binary codec — the
derive-style ergonomics of the field, with zero dependencies. Fields are declared
with a small type vocabulary (
u8/u16/u32/u64/str/bytes), and the macro emits the struct plusencode/decodebuilt oncodec::Writer/codec::Reader, so no byte is hand-indexed:
Structs§
- Packet
- A decoded frame. Borrows its payload from the input buffer — decoding allocates nothing.
Enums§
- Error
- Everything that can go wrong reading or writing a frame. Each variant is a distinct fail-closed outcome.
Constants§
- CHECKSUM_
LEN - The trailing CRC32C, in bytes.
- HEADER_
LEN - Bytes before the payload: magic (4) + version (1) + type (1) + length (4).
- MAGIC
- The 4-byte frame magic: ASCII
AATP. - MAX_
PAYLOAD - The largest payload a frame may declare or carry — the guard against a hostile
length field asking us to address gigabytes. 16 MiB is far above any real agent
message and keeps
HEADER_LEN + length + CHECKSUM_LENwell inside a 32-bitusize, so the frame arithmetic can never overflow. - OVERHEAD
- Non-payload bytes in a frame:
HEADER_LEN+CHECKSUM_LEN. - VERSION
- The only wire version this build speaks.
Functions§
- crc32c
- CRC32C (Castagnoli, reflected poly
0x82F6_3B78, init/xorout0xFFFF_FFFF) — the checksum of iSCSI and SSE4.2’scrc32instruction, here table-driven (one byte per step). Pinned by the standard"123456789" -> 0xE306_9283known-answer vector. - decode
- Decode one frame from the front of
buf, borrowing its payload.bufmay be longer than the frame (a stream): onlyHEADER_LEN + length + CHECKSUM_LENbytes are consumed and validated. Every access is a fail-closedslice::get; the CRC32C is verified last. - encode
- Encode a
(msg_type, payload)intoout, returning the number of bytes written. Allocation-free: the frame is laid down directly in the caller’s buffer.