Skip to content

Repository files navigation

BitChat for systems that care about correctness.

A pure-Zig mesh server and protocol library with explicit bounds, no runtime heap growth in the core, no C or third-party dependencies.

Targets Windows and Linux. Embeddable by design.

Build

Requires Zig 0.16.

zig build
zig build run -- help
zig build test

Quick start

Run a mesh server named alice, and greet the first peer once:

zitchat serve alice "hello world"

Other tools

Encode an announce frame and immediately decode it:

zitchat encode-announce alice | xargs zitchat decode

Run a mesh server while capturing every frame:

zitchat serve --trace-file capture.hex

replay the capture later:

while read frame; do zitchat decode "$frame"; done < capture.hex

Commands

Command Description
help Show the command list, or zitchat help <command> for one command
version Print semantic version and the git commit the binary was built from
adapters List Bluetooth adapters and radios
probe Check whether BLE hardware is present
scan Watch for BLE advertisements for ten seconds
serve Run the mesh server: announce, relay, and log peer traffic
rotate-identity Replace the on-disk identity, keeping the previous one as <path>.bak
encode-announce Print a hex-encoded announce frame
encode-message Print a hex-encoded public message frame
decode Decode a hex-encoded frame field by field

zitchat <command> --help documents a single command, including its flags and examples.

Global flags (-v, -vv, --log-level <level>, --trace-file <path>, --version) are accepted anywhere on the command line.

Exit codes

Code Meaning
0 Success
1 Unexpected internal error
2 Usage / invalid arguments / invalid content
3 probe: no Bluetooth radio
4 Transport / adapter / BLE operator failure
5 Protocol / identity / courier / undecodable frame

Logging and frame capture

Logging defaults to info.

  • --log-level <err|warn|info|debug> - set the runtime filter exactly
  • -v / --verbose - adds debug logs
  • -vv / --trace - additionally hex-dumps every raw frame as it crosses the transport (bounded to the first 256 bytes, like tcpdump -s)

--trace-file <path> writes every frame in full as a bare hex line, flushed immediately. A run that ends in a crash still leaves a complete capture, and each line feeds straight back into decode:

Linux (BlueZ) requirements

The Linux backend talks to BlueZ over the system D-Bus as an ordinary user. No root privileges are needed to run zitchat serve itself under the default BlueZ D-Bus policy.

Important! Before running against a real phone, disable bluetoothd's built-in battery plugin. It auto-reads a newly connected peer's Battery Level characteristic. iOS will answer with insufficient authentication which makes BlueZ start SMP pairing and raise a "Bluetooth Pairing Request" dialog on the phone which is not desirable. While that dialog is pending, the link never reaches connected.

Create /etc/systemd/system/bluetooth.service.d/10-no-battery-plugin.conf containing:

[Service]
ExecStart=
ExecStart=/usr/libexec/bluetooth/bluetoothd -P battery

NOTE: The bluetoothd path may differ per distribution. The example above is Debian/Raspberry Pi OS. Then apply it:

sudo systemctl daemon-reload && sudo systemctl restart bluetooth

BlueZ 5.62 or newer is expected. Zitchat reads a characteristic's GattCharacteristic1.MTU property, which older BlueZ releases do not expose.

Control plane (optional)

serve can expose a localhost-only WebSocket control plane so an external client such as an LLM, web UI, robot bridge, smoke test, etc. sees every server event and can send commands back. docs/CONTROL_PROTOCOL.md contains the protocol.

zitchat serve alice --control-port 8737
zitchat serve alice --control-port 8737 --control-token s3cret

Two clients ship with the repo:

A simple zitchat smoke test that uses control plane

# connect, send one message, assert the ack
zig build ws-smoke -- 8737   

A control plane HTML page with a connect form and live event log. Useful for observing traffic.

Testing

In addition to zig build test:

  • zig build fuzz is decoder-level fuzzing (generate-then-mutate).
  • zig build dst is a TigerBeetle-VOPR-style simulator with many protocol-accurate simulated phones over a simulated BLE radio with no hardware required. It uses virtual time (ex: a 10 virtual-minute run takes a few wall-clock seconds) and is fully reproducible from a seed. The simulated radio injects loss, duplication, corruption, truncation, disconnect storms, address rotation, connection drops, and some malformed frames.
zig build fuzz -- smoke                  # run every fuzz target once, quickly
zig build fuzz -- packet 123             # one target, a fixed reproducible seed
zig build dst                            # run simulation index 0 from the embedded seed file
zig build dst -- --seed 42               # quick fuzz mode: any u64 is a new universe
zig build dst -Drelease -- --index 0     # ~6x faster, identical digest -- soak with this

The full flag reference and how to read a DST report are in this Testing section. Soak scripts live under util/*_soak.sh.

Protocol notes

Canonical service and characteristic UUIDs are taken from the upstream BLEService.swift:

Purpose UUID
Mainnet service F47B5E2D-4A9E-4C5A-9B3F-8E1D2C3A4B5C
Characteristic A1B2C3D4-E5F6-4A5B-8C9D-0E1F2A3B4C5D

Design principles

  • Explicit bounds and assertions everywhere
  • No runtime heap growth in the core paths
  • Pure Zig with no hidden dependencies
  • Provide a useful tool and a reference implementation

Contributing

Bug reports, protocol questions, and carefully scoped patches are welcome.
Please keep changes in the spirit of Tiger Style.

License

MIT

Links

About

A pure-Zig BitChat protocol implementation and server

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages