Rust LXMF messaging and propagation for Reticulum.
rsLXMF is a Rust implementation of LXMF, the Reticulum messaging layer. This is not a fork of LXMF, this is LXMF written in a different language focused on staying interoperable. This is not a source of truth implementation, do not use it as such.
Commands are intentionally namespaced for Rust with the Rust-specific lxmd-rs command, so rsLXMF can live beside other
LXMF daemons on PATH without worry.
- Build It
- Rust API
- Operating lxmd-rs
- Configuration
- Delivery Model
- Feature Status
- Compatibility Notes
- Contributing
- License
This guide describes the 1.4.0 source line at b5c3365b5715f1f36f74963a9d2136238d956467, used by Ratspeak 1.0.37 with rsReticulum 1.4.0. The published standalone daemon download is v1.3.0; use its README and bundled --help for that build. The 1.4 message and durable relay APIs require a coordinated source migration.
The current development layout for rsLXMF requires rsReticulum as a sibling directory/repo next to it, such as:
ratspeak-src/
|-- rsReticulum/
`-- rsLXMF/
If you're starting fresh:
mkdir ratspeak-src
cd ratspeak-src
git clone https://github.com/ratspeak/rsReticulum
git clone https://github.com/ratspeak/rsLXMF
git -C rsReticulum checkout --detach 5b38cae702a5b3984adf12a435f6d3012fcf90e1
git -C rsLXMF checkout --detach b5c3365b5715f1f36f74963a9d2136238d956467
cd rsLXMFRun the build commands below from that rsLXMF directory. The host crates require Rust 1.89 or newer.
Install Rust with rustup, then install Apple's build tools:
xcode-select --installBuild from the sibling checkout:
cargo build --release --lockedDebian, Ubuntu, and Raspberry Pi OS:
sudo apt update
sudo apt install -y build-essential pkg-configFedora:
sudo dnf install gcc make pkgconf-pkg-configArch:
sudo pacman -S --needed base-devel pkgconfcargo build --release --lockedInstall Rust with the MSVC toolchain. If Rust or Cargo asks for Visual Studio Build Tools, install the "Desktop development with C++" workload.
Build from PowerShell:
cargo build --release --lockedAfter the build, use the commands below with ./target/release/lxmd-rs on
macOS/Linux or .\target\release\lxmd-rs.exe on Windows.
lxmf-tools builds one public command name:
| Binary | Purpose |
|---|---|
| lxmd-rs | Rust LXMF daemon and control utility. |
Print the example configuration to standard output:
lxmd-rs --exampleconfigRun a regular LXMF daemon:
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulumRun a propagation node:
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum --propagation-nodeSend a message and exit. Replace uppercase hash placeholders with the appropriate 32-character hexadecimal destination hash:
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum \
--send "DELIVERY_DESTINATION_HASH" "message body"The --send flag is an rsLXMF convenience. Normal
daemon and propagation-control operation does not require it for anything.
Send UTF-8 file content or select a delivery method:
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum \
--send "DELIVERY_DESTINATION_HASH" --send-file ./message.txt --send-method directSupported --send-method values are opportunistic, direct, and
propagated. Paper messages aren't supported yet in the CLI.
Attach custom LXMF fields from scripts:
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum \
--send "DELIVERY_DESTINATION_HASH" "with fields" \
--send-fields-json '{"200":"aGVsbG8=","201":"AAECAw=="}'The CLI JSON object maps IDs from 0 to 255 to base64-encoded binary values. Choose IDs and schemas agreed with the receiving application. This option does not construct structured MessagePack fields such as an LRGP envelope; use the library’s typed field API for those. The library supports a wider integer ID range than this CLI convenience.
Control a propagation node:
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum --status
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum --peers
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum --sync "PEER_PROPAGATION_HASH"
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum --break "PEER_PROPAGATION_HASH"These commands query an lxmf.propagation.control endpoint over Reticulum.
They do not inspect local files and do not start local daemon state. If no
reachable daemon answers, they time out with compatibility-oriented control
exit behavior.
For a remote propagation node, pass the node's propagation destination hash:
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum \
--status --peers --remote "PROPAGATION_DESTINATION_HASH" --timeout 5Control queries use <config-dir>/identity by default. Use --identity PATH
when the query should authenticate as a different LXMF identity.
Run an inbound hook:
lxmd-rs --config ~/.rsLXMF --rnsconfig ~/.rsReticulum --on-inbound /path/to/handlerThe handler receives the saved .lxm message path as an argument.
lxmd-rs --config <dir> expects a directory and reads <dir>/config.
lxmd-rs --rnsconfig <dir> expects a Reticulum config directory.
If no LXMF config directory is supplied, the default is:
| Platform | Default LXMF config file |
|---|---|
| Linux/macOS | /etc/rsLXMF/config, then ~/.config/rsLXMF/config, then ~/.rsLXMF/config |
| Windows | %APPDATA%\rsLXMF\config |
On Linux/macOS, the first two locations are selected only when their config file exists; otherwise the home-directory fallback is used.
If --rnsconfig is omitted, Reticulum config resolution follows
rsReticulum-specific defaults.
Recommended standalone locations:
| Environment | LXMF config | Reticulum config |
|---|---|---|
| macOS/Linux desktop | ~/.rsLXMF/config |
~/.rsReticulum/config |
| Windows desktop | %APPDATA%\rsLXMF\config |
%APPDATA%\rsReticulum\config |
| Linux service | /var/lib/rsLXMF/config |
/etc/rsReticulum/config or another explicit Reticulum directory |
Use existing LXMF or Reticulum directories, such as ~/.lxmd, ~/.lxmf, or
~/.reticulum, only by passing them explicitly. That keeps the default install
isolated while still allowing deliberate drop-in and migration tests.
Minimal config:
[lxmf]
display_name = Rat
announce_at_start = no
# stamp_cost = 12
delivery_transfer_max_accepted_size = 1
# on_inbound = /path/to/handler
[propagation]
enable_node = no
announce_at_start = yes
autopeer = yes
autopeer_maxdepth = 6
auth_required = no
# node_name = Rat Nest
# static_peers = 0123456789abcdef0123456789abcdef
# outbound_node = 0123456789abcdef0123456789abcdef
# max_peers = 20
# propagation_stamp_cost_target = 16
# propagation_stamp_cost_flexibility = 3
[logging]
loglevel = 4delivery_transfer_max_accepted_size limits incoming Direct Resource data
in decimal KB and defaults to 1 when omitted. The reusable router library has a separate 1000 KB default. Applications that accept
larger incoming messages should set their own explicit policy.
Common sections and supported keys (see --exampleconfig for annotated defaults):
| Section | Keys |
|---|---|
[lxmf] |
display_name, announce_at_start, announce_interval, delivery_transfer_max_accepted_size, stamp_cost, on_inbound |
[propagation] |
enable_node, node_name, auth_required, announce_at_start, announce_interval, autopeer, autopeer_maxdepth, message_storage_limit, propagation_message_max_accepted_size, propagation_sync_max_accepted_size, propagation_stamp_cost_target, propagation_stamp_cost_flexibility, peering_cost, remote_peering_cost_max, max_peers, static_peers, prioritise_destinations, control_allowed, from_static_only, sequential_pn_stamp_validation, static_peers_bypass_sequential, max_inbound_syncs, outbound_node, propagation_stamp_cost, propagation_limit, enforce_stamps |
[control] |
auth_required, allowed |
[logging] |
loglevel |
Optional hash-list files live next to <config-dir>/config:
| File | Meaning |
|---|---|
ignored |
LXMF destination hashes to ignore. |
allowed |
Reticulum identity hashes permitted to retrieve messages when propagation auth_required = yes. An empty list then admits no clients. |
Each file accepts one raw 32-character hex hash per line. The two files use different hash types. Propagation administration is separate: control_allowed contains authorized control identity hashes; the daemon’s own identity is also authorized. Client retrieval authorization does not make the node’s stored messages readable to other clients.
LXMF supports several delivery shapes. rsLXMF exposes them through the router
and, where network-backed, through lxmd-rs --send-method.
| Method | Behavior |
|---|---|
| Opportunistic | Single-packet delivery when the packed message fits the Reticulum packet path. Delivery completes only after an authenticated Reticulum proof; bounded retries remain in flight until then. The router switches oversized opportunistic messages to Direct. |
| Direct | Link-backed delivery over a Reticulum Link, with resource transfer for larger content. |
| Propagated | Store-and-forward delivery through a propagation node, including deposit, retrieve, peer sync, stamps, and tickets. |
| Paper | Library support for lxm:// URI generation and ingest. The CLI does not generate QR images. |
Direct and propagation Links use ordinary Reticulum path discovery only for their initial LINKREQUEST. After a valid LRPROOF, rsLXMF pins the Link to that proof's ingress interface and all later packets, Resources, proofs, keepalives, and teardown use the same interface. A lost interface closes the Link instead of silently rerouting it. Reverse Direct delivery is published only after its role-correct proof has been admitted to that bound endpoint.
An ordinary direct or opportunistic LXMF message is a signed Reticulum payload:
destination_hash 16 bytes
source_hash 16 bytes
signature 64 bytes
payload MessagePack([timestamp, title, content, fields, optional_stamp])
title and content are bytes on the wire. fields maps integer IDs to
MessagePack values, which can be binary, text, arrays, maps or other supported
values. Standard fields have defined schemas. The 1.4 library preserves signed
and unsigned integer IDs beyond the CLI’s 0–255 range and rejects duplicate
outer IDs. Encoding a structured field as a binary blob changes its meaning.
In the 1.4 API, construct LxMessageDraft and set include_ticket before
calling LxmRouter::prepare_outbound. Persist the returned ticket intent before
signing; a pending intent blocks signing. Signing consumes the draft, and
finalization produces an immutable message. See the 1.4 migration guide
for stamp work, typed fields, inbound verification and propagation storage.
| Area | Current behavior |
|---|---|
| Message format | Signed LXMF envelopes, custom field maps, standard reply/reaction/comment fields, propagation wrappers, .lxm containers, and paper URI encode/decode. |
| Delivery | Opportunistic, Direct, Propagated, callbacks, failure callbacks, cancellation, progress state, opportunistic-to-direct fallback, and compression signalling in delivery announces. |
| Propagation | Transactional disk-backed store, deposit, retrieve, peer sync, autopeer/static peers, weighted culling, duplicate checks, size checks, and stamp checks. |
| Stamps and tickets | Soft/hard stamp validation, HKDF-expanded workblocks, cached destination stamp costs, automatic reply-ticket issue and signed learning, ticket-backed stamps, and restart-safe directional persistence. |
| Control | --status, --peers, --sync, and --break over the propagation-control link. |
| Access lists | ignored and allowed hash-list files in the LXMF config directory. |
Most daemon and control flags are implemented: --config, --rnsconfig,
--propagation-node, --on-inbound, --status, --peers, --sync,
--break, --remote, --identity, --timeout, --exampleconfig, and
--version.
Additional rsLXMF-only flags: --send, --send-file, --send-method,
--send-timeout-secs, and --send-fields-json.
The complete corpus and live matrix for standalone 1.3.0 use Python LXMF 1.0.1 with Reticulum 1.4.2. A supplementary lane checks daemon, delivery, Resource/proof, and propagation behavior against exact LXMF 1.1.0 with the same Reticulum 1.4.2 source. Historical vectors retain their original provenance. Later source changes include selected LXMF 1.2.0 behavior; that does not establish complete compatibility with every later Python release.
If the issue or contribution belongs upstream as well, start there. Python LXMF and Reticulum remain the reference implementations.
PRs are closed for now until I have time to catch up on everything. I'm tired.
GNU Affero General Public License v3.0 or later. See LICENSE.