bitchat-protocol
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.swiftandroid/.../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.
Metadata
Release files for bitchat-protocol 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| bitchat_protocol-0.2.0.tar.gz | 22.2 kB | Without provenance | Details for bitchat_protocol-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| bitchat_protocol-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for bitchat_protocol-0.2.0-py3-none-any.whl |
Total release size: 38.8 kB
Release files / bitchat_protocol-0.2.0.tar.gz
| Download URL | bitchat_protocol-0.2.0.tar.gz |
|---|---|
| Size | 22.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c810a4ad115dc29d9614709b958ae4f850e230c413f32ca2c471eca8d7c64cbb
|
|
BLAKE2b-256 checksum How to use checksums |
b80e4b3eea555887fd9759d23b1fb29da01eaf608f20dbe55c803df57d7fbf37
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.2
|
Release files / bitchat_protocol-0.2.0-py3-none-any.whl
| Download URL | bitchat_protocol-0.2.0-py3-none-any.whl |
|---|---|
| Size | 16.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
38694ac9c8d9589c243a2fe89d93614842528f099810e2393d82ba18bcf9cc0c
|
|
BLAKE2b-256 checksum How to use checksums |
37ed369e11a1b1fcd63728ca13ee9b6179ed93e3de383dc63db18db35c6afc9e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.2
|
Security for bitchat-protocol 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.