feat(bt): BLE transport over BlueZ with identity-preserving Addr

Task 8 of the fleet-deployment plan. Adds the tines-bt crate: a real
`Transport` over the `bluer` crate (D-Bus BlueZ).

- `Addr` = peer's advertised `NodeId`, so identity survives BLE
  address rotation; advertising payload (flags + 16-bit service UUID +
  NodeId in service data) fits legacy 31-byte EIR (`advertise()`).
- `BtTransport` drops any inbound frame whose `from` differs from the
  connection's advertised NodeId (protocol violation at the transport;
  core never sees a mismatch).
- Connection symmetry: the node with the numerically smaller NodeId is
  the BLE central/initiator (`is_central`), independent of which side
  opens the radio.
- `mtu()` reports the real L2CAP CoC MTU (falling back to the ATT MTU);
  the transport does NOT split frames — core folds summaries/fragments
  to `mtu()`.
- Radio I/O sits behind the narrow `Radio` trait (a `poll`-driven event
  stream), so the pure logic (payload assembly, mismatch-drop, central
  rule) is unit-tested with a mock radio and never needs hardware.
- `BluerRadio` drives the stack on a detached tokio task: powers the
  adapter, advertises, monitors for the service UUID, opens an L2CAP
  byte pipe, and surfaces inbound bytes tagged with the peer NodeId.

Adds `tests/adv.rs` (8 mock-Radio integration tests covering the 31-byte
limit, NodeId round-trip, MTU clamp, mismatch-drop, per-frame MTU, and
central rule). Added to workspace members; workspace CI (fmt, doc,
test, clippy -D warnings) is green.
This commit is contained in:
2026-10-18 14:06:48 -07:00
parent ef53204fca
commit d089d9edaa
7 changed files with 1296 additions and 1 deletions
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "tines-bt"
version.workspace = true
edition.workspace = true
license.workspace = true
description = "BLE transport for tines over BlueZ via the bluer crate: identity-preserving (Addr = NodeId) messaging with testable radio I/O."
authors.workspace = true
repository.workspace = true
readme.workspace = true
documentation = "https://docs.rs/tines-bt"
keywords = ["robotics", "gossip", "delay-tolerant", "ble", "bluer"]
categories = ["science::robotics", "network-programming"]
[dependencies]
tines-core = { path = "../tines-core", version = "0.3.0" }
bluer = { version = "0.17", features = ["bluetoothd", "l2cap"] }
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "sync", "net", "time", "signal"] }
futures = "0.3"
log = "0.4"