Skip to content

feat(kerykeion): serial/TCP transport, frame codec, AES-CTR encryption, config handshake#56

Merged
forkwright merged 3 commits intomainfrom
p2-02/meshtastic-serial
Mar 19, 2026
Merged

feat(kerykeion): serial/TCP transport, frame codec, AES-CTR encryption, config handshake#56
forkwright merged 3 commits intomainfrom
p2-02/meshtastic-serial

Conversation

@forkwright
Copy link
Owner

Summary

  • codec.rs: tokio_util Decoder/Encoder with 4-byte Meshtastic frame header (0x94 0xC3 MSB LSB), magic seeking on corrupt input, max-512-byte enforcement, zero-length and back-to-back frame handling
  • transport/: SerialTransport (115 200 baud, DTR/RTS false, exponential backoff reconnect) and TcpTransport (port 4403, 3 s connect timeout); ConnectionHandle enum dispatch bridges the non-dyn-compatible async-fn trait
  • handshake.rs: config handshake state machine — sends want_config_id, collects MyNodeInfo / NodeInfo / Channel messages, completes on matching config_complete_id, 10 s timeout
  • crypto.rs: AES-128/256-CTR using the ctr crate; nonce layout matches Meshtastic firmware (packet_id as u64 LE || from_node u32 LE || 0x00000000); PSK expansion for 1-byte shorthands; multi-channel decrypt with protobuf probe
  • heartbeat.rs: 30 s keepalive loop driven by CancellationToken; uses empty ToRadio (no payload variant) as the keepalive signal — the vendored proto has no dedicated Heartbeat message type

61 tests in kerykeion, all passing. cargo clippy --workspace --all-targets -- -D warnings clean.

Notes

  • Box<dyn MeshConnection> is not viable because async fn in traits (Rust 2024) is not object-safe. ConnectionHandle enum dispatch is the idiomatic alternative.
  • The vendored mesh.proto does not define Config or ModuleConfig messages, so HandshakeResult carries only channels: Vec<Channel> from the dump.
  • ToRadio { payload_variant: None } is used as the heartbeat keepalive since no dedicated message type exists in the proto.

Test plan

  • cargo test -p kerykeion — 61 tests pass
  • cargo test --workspace — 418 tests pass, 0 failures
  • cargo clippy --workspace --all-targets -- -D warnings — 0 warnings
  • cargo fmt --all — no diffs

🤖 Generated with Claude Code

…n, config handshake

Implements the Meshtastic serial protocol layer in the kerykeion crate:

- codec.rs: tokio_util Decoder/Encoder with 4-byte frame header (0x94 0xC3
  MSB LSB), magic seeking, max-512-byte enforcement, zero-length and
  back-to-back frame handling (10 tests)

- transport/serial.rs: SerialTransport at 115 200 baud, no flow control,
  DTR/RTS false to avoid firmware reboot on connect, exponential backoff
  reconnect (1 s → 30 s cap)

- transport/tcp.rs: TcpTransport on default port 4403, 3 s connect timeout,
  same Meshtastic framing (2 tests)

- transport/mod.rs: ConnectionHandle enum dispatch (Serial | Tcp) for
  non-dyn-compatible async-fn traits; connect() factory

- handshake.rs: config handshake state machine — sends want_config_id,
  receives MyNodeInfo / NodeInfo / Channel / config_complete_id, 10 s
  timeout, discards in-flight MeshPackets, fails on Rebooted (3 tests)

- crypto.rs: AES-128/256-CTR via ctr crate; nonce layout matches
  Meshtastic firmware; PSK expansion for 1-byte shorthands; multi-channel
  decrypt with protobuf probe (15 tests)

- heartbeat.rs: 30 s keepalive loop driven by CancellationToken, empty
  ToRadio as keepalive signal (2 tests)

Also updates connection.rs to use proto types (ToRadio/FromRadio), adds
From<io::Error> to Error for tokio_util codec trait bounds, and exposes new
modules in lib.rs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link
Contributor

⚠️ Large PR detected — 13 files, 2102 lines changed.

Consider splitting into smaller PRs for easier review. Not a blocker, just a signal.

@forkwright forkwright merged commit 221f2b7 into main Mar 19, 2026
12 checks passed
@forkwright forkwright deleted the p2-02/meshtastic-serial branch March 20, 2026 13:34
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