Bitchat Android Binary Protocol Packet Header: Key Fields and Structure
The Bitchat Android binary protocol packet header is a fixed big-endian structure of either 14 bytes (v1) or 16 bytes (v2) that contains the Version, Type, TTL, Timestamp, Flags, and PayloadLength fields, after which variable sections such as sender ID, recipient ID, route, payload, and signature may follow depending on flag bits.
The permissionlesstech/bitchat-android repository implements a peer-to-peer messaging protocol that remains fully compatible with its iOS counterpart. The Bitchat Android binary protocol packet header sits at the start of every transmitted packet and dictates how the rest of the byte stream is parsed. Its layout, constants, and encode-decode logic are defined in app/src/main/java/com/bitchat/android/protocol/BinaryProtocol.kt (lines 39–46).
Fixed Header Layout
The fixed header uses big-endian byte order and occupies either 14 bytes for version 1 or 16 bytes for version 2.
| Offset | Size | Field | Description |
|---|---|---|---|
| 0 | 1 | Version | Protocol version (1u or 2u). Determines header size and PayloadLength width. |
| 1 | 1 | Type | Message type such as MessageType.MESSAGE or MessageType.FRAGMENT. |
| 2 | 1 | TTL | Time-to-live hop count. |
| 3 | 8 | Timestamp | Unix epoch milliseconds as an unsigned 64-bit integer (ULong). |
| 11 | 1 | Flags | Bit-field controlling optional sections (see below). |
| 12 | 2 (v1) / 4 (v2) | PayloadLength | Length of the payload, including compression overhead. |
Version
The first byte specifies the protocol version. A value of 1u produces a 14-byte header, while 2u produces a 16-byte header and widens the PayloadLength field to four bytes.
Type
The second byte stores the message type identifier. This field uses the same enumeration values as the iOS implementation so that cross-platform peers can interpret packets consistently.
TTL
The third byte holds the time-to-live counter. Each forwarding node decrements this hop count until the packet expires.
Timestamp
Bytes 3 through 10 encode an unsigned 64-bit Unix timestamp in milliseconds. This ULong value lets peers evaluate message age and order.
Flags
Byte 11 is a bit-field defined in BinaryProtocol.kt that signals which variable sections follow the header:
- Bit 0 —
HAS_RECIPIENT(recipient ID present) - Bit 1 —
HAS_SIGNATURE(signature present) - Bit 2 —
IS_COMPRESSED(payload is compressed) - Bit 3 —
HAS_ROUTE(source-route present; v2 only)
PayloadLength
Starting at byte 12, this field is two bytes wide in v1 and four bytes wide in v2. It stores the length of the payload that follows the header, including any extra bytes prepended when compression is active.
Variable Sections
After the fixed header, the parser reads optional blocks in a deterministic order. Their presence is determined by the flags byte. As implemented in permissionlesstech/bitchat-android, these sections are:
- Sender ID
- Recipient ID (if
HAS_RECIPIENTis set) - Route (if
HAS_ROUTEis set; v2+) - Payload
- Signature (if
HAS_SIGNATUREis set)
Encoding and Decoding Examples
Encoding a v1 Packet
import com.bitchat.android.protocol.*
val packet = BitchatPacket(
type = MessageType.MESSAGE.value,
senderID = hexStringToByteArray("a1b2c3d4e5f60708"),
timestamp = System.currentTimeMillis().toULong(),
payload = "Hello, world!".toByteArray(),
ttl = 5u, // 5 hops
version = 1u // v1 → 14‑byte header
)
val binary = packet.toBinaryData() // uses BinaryProtocol.encode
// `binary` now contains the 14‑byte header followed by sender ID, payload, etc.
Decoding Received Data
val received: ByteArray = ... // from BLE or Tor transport
val decoded = BitchatPacket.fromBinaryData(received)
if (decoded != null) {
println("Version: ${decoded.version}")
println("Type: ${decoded.type}")
println("TTL: ${decoded.ttl}")
println("Payload (${decoded.payload.size} bytes): ${String(decoded.payload)}")
}
Inspecting Header Bytes Manually
val header = binary.copyOfRange(0, if (binary[0] == 1.toByte()) 14 else 16)
val version = header[0].toUByte()
val type = header[1].toUByte()
val ttl = header[2].toUByte()
val timestamp = ByteBuffer.wrap(header, 3, 8).order(ByteOrder.BIG_ENDIAN).long.toULong()
val flags = header[11].toUByte()
val payloadLen = if (version == 1u.toUByte()) {
ByteBuffer.wrap(header, 12, 2).order(ByteOrder.BIG_ENDIAN).short.toInt()
} else {
ByteBuffer.wrap(header, 12, 4).order(ByteOrder.BIG_ENDIAN).int
}
Key Source Files
app/src/main/java/com/bitchat/android/protocol/BinaryProtocol.kt(lines 39–46) — Defines the header layout, flag constants, and size logic; also hosts theBitchatPacketdata class (lines 54–64).app/src/main/java/com/bitchat/android/model/FragmentPayload.kt— Contains the 13-byte fragment-payload header used when large packets are split.app/src/main/java/com/bitchat/android/protocol/MessagePadding.kt— Implements iOS-compatible padding after the header, interacting with header size calculations.
Summary
- The Bitchat Android binary protocol packet header is a fixed big-endian structure of 14 bytes (v1) or 16 bytes (v2).
- It contains six sequential fields: Version, Type, TTL, Timestamp, Flags, and PayloadLength.
- The Flags byte at offset 11 determines which optional sections—recipient ID, route, payload, or signature—appear after the header.
BinaryProtocol.ktinpermissionlesstech/bitchat-androidis the authoritative source for layout constants and encode/decode routines.
Frequently Asked Questions
What is the total size of the Bitchat Android binary protocol packet header?
The fixed header is 14 bytes for version 1 and 16 bytes for version 2. The size difference comes from the PayloadLength field, which expands from 2 bytes to 4 bytes in v2.
How does the Flags field affect packet parsing?
Byte 11 is a bit-field that tells the parser which optional blocks follow the fixed header. For example, if bit 0 (HAS_RECIPIENT) is set, the parser expects a recipient ID immediately after the sender ID. If bit 3 (HAS_ROUTE) is set and the version is 2 or higher, a source-route block is present before the payload.
What is the difference between v1 and v2 packet headers?
Version 2 extends the PayloadLength field from 2 bytes to 4 bytes and introduces the HAS_ROUTE flag in bit 3. Otherwise, the field order and semantics remain identical to maintain backward compatibility.
Where is the packet header defined in the source code?
The layout, constants, and encode/decode logic are defined in app/src/main/java/com/bitchat/android/protocol/BinaryProtocol.kt in the permissionlesstech/bitchat-android repository. The BitchatPacket data class declared inside that file (around lines 54–64) holds the header values and payload.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →