Skip to content

docs(architecture): land Android client + cross-repo Bluetooth plan#30

Merged
hyperpolymath merged 2 commits into
mainfrom
docs/android-client-plan
May 13, 2026
Merged

docs(architecture): land Android client + cross-repo Bluetooth plan#30
hyperpolymath merged 2 commits into
mainfrom
docs/android-client-plan

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Lands the design for burble's first native Android client and the cross-repo Bluetooth integration with sibling repo neurophone. Documentation only — no code changes, no Phase 0 implementation yet.

Key decisions (full detail in docs/architecture/ANDROID-CLIENT.adoc):

  • Two repos, no third. Burble owns the voice client, wire protocol, and server bridge. Neurophone gets BT presence as a sensor only (no mic, no voice).
  • minSdk 35 (Android 15). DistanceMeasurementSession (Channel Sounding) is API 35+; apologising for Bluedroid-era devices on a from-scratch native client is incoherent. Reno 13 is canonical smoke-test device.
  • io.getstream:stream-webrtc-android for libwebrtc — active fork, AAR published, tracks upstream M-130s.
  • Zig is the audio DSP, not a peer choice. WebRTC APM disabled. Intent baked structurally: BurbleAudioDspSoftFallback is named-and-shamed with removal target v1.4; type-level distinction in AudioPipeline.idr (Native is normal inhabitant, SoftFallback only reachable via Fallback : ErrorRecovery -> AudioPipeline); Avow attestation logs DSP mode per session; READINESS exit gate requires ≥95% native-path adoption over rolling 7 days.
  • BLE-SPA is the design. No legacy continuous-advertise mode ships. Translates burble's IP-layer SPA (port 7373 + UDP 9 --bolt knock) down to the BLE adv layer. Devices stay in passive scan (~3 mA) and broadcast nothing until an authenticated HMAC-knock is observed. Two-position toggle: Off (default) / Discoverable.
  • MAP 1.4 + AVRCP 1.6 + LEA MCP/MCS/TMAP via MessagingStyle + MediaSession + MediaBrowserService. In-car HMI works.
  • Full Bluetooth Audio Offload (HAL V2). App never pulls LC3 — codec stays in the BT controller.

Phase 0 follow-up PR will add a2ml manifests + Idris2 types + .affine signatures + stub bridges/neurophone.ex. Mergeable in burble alone.

Test plan

  • Doc renders as AsciiDoc
  • No code changes to verify
  • Cross-refs to existing docs (ARCHITECTURE, THREAT-MODEL, ABI-FFI-README, MediaPipeline.idr) are valid paths

🤖 Generated with Claude Code

hyperpolymath and others added 2 commits May 13, 2026 02:12
Captures the design for burble's first native Android client and the
cross-repo integration with sibling repo `neurophone` as a Bluetooth
presence sensor.

Decisions baked in:
- Two-repo layout (burble owns voice + protocol, neurophone owns
  presence sensor only — no mic, no voice).
- minSdk 35 (Android 15, required for Channel Sounding API surface).
- libwebrtc via io.getstream:stream-webrtc-android.
- Zig DSP is the audio engine; WebRTC APM disabled. Intent baked
  structurally: BurbleAudioDspSoftFallback is named-and-shamed and
  removal-tracked, type-level distinction in AudioPipeline.idr,
  Avow attestation logs dsp mode per session, READINESS exit gate
  requires >=95% native-path adoption.
- BLE-SPA is THE design — no legacy continuous-advertise mode ships.
- MAP 1.4 + AVRCP 1.6 + LEA MCP/MCS/TMAP via MediaSession +
  MessagingStyle + MediaBrowserService.
- Full audio offload (HAL V2) — never pull LC3 into the app.
- Smoke-test device: Oppo Reno 13 only; Phase 5 Channel Sounding
  gracefully no-ops on hardware without BT 6.0 CS.

Phase 0 is mergeable as a single follow-up PR (a2ml manifests +
Idris2 types + .affine signatures + stub bridges/neurophone.ex).
This commit is documentation only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Companion ADR for the Bolt QUIC transport branch landed in 54af9a6.
Captures the why behind the shape:

- dual-bind UDP + QUIC on the same port (7373) rather than splitting
  ports, so firewalls/NAPTR records do not need updating
- helper module `Burble.Bolt.Quic` rather than inlining into
  `Burble.Transport.QUIC` (Bolt needs a strict subset — datagrams only,
  no streams, no per-room state)
- sender default stays UDP so cold pokes do not pay a TLS handshake;
  QUIC is opt-in via `:transport`/`:try_quic`/`send_quic`
- broadcast targets refuse QUIC explicitly with
  `:quic_broadcast_unsupported` rather than silently downgrading
- cert kept out of code (`scripts/gen-bolt-cert.sh`) because writing
  into `priv/` from a release is a footgun and operators replacing the
  self-signed cert with a trust-rooted one should be deliberate
- 800 ms client handshake budget keeps `try_quic: true` auto-fallback
  responsive
- ALPN `"burble-bolt-v1"` distinct from voice (`"burble-voice-v1"`) so
  the two QUIC listeners can share a host without collision
- legacy code matched `{:quic, :datagram, ...}` but quicer ships
  `:dgram` — corrected in the implementation, noted here

Also enumerates the deliberate out-of-scope items (NAPTR ALPN
advertisement, msquic Guix/Nix packaging, mutual-auth certs) so the
next reader knows what is intentionally unfinished.
@hyperpolymath hyperpolymath merged commit acdcf9f into main May 13, 2026
16 of 19 checks passed
@hyperpolymath hyperpolymath deleted the docs/android-client-plan branch May 13, 2026 00:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant