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

DocumentScopeWhen to Reference
architecture-decisionsDecisions, integration pointsArchitecture questions, distribution model
rust-migration.md (this)Rust implementation strategyRust crate work, interop testing, wire protocol changes
PROVISIONING-VISIONProvisioning flow, wire protocol v2Wire format details, message type ranges
componentsComponent inventoryAdding or modifying crates
STYRENE-TUI-VISIONTUI screens, UXUI 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-rs binary 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 authoritystyrene_wire.py is 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

ComponentLocationStatus
TCP transportcrates/libs/rns-transport/Functional — connect, handshake, packet framing
UDP transportcrates/libs/rns-transport/Functional — similar maturity to TCP
Identity (X25519 + Ed25519)crates/libs/rns-core/Functional — key generation, signing, verification
Destinationscrates/libs/rns-core/Functional — single, group, plain types
Linkscrates/libs/rns-core/Functional — establishment, teardown, data transfer
Resourcescrates/libs/rns-core/Functional — chunked transfer over links
Ratchetscrates/libs/rns-core/Functional — forward secrecy key rotation
LXMF routercrates/internal/lxmf-legacy/Functional — message routing logic
LXMF propagationcrates/internal/lxmf-legacy/Functional — store-and-forward
LXMF stampscrates/internal/lxmf-legacy/Functional — proof-of-work anti-spam
LXMF delivery pipelinecrates/internal/lxmf-legacy/Functional — pack, encrypt, deliver, acknowledge

What’s Broken

IssueSeverityDescription
IFAC bugCriticalInterface Access Codes broken for multi-hop — packets routed through intermediate nodes fail IFAC validation. Breaks any deployment with >1 hop.
HMAC timing oracleHighHMAC comparison uses non-constant-time equality. Side-channel attack possible on link authentication.
Identity.encrypt() double-ephemeralMediumGenerates 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

CratePurpose
styrene-rnsRNS protocol core — identity (X25519 + Ed25519), destinations, links, resources, ratchets, packets, IFAC
styrene-rns-transportTransport interfaces — TCP, UDP, future Serial/KISS. Trait-based for extensibility.
styrene-lxmfLXMF messaging — router, propagation, stamps, delivery pipeline, message packing/unpacking
styrene-meshStyrene wire protocol envelope — binary format matching styrene_wire.py, message type taxonomy, msgpack payload serialization
styreneMeta-crate re-exporting all library crates for convenient dependency declaration
styrened-rsDaemon binary — CLI, async runtime, service orchestration (skeleton until interop gate)
interop-testCross-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

RangeCategoryExamples
0x01-0x0FControlPING, PONG, HEARTBEAT
0x10-0x1FStatusSTATUS_REQUEST, STATUS_RESPONSE
0x40-0x5FRPC CommandsEXEC, REBOOT, CONFIG_UPDATE
0x60-0x7FRPC ResponsesEXEC_RESULT, REBOOT_RESULT
0xC0-0xCFTerminalTERMINAL_REQUEST, TERMINAL_ACCEPT

Interop Contract

  1. Python encodes a message → Rust decodes it → fields match exactly
  2. Rust encodes a message → Python decodes it → fields match exactly
  3. Both derive identical destination hashes from the same identity
  4. Both produce compatible LXMF envelopes (FIELD_CUSTOM_TYPE, FIELD_DATA)
  5. Both handle ratchet key rotation identically (forward secrecy preserved across implementations)

Rust Implementation

The styrene-mesh crate implements this contract:

  • StyreneMessage struct matching Python’s wire format
  • StyreneMessageType enum with identical byte values
  • encode() / 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.

DeliverableDescription
Fork + restructureRename crates, merge legacy, organize workspace
CLAUDE.mdDevelopment guide for Rust contributors
CI4-job GitHub Actions (check, test, lint, interop)
Crate namesClaim styrene-* on crates.io with 0.0.1 placeholders
Security fixesConstant-time HMAC, fix double-ephemeral
justfileBuild/test/lint automation matching styrened patterns

Phase 2: Wire Parity

Goal: styrene-mesh crate produces byte-identical output to styrene_wire.py.

DeliverableDescription
styrene-mesh implementationFull wire format v2 encode/decode
Message type enumAll ranges (control, status, RPC, terminal)
msgpack compatibilityrmp-serde producing identical bytes to Python msgpack
Test vectorsPython script generating fixtures, Rust consuming them
Property testsFuzzing encode/decode round-trips

Phase 3: Interop Gate

Goal: Python and Rust implementations communicate over a live Reticulum mesh.

DeliverableDescription
TCP transport verifiedRust node connects to Python transport node
Identity exchangeRust identity recognized by Python RNS
LXMF deliveryRust sends LXMF message, Python receives and decodes
Styrene wire protocolFull round-trip: Rust RPC request → Python styrened → response
Ratchet interopForward 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.

DeliverableDescription
Serial/KISS transportLoRa radio support via serial interface
IFAC fixMulti-hop routing works correctly
Performance benchmarksCrypto throughput, packet processing latency, memory usage
ARM cross-compilationBuilds for aarch64 (Pi 4B) and armv7 (Pi Zero 2W)
ESP32 Phase 0Prove ESP-IDF std Rust compiles on xtensa-esp32s3-espidf, measure flash/heap budget
ESP32 Phase 1KISS 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.

DeliverableDescription
styrened-rs daemonFull service implementation (RPC, discovery, auto-reply)
Styrix integrationNixOS module for Rust daemon alongside or replacing Python
Fleet mixed-modeSome devices run Python, some Rust — mesh works identically
Edge-only deploymentPi Zero 2W runs Rust exclusively (Python removed from image)
ESP32 Phase 2-3Direct SX126x SPI driver, OTA over mesh, power management, NVS/littlefs storage
ESP32 fleet managementESP32 nodes managed via styrened fleet RPC over LXMF — no SSH required

Parallel Development Strategy

What Each Language Owns

ConcernPythonRust
Wire protocol authoritystyrene_wire.py is referenceMust match Python byte-for-byte
TUIstyrene-tui (Textual)Not planned
Edge daemon (current)styrened (production)styrened-rs (experimental)
Edge daemon (Phase 5)Hub/operator workstationConstrained devices
Ecosystem compat testingSideband, MeshChat, NomadNetN/A until Phase 3
IPC serverUnix socket (production)Must implement same socket contract

Synchronization Points

  1. Wire protocol changes — Any change to styrene_wire.py must be reflected in styrene-mesh before merge. CI enforces this via interop tests.
  2. Message type additions — New StyreneMessageType values added to Python enum must be added to Rust enum. The interop test suite catches drift.
  3. Crypto algorithm changes — Any change to RNS identity, encryption, or ratchet algorithms must be synchronized. This is rare (RNS upstream changes).
  4. 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

Revision History

DateChange
2026-02-24Initial document: fork strategy, crate architecture, 5-phase migration plan, wire protocol contract

Graph