Each command's aliases are now configured as an `aliases` key in its own
config section (e.g. [Wx_Command] aliases = !weather, !w) rather than a
separate [Aliases] section. BaseCommand._load_aliases_from_config() reads
and injects them into keywords at startup. CommandManager.load_aliases()
and _apply_aliases() are removed. No behaviour change for commands without
aliases configured.
Add live and offline packet-parsing tests using paho-mqtt:
- tests/test_mqtt_live.py: schema validation + live integration tests;
subscribes to meshcore/SEA/+/packets; validates JSON against schema
- tests/mqtt_test_config.ini: broker/topic/timeout config; primary LAN
broker (10.0.2.123:1883); letsmesh as documented alternative
- tests/fixtures/mqtt_packets.json: 8 real packets from SEA region for
offline fallback
Run live: pytest -m mqtt
Run offline: pytest -m "not mqtt"
Collect fixtures: python tests/test_mqtt_live.py --collect-fixtures
BUG-001: web viewer login/session auth (in web viewer commit)
BUG-002: db_manager ALTER TABLE for missing channel_operations and
feed_message_queue columns on startup
BUG-015: scheduler thread blocked on future.result(); replaced all
blocking waits with add_done_callback (fire-and-forget)
BUG-016: reboot_radio sends meshcore.commands.reboot() before disconnect
BUG-017: radio disconnect uses asyncio.wait_for(timeout=10)
BUG-022: custom asyncio loop exception handler suppresses IndexError
from meshcore parser at DEBUG level
BUG-024: last_db_backup_run updated after each run; 2-min startup
window; last-run seeded from DB on restart
BUG-025: send_channel_message retries up to 2 times (2s delay) on
no_event_received via _is_no_event_received() helper
BUG-026: split_text_into_chunks() and get_max_message_length() added
to CommandManager; keyword dispatch uses send_response_chunked()
BUG-028: byte_data = b"" initialised before try block in
decode_meshcore_packet to prevent UnboundLocalError in except handler
TraceCommand: path nodes reversed and return path truncated; fixed
format_elapsed_display: UTC normalisation before elapsed computation (#75)
RepeaterManager: auto_manage_contacts guard before any purge logic (#50)
Command aliases: [Aliases] config section injects shorthands at startup
JSON logging: _JsonFormatter; json_logging = true in [Logging]
Structured JSON logging compatible with Loki, Elasticsearch, Splunk
Discord bridge, Telegram bridge, and all service plugins updated
MeshGraph edge promotion logic corrected
Shutdown: scheduler and meshcore disconnect joined cleanly; log spam fixed
All modules: ruff and mypy cleanup applied (type annotations, imports)
Auth (BUG-001):
- Optional password via web_viewer_password in [Web_Viewer]; /login and
/logout; Flask session guard on all routes and SocketIO handlers
Contact management and export:
- Star contacts of any type; purge-preview + purge inactive contacts
- GET /api/export/contacts and /api/export/paths: CSV/JSON with time-range
Config tab and maintenance:
- /config page: SMTP, log rotation, DB backup settings in bot_metadata
- Nightly email digest (uptime, contacts, DB size, log errors); SMTP
timeout=30s; pre-rotation log attachment hook
- GET /api/maintenance/status: Maintenance Status card
DB backup, restore, and purge:
- POST /api/maintenance/backup_now; GET /api/maintenance/list_backups;
POST /api/maintenance/restore (SQLite magic-byte validation)
- POST /api/maintenance/purge: remove rows older than threshold
- Scheduled backups: daily/weekly/manual with retention pruning
- Config save validates db_backup_dir exists; 400 on missing path
Live streaming and realtime monitoring:
- Live Activity panel: colour-coded SocketIO feed with pause/clear
- capture_channel_message() feeds packet_stream; message_data event
- /realtime page: three independent stream panels; [#channel] prefix
- /logs page: subscribe_logs/log_line; log-tail thread; level colouring
- History replay: last 50/50/200 items on connect
- Werkzeug 3.1 WebSocket fix: _apply_werkzeug_websocket_fix()
- BUG-029: db_path resolved via config_base = Path(config_path).parent;
stored as self._config_base; dead _get_db_path() removed
Scroll/filter controls and connected agents:
- Scroll-to-top/bottom on Live Activity and all realtime panels
- Type-filter checkboxes (Packets/Commands/Messages) with applyFilters()
- GET /api/connected_clients: agent count clickable; Bootstrap modal
Add geographic_scoring_enabled = true/false to [Path_Command] config.
When disabled, path scoring uses hop count only and ignores GPS
coordinates. Evaluated per-command invocation; no restart required.
Add ScheduleCommand (DM-only by default). Displays configured scheduled
message times, target channels, message previews, and the current
advertisement interval. Read-only; does not modify schedule state.
Add ChannelRateLimiter to rate_limiter.py. Configure per-channel
cooldowns via [Rate_Limits] channel.<name>_seconds. Integrated into
_check_rate_limits() and send_channel_message() in command_manager.py.
GET /api/stats/rate_limiters exposes live stats for all four limiter
types.
Add POST /webhook endpoint to the web viewer. Authenticated via
Authorization: Bearer <token> set in [Webhook] config section. Relays
JSON or text payload to a configured MeshCore channel or user DM.
Add scripts/config_tui.py: browse, edit, save, validate, and migrate
config.ini interactively. Key bindings: r rename key, a add key+value,
d/Delete remove key with confirmation. Dynamic sections such as
[Scheduled_Messages] suppress the unknown-key ? marker. Access via
make config or CONFIG=path make config.
Add scripts/build-deb.sh using fakeroot and dpkg-deb to produce a
Debian package. Includes DEBIAN/control, postinst (venv setup, systemd
enable), prerm (systemd stop), and postrm (cleanup). Add make deb
target. Update install-service.sh and uninstall-service.sh for current
paths and Python version.
Add .github/workflows/docker-build.yml triggered on push to main and
version tags. Builds linux/amd64, linux/arm64, and linux/arm/v7 via
QEMU. Attaches SBOM and provenance attestations to the image manifest.
Update Dockerfile with non-root user and current Python base image.
Add four jobs to .github/workflows/test.yml:
- lint: ruff check modules/ tests/ — zero violations enforced
- typecheck: mypy modules/ with incremental strict mode; per-module
disallow_untyped_defs where applicable
- lint-frontend: ESLint (eslint-plugin-html) + HTMLHint on templates/
- lint-shell: ShellCheck --severity=warning on all .sh files
Add [tool.ruff] and [tool.mypy] sections to pyproject.toml.
Add .eslintrc.json, .htmlhintrc, package.json for frontend tooling.
Add Makefile with targets: install, dev, test, test-no-cov, lint, fix,
deb, config, clean. Uses .venv for isolation. Add pyproject.toml
[project] metadata and [project.optional-dependencies] dev and test
extras. Sync requirements.txt from pyproject.toml dependencies.
Test modules:
- test_enums: enum values and flag combinations
- test_models: MeshMessage dataclass field and type validation
- test_transmission_tracker: full TransmissionTracker coverage
- test_message_handler: path parsing, RF correlation, message routing
- test_repeater_manager: role detection, ACL, device type classification
- test_core: config loading, radio settings, reload paths
Tracking files:
- BUGS.md: known bugs and fix history log
- TESTING.md: test strategy, coverage targets, and how-to guide
- TODO.md: feature and task backlog with completion status
- scripts/update_todos.py: scans source for # TODO/FIXME/HACK markers
and regenerates the Inline TODOs section in TODO.md
- pytest-timeout>=2.1.0 added; timeout=30s per test prevents runaway
tests from hanging CI
- asyncio_mode=auto in pyproject.toml [tool.pytest.ini_options]; async
tests run without per-test markers
- fail_under=27 in [tool.coverage.report] as the enforced coverage
floor; target 40% tracked in TASK-14
- CI test matrix updated for Python 3.9, 3.11, 3.12
- Updated config.ini.example and discord-bridge.md to reflect the ability to fan out a single MeshCore channel to multiple Discord servers using a comma- or whitespace-separated list of webhook URLs.
- Modified DiscordBridgeService to handle multiple webhooks per channel, including validation and logging improvements for better monitoring of configured webhooks.
- Added patterns to .gitignore to exclude log files and local configuration files.
- Improved the install-service.sh script to verify the integrity of the virtual environment and ensure pip is available and up to date before installing dependencies.
- Updated the _cleanup_web_viewer and _cleanup_mesh_graph methods to avoid logging errors during shutdown, as the logger's stream may be closed at that time.
- Modified the shutdown method in MeshGraph to prevent logging of flushing errors, enhancing stability during the atexit process.
- Adjusted test configurations to use Path objects for bot_root and local_root, improving path handling in tests.
- Modified the test file to include a conditional import for the CheckInService, allowing tests to be skipped if the plugin is not installed. This change improves test reliability and clarity regarding plugin dependencies.
- Changed the meshcore dependency from a fixed version (2.2.14) to a minimum version (2.2.31) to ensure compatibility with newer features and improvements.
- Modified the RepeaterManager class to enable or disable auto-purge based on the 'auto_manage_contacts' setting from the bot's configuration.
- This change allows for more flexible management of contacts, aligning with the bot's operational mode.
- Changed the default IATA code from 'LOC' to 'XYZ' in `config.ini.example` and `packet-capture.md` to reflect updated routing requirements.
- Updated the `PacketCaptureService` to use 'XYZ' as the fallback IATA code, ensuring consistency across the application.
- Enhanced the ServicePluginLoader to add the modules directory to the path for local services, ensuring that utility modules can be resolved correctly.
- This change improves the loading mechanism for service plugins by allowing better integration of local utilities.
- Updated the ServicePluginLoader to include a built-in services directory in the module path, improving the loading mechanism for service plugins.
- Modified the class to accept BaseServicePlugin from both built-in and local services, allowing for greater flexibility in service integration.
- Enhanced the logic for identifying service classes, ensuring compatibility with multiple base service implementations.
- Updated the TraceCommand class to enhance the labeling of hops in the trace output.
- Replaced direct hash retrieval with a conditional approach to determine the source label for each hop, improving clarity in the output format.
- Ensured consistent labeling for the first and last hops, enhancing the readability of the trace results.
- Updated the command to handle 1-, 2-, or 3-byte hex prefixes, with longer input truncated to 3 bytes.
- Improved usage instructions and examples to clarify accepted prefix formats (2, 4, or 6 hex characters).
- Enhanced validation logic for prefix format, ensuring proper handling of hex strings and truncation.
- Updated documentation strings to reflect changes in prefix handling and expected input formats.
- Updated the MeshGraph class to prevent promoting a 1-byte edge to a 3-byte edge when the existing 1-byte edge lacks public keys, ensuring accurate observation attribution.
- Added a new test case to verify the behavior of edge promotion under specific conditions.
- Modified the BotDataViewer API to return the prefix length dynamically based on the edges, improving data consistency and user experience in the web viewer.
- Enhanced the mesh.html template to support displaying prefix byte counts, providing clearer information on node connections.
- Introduced a check in `load_service_from_path` to ensure the `local_services` module is created in `sys.modules` if it doesn't exist, facilitating proper relative and absolute imports for service plugins.
- This enhancement improves the loading mechanism for local service plugins, ensuring smoother integration and functionality.
- Added support for rotating log files in `core.py` using `RotatingFileHandler`, with a maximum size of 5 MB and up to 3 backup files.
- Updated `config.ini.example`, `config.ini.minimal-example`, and `config.ini.quickstart` to include descriptions of the new log rotation feature.
- Enhanced `data-retention.md` to clarify log file management and retention policies.
- Updated documentation in `data-retention.md` and `web-viewer.md` to clarify how stats are collected and displayed, including the new `collect_stats` configuration option.
- Modified `StatsCommand` in `stats_command.py` to introduce `collect_stats`, allowing message and command statistics to be recorded even when the `stats` command is disabled.
- Adjusted logic in `record_message`, `record_command_stats`, and `record_path_stats` methods to utilize the new `collect_stats` setting for improved flexibility in stats tracking.
- Updated `config.ini.example` and `config.ini.minimal-example` to include optional `local_dir_path` for specifying a custom local plugins directory.
- Enhanced `install-service.sh` to preserve the `local/` directory during installation and create its structure if it doesn't exist.
- Modified `CommandManager` and `MeshCoreBot` to resolve the local commands and services directory based on the new configuration.
- Added validation in `config_validation.py` to check the existence and readability of the specified local plugins path.
- Improved documentation in `local-plugins.md` to clarify the usage of local plugins and the new configuration options.
- Added a `join` method in `MessageScheduler` to wait for the scheduler thread to finish during shutdown.
- Updated the shutdown sequence in `core.py` to include a timeout for the `meshcore.disconnect()` method, with error handling for potential timeouts and exceptions.
- Improved logging to provide clearer information during the shutdown process.
- Added `send_channel_messages_chunked` method to `CommandManager` for sending multiple messages with appropriate rate-limit spacing.
- Introduced `send_response_chunked` method in `BaseCommand` to facilitate chunked responses in both channels and DMs.
- Updated `GreeterCommand` to utilize the new chunked response functionality, simplifying the greeting message sending process.
- Enhanced documentation in `local-plugins.md` with examples for using chunked message sending, improving clarity on handling long messages and rate limits.
- Changed `meshcore` dependency version in `requirements.txt` from `>=2.2.14` to `==2.2.14` for consistency.
- Added detailed instructions in `SERVICE-INSTALLATION.md` regarding Python version compatibility and potential f-string issues with Python 3.11.
- Introduced a new section in `local-plugins.md` explaining how to handle message chunking and bot rate limits, providing code examples for better clarity.
- Updated MeshGraph to support multi-resolution storage and node identity, allowing for better handling of 1-byte, 2-byte, and 3-byte edges.
- Implemented logic for merging and promoting edges based on observation counts and specificity.
- Enhanced BotDataViewer API to dynamically adjust prefix length based on configuration, improving data representation.
- Updated mesh.html template to display prefix byte counts, enhancing user experience with clearer information on node connections.
- Added support for bytes per hop in the BotDataViewer, allowing for better path data representation.
- Updated the contacts template to display bytes per hop and adjusted path formatting based on this value.
- Improved the decode path functionality to utilize the correct bytes per hop for decoding paths, enhancing overall path handling and user experience.