Skip to content
metadatasticianPublic

About

Voice first. Friction last. Complexity optional. Pre-production, self-hosted voice communications platform with browser-based WebRTC joining and spatial audio, designed for low latency, privacy and operator control.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Latest commit

 

History

479 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Burble

Green Hosting OpenSSF Scorecard

Voice first. Friction last. Complexity optional.

What is Burble?

Burble is a self-hostable voice communications platform built for people who care about latency, privacy, and control. Think Mumble’s audio seriousness meets modern browser-based joining — no downloads, no accounts, no friction.

One command to deploy. Sub-second to join. Your server, your rules.

Status

Burble is pre-production (self-assessed CRG grade C, targeting B). The core — self-hosted WebRTC voice, browser join, OTP fault isolation — is operational and tested. Per ADR-0007, every claim below maps to code + a test, or is flagged here:

  • Experimental (optional, off by default): QUIC transport and SNIF WASM crash-isolation require optional NIFs (quicer/msquic, wasmex) that are disabled in the default build; the runtime degrades gracefully without them.

  • Hardware-gated: sub-microsecond PTP needs a PTP-capable NIC; without one the system uses NTP (~1ms). PTP hardware is unvalidated (I210 pending).

  • Roadmap: the Idris2 ABI proofs compile and type-check; runtime enforcement is not yet wired (ADR-0008, Option C; PoC tracked in issue #55).

  • Not yet benchmarked: end-to-end mic-to-speaker latency and 500+ concurrent scale (issue #52).

  • Signalling wire format: SDP bodies ride the Bebop binary plane by default since 2026-08-04 (PR #180), on codecs generated from server/priv/schemas/voice_signal.bop. JSON remains an explicit opt-out (config :burble, :signaling_wire_format, :json) and the automatic fallback if a Bebop encode fails; decode of both planes is always accepted, so mixed-version peers keep working, and clients still receive a plain sdp string. The plane satisfies spline ADR-0005 criterion (a); a second consumer parses the same bytes (criterion (b), gossamer#145) and the alignment is CI-enforced in groove (criterion (c), groove#30).

  • Test gate: the full server suite passes and the CI pass/fail gate is armed — a failing test turns CI red (elixir-ci.yml "Run server tests", re-armed 2026-07-07). The green gated run on main (Elixir CI @2b5914b) is the receipt for the current self-assessed CRG grade C (issue #100 closed; see .machine_readable/descriptiles/STATE.a2ml). The Dialyzer lane remains advisory.

Foundational hardening is tracked in the "Earn the Core" epic (issue #53).

Known debt is enumerated, with the evidence for each item, in DEBT.md — measured 2026-08-07 across build, supply chain, CI/CD, licence, documentation, code, proof and test. It indexes rather than replaces PROOF-NEEDS.md, TEST-NEEDS.md and READINESS.adoc. Read it before trusting a green badge or a passing gate here: several of the findings are gates that cannot fail.

Why Burble?

Burble Mumble Discord Jitsi

Self-host

Yes

Yes

No

Yes

Browser join

Yes (WebRTC)

No (native only)

Browser + app

Yes

Latency

<10ms* (kernel-path target)

~15ms

~50-100ms

~30ms

Privacy

DTLS-SRTP today; peer-held E2EE part-way †

Encrypted, no telemetry

Telemetry, scanning

E2EE optional (vendor claim)

Precision timing

IEEE 1588 PTP (<1µs)*

No

No

No

Embeddable

Yes (game/app integration)

No

No

Partial

Note

Honest status (ADR-0007): <10ms is a Zig kernel-path target — end-to-end mic-to-speaker latency and 500+ concurrent scale are not yet benchmarked (issue #52). IEEE 1588 PTP <1µs requires a PTP-capable NIC; without one the system falls back to NTP (~1ms) and the PTP hardware path remains unvalidated.

† End-to-end encryption is part-way: no room is end-to-end encrypted today. See [_encryption_what_is_true_today].

Encryption: what is true today

Burble’s target is the one ADR-0011 sets out: the server has no capability to see call content, rather than a promise not to look. Burble is only part of the way there, and this section is the scoreboard.

What protects a call today. Every hop is encrypted with DTLS-SRTP. That is real encryption, and it is not end-to-end: it terminates at the SFU, so the server can decrypt the media it forwards. Where an older version of this file, a UI string, or a third-party summary says "E2EE", discount it.

What is implemented (server side, 2026-10-21). ADR-0016 moved Burble to peer-held group keys; the coordination half is in the tree:

  • Burble.Media.E2EE no longer generates or returns a room key. It stores, per room, public X25519 keys, an epoch counter, and opaque sealed blobs. There is no key-returning function in its API.

  • Each peer would generate its own ephemeral keypair; one peer per epoch (the lowest peer id) generates the room key and seals it to every other peer under their pairwise X25519 + HKDF key. The server relays ciphertext it cannot open.

  • Pairwise agreement, sealing/opening and per-frame derivation are peer-side pure functions (pairwise_key/6, seal_group_key/6, open_group_key/6, derive_frame_key/3) with domain-separated labels, AAD bound to room/epoch/direction, an all-zero-shared-secret check (RFC 7748 §6.1) and size caps. They are the part a test suite can exercise without a browser.

  • The room channel exposes the coordination events a client needs — e2ee_register_key, e2ee_key_set, e2ee_publish_sealed, e2ee_fetch_sealed, e2ee_status — so the protocol is reachable and not merely a library (docs/PROTOCOL.md). Every payload is a public key or an opaque blob.

  • Any membership change bumps the epoch and drops every sealed blob, so a departing peer never learns the next key and a joining peer never learns a past one. Burble.Security.KeyRotation now requests these epoch bumps; it does not, and cannot, ratchet a key the server does not have.

The browser half exists, and is tested — it is not yet on the call path.

  • client/web/e2ee.js is the peer side: X25519 keypairs, pairwise agreement, sealing, per-speaker frame keys, AES-GCM frame encryption, a replay window and epoch tracking. It is checked against the server’s own output by committed test vectors, so the two halves cannot drift apart silently (bun test client/web/tests/ — 97 tests across 12 files, all green).

  • client/web/e2ee-session.js is the conversation that goes with it: it speaks the five channel events, elects the key-maker (lowest peer id), seals the room key to each peer, opens what arrives, and follows epoch bumps. It handles the two orderings that actually happen — a sealed key arriving before the key set, and one arriving from an epoch ahead of ours.

  • client/web/e2ee.js also carries the Encoded Transform layer: FrameCryptor, senderTransform/receiverTransform and attachFrameCrypto(pc, …), which pipes them into a RTCPeerConnection and reports ok: false when the browser lacks createEncodedStreams.

What is missing is the last mile, and it is the difference between "the protocol is implemented" and "your call is private":

  • Nothing calls them yet. VoiceEngine.res does not build the session, does not attach the transforms to the peer connection, and has no key-exchange UI. So no call is encrypted beyond DTLS-SRTP, and the server can still decrypt what it forwards.

  • It has never run in a browser. The tests exercise real WebCrypto and real TransformStream`s, but with a fake peer connection shaped like Chrome’s API. `createEncodedStreams presence, frame mutation in place, and the SFU’s fan-in are all modelled, not observed.

  • There was already a client E2EE module, and it was not encryption. client/web/src/app/voice/E2EE.res claimed "AES-256-GCM with HKDF-SHA256" and XORed each frame with the local private key, repeated. Nothing imported it, it derived its key from one peer’s private key alone (so two peers could not have agreed), and the build artifact in a working tree is not even valid JavaScript. All three are luck rather than design. It is now a failing stub that points at e2ee.js. Had anyone wired it up, Burble would have shipped something labelled E2EE that is trivially breakable.

  • The server-side coprocessor pipeline still runs in :e2ee rooms. It no longer receives a key (the engine refuses one and logs a warning) and cannot decrypt, but ADR-0011 §3 puts content DSP at the edge; that move is not done.

  • No mid-epoch key-leader failover, and nothing on this path is formally verified.

Two design points the client work forced. The room key is shared, so on its own it authenticates the room, not the speaker — any member could forge frames that appear to come from any other. The frame layer therefore derives a per-sender key and binds the sender id into the AAD, and each frame states its speaker in its header: rewriting that header makes the receiver derive the wrong key, so the AAD check catches the forgery. A peer can still forge its own frames, which it is entitled to do; it cannot speak as someone else.

The second point is topology. Burble’s media plane is an SFU, so one receive path carries every participant; a frame layer that assumed "the remote peer of this connection" could not have worked. Frames carry their speaker and the replay window is per speaker, which is also what makes the same code correct in a mesh call.

So the names changed, to stop the drift. :e2ee_key became :pipeline_key (server-side, explicitly not the room key); the pipeline health map reports pipeline_encrypted:; the peer offer carries e2ee_requested: — the policy asked for, not proof; Privacy.e2ee_config/1 returns enabled: false, implemented: false; and GET /api/v1/setup/check reports e2ee_coordinator_running: instead of e2ee_keys_generated:. A room marked :e2ee is a request until the client half lands.

Key Features

Audio kernels — Zig remains the estate-policy implementation language. Historical direct-NIF microbenchmarks are retained below as provenance only; the direct loader has been retired. Production acceleration now requires proved ReleaseSafe WASM guests through SNIF, which are not yet shipped. The live call path forwards opaque Opus and does not currently traverse these kernels.

Precision Time Protocol — IEEE 1588 PTP clock source detection with graceful fallback (PTP hardware → phc2sys → NTP → system). Sub-microsecond accuracy when a PTP hardware clock is available; ~1ms via NTP on typical deployments. Jitter measurement, telemetry, and multi-node alignment data export built in. Direct in-VM device access is forbidden; a PTP hardware path requires an isolated OS service and real-hardware validation.

Four topology modes — From single-server (monarchic) to fully distributed mesh. The serverless mode is designed for mandatory E2EE and will enforce it once the client half ships; today no topology is end-to-end encrypted. Set BURBLE_TOPOLOGY and go.

Erlang/OTP backbone — Supervision trees, hot code upgrades, fault isolation. If a room crashes, everything else keeps running. That’s not marketing — that’s OTP.

Bridge interop (experimental) — a Mumble/Murmur relay module exists but is not yet started by the supervision tree or validated against a real Murmur server. Jitsi and Matrix bridges are roadmap items, not code.

Embeddable client library — Drop Burble voice into your game, workspace, or app. Used in IDApTIK (asymmetric co-op game) and PanLL (panel workspace).

Quick Start

One-command container deployment

git clone --recurse-submodules https://github.com/hyperpolymath/burble && cd burble
just deploy       # builds selur-compose if needed (~3 min first run), then brings up the stack
# Server:     http://localhost:4000
# Web client: http://localhost:8080
# VeriSimDB:  http://localhost:8081

just deploy uses selur-compose — the TOML-native Rust compose driver developed in-tree. No Python, no Docker Compose, no podman-compose.

To stop the stack:

just down
Note
--recurse-submodules is required. VeriSimDB is integrated as a submodule under tools/nextgen-databases (paralleling tools/affinescript) because Burble’s anti-purpose forbids vendoring it in-tree. If you already cloned without it, run git submodule update --init --recursive — or just run ./setup.sh, which now does that automatically.

Development setup

Note
Building from source requires system packages for the quicer/msquic QUIC transport library (cmake, perl, build tools, OpenSSL headers). See CONTRIBUTING.md for the full package list and per-distro install commands.
# Prerequisites: Elixir 1.17+, Zig 0.15+, Bun
# (plus system packages — see CONTRIBUTING.md)

# Test Zig kernel sources in the estate-required safety mode
cd ffi/zig && zig build test -Doptimize=ReleaseSafe

# Start server
cd server && mix deps.get && mix phx.server

# Start web client (separate terminal)
cd client/web && bun run dev

Run tests

just test          # All tests
just test-server   # Elixir tests (300+)
just test-ffi      # Zig coprocessor tests
just bench         # Elixir vs Zig benchmarks

Coprocessor Benchmarks

The table below is historical direct-NIF benchmark provenance, not a current production claim. The direct NIF has been retired; equivalent measurements must be reproduced against proved SNIF guests before acceleration is described as available. The live SFU path forwards opaque Opus RTP and does not execute these kernels. End-to-end latency remains unbenchmarked (ADR-0007; issue #52).

Operation Elixir Retired direct-NIF result Historical speedup

LZ4 compress

83ms

3.1us

26,350x

Echo cancel

19.4ms

310us

62x

FFT (256pt)

826us

22us

37x

Convolution

300us

11us

27x

Neural denoise

54us

9us

6x

Wondering How This Works?

If you’re curious about the tech behind these claims — how we hit 26,350x, what PTP actually does, why OTP gives us fault isolation — see EXPLAINME.adoc for the receipts.

Documentation

Questions?

Open an issue on GitHub or reach out directly — happy to explain anything in more detail.

License

SPDX-License-Identifier: CC-BY-SA-4.0

Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>

See LICENSE for full text.

About

Voice first. Friction last. Complexity optional. Pre-production, self-hosted voice communications platform with browser-based WebRTC joining and spatial audio, designed for low latency, privacy and operator control.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages