Commit Graph
799 Commits
Author SHA1 Message Date
agessaman c4d7fe6fd3 feat(contacts): implement pagination, search, and sorting for contacts API
- Enhanced the `/api/contacts` endpoint to support pagination, allowing users to specify `page` and `page_size` parameters.
- Added search functionality to filter contacts based on a search term.
- Implemented sorting options for the contacts list, enabling users to sort by various fields.
- Updated the frontend to reflect these changes, including a new pagination UI and improved loading states.
- Introduced caching for multibyte hop chunks to optimize performance when retrieving contact badge evidence.
- Added tests to ensure the new features work as expected and maintain existing functionality.
2026-07-23 08:26:00 -07:00
Adam Gessaman d83524b10a Merge pull request #223 from agessaman/p0-security-hardening
Harden P0 security and release boundaries
2026-07-22 16:39:52 -07:00
agessaman 5f97cff8d7 ci: install the geo extra so location tests match a real install
Tests (all four Python versions) failed on
test_over_budget_same_state_omits_prefix while passing locally. Root
cause: CI installed only ".[test]", leaving out the optional geo extra
(us, pycountry). normalize_us_state() returns (None, None) when `us` is
absent, so "Washington" no longer normalizes to "WA" and the AQI
prefix-budget path wrongly concluded the resolved state differed from
default_state, keeping the "Seattle, WA: " prefix.

Install ".[test,geo]" in both the test matrix and the mypy job. This
also un-skips ~27 geo-dependent tests that were silently skipping in CI
and which pass with the extra present.

Also ignore _debug/, a local scratch directory for captured configs and
logs that should never be committed.
2026-07-22 14:55:58 -07:00
agessaman 388c2925cf chore(ci): fix mypy Optional inference and ruff import order
Finish the lint/type cleanup so all CI gates pass:

- location.py: declare lat/lon as Optional[float] up front. The
  fallback/coordinates/repeater branches bind plain floats while the
  zipcode/city branches bind float | None from best-effort geocoding, so
  a single Optional declaration keeps mypy from pinning the first binding.
- rain_command.py: sort imports to ruff/isort order and mark the
  US_STATE_ABBRS / titlecase_location re-exports noqa: F401. They are
  listed in __all_location_reexports__ rather than __all__, so ruff does
  not recognize them as intentional re-exports on its own.
- test_location_characterization.py: drop a stray blank line (ruff E303).
2026-07-22 14:54:31 -07:00
agessamanandClaude Opus 4.8 03a1d4056b Merge origin/dev into codex/p0-security-hardening
Resolve the feeds.html conflict by keeping the branch's XSS-safe DOM
construction of the feed-details view and re-adding dev's per-feed and
reset-all error buttons via addEventListener instead of inline onclick,
since /feeds is a nonce-CSP page where inline handlers are blocked.

Update tests/test_feed_manager_extended.py::TestPollFeedPosting to patch
the SafeUrlPolicy.validate_async path (this branch's SSRF refactor) rather
than the removed module-level validate_external_url symbol, matching the
idiom already used throughout that test file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 14:28:13 -07:00
agessamanandClaude Opus 4.8 08ed8464b8 fix(security): close IPv4-mapped IPv6 metadata SSRF bypass and mypy types
_check_address now canonicalizes IPv4-mapped IPv6 addresses (e.g.
::ffff:169.254.169.254) to their embedded IPv4 target before the metadata
and non-unicast checks. Previously the mapped form was a distinct address
object absent from _METADATA_ADDRESSES with is_reserved=False/is_global=False,
so under allow_private=True it slipped past every check and the socket layer
still dialed the mapped IPv4 metadata endpoint. test_allow_private_does_not_
allow_metadata is parametrized over the plain and mapped spellings.

Also cast SafeAiohttpResolver.resolve's ResolveResult host/port to str/int to
satisfy aiohttp's TypedDict (fixes the two mypy errors in the strict-overrides
CI gate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 14:15:51 -07:00
Adam Gessaman c93025061a Merge pull request #224 from agessaman/refactor/consolidate-location-tools
Enhance feed management and location resolution features
2026-07-22 13:46:16 -07:00
agessaman 3a896ec1ea feat(location): enhance geocoding and error handling in location resolution
- Added asynchronous support for location resolution, improving performance and responsiveness.
- Introduced detailed error handling for invalid latitude and longitude inputs, providing specific feedback to users.
- Implemented a caching mechanism for geocoding results to optimize repeated lookups.
- Refactored the AQI command to streamline location handling and improve clarity in error messages.
- Expanded unit tests to cover new geocoding features and error scenarios, ensuring robust functionality.
2026-07-20 09:35:13 -07:00
agessaman 6cb5c34a9a feat(location): enhance location resolution and geocoding functionality
- Introduced a shared API for location resolution using `modules.location`, allowing for better handling of place lookups (coordinates, ZIP codes, city names).
- Updated geocoding functions to accept both strings and structured dictionaries, improving flexibility in location queries.
- Refactored existing commands (AQI, Rain, Wx) to utilize the new location resolution methods, streamlining the codebase and enhancing maintainability.
- Removed redundant location handling logic from commands, centralizing functionality in the new location module.
- Added tests to ensure proper classification and resolution of various location formats, including multi-word international cities and ZIP codes with surrounding whitespace.
2026-07-19 18:23:00 -07:00
agessaman bbe91b5f2d feat(feed-manager): add support for custom post limits and improve emoji handling
- Introduced max_posts_per_check to control the number of posts processed per check, maintaining backward compatibility with max_items_per_check.
- Enhanced FeedManager logic to ensure only the specified number of posts are processed while examining a larger set.
- Improved emoji selection to prioritize per-item emojis from the API, with fallback heuristics based on feed names.
- Added new functions for message truncation and substring handling, enhancing text formatting in feed outputs.
- Implemented API endpoints for resetting feed error counts, improving error management in the web viewer.
2026-07-17 21:01:17 -07:00
agessaman c1c3ac2511 feat(feed-manager): introduce max_posts_per_check and enhance emoji handling
- Added max_posts_per_check to limit the number of items posted per check, defaulting to max_items_per_check for backward compatibility.
- Updated logic in FeedManager to process new items, ensuring that only the specified number of posts are made while examining a larger set of items.
- Enhanced emoji selection to prioritize per-item emojis from the API, falling back to heuristics based on feed names when absent.
- Introduced new truncation and substring functions for formatting messages, improving text handling in feed outputs.
- Added API endpoints to reset feed error counts globally or for individual feeds, enhancing error management capabilities in the web viewer.
2026-07-17 16:43:08 -07:00
agessaman 1f0ec48448 Harden feed, config reload, and wx async paths 2026-07-16 15:40:20 -07:00
agessaman a24d756abd Harden outbound URL and async provider boundaries 2026-07-16 15:22:12 -07:00
agessaman 119fce2211 feat(command-manager): implement DM length guard and auto-splitting for oversized messages
- Added a mechanism to check the byte length of direct messages (DMs) before sending, ensuring they do not exceed the firmware's maximum limit.
- Implemented a method to split oversized messages into smaller chunks, preserving UTF-8 encoding and avoiding mid-codepoint splits.
- Enhanced logging to provide warnings when messages are auto-split, including details on the number of chunks created.
- Updated the DM sending logic to handle both single and split messages efficiently.
- Introduced a new static method for splitting text into UTF-8 chunks, improving message handling across the application.
2026-07-16 14:56:42 -07:00
agessaman f804d05d7f feat(command-manager): implement DM length guard and auto-splitting for oversized messages
- Added a mechanism to check the byte length of direct messages (DMs) before sending, ensuring they do not exceed the firmware's maximum limit.
- Implemented a method to split oversized messages into smaller chunks, preserving UTF-8 encoding and avoiding mid-codepoint splits.
- Enhanced logging to provide warnings when messages are auto-split, including details on the number of chunks created.
- Updated the DM sending logic to handle both single and split messages efficiently.
- Introduced a new static method for splitting text into UTF-8 chunks, improving message handling across the application.
2026-07-16 12:49:38 -07:00
agessaman af7b82dfdf Fix remaining Python 3.10 UTC usage 2026-07-16 10:46:46 -07:00
agessaman 2922b4e640 Fix Python 3.10 and plugin loader CI 2026-07-16 10:40:37 -07:00
agessaman 9ae9ebb327 Harden P0 security and release boundaries 2026-07-16 10:21:50 -07:00
Adam Gessaman 614bf81fe1 Merge pull request #219 from agessaman/perf/mesh-graph-load
perf(web-viewer): speed up mesh graph load
2026-07-12 16:31:23 -07:00
agessaman ffee28c0ff fix(config): add type ignore for optionxform and handle None values in path inference
- Added a type ignore comment for the assignment of optionxform in config_schema.py to suppress type checker warnings.
- Updated path inference logic to return None when bot latitude or longitude is not set, ensuring safer handling of missing values.
- Enhanced the decoding of path nodes to check for None before appending repeater details, preventing potential errors.
2026-07-12 16:31:05 -07:00
agessaman 2324f23832 perf(web-viewer): speed up mesh graph load
Three independent wins, measured against a 1.4 GB production database
(714k observed_paths, 38.7k mesh_connections):

- Migration 0014: partial covering index on observed_paths for
  bytes_per_hop >= 2. The multibyte evidence query was a full table
  scan (2.2 s); with the index it reads only the index (77 ms). The
  optional days filter is covered too via last_seen in slot 2.

- Compress responses with flask-compress when the client supports it.
  /api/mesh/edges alone is 16.3 MB of JSON uncompressed, 3.9 MB
  gzipped. Falls back gracefully (with a warning) if the package is
  not installed.

- Fetch stats, edges, and nodes concurrently on the mesh page instead
  of three serialized awaits. Node prefixes are now computed client-side
  from public_key at the edge prefix length, which removes the
  edges-before-nodes ordering dependency. Rendering still waits for
  stats so initial framing can use the bot location.

Also call map.invalidateSize() before the initial fitBounds: if the
map was created while its container had no layout (e.g. a background
tab), Leaflet's cached zero width made fitBounds zoom to max.
2026-07-12 15:00:46 -07:00
agessaman 91b1607bf5 feat(web-viewer): frame initial mesh map on the home multi-byte component
Nodes occasionally carry wrong GPS, which stretched the initial
fit-to-all-nodes view across continents. /api/mesh/stats now exposes
the bot's configured position ([Bot] bot_latitude/bot_longitude), and
the mesh page BFSes the multi-byte-evidence subgraph from repeaters
within 30 km of the bot (nearest one as fallback), framing the initial
map view to that connected component. Falls back to the old fit when
the bot position is unset or the component is trivially small; other
mesh islands stay on the map, just outside the initial frame.
2026-07-12 11:57:28 -07:00
agessaman b2f700e15f feat(web-viewer): heat-colored repeater dots and density-aware sizing
- Hot connections mode now also colors repeater dots with viridis by
  their total observation count across touching edges (log-normalized,
  same dark-mode ramp compression as edges); node popup gains a Path
  Observations row and the heat legend a clarifying note
- Density pass: smaller dot base/cap (5..13 vs 6..16), edge widths
  capped at 5px, and both dots and lines scale down to 55% as the map
  zooms out (full size from zoom 12 in), rescaled live on zoomend
- Node degree and observation totals are now precomputed once per
  filter pass (computeNodeStats) instead of per-node scans over all
  edges, using the same endpoint resolution as the renderers
2026-07-12 11:57:28 -07:00
agessaman 47e2047d5f fix(web-viewer): allow CARTO basemap tiles in the CSP img-src
The dark-mode basemap loads from *.basemaps.cartocdn.com, which the
Content-Security-Policy image allowlist blocked.
2026-07-12 11:57:28 -07:00
agessaman 1f7a1db1b0 feat(web-viewer): mesh graph dark mode, viridis heat coloring, compact header
- Multi-byte evidence is now the default mode for the mesh graph
- Mesh display theme: follows the site theme until toggled on the page,
  then remembered independently (localStorage); dark CARTO basemap,
  dark Leaflet popups/controls/legends, dark graph canvas
- Hot connections: optional viridis edge coloring by log-scaled
  observation count with a gradient legend; the near-black cold end of
  the ramp is compressed away in dark mode so cold edges stay visible
- Single-row header (title + stat chips + icon controls) replaces the
  title and stats-card rows; footer hidden on this page to maximize the
  visualization viewport
- Mobile: page scrolls naturally instead of starving the fixed-height
  flex container (which stranded the absolutely-positioned legends)
2026-07-12 11:57:28 -07:00
agessaman 2fb389b268 fix(launch): point web-viewer launch config at repo config.ini
The committed config path referenced a session scratchpad directory that
no longer exists, so the web-viewer launch entry could not start.
2026-07-12 11:57:28 -07:00
agessaman fb9d5f4115 fix(web-viewer): log-scale Min Observations slider with sane default
The old slider defaulted to floor(avg observations) — on heavy-tailed data
the average sits above the 75th percentile, so the first visit hid most of
the graph. Worse, the default (e.g. 98) exceeded the slider's max of 50:
the browser clamped the control while the label kept showing the unclamped
value, so the label lied about the active filter.

- slider position now maps logarithmically onto 1..max(observation_count),
  rescaled whenever edge data loads, giving fine control at the low end
  and full reach into the tail
- first-visit default is the median observation count of the loaded edges
- the persisted value is the threshold itself, so it stays meaningful when
  the scale changes (e.g. switching evidence modes)
- label shows the threshold plus live 'shown/total edges' feedback
2026-07-12 11:57:28 -07:00
agessaman f2dc37f28f feat(web-viewer): multi-byte evidence mode for the mesh graph
Single-byte repeater identity guessing has been persistently unreliable,
and mesh_connections flattens away evidence provenance (confirmed_2byte
never persists). observed_paths retains it: bytes_per_hop marks which
paths carry unambiguous multi-byte hops.

- /api/mesh/edges?evidence=multibyte derives edges purely from unique
  multi-byte path observations (consecutive hop pairs, aggregated with a
  distinct-path count); 2-byte edges coalesce into a 3-byte edge only on
  a unique prefix match, mirroring MeshGraph.add_edge
- default mode tags each edge evidence=multibyte|singlebyte by key length
- Evidence filter on the mesh page (persisted with the other filters);
  single-byte edges render dashed in map and graph views, with an
  evidence line in tooltips and a legend entry
- client-side haversine fallback for edges without a stored distance
- prefix-compatible edge/node matching so degree sizing and node details
  work when edge and node prefixes differ in resolution
- /api/mesh/stats reports multibyte_edges; shown on the Edges stat card
2026-07-12 11:57:28 -07:00
agessaman 4d49c6b0b8 feat(web-viewer): Node Settings card on the Radio page
Adds device-level companion settings to /radio, headlined by the response
path hash size (mode 0-2 = 1-3 bytes per hop) wired to the existing
firmware config endpoints. New sections: Identity & Adverts (name, advert
lat/lon, location policy, zero-hop/flood advert buttons), Mesh Behavior
(extra ACKs, telemetry permissions), and write-only Advanced Tuning
(RX delay base, airtime factor, sent x1000 per the wire format).

Backend: GET /api/radio/params now returns the full SELF_INFO node fields;
POST accepts the new fields with validation; new POST /api/radio/advert;
scheduler ops handle the writes (other-params group as one read-modify-write
frame so partial updates never clobber device state).

Config-managed settings are not writable from the panel: new-contact mode
is owned by [Bot] auto_manage_contacts and shown read-only; the node name
is locked while bot_name + auto_update_device_name manage it. loop.detect
is removed from the firmware endpoints entirely - it is repeater/room-server
CLI config, and companion custom vars map to sensor settings.

Also: dark-mode styling for disabled form fields (base.html), TASK-01 guard
test updated for the deliberate reintroduction, 23 endpoint/template tests.
2026-07-12 11:57:28 -07:00
agessaman 8c5b932634 refactor(config): single source for legacy enabled-key aliases
The legacy [Section] enabled alias map (e.g. [Jokes] joke_enabled,
[Stats] stats_enabled) was duplicated in BaseCommand.get_config_value and
modules/settings_schema.py, so runtime behavior and the web settings UI
could drift apart. Both now read config_schema.LEGACY_ENABLED_ALIASES.
2026-07-12 11:57:28 -07:00
agessaman 65d0f48c04 style: ruff import fixes 2026-07-12 11:57:28 -07:00
agessaman 1211c5ebe9 docs(web-viewer): document the Plugins settings page 2026-07-12 11:57:28 -07:00
agessaman bb237ecfa8 feat(startup): validate config on every start; polish ini_writer appends
- meshcore_bot.py: run config validation on normal startup and print
  errors/warnings (capped at 15) — misspelled sections/keys previously
  failed silently, the top source of configuration confusion
- ini_writer: append new keys directly under a section's last entry
  instead of after its trailing blank line
- add .claude/launch.json for browser-preview of the standalone viewer

Verified end-to-end in the browser: /plugins renders 43 command cards,
opt-in commands show off, dynamic-row add/save writes config.ini with a
timestamped backup and preserved comments.
2026-07-12 11:57:28 -07:00
agessaman 448dc39261 docs(config): document settings-UI keys in config.ini.example + drift test
Eight keys that plugins read and the web settings UI writes were missing
from config.ini.example, so config validation flagged them as unknown:
Path_Command confidence symbols, Alert_Command max_distance_km /
max_incident_age_hours, Wx_Command temperature_unit / wind_speed_unit,
and PacketCapture mqtt_enabled.

Adds a sync test so any future settings_schema key must be documented in
the example config (and thus known to validation) or CI fails.
2026-07-12 11:57:28 -07:00
agessaman 2e8ccf283a fix(settings-ui): make config reload honor deletions and cached command settings
- reload_config: clear all sections before re-reading so keys deleted from
  config.ini (e.g. rows removed in the settings UI) don't survive in memory
  (ConfigParser.read merges instead of replacing)
- reload_config: re-instantiate command plugins so settings cached in
  __init__ (including 'enabled') take effect on reload
- /api/plugins save: treat dynamic_sections / repeating_blocks absent from
  the payload as 'don't touch' instead of 'delete all managed keys'
- settings view: respect per-plugin settings_enabled_default so opt-in
  commands (Announcements, Greeter) display as off when unconfigured
- ruff: drop unused import, organize command_manager imports
- add tests for ini_writer, settings_schema, settings_store and the
  /api/plugins endpoints (38 cases) plus reload regression tests
2026-07-12 11:57:28 -07:00
agessaman 3ccfd63613 feat(config): add config panel for modifying plugin settings, with config schema in each command and service plugin. 2026-07-12 11:57:28 -07:00
agessaman c5bcfb3f26 feat(config): enhance connection configuration and documentation
- Updated `config.ini.example`, `config.ini.minimal-example`, and `config.ini.quickstart` to clarify connection type precedence and required keys for serial, BLE, and TCP connections.
- Added detailed comments and examples for each connection type, ensuring users understand how to configure their connection settings effectively.
- Introduced a new `[Service_Overrides]` section in `config.ini.example` for alternative service plugin implementations, improving extensibility.
- Enhanced documentation in `configuration.md` to reflect these changes and provide clearer guidance on connection options and templates.
2026-07-09 23:25:11 -07:00
agessaman b02f00cac6 feat(webhook_service): start webhook service early and handle readiness
- Modified the core service to start the webhook service before establishing a radio connection, reducing the window for connection refusals.
- Implemented a readiness check in the webhook service to return a 503 status when the bot is not connected, ensuring clear communication to external callers.
- Added unit tests to verify the webhook service's behavior when the bot's connection status changes, including rate limiting and readiness responses.
2026-07-08 14:48:47 -07:00
agessaman 7e7279875c feat(packet_capture): decode packet payloads to MQTT and log
Add optional payload decoding to the packet capture service. GRP_TXT
channel messages are decrypted (sender/text), ADVERTs are parsed
(name/role/lat-lon), and a nested "decoded" object is attached to each
packet alongside the unchanged raw fields.

- Comprehensive channel key store: bot's configured radio channels plus
  decode_hashtag_channels, [Channels_List], decode_channel_keys, and the
  built-in default Public key.
- Publishing the decoded object to MQTT is off by default and
  configurable per broker via mqttN_include_decoded.
- Configurable packet-log rotation (off/size/time) for historical dumps.

The decoder lives in a standalone, dependency-free module
(modules/meshcore_payload_decode.py) so it can be shared verbatim with
the meshcore-packet-capture project.

Closes #197
Closes #35
2026-07-08 10:49:06 -07:00
agessaman 055fa3291b refactor(message_handler): change logging level for event handling
- Updated logging statements in the `MessageHandler` class to use `debug` level instead of `info` for `RAW_DATA` and `NEW_CONTACT` events, reducing log verbosity while maintaining essential information.
- This change enhances the clarity of logs by reserving `info` level for more significant events.
- Resolves issue #214
2026-07-08 09:12:18 -07:00
agessaman 02ce8beaaf feat(config, validation): enhance config validation with strict mode and update example checks
- Added a `--strict` option to `validate_config.py` to fail on "Unknown section" messages, aimed at CI validation of example configs.
- Updated GitHub Actions workflow to validate shipped example configs against the canonical section list using the new strict mode.
- Expanded the `CANONICAL_NON_COMMAND_SECTIONS` in `config_validation.py` to include additional sections for improved validation accuracy.
- Introduced a regression test to ensure example configs do not trigger unknown section warnings, maintaining consistency with the canonical list.
2026-07-08 08:56:05 -07:00
agessaman 95b0601980 feat(command_prefix, config): enhance command prefix handling and documentation
- Updated `config.ini.example` to support single, multiple, and decorative command prefixes, improving flexibility in command invocation.
- Refactored `CommandManager` and `BaseCommand` to utilize new command prefix parsing logic, allowing for better handling of multiple prefixes and optional bare commands.
- Enhanced documentation to clarify command prefix configuration options and behavior, ensuring users understand how to set and use prefixes effectively.
- Added unit tests to verify the correct functionality of command prefix handling, including support for permissive and strict prefix modes.
2026-07-07 09:37:28 -07:00
agessaman 18f231b142 feat(config, command): add hops placeholders to path command responses
- Updated configuration examples to include new `{hops}` and `{hops_label}` placeholders for path command replies, enhancing response detail.
- Modified `BaseCommand` to support hop count retrieval and formatting, ensuring accurate display of hop information in responses.
- Enhanced unit tests to verify correct formatting of hop-related placeholders in response messages, improving overall command functionality.
2026-07-06 11:29:47 -07:00
agessaman b59fa9cec9 feat(config, docs): enhance rain command and stats collection configuration
- Updated the `[Rain_Command]` section in `config.ini.example` to include support for snow alongside rain, improving the command's functionality.
- Enhanced documentation for the rain command to reflect the new snow alias and clarify response behavior based on the selected keyword.
- Added a new `collect_stats` option in the `[Stats_Command]` section, allowing stats collection to be enabled independently of the user-facing command, with updated documentation to explain its behavior.
- Improved the web viewer documentation to clarify how stats are collected and displayed, ensuring users understand the configuration options.
2026-06-28 13:28:23 -07:00
agessaman b8decd8d66 feat(command_manager): enhance help command channel access handling
- Updated the `CommandManager` to respect the `channels` override for the help command, ensuring it checks channel permissions directly when processing help requests.
- Refactored the logic to fall back to global `monitor_channels` only when no specific help command is loaded, improving channel-specific access control.
- Added unit tests to verify the correct behavior of the help command in disallowed channels, ensuring it only responds in permitted contexts.
2026-06-28 09:52:37 -07:00
agessaman 0c37f48a45 feat(command_manager, help_command): enhance help command handling and testing
- Updated the `CommandManager` to respect the `help_enabled` flag for the help command, ensuring it only responds when the command is enabled.
- Refactored help request processing to streamline checks for help keywords and channel restrictions.
- Added unit tests to verify the behavior of the help command when enabled and disabled, ensuring proper suppression of responses when the command is not active.
2026-06-28 09:45:58 -07:00
agessaman d23ee5db9e refactor(config): remove custom syntax section from configuration files
- Deleted the `[Custom_Syntax]` section from `config.ini.example` and `core.py`, simplifying the configuration by removing outdated custom syntax patterns and examples.
- This change streamlines the configuration files, focusing on essential settings and improving clarity for users.
2026-06-28 08:55:25 -07:00
agessaman 24fdfb2370 feat(command_manager, help_command): improve command prefix handling and help command visibility
- Enhanced `CommandManager` to persist normalized message content, preventing prefix stripping from interfering with command matching.
- Updated `BaseCommand` to skip prefix handling if already normalized, ensuring all commands can be matched correctly.
- Modified `HelpCommand` to ensure unused commands are listed even when command statistics are present, improving user accessibility to all commands.
- Added unit tests to verify the correct behavior of command matching and help command functionality under various scenarios.
2026-06-28 08:42:58 -07:00
agessaman 6584800cb7 feat(command_manager, models): enhance flood scope handling for channels
- Introduced per-channel flood scope configuration in `config.ini.example`, allowing for more granular control over message routing.
- Updated `CommandManager` to normalize channel names and resolve flood scopes based on channel-specific settings, improving message delivery accuracy.
- Enhanced `MeshMessage` to consider channel-specific flood scopes when determining the effective outgoing flood scope.
- Added unit tests to verify the new flood scope resolution logic and ensure correct behavior across various scenarios.
2026-06-27 17:09:28 -07:00
agessaman cee36d7d31 docs(web-viewer): add reverse proxy configuration with Nginx and basic auth
- Introduced a new section in the web viewer documentation detailing how to set up a reverse proxy using Nginx with basic authentication for enhanced security when exposing the web viewer outside the local network.
- Updated instructions for network access to recommend setting a password or using a reverse proxy for authentication.
- Included example Nginx server block configuration and necessary proxy parameters for proper functionality.
2026-06-27 17:00:31 -07:00