Skip to main content

BitChat binary protocol encode/decode — packet structs, TLV codec, peer ID utilities

Project description

bitchat-protocol

PyPI Python 3.10+ License: Unlicense

BitChat binary protocol encode/decode for Python.

Implements the wire format from the BitChat mesh networking protocol: binary packet encode/decode, TLV codec for announcement and private message structures, and peer ID derivation utilities.

Installation

pip install bitchat-protocol

Requires Python 3.10+.

Quick Start

from bitchat_protocol import (
    encode, decode,
    BitchatPacket, MessageType,
    encode_announcement, decode_announcement,
    AnnouncementPacket,
    peer_id_from_noise_key,
)

# Encode a broadcast message
import time
packet = BitchatPacket(
    version=1,
    type=int(MessageType.MESSAGE),
    ttl=7,
    timestamp=int(time.time() * 1000),
    flags=0,
    sender_id=bytes.fromhex('abcdef0123456789'),
    payload='Hello, BitChat!'.encode(),
)
wire = encode(packet, padding=True)   # padded for BLE transmission

# Decode from bytes received over BLE or Nostr relay
decoded = decode(wire)
if decoded:
    print('type:', decoded.type)
    print('payload:', decoded.payload.decode())

API

Packet Encode/Decode

encode(packet: BitchatPacket, *, padding: bool = False) -> bytes
decode(data: bytes) -> BitchatPacket | None

decode() returns None (never raises) on invalid or truncated input.

TLV: AnnouncementPacket

encode_announcement(packet: AnnouncementPacket) -> bytes
decode_announcement(data: bytes) -> AnnouncementPacket | None

Decoder is lenient: unknown TLV tags are skipped (forward-compatible).

TLV: PrivateMessagePacket

encode_private_message(packet: PrivateMessagePacket) -> bytes
decode_private_message(data: bytes) -> PrivateMessagePacket | None

Decoder is strict: returns None on any unknown TLV tag.

TLV: RequestSyncPacket

REQUEST_SYNC gossip-sync payloads carrying GCS filter parameters. Unlike the TLVs above, these use 16-bit big-endian lengths.

encode_request_sync(packet: RequestSyncPacket) -> bytes
decode_request_sync(data: bytes, max_accept_bytes: int = MAX_ACCEPT_FILTER_BYTES) -> RequestSyncPacket | None

@dataclass
class RequestSyncPacket:
    p: int                                    # Golomb-Rice parameter (decode accepts 1..=MAX_P = 32)
    m: int                                    # hash range M = N * 2^P (uint32)
    data: bytes                               # GR bitstream bytes (MSB-first)
    types: int | None = None                  # sync-type flags bitmask
    since_timestamp: int | None = None        # only sync packets newer than this (ms, uint64)
    fragment_id_filter: str | None = None     # restrict sync to one fragment ID
from bitchat_protocol import (
    MessageType, RequestSyncPacket,
    encode_request_sync, decode_request_sync, sync_type_flags_from_message_types,
)

wire = encode_request_sync(RequestSyncPacket(
    p=19,
    m=1 << 19,
    data=gcs_filter_bytes,
    types=sync_type_flags_from_message_types([MessageType.ANNOUNCE, MessageType.MESSAGE]),
))
request = decode_request_sync(wire)  # None on any invalid input

The decoder is lenient about unknown tags (forward-compatible — the optional types/since_timestamp/fragment_id_filter TLVs are iOS-side extensions that older decoders skip) and strict about validity: it rejects p outside 1..=MAX_P (32), m = 0, missing required fields, and filter data larger than max_accept_bytes (default MAX_ACCEPT_FILTER_BYTES = 1024, a DoS guard).

Helpers: sync_type_flags_from_message_types(types) / sync_type_flags_to_message_types(flags) convert between list[MessageType] and the flags bitmask.

Peer ID Utilities

peer_id_from_noise_key(noise_public_key: bytes) -> str   # 16-char hex
peer_id_to_bytes(peer_id: str) -> bytes                  # 8 bytes
peer_id_from_bytes(data: bytes) -> str                   # 16-char hex
nostr_geo_dm_peer_id(nostr_pubkey_hex: str) -> str       # "nostr_" + prefix
nostr_geo_chat_peer_id(nostr_pubkey_hex: str) -> str     # "nostr:" + prefix

Utilities

hex_to_bytes(hex_str: str) -> bytes
bytes_to_hex(data: bytes) -> str

Wire Format

v1 Header (14 bytes)

[version:1][type:1][ttl:1][timestamp:8 BE uint64][flags:1][payloadLen:2 BE uint16]
[senderID:8]
[recipientID:8]         — if flags & HAS_RECIPIENT
[payload:payloadLen]
[signature:64]          — if flags & HAS_SIGNATURE

v2 Header (16 bytes)

Same but payloadLen is 4 bytes (BE uint32) and source routing is supported.

Flags Byte

Bit Value Name
0 0x01 HAS_RECIPIENT
1 0x02 HAS_SIGNATURE
2 0x04 IS_COMPRESSED
3 0x08 HAS_ROUTE (v2+ only)
4 0x10 IS_RSR

Running Tests

pip install -e ".[dev]"
pytest

Compatibility

This package implements the same binary wire format as:

  • ios/bitchat/Protocols/BinaryProtocol.swift
  • android/.../BinaryProtocol.kt

Cross-language compatibility is verified against the bitchat-sdk/spec-tests golden fixture suite. Clone that repo as a sibling of this one, then pytest runs all cross-language vectors automatically — without it, the fixture suite silently skips.

License

Unlicense — public domain.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bitchat_protocol-0.2.0.tar.gz (22.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bitchat_protocol-0.2.0-py3-none-any.whl (16.6 kB view details)

Uploaded Python 3

File details

Details for the file bitchat_protocol-0.2.0.tar.gz.

File metadata

  • Download URL: bitchat_protocol-0.2.0.tar.gz
  • Upload date:
  • Size: 22.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.2

File hashes

Hashes for bitchat_protocol-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c810a4ad115dc29d9614709b958ae4f850e230c413f32ca2c471eca8d7c64cbb
MD5 de943ef1efd9840cd4d9f373e5061c95
BLAKE2b-256 b80e4b3eea555887fd9759d23b1fb29da01eaf608f20dbe55c803df57d7fbf37

See more details on using hashes here.

File details

Details for the file bitchat_protocol-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for bitchat_protocol-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 38694ac9c8d9589c243a2fe89d93614842528f099810e2393d82ba18bcf9cc0c
MD5 c40ba26da55ed594a81b8121a41f600a
BLAKE2b-256 37ed369e11a1b1fcd63728ca13ee9b6179ed93e3de383dc63db18db35c6afc9e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page