Rust Parallel Implementation
Document Type: Architecture vision and migration strategy Last Updated: 2026-02-24 Status: Active
Styrene’s wire protocol is the shared contract between implementations. This document describes the Rust parallel implementation — a fork of FreeTAKTeam/LXMF-rs — that will run alongside the Python stack, share the wire protocol, and eventually enable selective component replacement.
Document Hierarchy
| Document | Scope | When to Reference |
|---|---|---|
| architecture-decisions | Decisions, integration points | Architecture questions, distribution model |
| rust-migration.md (this) | Rust implementation strategy | Rust crate work, interop testing, wire protocol changes |
| PROVISIONING-VISION | Provisioning flow, wire protocol v2 | Wire format details, message type ranges |
| components | Component inventory | Adding or modifying crates |
| STYRENE-TUI-VISION | TUI screens, UX | UI that consumes either implementation |
Motivation
Why Rust
Python is correct for rapid iteration on protocol semantics, TUI development, and ecosystem compatibility. It is not correct for:
- Edge device resource constraints — 512 MB RAM on Pi Zero 2W, shared with BATMAN-adv, RNS, and application workloads. Python’s memory overhead (~30-50 MB baseline for styrened) is significant on constrained devices.
- Transport-layer performance — Packet processing, cryptographic operations (X25519, Ed25519, AES-256), and high-throughput mesh routing benefit from native code. RNS’s Python crypto layer is a measurable bottleneck on ARM.
- Static binaries for fleet deployment — A single
styrened-rsbinary with no runtime dependencies simplifies Styrix NixOS images, eliminates Python version management on edge devices, and reduces attack surface. - Concurrency model — Tokio async runtime vs Python’s GIL for handling simultaneous RNS links, LXMF deliveries, RPC handlers, and terminal sessions.
Why Fork LXMF-rs
An audit of every non-Python Reticulum implementation (20 projects across 7 languages — Rust, C, C++, Go, Java, Kotlin, TypeScript) found that FreeTAKTeam/LXMF-rs is the most complete:
- Working: TCP/UDP transport, identity management (X25519 + Ed25519), destinations, links, resources, ratchets
- Working (legacy location): LXMF router, propagation, stamps, delivery pipeline, message packing/unpacking
- Scaffolding: Serial/KISS transport stubs, partial I2P integration
- 55-job CI proving active development (overly complex, but evidence of investment)
No other implementation covers both RNS and LXMF. Starting from a working TCP handshake and identity exchange saves months compared to greenfield.
What Python Keeps
Python remains the primary implementation until the Rust stack passes the interop gate (Phase 3). Specifically:
- styrened — Production daemon, all RPC services, IPC server, conversation/node services
- styrene-tui — Textual TUI (Rust has no equivalent TUI framework at this maturity)
- Wire protocol authority —
styrene_wire.pyis the reference; Rust must match byte-for-byte - Ecosystem compatibility — NomadNet, Sideband, MeshChat interop testing stays in Python
Fork Assessment: FreeTAKTeam/LXMF-rs
What Works
| Component | Location | Status |
|---|---|---|
| TCP transport | crates/libs/rns-transport/ | Functional — connect, handshake, packet framing |
| UDP transport | crates/libs/rns-transport/ | Functional — similar maturity to TCP |
| Identity (X25519 + Ed25519) | crates/libs/rns-core/ | Functional — key generation, signing, verification |
| Destinations | crates/libs/rns-core/ | Functional — single, group, plain types |
| Links | crates/libs/rns-core/ | Functional — establishment, teardown, data transfer |
| Resources | crates/libs/rns-core/ | Functional — chunked transfer over links |
| Ratchets | crates/libs/rns-core/ | Functional — forward secrecy key rotation |
| LXMF router | crates/internal/lxmf-legacy/ | Functional — message routing logic |
| LXMF propagation | crates/internal/lxmf-legacy/ | Functional — store-and-forward |
| LXMF stamps | crates/internal/lxmf-legacy/ | Functional — proof-of-work anti-spam |
| LXMF delivery pipeline | crates/internal/lxmf-legacy/ | Functional — pack, encrypt, deliver, acknowledge |
What’s Broken
| Issue | Severity | Description |
|---|---|---|
| IFAC bug | Critical | Interface Access Codes broken for multi-hop — packets routed through intermediate nodes fail IFAC validation. Breaks any deployment with >1 hop. |
| HMAC timing oracle | High | HMAC comparison uses non-constant-time equality. Side-channel attack possible on link authentication. |
Identity.encrypt() double-ephemeral | Medium | Generates ephemeral key twice instead of reusing — wastes entropy, produces non-standard ciphertext that Python RNS cannot decrypt. |
What’s Scaffolding
- Serial/KISS transport — type stubs only, no implementation
- I2P transport — partial integration, non-functional
crates/internal/separation — “legacy” crates contain the working LXMF code; “libs” versions are incomplete rewrites
Crate Architecture
Workspace Layout
styrene-rs/
Cargo.toml # workspace root
justfile
LICENSE # MIT (preserved from fork)
UPSTREAM.md # Fork attribution, upstream tracking
CLAUDE.md
crates/
libs/
styrene-rns/ # RNS protocol core
styrene-rns-transport/ # Transport interfaces
styrene-lxmf/ # LXMF messaging protocol
styrene-mesh/ # Styrene wire protocol envelope
apps/
styrened-rs/ # Daemon binary (skeleton)
interop-test/ # Interop test harness
meta/
styrene/ # Meta-crate re-exporting all libs
tests/
interop/
python/ # Python scripts for cross-impl testing
fixtures/ # Binary test vectors generated from Python
Crate Dependency Graph
styrene (meta-crate)
├── re-exports styrene-rns
├── re-exports styrene-lxmf
├── re-exports styrene-rns-transport
└── re-exports styrene-mesh
styrene-lxmf
├── styrene-rns (identity, destinations, links)
└── styrene-rns-transport (send/receive)
styrene-mesh
├── styrene-rns (identity for signing)
└── styrene-lxmf (LXMF envelope)
styrene-rns-transport
└── styrene-rns (packet types, identity)
styrened-rs
├── styrene (all libs via meta-crate)
├── tokio (async runtime)
└── clap (CLI)
interop-test
├── styrene (all libs via meta-crate)
└── test fixtures from Python
Crate Descriptions
| Crate | Purpose |
|---|---|
styrene-rns | RNS protocol core — identity (X25519 + Ed25519), destinations, links, resources, ratchets, packets, IFAC |
styrene-rns-transport | Transport interfaces — TCP, UDP, future Serial/KISS. Trait-based for extensibility. |
styrene-lxmf | LXMF messaging — router, propagation, stamps, delivery pipeline, message packing/unpacking |
styrene-mesh | Styrene wire protocol envelope — binary format matching styrene_wire.py, message type taxonomy, msgpack payload serialization |
styrene | Meta-crate re-exporting all library crates for convenient dependency declaration |
styrened-rs | Daemon binary — CLI, async runtime, service orchestration (skeleton until interop gate) |
interop-test | Cross-implementation test harness — consumes Python-generated binary fixtures, validates wire compatibility |
Wire Protocol Contract
The wire protocol is the shared contract between Python and Rust implementations. Both must produce and consume identical byte sequences.
Reference Implementation
Python: styrened/src/styrened/models/styrene_wire.py
Wire Format v2
[namespace:10][version:1][type:1][request_id:16][payload:variable]
"styrene.io" 0x01 enum os.urandom(16) msgpack-encoded
Message Type Ranges
| Range | Category | Examples |
|---|---|---|
0x01-0x0F | Control | PING, PONG, HEARTBEAT |
0x10-0x1F | Status | STATUS_REQUEST, STATUS_RESPONSE |
0x40-0x5F | RPC Commands | EXEC, REBOOT, CONFIG_UPDATE |
0x60-0x7F | RPC Responses | EXEC_RESULT, REBOOT_RESULT |
0xC0-0xCF | Terminal | TERMINAL_REQUEST, TERMINAL_ACCEPT |
Interop Contract
- Python encodes a message → Rust decodes it → fields match exactly
- Rust encodes a message → Python decodes it → fields match exactly
- Both derive identical destination hashes from the same identity
- Both produce compatible LXMF envelopes (FIELD_CUSTOM_TYPE, FIELD_DATA)
- Both handle ratchet key rotation identically (forward secrecy preserved across implementations)
Rust Implementation
The styrene-mesh crate implements this contract:
StyreneMessagestruct matching Python’s wire formatStyreneMessageTypeenum with identical byte valuesencode()/decode()producing byte-identical output to Python- msgpack serialization via
rmp-serde - Property-based tests with Python-generated fixtures
Key Decisions
Fork Over Greenfield
Decision: Fork FreeTAKTeam/LXMF-rs rather than writing Rust RNS from scratch.
Rationale: The fork provides working TCP/UDP transport, complete identity management, and a real LXMF implementation. Greenfield would require 6-12 months to reach the same baseline. Known bugs (IFAC, HMAC timing, double-ephemeral) are fixable; missing features are additive.
Trade-off: Inherit upstream code style and structure. Mitigated by immediate restructuring (crate rename, legacy merge, workspace reorganization).
Wire Protocol as Contract
Decision: The wire protocol (not a library API) is the integration boundary between Python and Rust.
Rationale: This is already the architecture — architecture-decisions.md specifies “coupling at the package boundary (dependency declaration + IPC/wire protocol) rather than deep library entanglement, enabling future rewrites of individual components in other languages.” The Rust implementation validates this architectural choice.
Implication: No FFI, no PyO3 bindings, no shared memory. Implementations communicate over LXMF like any two Reticulum nodes. A Python styrened and a Rust styrened-rs can coexist on the same mesh without knowing about each other.
styrene- Crate Namespace
Decision: All crates use the styrene- prefix (not rns- or lxmf-).
Rationale: Upstream uses rns-core, lxmf-core — generic names that could conflict with other RNS implementations. The styrene- prefix claims a clear namespace, signals that these are Styrene project crates (not official RNS), and aligns with the styrene-lab org name.
Python Primary Until Interop Gate
Decision: Python styrened remains the production implementation until Rust passes the interop gate (Phase 3).
Rationale: The Rust stack must prove wire compatibility before it replaces anything. The interop gate is a hard requirement: Python-generated test vectors must round-trip through Rust perfectly. Until then, Rust is experimental and Python is production.
Migration Phases
Phase 1: Fork and Foundation
Goal: Clean fork, restructured workspace, all crates build and test.
| Deliverable | Description |
|---|---|
| Fork + restructure | Rename crates, merge legacy, organize workspace |
| CLAUDE.md | Development guide for Rust contributors |
| CI | 4-job GitHub Actions (check, test, lint, interop) |
| Crate names | Claim styrene-* on crates.io with 0.0.1 placeholders |
| Security fixes | Constant-time HMAC, fix double-ephemeral |
| justfile | Build/test/lint automation matching styrened patterns |
Phase 2: Wire Parity
Goal: styrene-mesh crate produces byte-identical output to styrene_wire.py.
| Deliverable | Description |
|---|---|
styrene-mesh implementation | Full wire format v2 encode/decode |
| Message type enum | All ranges (control, status, RPC, terminal) |
| msgpack compatibility | rmp-serde producing identical bytes to Python msgpack |
| Test vectors | Python script generating fixtures, Rust consuming them |
| Property tests | Fuzzing encode/decode round-trips |
Phase 3: Interop Gate
Goal: Python and Rust implementations communicate over a live Reticulum mesh.
| Deliverable | Description |
|---|---|
| TCP transport verified | Rust node connects to Python transport node |
| Identity exchange | Rust identity recognized by Python RNS |
| LXMF delivery | Rust sends LXMF message, Python receives and decodes |
| Styrene wire protocol | Full round-trip: Rust RPC request → Python styrened → response |
| Ratchet interop | Forward secrecy key rotation works across implementations |
Gate criteria: All tests in tests/interop/ pass with a Python RNS/LXMF/styrened instance and a Rust styrene-rs instance on the same Reticulum mesh.
Phase 4: Transport Expansion
Goal: Rust implementation covers transport types beyond TCP/UDP.
| Deliverable | Description |
|---|---|
| Serial/KISS transport | LoRa radio support via serial interface |
| IFAC fix | Multi-hop routing works correctly |
| Performance benchmarks | Crypto throughput, packet processing latency, memory usage |
| ARM cross-compilation | Builds for aarch64 (Pi 4B) and armv7 (Pi Zero 2W) |
| ESP32 Phase 0 | Prove ESP-IDF std Rust compiles on xtensa-esp32s3-espidf, measure flash/heap budget |
| ESP32 Phase 1 | KISS serial to RNode modem — two-board rig (ESP32-S3 + RNode over UART) |
ESP32 detail: A 4-phase ESP32 bring-up plan in styrene-edge covers the full path from ESP-IDF validation through direct SX126x SPI radio drivers. ESP32 cannot run Python styrened — Rust is the only viable path to autonomous Styrene on ESP32.
Phase 5: Selective Replacement
Goal: Rust replaces Python on resource-constrained edge devices.
| Deliverable | Description |
|---|---|
styrened-rs daemon | Full service implementation (RPC, discovery, auto-reply) |
| Styrix integration | NixOS module for Rust daemon alongside or replacing Python |
| Fleet mixed-mode | Some devices run Python, some Rust — mesh works identically |
| Edge-only deployment | Pi Zero 2W runs Rust exclusively (Python removed from image) |
| ESP32 Phase 2-3 | Direct SX126x SPI driver, OTA over mesh, power management, NVS/littlefs storage |
| ESP32 fleet management | ESP32 nodes managed via styrened fleet RPC over LXMF — no SSH required |
Parallel Development Strategy
What Each Language Owns
| Concern | Python | Rust |
|---|---|---|
| Wire protocol authority | styrene_wire.py is reference | Must match Python byte-for-byte |
| TUI | styrene-tui (Textual) | Not planned |
| Edge daemon (current) | styrened (production) | styrened-rs (experimental) |
| Edge daemon (Phase 5) | Hub/operator workstation | Constrained devices |
| Ecosystem compat testing | Sideband, MeshChat, NomadNet | N/A until Phase 3 |
| IPC server | Unix socket (production) | Must implement same socket contract |
Synchronization Points
- Wire protocol changes — Any change to
styrene_wire.pymust be reflected instyrene-meshbefore merge. CI enforces this via interop tests. - Message type additions — New
StyreneMessageTypevalues added to Python enum must be added to Rust enum. The interop test suite catches drift. - Crypto algorithm changes — Any change to RNS identity, encryption, or ratchet algorithms must be synchronized. This is rare (RNS upstream changes).
- Transport behavior — TCP/UDP framing, handshake sequences, and packet formats must match. Verified by interop transport tests.
Development Workflow
Python (styrened) Rust (styrene-rs)
│ │
│ wire protocol change │
├──────────────────────────────────► │
│ │ update styrene-mesh
│ │
│ generate test vectors │
├──────────────────────────────────► │
│ │ run interop tests
│ │
│ interop CI gate │
│◄──────────────────────────────────►│
│ (both must pass before merge) │
References
- architecture-decisions — Distribution model enabling language-independent replacement
- PROVISIONING-VISION — Wire Format v2 specification
- components — Component inventory including Rust crates
- FreeTAKTeam/LXMF-rs — Upstream fork source
- Reticulum Documentation — Protocol specification
Revision History
| Date | Change |
|---|---|
| 2026-02-24 | Initial document: fork strategy, crate architecture, 5-phase migration plan, wire protocol contract |