Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/archive/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Archive Akroasis Docs
# Archive - Akroasis Docs

Completed or superseded documentation preserved for historical reference.

Expand Down
2 changes: 1 addition & 1 deletion docs/archive/STANDARDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ Add `#[allow(clippy::expect_used, clippy::unwrap_used)]` to `#[cfg(test)]` modul
- Unit tests live in the same file, inside `#[cfg(test)] mod tests { ... }`.
- Integration tests live under `tests/` in the crate root.
- Test names describe what is being verified: `fn valid_coordinates_accepted()`.
- Use `assert!`, `assert_eq!`, `assert_ne!` do not write custom assertion logic unless necessary.
- Use `assert!`, `assert_eq!`, `assert_ne!` - do not write custom assertion logic unless necessary.
- Doc-tests (`cargo test --workspace --doc`) must also pass.
12 changes: 6 additions & 6 deletions research/P2-R1-meshtastic-rust-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ pub(crate) fn decrypt(

The `ChannelSettings.psk` field holds raw PSK bytes. Three cases:

**Default channel key** (`psk == [0x01]`): expand to the well-known 16-byte default key. Verify the exact bytes against the pinned firmware tag's `Default.h` constant `DEFAULT_PSK` do not hardcode from memory.
**Default channel key** (`psk == [0x01]`): expand to the well-known 16-byte default key. Verify the exact bytes against the pinned firmware tag's `Default.h` constant `DEFAULT_PSK` - do not hardcode from memory.

```rust
/// Meshtastic default channel key, expanded from PSK byte 0x01.
Expand Down Expand Up @@ -568,7 +568,7 @@ pub(crate) enum Psk {

### 3.7 Multi-Channel Decryption

For broadcasts on an unknown channel, try each registered channel's PSK. A successful decrypt produces parseable protobuf; an invalid decode means the wrong key. This is a heuristic AES-CTR has no authentication tag. A valid `Data::decode` is necessary but not sufficient; in practice, false positives are rare.
For broadcasts on an unknown channel, try each registered channel's PSK. A successful decrypt produces parseable protobuf; an invalid decode means the wrong key. This is a heuristic - AES-CTR has no authentication tag. A valid `Data::decode` is necessary but not sufficient; in practice, false positives are rare.

```rust
pub(crate) fn decrypt_any_channel(
Expand Down Expand Up @@ -812,8 +812,8 @@ States: Disconnected Connecting Handshaking Ready Error
| `Ready` | `FromRadio` received | `Ready` | process, reset stale timer |
| `Ready` | stale timer fires (300 s) | `Error` | log stale |
| `Ready` | OS error on read/write | `Error` | log |
| `Error` | cleanup complete | `Disconnected` | |
| `Disconnected` | backoff elapsed | `Connecting` | |
| `Error` | cleanup complete | `Disconnected` | - |
| `Disconnected` | backoff elapsed | `Connecting` | - |

**Handshake sub-states:**

Expand Down Expand Up @@ -999,9 +999,9 @@ File header:
reserved[2] = 0x0000

Record:
timestamp_us[8] u64 LE microseconds since Unix epoch
timestamp_us[8] u64 LE - microseconds since Unix epoch
direction[1] 0x00 = device→host, 0x01 = host→device
length[2] u16 LE raw frame bytes (header + payload)
length[2] u16 LE - raw frame bytes (header + payload)
data[length] raw bytes as they appeared on the wire
```

Expand Down
22 changes: 11 additions & 11 deletions research/P2-R2-mesh-topology-routing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# P2-R2: Mesh Topology and Routing

**Phase:** 2 Kerykeion
**Phase:** 2 - Kerykeion
**Block:** P2-03, P2-04
**Depends on:** P2-R1 (Meshtastic routing mechanics), P2-R27 (DTN patterns)

Expand All @@ -19,7 +19,7 @@
| Maintenance | Active, widely used | Slow | N/A |
| `no_std` path | No (not needed) | No | Possible |

`graphlib` is a thin wrapper with a sparser API. A custom adjacency list is warranted only if the graph structure is specialized (it is not here this is a sparse, small, weighted digraph). `petgraph` covers the use case and does not require writing traversal algorithms.
`graphlib` is a thin wrapper with a sparser API. A custom adjacency list is warranted only if the graph structure is specialized (it is not here - this is a sparse, small, weighted digraph). `petgraph` covers the use case and does not require writing traversal algorithms.

**Concrete types:**

Expand Down Expand Up @@ -186,7 +186,7 @@ Every packet the server receives carries:
- `hop_start`: hops remaining when the packet left the originator
- `hop_limit`: hops remaining when the server received the packet

From these: `hops_traversed = hop_start - hop_limit`. If `relay_node` is non-zero, we know the final hop is `relay_node → server_connected_node` with SNR `rx_snr`. Add that directed edge to the graph immediately no traceroute required.
From these: `hops_traversed = hop_start - hop_limit`. If `relay_node` is non-zero, we know the final hop is `relay_node → server_connected_node` with SNR `rx_snr`. Add that directed edge to the graph immediately - no traceroute required.

### 3.2 Update Frequency

Expand Down Expand Up @@ -269,9 +269,9 @@ Thresholds:

| Score | Classification |
|-------|---------------|
| 0–15 | Good direct or short path with strong SNR |
| 16–35 | Degraded long path or weak links |
| 36–60 | Poor marginally usable; prefer store-and-forward |
| 0–15 | Good - direct or short path with strong SNR |
| 16–35 | Degraded - long path or weak links |
| 36–60 | Poor - marginally usable; prefer store-and-forward |
| 61+ | Effectively disconnected |

### 4.4 Multi-Path Tracking
Expand Down Expand Up @@ -434,7 +434,7 @@ The engine checks gateway health every 30 seconds (cheap in-memory scan, no mesh

### 6.5 Bridge Architecture

The gateway is an RF-to-internet bridge. kerykeion does not speak directly to the internet from the gateway node's perspective it routes a Meshtastic packet to the gateway, which then forwards it outbound via MQTT or a direct API call. The return path is symmetric.
The gateway is an RF-to-internet bridge. kerykeion does not speak directly to the internet from the gateway node's perspective - it routes a Meshtastic packet to the gateway, which then forwards it outbound via MQTT or a direct API call. The return path is symmetric.

**Outbound (server → internet):**

Expand Down Expand Up @@ -511,7 +511,7 @@ This information informs PACE planning: a sub-mesh may have internal mesh commun

### 8.1 What opsis Needs

opsis (ratatui TUI) renders the mesh topology. It needs a serializable snapshot that decouples the topology engine from the rendering layer ratatui runs in the terminal event loop, not the async mesh loop.
opsis (ratatui TUI) renders the mesh topology. It needs a serializable snapshot that decouples the topology engine from the rendering layer - ratatui runs in the terminal event loop, not the async mesh loop.

```rust
/// Snapshot of the mesh topology, suitable for rendering without locking the graph.
Expand Down Expand Up @@ -677,15 +677,15 @@ pub enum MessageFailureReason {
}
```

These signals flow into the `GeoSignal` broadcast channel and are consumed by semaino (aggregation) and opsis (display). No signal is emitted without a corresponding state change in the graph events are not re-emitted on polling, only on transition.
These signals flow into the `GeoSignal` broadcast channel and are consumed by semaino (aggregation) and opsis (display). No signal is emitted without a corresponding state change in the graph - events are not re-emitted on polling, only on transition.

---

## 10. Bandwidth Budget

### 10.1 Assumptions

- LoRa SF10, BW125 kHz, CR 4/5 typical Meshtastic default for longer range
- LoRa SF10, BW125 kHz, CR 4/5 - typical Meshtastic default for longer range
- Effective data rate: ~980 bps (SF10 BW125)
- Duty cycle limit: 1% per LoRa regulatory requirement (EU); US has no duty cycle limit but we budget conservatively
- 7 nodes total; 4 have regular activity (2 T-Deck Plus personal carry, RAK gateway, WisBlock)
Expand Down Expand Up @@ -729,7 +729,7 @@ If the firmware reports `channel_utilization > 20%`:

If `channel_utilization > 30%`:
- Suspend Default-priority S&F messages.
- Emit `ChannelCongestion` signal (new `MeshDetail` variant, not defined above add when implementing congestion handling).
- Emit `ChannelCongestion` signal (new `MeshDetail` variant, not defined above - add when implementing congestion handling).

This keeps kerykeion from amplifying congestion loops.

Expand Down
8 changes: 4 additions & 4 deletions research/P2-R3-mesh-networking-crates.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# P2-R3: Mesh networking crates for kerykeion

**Date:** 2026-03-18
**Scope:** Rust crate selection for kerykeion the clean-room Meshtastic stack within Akroasis. Covers serial, BLE, protobuf, crypto, graph, discovery, and test infrastructure. Excludes the official `meshtastic` crate (0.1.8, GPL-3.0) per project constraints.
**Scope:** Rust crate selection for kerykeion - the clean-room Meshtastic stack within Akroasis. Covers serial, BLE, protobuf, crypto, graph, discovery, and test infrastructure. Excludes the official `meshtastic` crate (0.1.8, GPL-3.0) per project constraints.

kerykeion (from Greek keryx, the herald's staff carried by Hermes) handles the physical-to-application boundary: receiving LoRa packets from Meshtastic radio hardware, decrypting and decoding them, and presenting structured mesh topology to the rest of Akroasis.

Expand Down Expand Up @@ -42,7 +42,7 @@ kerykeion (from Greek keryx, the herald's staff carried by Hermes) handles the p

Use `tokio-serial 5.4.5` with `tokio-util 0.7.18` codec framing.

`tokio-serial` wraps `serialport 4.9.0` (the established synchronous serial library) with a Tokio async layer using `mio-serial` under the hood. Linux support for CP2102, CH340, and CH9102 USB serial chipsets is OS-driver level all three present as `/dev/ttyUSBn` on Linux and work without special crate handling. Baud rate configuration, hardware flow control (RTS/CTS, DTR/DSR), and 8N1 framing are all supported through `serialport::SerialPortBuilder`.
`tokio-serial` wraps `serialport 4.9.0` (the established synchronous serial library) with a Tokio async layer using `mio-serial` under the hood. Linux support for CP2102, CH340, and CH9102 USB serial chipsets is OS-driver level - all three present as `/dev/ttyUSBn` on Linux and work without special crate handling. Baud rate configuration, hardware flow control (RTS/CTS, DTR/DSR), and 8N1 framing are all supported through `serialport::SerialPortBuilder`.

`serialport` uses MPL-2.0. This is file-level copyleft: modifications to `serialport`'s own source files must be shared, but code that calls it is not affected. AGPL-3.0 is compatible with this.

Expand Down Expand Up @@ -172,7 +172,7 @@ pub(crate) async fn open_radio(

Use `btleplug 0.12.0`.

Both `btleplug` and `bluer` use D-Bus to talk to BlueZ on Linux neither is truly free of system daemon coupling. The choice comes down to API ergonomics and cross-platform future.
Both `btleplug` and `bluer` use D-Bus to talk to BlueZ on Linux - neither is truly free of system daemon coupling. The choice comes down to API ergonomics and cross-platform future.

`btleplug` provides a `Central` trait for scanning and a `Peripheral` trait for GATT operations. Characteristic discovery, read/write, and notification subscribe all have clean async interfaces. The triple license (MIT/Apache-2.0/BSD-3-Clause) causes no AGPL complications. The 2026-03-09 update date confirms active maintenance.

Expand Down Expand Up @@ -386,7 +386,7 @@ bytes 12..15 : extraNonce as u32, little-endian (0 for normal packets)

This is a 128-bit nonce used as the initial counter value. The intended layout uses a u64 packet ID (not u32).

**Firmware bug (confirmed in `CryptoEngine.cpp`):** The `extraNonce` branch writes to offset `sizeof(uint32_t)` (offset 4) instead of `sizeof(uint64_t) + sizeof(uint32_t)` (offset 12). When `extraNonce != 0`, it overwrites bytes 4–7 (the high word of `packetId`) rather than bytes 12–15. This is a latent firmware defect. For normal mesh packets `extraNonce` is always 0, so this bug is harmless in practice the nonce layout above is correct for all packets kerykeion will receive.
**Firmware bug (confirmed in `CryptoEngine.cpp`):** The `extraNonce` branch writes to offset `sizeof(uint32_t)` (offset 4) instead of `sizeof(uint64_t) + sizeof(uint32_t)` (offset 12). When `extraNonce != 0`, it overwrites bytes 4–7 (the high word of `packetId`) rather than bytes 12–15. This is a latent firmware defect. For normal mesh packets `extraNonce` is always 0, so this bug is harmless in practice - the nonce layout above is correct for all packets kerykeion will receive.

### CTR variant selection

Expand Down