Styrene Architecture

Document Type: Executive summary and decision record Last Updated: 2026-02-19 Status: Active

Styrene is a Reticulum-native fleet provisioning and management system for edge devices. It replaces fragile bash installer scripts with a robust, mesh-connected architecture enabling SSH-free device management through encrypted LXMF messaging.

Document Hierarchy

DocumentScopeWhen to Reference
architecture-decisions.md (this)Decisions, integration pointsArchitecture questions, new contributor onboarding
mesh-vpn-architectureWireGuard tunnels over LXMF signaling, transport tiersTunnel implementation, mesh VPN work
rust-migrationRust parallel implementation, crate architectureRust crate work, interop testing
PROVISIONING-VISIONProvisioning flow, Hub, fleet communicationBackend/protocol implementation
STYRENE-TUI-VISIONTUI screens, UX, implementation phasesFrontend/UI implementation
Published DocsUser-facing documentationEnd-user reference
Research: Organic DocsMolecular metaphor, polymer architecturesConceptual understanding

Distribution Model

Key Decision: Isolated Package Dependencies

Styrene is distributed as independent Python packages with explicit dependency relationships. This keeps coupling at the package boundary (dependency declaration + IPC/wire protocol) rather than deep library entanglement, enabling future rewrites of individual components in other languages (e.g., Rust).

styrened (pip install styrened)
├── Standalone daemon + CLI + library
├── LXMF messaging core (NomadNet/MeshChat wire-compatible)
├── Styrene protocols (RPC, discovery, terminal)
├── IPC control server (Unix socket)
├── Optional: [tui] extra — Textual-based operator interface (styrened.tui subpackage)
├── Optional: [web] extra — HTTP API endpoints
├── Optional: [metrics] extra — Prometheus metrics
└── Installable alone for headless/edge use; pre-installed in Styrix NixOS

styrene (pip install styrene)
├── PyPI meta-package — installs styrened[tui]
├── Extras: [web], [metrics], [yubikey]
└── User-facing install target (pip install styrene = full operator stack)

styrene-edge (git clone, not pip — device support repo)
├── sbc/ NixOS configurations + flake (Styrix)
├── Device profiles YAML (consumed by TUI at runtime via remote fetch)
├── Polymerize scripts
└── Contributors add device support here; operators never clone it

styrene community hub (styrened + nomadnet, deployed on K8s)
├── RNS transport node (rns.styrene.io:4242) — relays announces between peers
├── LXMF propagation node — store-and-forward message delivery
├── NomadNet BBS / hosted pages
└── Fleet coordination

Installation scenarios:

  • Headless edge device: pip install styrened or pre-installed via Styrix NixOS
  • Operator workstation: pipx install styrene
  • Community Hub: styrened[web,metrics] + nomadnet in container
  • Device contributor: git clone styrene-edge, work locally, submit PR

Key Decision: TUI as Subpackage

The TUI was originally a separate repo (styrene-tui), now archived. It lives as styrened.tui, installed via the [tui] extra. This keeps the entire operator stack in one package with one version number. The styrene PyPI meta-package provides a user-friendly install alias.

Key Decision: TUI Manages Daemon Lifecycle

On Styrix edge devices, styrened runs as a permanent systemd service. On macOS, a launchd plist manages the daemon. On an operator’s workstation, the TUI manages styrened’s lifecycle — starts it on launch, tears it down on exit. No systemctl enable required to use the TUI. The IPC contract (Unix socket) is the same in both cases; the TUI just owns the other end of the socket when there’s no system daemon.

Key Decision: styrene-edge as Upstream Device Support

styrene-edge is a device support repository, not an application. It’s the equivalent of a firmware tree or board support package. Contributors work there to add new device patterns (NixOS configs, device profiles, polymerize scripts). Operators never clone it.

The TUI’s provisioning flow fetches device profiles and NixOS configs from styrene-edge at runtime (GitHub raw, release artifact, or Cachix), the same way it fetches NixOS base layers from nixpkgs or the Cachix binary cache. When a contributor merges a new device PR, it appears in the TUI’s catalog on next fetch.

System Components

System overview

Component Details:

Network Architecture

Layer Model

Network layer stack

In OSI terms, Reticulum replaces layers 3–6 (Network through Presentation) with cryptographic identity-based routing and end-to-end encryption. BATMAN-adv handles layer 2, and physical transports (WiFi, LoRa, Ethernet) provide layer 1. Standard internet protocols can coexist on the same hardware — the stack is backward-compatible, not a replacement.

Key Decision: BATMAN-adv + Reticulum (Dual Layer)

Why two layers? They solve different problems:

  • BATMAN-adv (L2): “How do I get an Ethernet frame from radio A to radio B across 3 hops?”

    • Uses MAC addresses, measures real throughput, adapts to interference
    • Creates virtual switch across mesh nodes
  • Reticulum (Overlay): “How do I send an encrypted message to cryptographic identity X?”

    • Uses 128-bit destination hashes, transport-agnostic
    • End-to-end encryption, identity-based routing

Reticulum sees BATMAN-adv’s bat0 interface as just another network interface. The layers complement each other.

Details: provisioning-vision.md#layer-2-mesh-batman-adv

Key Decision: Graceful Degradation

The network stack is designed to degrade gracefully as connectivity drops:

LevelAvailableStyrene Behavior
FullInternet + BATMAN-adv + RNSStandard ops, packages from cache.nixos.org + styrene.cachix.org, full fleet management
LAN onlyBATMAN-adv + RNSFleet management continues, no upstream packages, Hub serves cached content
Mesh onlyRNS (WiFi or LoRa)RPC, status, terminal sessions — all core fleet ops work without IP connectivity
OfflineNothingDevice runs autonomously on last-known config

BATMAN-adv provides the IP-compatible mesh for conventional traffic. RNS maintains mesh topology and path establishment. If proper backbones drop out, operations degrade to RNS-only — all fleet management commands, status checks, and terminal sessions continue to function because they ride LXMF, not IP.

When high-bandwidth transports are available (802.11s WiFi, Ethernet), nodes can automatically promote to WireGuard tunnels negotiated via LXMF — providing full IP connectivity at line speed while maintaining LXMF signaling as the control plane. See mesh-vpn-architecture for the 4-tier transport model and tunnel negotiation protocol.

Key Decision: Styrix (NixOS Edge Flavor)

Decision: The NixOS configuration for edge fleet devices is named Styrix (Styrax + Nix). Styrix is NixOS with styrened, RNS, BATMAN-adv, and the Imperial CRT console theme baked in.

Rationale:

  • From the Styrax tree (etymological origin of “styrene”) + Nix — places it in the styr- naming family (styrene, styryl, styrax, storax, styrix)
  • Not a fork — Styrix is opinionated NixOS configuration, not a separate distribution
  • Cross-platform polymerization doesn’t need per-platform names (“Styrene for Android”, “Styrene for Mac”), but the purpose-built NixOS edge substrate earns its own identity

Scope: Styrix applies to edge fleet devices running NixOS. Operator workstations, containers, and non-NixOS platforms simply run styrened.

Key Decision: NetworkManager for NixOS Fleet

Decision: Standardize on NetworkManager with 802.11s mesh for all Styrix fleet devices.

Rationale:

  • Consistent interface across RPi4B, Zero 2W, T100TA, x86 (MiniGMK, Q502L)
  • Robust 802.11s mesh support with automatic reconnection
  • ~15 MB RAM overhead acceptable even on 512 MB Zero 2W
  • nmcli/nmtui available for debugging

Implementation: batman-mesh.nix module in styrene-edge/sbc/common/

Configuration Decisions

Mesh Key: Fleet-Wide Shared

Decision: Single PSK shared across all devices in a fleet.

Rationale:

  • Reticulum provides end-to-end encryption for all application traffic
  • Mesh key only controls L2 access (who can join the mesh)
  • Small trusted fleet doesn’t benefit from per-device key complexity
  • Easy rotation: change in Vault, re-provision or push via Polymerize RPC

Storage: Vault secret, injected at provision time.

Gateway Mode: Per-Instance Configuration

Decision: Gateway mode is deployment-specific, not device-type specific.

RoleGateway ModePurpose
Hub NodeserverBridges mesh to LAN, provides DHCP
Mesh RoutersoffPure L2 mesh coordination
Fleet DevicesclientUses gateway when available

Offline Operation: Set all nodes to gw_mode=off. Mesh works for internal communication without internet.

Configuration: Set in mesh.conf (OpenWrt) or NixOS module option at provision time.

Styrix Module: batman-mesh.nix

All Styrix fleet devices import a common module providing:

  • batman-adv kernel module
  • batctl package
  • NetworkManager 802.11s mesh profile
  • systemd service to attach interface to bat0

Options:

styrene.mesh = {
  enable = true;
  meshId = "styrene";
  meshKey = "fleet-key-from-vault";
  channel = 6;
  gwMode = "client";  # or "server" or "off"
};

TUI Integration Points

ComponentTUI SurfacePhaseReference
Storage detectionProvision screen device picker1 ✓Absorbed from Forge
Device specsConfig validation, partition sizes1 ✓Fetched from styrene-edge at runtime
Media preparationProgress bar (Forge, absorbed)1 ✓TUI provisioning module
LXMF inbox/chatInbox, conversation screens2 ✓styrened ConversationService via IPC
Mesh statusDashboard panel (bat0 neighbors)2batctl n data
Fleet devicesDevice list with live status2 ✓styrened RPCClient via IPC
Hub contentBrowser screen [TODO]3NomadNet pages
Device actionsShell, Config, Logs, Reboot2 ✓styrened RPCClient via IPC

Implementation Phases

PhaseFocusStatusKey Deliverables
1Local Provisioning✅ Largely CompleteForge (media writer, bundle builder), polymerize.sh, SD images
2Mesh Integration✅ Largely Completestyrened daemon, wire protocol v2, RPC
3Fleet OperationsPendingBatch ops, Hub, config versioning, audit

Details: styrene-tui-vision.md#implementation-phases

Wire Protocol Summary

AspectChoice
TransportLXMF over Reticulum (FIELD_CUSTOM_TYPE/DATA)
Serializationmsgpack (NOT Protobuf)
Wire Format[styrene.io:][version:1][type:1][request_id:16][payload]
ArchitecturePython only (styrened daemon, no Go component)

Message Type Ranges (StyreneMessageType enum):

  • 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 Sessions (TERMINAL_REQUEST, TERMINAL_ACCEPT)
  • 0xD8-0xDE: Tunnel Negotiation (TUNNEL_OFFER, TUNNEL_ACCEPT, TUNNEL_REKEY)

Details: provisioning-vision.md#fleet-communication-protocolTunnel protocol: mesh-vpn-architecture

Key Decision: Rust Parallel Implementation

Decision: Fork FreeTAKTeam/LXMF-rs as styrene-lab/styrene-rs and develop a parallel Rust implementation of the RNS/LXMF stack. Python remains primary until the Rust stack passes the interop gate.

Rationale:

  • The distribution model already 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”
  • Rust provides native performance, static binaries, and lower memory for constrained edge devices (Pi Zero 2W)
  • LXMF-rs is the most complete non-Python RNS implementation (TCP/UDP transport, identity, destinations, links, LXMF router/propagation)
  • Wire protocol is the contract — no FFI, no shared memory, implementations communicate over LXMF

Scope: Rust replaces Python on resource-constrained edge devices (Phase 5). Python keeps TUI, Hub, and operator workstation roles. Both implementations coexist on the same mesh.

Details: rust-migration

Key Decision: Community Hub as Default Infrastructure

Decision: Every new install connects to the Styrene Community Hub by default for both RNS transport and LXMF propagation.

Implementation:

  • COMMUNITY_HUB_PROPAGATION_HASH constant in models/config.py — the stable LXMF propagation destination hash, derived from PVC-backed identity on the hub
  • get_profile_defaults(Profile.OPERATOR) sets lxmf.propagation_destination to this hash
  • rns.styrene.io:4242 included as default peer for all profiles
  • Hub runs autopeer: false (it IS the propagation node, not a client seeking one)
  • Hub runs enable_propagation() via lxmf.propagation_node.enabled: true
  • TUI Network tab exposes a single STYRENE COMMUNITY HUB toggle (default ON) that controls both the transport peer and the propagation destination

Rationale: Zero-config mesh connectivity. New users get store-and-forward messaging immediately without understanding transport vs. propagation. Power users can disable the community hub and point to custom infrastructure.

Open Questions

QuestionStatusNotes
Hub hosting (K8s vs dedicated)PendingK8s on brutus for now; revisit for off-grid scenarios
Identity lifecycle/revocationPendingNeed revocation list specification
Offline provisioningPendingPre-cache everything on USB? Separate offline mode?

Document Maintenance

Keeping Documents in Sync

When updating Styrene architecture:

  1. Decision changes → Update architecture-decisions.md first, then update detail docs
  2. Implementation details → Update vision docs, verify architecture-decisions.md links still valid
  3. New components → Add to architecture-decisions.md with link to new/existing detail doc

Drift Prevention Checklist

Before committing changes to any Styrene document:

  • architecture-decisions.md reflects current decisions
  • Cross-document links are valid
  • No contradictions between documents
  • Revision history updated in modified docs

Document Ownership

DocumentScopeUpdate Trigger
architecture-decisions.mdDecisions, integrationAny architectural change
provisioning-vision.mdProvisioning, Hub, wire protocolBackend/protocol changes
styrene-tui-vision.mdTUI screens, UXFrontend/UI changes
styrene-edge/router/README.mdOpenWrt mesh routersRouter config changes
styrene-edge/sbc/README.mdNixOS fleet devicesSBC config changes

Claude Code Directive

When working on Styrene-related code:

  1. Read architecture-decisions.md first for current decisions
  2. Follow links to relevant detail docs for implementation specifics
  3. After making architectural changes, update architecture-decisions.md
  4. Verify cross-document consistency before committing

References

Revision History

DateChange
2025-01-20Initial architecture document created
2026-02-01MAJOR CORRECTIONS: Wire protocol is msgpack (not Protobuf), Python only (no Go); “Polymerize” (formerly “Bond”) renamed to “styrened”; Phase status updated; component diagram updated; fixed broken anchor links
2026-02-01styrened v0.3.6: TerminalService implemented, added ConversationService, ReadReceiptProtocol, NodeStore
2026-02-13Added OSI layer mapping, graceful degradation decision, updated Layer Model table
2026-02-18Phase 1 status updated (Forge replaces planned scripts); edge-fleet→styrene-edge naming; added Cachix to graceful degradation; updated device list and document ownership paths
2026-02-18Named NixOS edge flavor Styrix (Styrax + Nix); renamed “Bond” to “Polymerize” across all docs; added Monomer to glossary
2026-02-24Added Rust Parallel Implementation key decision; linked to rust-migration
2026-02-19MAJOR UPDATE: Distribution architecture and TUI scope
— Added Distribution Model section: isolated package dependencies, dependency graph
— TUI absorbs Forge provisioning (no standalone Forge binary)
— TUI replaces NomadNet/MeshChat as operator’s LXMF client
— TUI manages styrened lifecycle on operator workstations (no systemd required)
— TUI communicates with styrened via IPC (not direct library initialization)
— styrene-edge becomes upstream device support repo (fetched at runtime, not cloned)
— Added gaming handheld devices (RG35XX H, R36S) to styrene-edge
— Updated system component diagram with new architecture
2026-03-01TUI merged into styrened as subpackage (styrened.tui, [tui] extra)
— styrene-tui repo archived; styrene PyPI meta-package wraps styrened[tui]
— Added Community Hub as Default Infrastructure decision
— v0.10.77: Community hub propagation hash, Network tab 7-panel redesign
— Updated distribution model and installation scenarios

Graph