Apache Iggy
Binary Protocol

Commands

Catalog

The u32 command codes. Replicated commands are identified on the wire by their operation byte (see Operation discriminants). The code column below is the protocol-level registry and, for non-replicated commands, the value carried in header bytes 196..200.

# System
PING = 1                                  # non-replicated; works without login
GET_STATS = 10                            # non-replicated
GET_SNAPSHOT = 11                         # non-replicated
GET_CLUSTER_METADATA = 12                 # non-replicated
DESCRIBE_OPTIONS = 13                     # non-replicated
GET_ME = 20                               # non-replicated
GET_CLIENT = 21                           # non-replicated
GET_CLIENTS = 22                          # non-replicated

# Users
GET_USER = 31                             # non-replicated
GET_USERS = 32                            # non-replicated
CREATE_USER = 33                          # operation 141
DELETE_USER = 34                          # operation 143
UPDATE_USER = 35                          # operation 142
UPDATE_PERMISSIONS = 36                   # operation 145
CHANGE_PASSWORD = 37                      # operation 144
LOGIN_USER = 38                           # legacy; server rejects with MalformedLogin eviction
LOGOUT_USER = 39                          # operation 3 (Logout)
LOGIN_REGISTER = 40                       # operation 1 (Register)
LOGIN_REGISTER_WITH_PAT = 45              # operation 1 (Register)

# Personal access tokens
GET_PERSONAL_ACCESS_TOKENS = 41           # non-replicated
CREATE_PERSONAL_ACCESS_TOKEN = 42         # operation 146
DELETE_PERSONAL_ACCESS_TOKEN = 43         # operation 147
LOGIN_WITH_PERSONAL_ACCESS_TOKEN = 44     # legacy; server rejects with MalformedLogin eviction

# Messages
POLL_MESSAGES = 100                       # non-replicated
SEND_MESSAGES = 101                       # operation 160
FLUSH_UNSAVED_BUFFER = 102                # parses, but always fails with FeatureUnavailable

# Consumer offsets
GET_CONSUMER_OFFSET = 120                 # non-replicated
STORE_CONSUMER_OFFSET = 121               # operation 161
DELETE_CONSUMER_OFFSET = 122              # operation 162

# Streams
GET_STREAM = 200                          # non-replicated
GET_STREAMS = 201                         # non-replicated
CREATE_STREAM = 202                       # operation 128
DELETE_STREAM = 203                       # operation 130
UPDATE_STREAM = 204                       # operation 129
PURGE_STREAM = 205                        # operation 131

# Topics
GET_TOPIC = 300                           # non-replicated
GET_TOPICS = 301                          # non-replicated
CREATE_TOPIC = 302                        # operation 132
DELETE_TOPIC = 303                        # operation 134
UPDATE_TOPIC = 304                        # operation 133
PURGE_TOPIC = 305                         # operation 135

# Partitions
CREATE_PARTITIONS = 402                   # operation 136
DELETE_PARTITIONS = 403                   # operation 137

# Segments
DELETE_SEGMENTS = 503                     # operation 138

# Consumer groups
GET_CONSUMER_GROUP = 600                  # non-replicated
GET_CONSUMER_GROUPS = 601                 # non-replicated
CREATE_CONSUMER_GROUP = 602               # operation 139
DELETE_CONSUMER_GROUP = 603               # operation 140
JOIN_CONSUMER_GROUP = 604                 # operation 148
LEAVE_CONSUMER_GROUP = 605                # operation 149
SYNC_CONSUMER_GROUP = 606                 # non-replicated

FLUSH_UNSAVED_BUFFER still decodes on the wire, but the server has no on-demand flush primitive and answers every call with FeatureUnavailable. Per-topic durability is configured with the enforce_fsync topic option instead.

Payloads

Payloads below are the request body (the bytes after the 256-byte header). Types reference the shared encodings. Empty payload means the body has zero bytes.

System

Ping. Code: 1. Empty payload. The only command accepted before login.

Get stats. Code: 10. Empty payload.

Get snapshot. Code: 11.

[compression: u8][types_count: u8][snapshot_type: u8] x types_count

Compression codes: 1 = Stored, 2 = Deflated, 3 = Bzip2, 4 = Zstd, 5 = Lzma, 6 = Xz. Snapshot type codes: 1 = FilesystemOverview, 2 = ProcessList, 3 = ResourceUsage, 4 = Test, 5 = ServerLogs, 6 = ServerConfig, 100 = All.

Get cluster metadata. Code: 12. Empty payload.

Describe options. Code: 13.

[scope: u8]

Scope: 1 = topic, 2 = stream, 3 = user. Returns the option catalog for the scope: keys, value kinds, defaults, and descriptions. See Options block.

Get me. Code: 20. Empty payload.

Get client. Code: 21.

[client_id: u32]

Get clients. Code: 22. Empty payload.

Streams

Get stream. Code: 200.

[stream_id: Identifier]

Get streams. Code: 201. Empty payload.

Create stream. Code: 202.

[name_len: u8][name: N][options block to end]

The stream option catalog is empty today, so the block is normally empty (zero bytes).

Delete stream. Code: 203.

[stream_id: Identifier]

Update stream. Code: 204.

[stream_id: Identifier][name_len: u8][name: N][options block to end]

Patch semantics: absent option keys are left unchanged.

Purge stream. Code: 205.

[stream_id: Identifier]

Topics

Get topic. Code: 300.

[stream_id: Identifier][topic_id: Identifier]

Get topics. Code: 301.

[stream_id: Identifier]

Create topic. Code: 302.

[stream_id: Identifier][partitions_count: u32][name_len: u8][name: N][options block to end]

The fixed fields are the shape of the operation: which stream, how many partitions, what name. Every topic setting (compression_algorithm, message_expiry, max_topic_size, segment_size, enforce_fsync, messages_required_to_save, size_of_messages_required_to_save, preallocate_segments) rides the options block. See Topic options. partitions_count is an argument, not a setting: it is consumed at admission and never persisted as an option.

Delete topic. Code: 303.

[stream_id: Identifier][topic_id: Identifier]

Update topic. Code: 304.

[stream_id: Identifier][topic_id: Identifier][name_len: u8][name: N][options block to end]

Only compression_algorithm, message_expiry, and max_topic_size are updatable. Absent keys are left unchanged.

Purge topic. Code: 305.

[stream_id: Identifier][topic_id: Identifier]

Partitions

Create partitions. Code: 402.

[stream_id: Identifier][topic_id: Identifier][partitions_count: u32]

Delete partitions. Code: 403.

[stream_id: Identifier][topic_id: Identifier][partitions_count: u32]

Segments

Delete segments. Code: 503.

[stream_id: Identifier][topic_id: Identifier][partition_id: u32][segments_count: u32]

Deletes the segments_count oldest sealed segments of the partition.

Messages

Poll messages. Code: 100.

[consumer: Consumer][stream_id: Identifier][topic_id: Identifier]
[partition_flag: u8][partition_id: u32]
[strategy: 9 bytes][count: u32][auto_commit: u8]

strategy is a polling strategy. count is the requested number of messages. auto_commit = 1 stores the consumer offset server-side as part of the poll.

The response body:

[partition_id: u32][current_offset: u64][messages_count: u32]
[batch records to end]

The 16-byte prefix is followed by a stream of batch records served as stored: each record's header carries the stamped base_offset and base_timestamp, and each frame's deltas resolve against them. A record may be a server-sliced view of a larger stored batch, so the first polled offset is base_offset + offset_delta of the first frame, not necessarily base_offset itself. current_offset is the partition's newest offset at poll time.

Send messages. Code: 101.

[metadata_length: u32]
[stream_id: Identifier][topic_id: Identifier][partitioning: Partitioning]
[messages_count: u32]
[batch: 256-byte batch header + frames]

metadata_length counts the bytes from stream_id through messages_count inclusive, so a reader can skip straight to the batch. The producer leaves partition_id, base_offset, and base_timestamp zero in the batch header, and the server stamps them. Every checksum is producer-computed and verified at admission. The reserved regions must be zero.

Flush unsaved buffer. Code: 102.

[stream_id: Identifier][topic_id: Identifier][partition_id: u32][fsync: u8]

Parses, but the server always answers FeatureUnavailable: there is no on-demand flush primitive. Use the per-topic enforce_fsync option for durability guarantees.

Consumer offsets

Get consumer offset. Code: 120.

[consumer: Consumer][stream_id: Identifier][topic_id: Identifier]
[partition_flag: u8][partition_id: u32]

Response body (20 bytes):

[partition_id: u32][current_offset: u64][stored_offset: u64]

Store consumer offset. Code: 121.

[consumer: Consumer][stream_id: Identifier][topic_id: Identifier]
[partition_flag: u8][partition_id: u32][offset: u64][ack: u8]

Delete consumer offset. Code: 122.

[consumer: Consumer][stream_id: Identifier][topic_id: Identifier]
[partition_flag: u8][partition_id: u32][ack: u8]

The trailing ack byte is mandatory on both write commands. A payload without it fails to decode. Ack levels:

ValueLevelMeaning
0NoAckLeader-local write; respond as soon as local state is updated (the fast path PollMessages auto-commit uses)
1QuorumReplicate through partition consensus; respond after a quorum commit (default for explicit writes)

Consumer groups

Get consumer group. Code: 600.

[stream_id: Identifier][topic_id: Identifier][group_id: Identifier]

Get consumer groups. Code: 601.

[stream_id: Identifier][topic_id: Identifier]

Create consumer group. Code: 602.

[stream_id: Identifier][topic_id: Identifier][name_len: u8][name: N]

Delete consumer group. Code: 603.

[stream_id: Identifier][topic_id: Identifier][group_id: Identifier]

Join consumer group. Code: 604.

[stream_id: Identifier][topic_id: Identifier][group_id: Identifier]

Leave consumer group. Code: 605.

[stream_id: Identifier][topic_id: Identifier][group_id: Identifier]

Sync consumer group. Code: 606.

[stream_id: Identifier][topic_id: Identifier][group_id: Identifier]

Read-only: a member asks for its current partition assignment and the group generation, so it can select partitions to poll client-side.

Users

Get user. Code: 31.

[user_id: Identifier]

Get users. Code: 32. Empty payload.

Create user. Code: 33.

[username_len: u8][username: N][password_len: u8][password: N][status: u8]
[has_permissions: u8][permissions_len: u32, only when has_permissions = 1][permissions: M bytes]
[options block to end]

permissions_len and the permissions bytes are present only when has_permissions = 1. When it is 0 the options block follows immediately. The user option catalog is empty today, so the block is normally empty. status: 1 = active, 2 = inactive.

Delete user. Code: 34.

[user_id: Identifier]

Update user. Code: 35.

[user_id: Identifier]
[has_username: u8][username_len: u8, only when 1][username: N, only when 1]
[has_status: u8][status: u8, only when 1]
[options block to end]

Update permissions. Code: 36.

[user_id: Identifier][has_permissions: u8]
[permissions_len: u32, only when has_permissions = 1][permissions: M bytes, only when 1]

As in CreateUser, the length field is conditional: with has_permissions = 0 the payload ends at the flag.

Change password. Code: 37.

[user_id: Identifier]
[current_password_len: u8][current_password: N]
[new_password_len: u8][new_password: N]

Authentication

Login and logout are part of the connection lifecycle:

  • Login register. Code: 40. Operation::Register. The body is ClientVersionInfo + credentials, described in Login-register.
  • Login register with PAT. Code: 45. Operation::Register. The body is ClientVersionInfo + token.
  • Logout user. Code: 39. Operation::Logout. Empty payload.
  • Codes 38 and 44 are the pre-register login shapes. The server rejects both with a MalformedLogin eviction.

Personal access tokens

Get personal access tokens. Code: 41. Empty payload.

Create personal access token. Code: 42.

[name_len: u8][name: N][expiry: u64]

expiry is a duration in microseconds. Two sentinel values exist: 0 (server default, which currently means no expiry) and u64::MAX (never expires).

Delete personal access token. Code: 43.

[name_len: u8][name: N]

On this page