Skip to main content

LXMFy

Python framework for LXMF bots on the Reticulum Network.

Docs

Features

Category Capabilities
Core Interactive CLI, command prefixes, cron-style task scheduler, middleware and event systems
Connectivity Direct delivery with propagation fallback, auto-peering, RNS links, opportunistic sending, RRC hub client
Security Spam protection, role-based permissions, identity pinning, message signature policy, Landlock LSM filesystem sandbox on Linux
NLP Local offline intent classification, type-hinted argument parsing
Extensions Python cogs, external script cogs (Bash, Go, C, and others), subprocess sandboxing via Landlock, bwrap, or firejail
Storage JSON, SQLite, and in-memory backends, crash-safe outgoing message persistence
Reliability Stability and stress tests, chaos engineering hooks, resource leak checks
UX Help on first message, auto-generated help menus, customizable bot icons, attachments

Installation

Requirements: Python 3.11+, RNS 1.5.4+, LXMF 1.1.1+, cbor2 6.1.0+ (pulled in with LXMFy).

From PyPI

# pip
pip install lxmfy

# pipx
pipx install lxmfy

From source

git clone https://github.com/Quad4-Software/LXMFy.git
cd LXMFy
poetry install

Standalone zipapp

Each GitHub release ships a lxmfy-x.y.z.pyz with all dependencies bundled:

python3 lxmfy-2.0.4.pyz --help

Usage

lxmfy

Create a bot project:

lxmfy create

Debug send and receive:

lxmfy debug
lxmfy debug --config ./config --output ./lxmfy-debug-report.txt
lxmfy debug probe <destination_hash> --request-path --wait 30
lxmfy debug send <destination_hash>
lxmfy debug receive
lxmfy debug compare <hash_a> <hash_b>

Doctor mode prints a verdict and next steps, then categorized checks (OS, shared vs owned instance, disk permissions, interfaces, announce, send pipeline / storage history, receive readiness). It saves a privacy-redacted lxmfy-debug-*.txt you can share. Colors turn off when stdout is not a TTY, when NO_COLOR is set, or when Windows VT is unavailable. Use --no-color or NO_COLOR=1 for plain output.

Docker

Build and run

From the project root:

docker build -t lxmfy-test .
docker run -d \
    --name lxmfy-test-bot \
    -v $(pwd)/config:/bot/config \
    -v $(pwd)/.reticulum:/root/.reticulum \
    --restart unless-stopped \
    lxmfy-test

Host networking (AutoInterface):

docker run -d \
    --name lxmfy-test-bot \
    --network host \
    -v $(pwd)/config:/bot/config \
    -v $(pwd)/.reticulum:/root/.reticulum \
    --restart unless-stopped \
    lxmfy-test

Build a wheel

docker build -f docker/Dockerfile.Build -t lxmfy-wheel-builder .
docker run --rm -v "$(pwd)/dist_output:/output" lxmfy-wheel-builder

That copies the built wheel into ./dist_output.

Example

The smallest working bot needs a name and one command:

from lxmfy import LXMFBot

bot = LXMFBot("LXMFy Test Bot")


@bot.command("ping", description="Test if bot is responsive")
def ping(ctx):
    ctx.reply("Pong!")


@bot.command("echo", description="Echo a message", admin_only=True)
def echo(ctx, message: str):
    ctx.reply(message)


bot.run()

Everything else has defaults. Pass keyword arguments to LXMFBot to override them:

bot = LXMFBot(
    "LXMFy Test Bot",
    announce=5400,  # Announce every hour, set to 0 to disable.
    announce_enabled=True,  # Set to False to disable all announces (both initial and periodic)
    announce_immediately=True,  # Set to False to disable initial announce
    admins=["your_lxmf_hash_here"],  # List of admin hashes.
    hot_reloading=True,  # Enable hot reloading.
    command_prefix="/",  # Set to None to process all messages as commands.
    cogs_dir="cogs",  # Specify cogs directory name.
    rate_limit=5,  # 5 messages per minute
    cooldown=5,  # 5 seconds cooldown
    max_warnings=3,  # 3 warnings before ban
    warning_timeout=300,  # Warnings reset after 5 minutes
    signature_verification_enabled=True,  # Enable cryptographic signature verification
    require_message_signatures=False,  # Allow unsigned messages but log them
    propagation_fallback_enabled=True,  # Enable propagation fallback after direct delivery fails
    propagation_node="your_propagation_node_hash_here",  # Manual propagation node (optional)
    autopeer_propagation=True,  # Auto-discover propagation nodes (optional)
    autopeer_maxdepth=4,  # Max hops for auto-peering (default: 4)
    enable_propagation_node=False,  # Run as propagation node (default: False)
    message_storage_limit_mb=500,  # Storage limit in MB for propagation node (default: 500)
    direct_delivery_retries=3,  # Number of direct delivery attempts before falling back to propagation
    landlock_enabled=True,  # Linux Landlock LSM sandbox for the bot process (default)
    external_cogs_sandbox_enabled=True,  # Sandbox external script cogs on Linux
    external_cogs_sandbox_type="auto",  # auto, landlock, bwrap, firejail, or none
)

RRC (Reticulum Relay Chat)

Bots can join RRC hubs as ordinary clients over RNS Links with CBOR envelopes:

from lxmfy import LXMFBot, RRCMessage

bot = LXMFBot(
    name="RoomBot",
    rrc_enabled=True,
    rrc_hubs=["your_rrc_hub_destination_hash"],
    rrc_rooms=["lobby"],
    rrc_nick="RoomBot",
)


@bot.on_rrc
def on_rrc(event, client, payload):
    if event == "msg" and isinstance(payload, RRCMessage) and payload.mention:
        client.send_message(payload.room, f"Heard you, {payload.nick}")


bot.run()

Or connect at runtime with bot.connect_rrc(hub_hash, rooms=["lobby"]).

Hub sessions persist across restarts by default (rrc_persist_sessions=True). Outgoing LXMF messages are also persisted by default (message_persistence_enabled=True) so a crash mid-queue does not drop them. The outbound queue is bounded (message_queue_size, default 50) and drops the oldest message when full.

Propagation Node Configuration

LXMFy supports three modes for propagation node usage.

Manual configuration

Set a specific propagation node by hash:

bot = LXMFBot(
    name="MyBot",
    propagation_fallback_enabled=True,
    propagation_node="your_propagation_node_hash_here",
    direct_delivery_retries=3,
)

Automatic discovery (auto-peering)

Discover propagation nodes from network announces:

bot = LXMFBot(
    name="MyBot",
    propagation_fallback_enabled=True,
    autopeer_propagation=True,
    autopeer_maxdepth=4,
)

The bot peers with suitable nodes within autopeer_maxdepth hops.

Run as a propagation node

Store and forward messages for offline recipients:

bot = LXMFBot(
    name="MyPropagationBot",
    enable_propagation_node=True,
    message_storage_limit_mb=500,
)

message_storage_limit_mb caps disk use. Set to 0 for unlimited storage (not recommended).

Querying propagation status

status = bot.get_propagation_node_status()
print(f"Current outbound node: {status['current_outbound_node']}")
print(f"Discovered peers: {status['discovered_peers']}")

Setting the propagation node at runtime

bot.set_propagation_node("new_propagation_node_hash")

Storage limits

stats = bot.get_propagation_storage_stats()
print(f"Storage used: {stats['storage_size_mb']:.2f} MB")
print(f"Storage limit: {stats['storage_limit_mb']} MB")
print(f"Utilization: {stats['utilization_percent']:.1f}%")
print(f"Messages stored: {stats['message_count']}")

bot.set_message_storage_limit(megabytes=1000)

Propagation notes

  • Without manual config, auto-peering, or running as a node, messages that need propagation fail
  • You can combine modes (manual node plus auto-peering as backup)
  • A propagation-node bot still sends and receives normally
  • Auto-peering respects autopeer_maxdepth so distant nodes are skipped

Security and Sandboxing

On Linux kernels with Landlock support (5.13+), LXMFy can restrict filesystem access for the bot process and for external script cogs.

Bot process sandbox

When landlock_enabled=True (default), the bot applies a Landlock LSM sandbox after startup. System paths are read-only. Bot storage, config, cogs, Reticulum config, and temp directories stay writable.

bot = LXMFBot(
    name="SecureBot",
    landlock_enabled=True,
)

status = bot.get_landlock_status()
print(status)

Environment overrides:

  • LXMFY_LANDLOCK=0: disable Landlock
  • LXMFY_LANDLOCK=1: force an attempt on Linux
  • unset: follow landlock_enabled and kernel auto-detection

External script cog sandbox

Executable cogs in cogs/ can run in a restricted environment when external_cogs_sandbox_enabled=True (default). Set external_cogs_sandbox_type to:

  • auto (default): prefer Landlock, then bwrap, then firejail
  • landlock: Landlock-only via preexec_fn
  • bwrap: bubblewrap read-only bind sandbox
  • firejail: firejail private profile with no network
  • none: no subprocess sandbox

See the docs for full configuration details.

Development

poetry install
poetry run lxmfy run echo

Common Makefile targets:

make lint       # ruff check
make typecheck  # pyright lxmfy
make test       # pytest
make ci         # lint, typecheck, security check, test, build

Contributing

Send ideas and issues to LXMF: 7cc8d66b4f6a0e0e49d34af7f6077b5a

License

0BSD

Metadata

Release files for lxmfy 2.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lxmfy 2.1.1
File Size Uploaded
lxmfy-2.1.1.tar.gz 117.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lxmfy 2.1.1
File Interpreter ABI Platform
lxmfy-2.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 251.9 kB

Release files / lxmfy-2.1.1.tar.gz

Download URL lxmfy-2.1.1.tar.gz
Size 117.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6f1001a5988b6094f23734ccd313a704b201aafc0eb945ddf4d8f88193301be3
BLAKE2b-256 checksum
How to use checksums
bebebfbefd06a9aa4b7ee596c488688397e05a1cdf6cb0df1fc3c6deea3807f6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release files / lxmfy-2.1.1-py3-none-any.whl

Download URL lxmfy-2.1.1-py3-none-any.whl
Size 134.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
29d913db7261da765a8af2050b7f9580f244f3610feec57c005d1e38c50df77f
BLAKE2b-256 checksum
How to use checksums
5bccf0345cc154aca2ea5949d6908d52da3878c5c265b9dfe62ed6d6894ebd33
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.1.1 This release

2 release files

2.0.3

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.6.5

2 release files

1.6.4

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.8.0

2 release files

0.7.8

2 release files

0.7.7

2 release files

0.7.6

2 release files

0.7.5

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.9

2 release files

0.6.8

1 release file

0.6.7

2 release files

0.6.5

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.5.1

2 release files

0.4.9

2 release files

0.4.8

2 release files

0.4.7

2 release files

0.4.6

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.0

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page