This repository was archived by the owner on Oct 13, 2026. It is now read-only.
Repository navigation
·
11 commits
to master
since this release
Immutable
release. Only release title and notes can be modified.
Features
- Async def command callbacks and message handlers now run instead of silently returning coroutines
- Outbound messages to unknown destinations are held and retried on the peer's next announce instead of dropped
- LXMFBot accepts the bot name positionally: LXMFBot("mybot")
- Configurable lxmfy log_level and RNS loglevel passthrough
- Sender controls: ignore_destination, unignore_destination, is_ignored, allow_destination, disallow_destination, prioritise_destination, unprioritise_destination
- Inbound stamp controls: set_inbound_stamp_cost, enforce_stamps, ignore_stamps
- LXMF tickets: generate_ticket, get_inbound_tickets, get_outbound_ticket, get_outbound_ticket_expiry, get_outbound_stamp_cost
- Outbound delivery control: outbound_queue, get_outbound_progress, cancel_outbound, delivery_link_available
- Propagation sync: sync_propagation_node, cancel_propagation_sync, get_propagation_stats, set_retain_on_node, announce_propagation_node, allow_control_identity, disallow_control_identity
- ingest_lxm_uri imports an LXM from an lxm:// URI
- LXMF reactions: bot.react(destination, message_hash, reaction) sends a FIELD_REACTION delivery, on_reaction registers inbound handlers, msg.reaction exposes parsed reaction metadata
- pack_reaction and unpack_reaction build and parse FIELD_REACTION dicts
- Landlock sandbox now uses the landlockpy package instead of in-module syscall bindings
- Documentation moved from Sphinx/RST to Zensical/Markdown, deployed to GitHub Pages on every docs change; PDF, EPUB, and text bundles are built at release time and attached to the GitHub release
- Docs translated into German, Spanish, French, Portuguese, Ukrainian, Russian, and Simplified Chinese with release bundles per language
- Docs use a dark-first quad4 "void" theme with a light "paper" scheme, system-preference detection, and a palette toggle
- Docs are an installable PWA: service worker precaches the core pages, caches visited pages offline, and refreshes content when the browser comes back online
- Docs header gains a language picker and hreflang links for all eight languages
- Docs ship llms.txt and a generated llms-full.txt for LLM consumers
- Docs emit Open Graph, Twitter card, theme-color, and JSON-LD (WebSite plus SoftwareSourceCode) metadata with a social card image, plus robots.txt
- Delivery observability: bot.delivery records outbound lifecycle events (queued, deferred, dispatched, delivered, failed, cancelled, dropped) with subscribers via on_delivery_event and a persisted tail
- Built-in admin commands: /queue, /cancel, /delivery, /loadext, /reloadext
- Per-command rate limits via @bot.command(rate_limit=N), sharing the global cooldown window and persisted counters
- lxmfy init scaffolds a project interactively (name, template, storage, prefix, admins, cogs) with --yes/--force/--here for scripts
- Debugger reports a delivery timeline summary in send pipeline checks
- Docs are versioned with mike: master deploys as dev, releases deploy their tag and the latest alias
- Monthly dependency freshness workflow reports outdated packages to a tracking issue
- Security workflow audits dependencies with pip-audit and uploads a CycloneDX SBOM
- Docs tooling pins live in docs/requirements-docs.txt shared by the docs and release workflows
- All pip installs in workflows and Dockerfiles are hash-pinned (--require-hashes)
- Docker base images are pinned by digest and Poetry installs from a hashed requirements file
- CodeQL workflow provides GitHub-native SAST on pushes, PRs, and a weekly schedule
- master branch protection blocks force pushes and branch deletion
- Reply threading: send(reply_to=, quote=, thread=) sets FIELD_REPLY_TO/FIELD_REPLY_QUOTE/FIELD_THREAD, msg.reply() threads automatically, inbound replies parse onto msg.reply_to/msg.reply_quote/msg.thread
- Conversations: msg.ask() blocks a handler for the sender's next message, msg.ask_async() awaits it in async handlers, and on_answer/on_timeout callbacks handle long waits. Sending a command cancels a pending question. Validators re-prompt on bad answers
- lxmfy.testing: TestBot runs the real inbound pipeline without a Reticulum instance and captures outbound sends. receive, receive_later, drain, wait_sent, last_sent, and outbox cover sync and threaded handlers
- Peer discovery: get_peer_app_data, get_peer_lxmf_data, get_peer_announce, and list_peer_announces read announce metadata from the RNS transport
- Inbound introspection: has_message, inbound_count, inbound_transfers, cancel_inbound, cancel_all_inbound, plus the /inbox admin command
- pack_reply and unpack_reply build and parse reply threading fields
Fixes
- Delivery destination keeps LXMF's inbound link callbacks, so link-based delivery completes
- Scheduler no longer crashes on sub-minute announce intervals or malformed cron fields
- cleanup() only exits Reticulum when this bot started it, so shared instances survive
- Command POST_COMMAND middleware runs even when a command raises
- Outbound queue persistence snapshots under the queue lock
- Closed links are removed from the link table instead of leaking
- request_link validates destination hashes and reports typed errors
- Generated template bots accept a name argument instead of a dead attribute write
- cogs directory is only created when cogs are enabled
- CLI signatures test verifies against the identity it signed with instead of an unrecallable lookup
- disallow_destination, unprioritise_destination, and disallow_control_identity edit router lists directly because the matching LXMF 1.1.1 methods pop by index or reference unbound names
- Identity files that fail to load raise a clear RuntimeError instead of passing None to LXMRouter
- connect_rrc and disconnect_rrc guard against an uninitialized RRC manager
- Configured admins now receive the admin role when permissions are enabled, so admin-only commands and spam bypasses apply to them
Tests
- Localhost UDP pair live test with two subprocess routers (LXMFY_LIVE_UDP=1)
- Propagation tests run against a real isolated router instead of no-op asserts
- Subprocess tests pass PYTHONPATH so they work outside editable installs
- Removed permanently skipped and tautological tests
- Router control tests cover ignore/allow/prioritise lists, stamp costs, tickets, outbound queue and cancel, propagation sync, and URI ingestion against a real router
- Benchmark suite for hot paths: field packing, command unpacking, hash parsing, permission checks, canonicalization, signatures, storage
- Delivery tests cover tracker persistence and filters, outbound queued events, the /delivery command, per-command rate limits, admin permission wiring, and the init scaffold
CI/CD
- GitHub Actions for lint, typecheck, tests, live-local Alice/Bob, live UDP pair, and build
- Security workflow: zizmor workflow audit, bandit SAST, gitleaks secret scan
- Coverage XML artifact on the 3.13 test leg with a 65% coverage gate
- Releases build a self-contained .pyz zipapp (shiv) alongside the wheel and attach it to the GitHub release
- Publish workflow triggers on tag pushes and creates the GitHub release as a draft, attaches assets, then publishes, which immutable releases require
- SLSA build provenance attestations on the wheel, sdist, and pyz via GitHub artifact attestations (Sigstore)
- Test matrix covers Python 3.11 through 3.14
- Benchmark job archives results per run
- Secure defaults: SHA-pinned actions, least-privilege token, Dependabot, dependency review, Scorecard, CODEOWNERS
- OIDC publish to PyPI on GitHub release (or confirmed manual run)
Updates
- Dropped former self-hosted package registry publish targets
- Project links point at GitHub
- Pyright runs in strict mode with the untyped-dependency noise rules disabled
- Ruff lint expanded to the modern ruleset (bugbear, bandit, refactor, perf, try-except, datetime, logging, and friends)