Files
potato-mesh/CHANGELOG.md
T

855 lines
85 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- Copyright © 2025-26 l5yth & contributors -->
<!-- Licensed under the Apache License, Version 2.0 (see LICENSE) -->
# CHANGELOG
### Features
* Web/Data: Reticulum gets its own visual identity - trifoil marker on violet `#7b61ff`, MeshCore's tile moves to `#1f2937`, a four-step role color ramp, hexagon shape, and a legend group under MeshCore (SPEC RD1-RD8)
* Data: Reticulum node roles are derived from the announce aspect, so a multi-aspect peer no longer flips roles (SPEC RD4/RE10)
* Data/Web: the Reticulum host appears on the map at the position set by the `latitude`, `longitude` and `height` keys of its RNS config's first RNodeInterface block (SPEC RP1-RP10); keys already in that block are published after upgrading; remove them and restart the ingestor to stop (README Reticulum)
* Web: the federation table renders a Reticulum zero count as a dash instead of a tile (SPEC RD8)
* Web: the map legend stacks Reticulum under MeshCore, two columns instead of three (SPEC LS1)
* Web: mesh.dmz.pt joins the default federation seed nodes and potatomesh.jmrp.io leaves them
* Data/Web: MeshCore channel messages carry their path, RSSI and flood scope, and chat shows a route chip with hops and scope; path and RSSI were never captured before (#765, SPEC SC1-SC10)
* Data: MeshCore channel messages scoped to a country code, `eu`, or a German or Austrian state or Swiss canton code (`de-by`, `at-9`, `ch-zh`) show that region also when the radio's default flood scope is another region or unset (SPEC SC3)
### Fixes
* Web: MeshCore `@[Name]` mentions and replies no longer create, refresh, or revive nodes (#883)
* Web: a live refresh no longer moves the chat: a scrolled-up reader keeps their place, the channel tab strip stops sliding back, and tab focus and text selection survive (#881, SPEC DR1/DR3)
* Web: chat lines lead with `HH:MM` and the message: seconds, frequency and preset move to the time's tooltip, channel tabs whose messages share one radio drop the `[freq][preset]` tag and protocol icon, the hop count and scope follow the text, and a reply shows an arrow and the replied-to badge (SPEC CD1-CD4)
* Web: the chat tab bar shows the channel dropdown only when the tabs do not fit (SPEC CD6)
* Web: chat day dividers read like "Thu 8 Oct" on a centred rule (SPEC CD5)
* Web: on screens 660 to 1024 px wide the dashboard chat takes 40% of the height under a 45% map (SPEC CD7)
* Web: above 900 px the full-screen chat uses 13 px type in lines of at most 120 characters and shows its right border (SPEC CD8)
* Web: a MeshCore channel message stays on its sender's node and no longer moves to, or revives, a same-name node; a sender named by a stale roster is credited to the current key (#883)
* Web/Matrix: MeshCore channel messages, whose sender is matched by the name in the text, carry `sender_verified: false` in `GET /api/messages`; the Matrix bridge posts them as its appservice user instead of the node's puppet. In an invite-only room, invite the appservice user (SPEC SV1-SV5)
* Web: the chat no longer shows "unverified" on a MeshCore channel line whose sender is matched by name (SPEC SV3)
* Web: the chat route chip shows `??` for an unknown flood scope, with an "unknown scope" tooltip, instead of `scoped` (SPEC SC7)
* Web: the map legend's Reticulum group sits 12 px further below the MeshCore group (SPEC LS1)
* Web: the map legend shows visible roles as plain text and hidden roles grey and struck through, and Clear filters turns blue while a role or protocol filter is active (SPEC ML1)
* Web: the map zoom buttons match the dark map controls on the dashboard, /map and Federation maps, and grow to 44 px on touch screens (SPEC ML2)
* Web: on the dashboard, /map and Federation maps the legend stays inside the map and below its buttons, and scrolls with the mouse wheel or a swipe when the map is short (SPEC ML3)
* Web: node roles read as words, such as "Router late", in the map legend, the nodes table, the node page and the map overlays; the role code shows on hover (SPEC ML4)
* Web: the map legend, toolbar, activity card, status message, zoom buttons and popups share one dark style, and Federation map popups are no longer white (SPEC ML5)
* Data: the MeshCore ingestor no longer posts placeholder nodes for chat senders or mentions, so hidden-channel names stay hidden; the web app ignores such posts from older ingestors (#883)
* Data: a MeshCore sender name shared by several roster contacts resolves to the most recently advertised one (#883)
* Data: the MeshCore ingestor posts an advert from the radio's RX log only when its signature verifies, refusing small-order keys and replayed adverts, so a node's SNR, RSSI and hops now come from the first copy heard; roster contacts are unchanged (SPEC SG1-SG5)
* Web: live refreshes keep unchanged nodes-table rows, so an open `+` row, focus, a text selection, an overlay opened from a badge and the row under a scrolled page stay put; a moved overlay's close button now works (#881, SPEC DR1)
* Web: nodes table cells show bare numbers with fixed decimals, and each numeric column header names its unit, Frequency (MHz) included; a powered battery reads `100 ⚡` (SPEC DV1)
* Web: long names in the nodes table are underlined only while their row is hovered or the link has keyboard focus (SPEC DV2)
* Web: the node page and /charts align their content to the 16 px page edge, the node overlay to its dialog padding (SPEC DV4)
* Web: the federation table uses 12 px text, the nodes table's size, and 5 px 8 px cells (SPEC DV5)
* Web: chart titles are 15 px, chart time ranges and legends 12 px (SPEC DV6)
* Web: code on static pages uses the app's monospace font (SPEC DV7)
* Web: static pages no longer scroll sideways on phones; a wide code block scrolls inside the page (SPEC DV8)
* Web: the dashboard no longer scrolls sideways on a 375 px phone; the nodes table fits its column (SPEC DV9)
* Matrix: the bridge crate declares its crates.io package metadata, and its README screenshot no longer links outside the crate (#879)
* Data: `TRANSPORT=udp` listens on both Meshtastic multicast groups by default, `239.0.0.69` (firmware 2.8+) and `224.0.0.69`; `MESH_UDP_GROUP` takes a comma-separated list. If you set `MESH_UDP_GROUP=224.0.0.69` before, remove it to also hear firmware 2.8+ nodes (#903)
* Data: `MESH_UDP_ALLOWED_SOURCES` limits which hosts the UDP transport accepts packets from: comma-separated IPv4 addresses or CIDRs, e.g. your node's IP; unset accepts every host that can reach the multicast group, as before (SPEC UT1-UT4)
* Data: `MESHCORE_TELEMETRY_POLL_24H_EXEMPT` reaches every packaged deployment - `docker-compose.yml`, both `data/Dockerfile` stages and the NixOS module (`meshcoreTelemetryPoll24hExempt`); setting it in `.env` had no effect under Compose or NixOS (SPEC PX5)
* Data: `mesh.sh` builds `data/.venv` and runs pip for one start at a time, and runs no pip when `requirements.txt` and the venv's Python are unchanged; it also works when started from the checkout root. If a unit fails to import its packages: stop the units, remove `data/.venv`, start them (SPEC VL1-VL3)
* Web: live refreshes keep map overlays, line tooltips and waypoint cards open, and repaint only the table, map or chat whose data changed (#881, SPEC DR2/DR4)
* Web: a node role or modem preset named like a built-in object property, such as `constructor`, gets its protocol's base color and its derived preset code instead of an uncolored badge or a `[FU]` tag (SPEC FE1)
* Web: a node id containing `"` no longer stops the live-update flashes of a refresh (SPEC FE2)
* Web: on touch screens the menu, region, protocol, live and legend toggles, map toolbar, chat tabs, channel select, filter box and node dialog close are at least 44 px tall; the filter's × and the badge popover's close take taps in a 44 px square, and filter text stops short of the ×; each sort button takes taps across its header cell, now 45 px tall; none of these reaches a table row or another button (SPEC CT1)
* Web: every button and button-like control without a focus ring of its own shows the accent focus ring when reached by keyboard, and chat tabs show it inside the tab strip (SPEC CT2)
* Web: the protocol toggles stay hidden on an instance with one active protocol, the filter shows its × only with text in it, the region selector hides with fewer than two regions, the map's full-screen button hides where the browser has no full-screen support, and the meta row no longer jumps while the dashboard loads (SPEC CT3)
* Web: the live toggle reads "● live · pause" and "❚❚ paused HH:MM · resume", framed in the accent while paused; on phones it reads "● live" and "❚❚ paused HH:MM" (SPEC CT4)
* Web: the Chromium that renders `/og-image.png` keeps its sandbox unless `OG_IMAGE_NO_SANDBOX=1`, which the Docker images and the Compose default set, and keeps the same-origin policy and site isolation on (SPEC FE3/FE4)
* Web: the federation page shows a numeric frequency or version instead of an empty cell (SPEC FE5)
* Web: buttons, row toggles, the footer, the badge popover and the nodes table's group header use the dashboard's blue-black palette instead of neutral greys (SPEC GF1/GF2/GF3)
* Web: the chat panel uses the dashboard's palette, and its empty-channel note meets 4.5:1 contrast (SPEC GF2)
* Web: both pinned header rows of a table are opaque, so rows scrolled beneath them no longer show through (SPEC GF4)
* Web: the node detail dialog has a panel fill and a hairline border and keeps a margin on every side of the window (SPEC GF5)
* Web: the federation map is half the window tall and at least 320 px (320 px at 900 px wide and narrower), with one border, and /map no longer scrolls 2 px sideways (SPEC GF6)
* Docs: the README documents `MESHTASTIC_PSK_B64` (SPEC PX7)
* CI: the env-surface check runs in pytest whenever a deployment surface changes, and also checks names with digits, names read through helpers, and retired names (SPEC PX6/PX8)
* CI: test results reach Codecov also when tests fail; workflows use codecov-action v7 and checkout v7; Dependabot opens up to 20 GitHub Actions update PRs at a time (SPEC DW1/DW2/DW4)
* Docker: the root `Dockerfile` builds the same image as `web/Dockerfile`, including Chromium for Open Graph previews (skip it with `--build-arg WITH_OG_IMAGE=0`), the custom pages directory, and the `APP_VERSION` build arg (SPEC DF1)
* Docs: `SECURITY.md` says how to report a vulnerability privately (SPEC SEC1)
* Web: `/metrics` no longer exports nodes that opted out or that private mode hides; series exported before an opt-out stop at once (SPEC PM1/PM2)
* Web: with `PRIVATE=1`, a hidden client (`CLIENT_HIDDEN`) is left out of positions, telemetry, neighbors, traces, destinations, ingestors, the stats counts, the `/version` and well-known freshness hints, the preview image and its node page, not only the node list; a trace it relayed between other nodes keeps its other hops (SPEC HC1-HC4, HC7)
* Web: `/api/traces/:id` for an opted-out node returns nothing, as for its other reads (SPEC HC7)
* Docs: the README says how to hide a node with the U+1F6D1 (stop sign) marker, where Reticulum takes it, and how long data is kept (SPEC HC6)
* Web: on screens 600px wide and narrower the footer scrolls with the page instead of covering its end (SPEC SH1)
* Web: the mobile menu lists the static pages, GitHub and the chat link, the phone footer drops its links row, and the footer escapes `CONTACT_LINK` (SPEC SH2)
* Web: `ANNOUNCEMENT` accepts `[label](https://...)` links, a bare link no longer swallows a trailing period or parenthesis, banner links take the banner colour, and on phones the banner shows three whole lines (SPEC SH4)
* Web: the region, menu and close buttons draw SVG icons instead of emoji and text glyphs, and the region toggle is no longer greyed out (SPEC SH5)
* Web: the region toggle sits beside the site title and shows an accent frame while open (SPEC SH6)
* Web: mobile menu links and the close button are 44px targets (SPEC SH7)
* Data/Web: Reticulum reports a frequency and LoRa preset, read from the RNS config (SPEC RL1-RL3)
* Web: federation node counts distinguish zero from not-reported uniformly across all three protocols (SPEC RL4)
* Web: the Reticulum destination count matches the table, and `/api/destinations` serves only destinations of nodes inside the node window (SPEC RA3/RA8)
* Web: a destination's identifier links to its identity page (SPEC RL5)
* Web/Data: ingested strings are capped in bytes by column (node name 512, message 1024, instance fields 256, instance key 2048), a numeric field that is not a number, an integer beyond 64 bits or a non-finite number is stored as NULL (before: text, an approximate REAL or infinity), a `POST /api/nodes` key or record id that is not a node id is skipped, as is a node entry or nested map of one that is not a mapping, an unknown ingestor protocol registers as `meshtastic`, and an instance announcement with a field over its cap is refused; rows stored before the upgrade keep their values until retention removes them (SPEC SL1-SL10)
* Web: Reticulum rows match every other row's height; one dash glyph throughout the nodes table (SPEC RA11)
* Web: a federation peer whose newest node is up to 7 days old is accepted and refreshed again, not only within 24 hours (ACCEPTANCE FS-A5)
* Data/Web: Reticulum nodes are keyed on the announcing identity, not on each destination - one node, several destinations (SPEC RE7/RE10)
* Data/Web: a Meshtastic NodeInfo or NeighborInfo whose node id is not exactly its sender's is dropped with a warning, whether the ingestor decodes it or the web app decrypts it, and every Meshtastic record is filed under the node that sent it
* Web: a node keeps its names, role, position and opt-out marker against a node record under another key or none; a new key takes the node over once the old one has been silent for four weeks
* Data/Web: a MeshCore or Reticulum node that shares its 4-byte id with another key keeps the first key's names, position, destinations and chat messages
* Web: position, telemetry and last-seen updates no longer change the node row of another protocol that shares its id
* Web: a Meshtastic client first heard through a position or telemetry packet no longer stays hidden after its NodeInfo arrives
* Data: the ingestor's own node id is its primary announced identity (SPEC RE8)
* Web: the map legend lists only protocols in view: a protocol toggled off, or with no node matching the filter, leaves it; Clear filters also clears the filter text (SPEC LP1)
* Data: the host's own transport aspect is ingested when the stack has transport enabled (SPEC RE9)
* Data: the Reticulum ingestor re-posts its host's own destinations every hour, so `rns.transport` and the host aspects stay fresh on a connection that never reconnects (SPEC RE8)
* Data: the Reticulum ingestor logs `Reticulum stack state` after each connect and `Reticulum announce summary` every hour; the summary is a warning with hints when no announce arrives or the interface scope drops them all (SPEC RG1-RG4)
* Data: after a failed Reticulum connect, the next connect no longer posts and counts every announce twice (SPEC RG5)
* Web: a federation record relayed by another instance is stored only when its domain's well-known document names its key, except a refresh under the key already stored; relayed records and `POST /api/instances` announcements need an id equal to the SHA-256 of their public key (SPEC FS8/FS9)
* Web: a federation crawl fetches each peer once, however often it is listed, keeps the newest signed copy of a record and never fetches this instance; `FEDERATION_MAX_DOMAINS_PER_CRAWL` counts the domains a crawl fetches (SPEC FL1)
* Web: in one crawl the port variants of a host share one well-known fetch beside the bare domain's own, and a domain ending in `:443` or `:80` counts as the bare domain (SPEC FL1)
* Web: the crawl and `POST /api/instances` judge a peer on one `/api/nodes?limit=10` request and ask a peer for counts only when its record lacks one (SPEC FL2)
* Web: an announcement no longer starts a crawl; crawls run after boot and every 8 hours, and a peer is fetched at most once per `FEDERATION_PEER_FETCH_COOLDOWN` (900 s), which replaces `FEDERATION_CRAWL_COOLDOWN` (SPEC FL3)
* Web: federation requests revalidate with `If-None-Match` and `If-Modified-Since`, honour `Retry-After` on 429 and 503, and back off a failing peer for up to 24 hours (SPEC FL4)
* Web: a crawl stops at `FEDERATION_MAX_REQUESTS_PER_CRAWL` (1024) requests, its domain limit or `FEDERATION_TASK_TIMEOUT` (SPEC FL6)
* Web: `POST /api/instances` verifies a peer within `REMOTE_INSTANCE_REQUEST_TIMEOUT`, at most four at once, and reads its well-known only at first contact or on a key change; inside the peer's cooldown it judges the peer by what the last fetch kept, and answers 503 with `Retry-After` when that cannot decide (SPEC FL7)
* Web: `POST /api/instances` answers an announcement under the stored key that is no newer than the stored row 201 and leaves the row as it is (SPEC FL1, FL7)
* Docs: the README names the federation User-Agent and states the crawl after boot and every 8 hours, and `deploy/nginx.example.conf` has an optional rate limit for federation requests (SPEC FL8)
* Data: `PROTOCOL=reticulum` needs `INGESTOR_NODE_ID` in Docker, when nothing on the ingestor's RNS stack announces, or when two local identities tie; otherwise the node id is derived once a local app announces (SPEC RE5/RE8)
* Data: a set `CONNECTION` is no longer silently ignored under `PROTOCOL=reticulum` - the ingestor now logs that it is dropping the value (SPEC RN10)
* Data/Web: Meshtastic records whose radio clock is off by more than an hour get the ingestor's receive time, so they show in charts and tables again; the node page shows a battery above 100% as the nodes table does (SPEC RK1-RK3, UX10)
* Data: when a Meshtastic radio's clock is off by more than an hour, the node list it sends at connect is posted with each `lastHeard` shifted to the ingestor's clock; entries stamped more than an hour before the radio's current boot are posted as stamped when the radio reports its uptime, entries stamped ahead of its clock are skipped, and the ingestor logs one warning. Without a clock reading, an entry more than an hour ahead of the ingestor's clock is skipped (SPEC RK4)
* Data: `RETICULUM_INTERFACES` now resolves interface names through the shared RNS instance, fixing an allowlist that matched nothing on a shared stack (SPEC RE3)
* Data: a Reticulum announce with no display name no longer overwrites a stored name
* Docs: document the Reticulum config directory, its Docker volume, and how to add interfaces to it
* Data: `ALLOWED_CHANNELS`, `HIDDEN_CHANNELS`, and `MAP_ZOOM` no longer break on the packaged Compose default's quoted-empty value (ACCEPTANCE CH-A1-CH-A3)
* Data/Web: `nodes.identity_hash` keeps each Reticulum node's full identity hash, as do its `destinations` rows (SPEC RE2/RE7)
* Data/Web: `user.publicKey` carries the identity's real public key; destinations get their own table, served by `GET /api/destinations`, which honors the node opt-out marker (SPEC RE2)
* Data: new `RETICULUM_INTERFACES` allowlist scopes announce ingestion by interface, applied from one hop out; the operator's own nodes are always ingested (SPEC RN4/RE4)
* Data: `PROTOCOL=reticulum` ingests only RNode interfaces by default; set `RETICULUM_INTERFACES=*` to ingest every interface as before (SPEC RN4)
* Config: `configure.sh` asks for `INGESTOR_NODE_ID` under `PROTOCOL=reticulum` (SPEC RE8)
* Web: the `/charts` mesh-activity figure draws the Reticulum series (SPEC RN2)
* Web: `instances.reticulum_nodes_count` gets its own migration (SPEC RN7)
* Docs: every environment variable the ingestor or web app reads is documented (SPEC DOC2)
* Docs/Config: `configure.sh` and the NixOS module are no longer Meshtastic-only (SPEC DOC3)
* Docs: the README documents every route (SPEC DOC4)
* Docs: the dual basemap-CDN egress disclosure was restored (SPEC SB6)
* Docs: `CONTRACTS.md` corrected to match the served `/api/destinations` shape (SPEC DOC5)
* Docs: `ACCEPTANCE.md` amended for the shipped Reticulum stats scope; README gains a Reticulum section
* Web: picking a channel from the chat dropdown on mobile no longer fails while live updates arrive (#882)
* Data: `ALLOWED_CHANNELS`, `HIDDEN_CHANNELS` and `PRIMARY_CHANNEL_ONLY` drop every packet type and node-list entry from excluded channels, not only text messages (#784, SPEC CF1-CF3)
* Data: node-list snapshots no longer carry the radio library's copy of each node's last packet (#784, SPEC CF3)
* Data: under `TRANSPORT=udp`, `ALLOWED_CHANNELS` and `HIDDEN_CHANNELS` match channel 0 by `PRIMARY_CHANNEL_NAME` (#784, SPEC CF2)
* Data: `PRIMARY_CHANNEL_ONLY` accepts `true`/`yes`/`on` like the `TX_*` flags; an unrecognized value enables it (#784, SPEC CF5)
* Data: new `DROP_VIA_MQTT=1` drops Meshtastic packets and nodes relayed via MQTT, on the API and UDP transports (#884, SPEC VM1-VM2)
* Web: the web service no longer receives the unused `ALLOWED_CHANNELS`/`HIDDEN_CHANNELS` settings (#784, SPEC CF6)
* Docs: `CHANNEL_INDEX` documented as the announcement channel, not an ingest filter (#884, SPEC CF7)
* CI: Dependabot version updates run for every ecosystem; the Ruby and Python entries use the documented `bundler` and `pip` names (SPEC DP1)
* Web: `web/Gemfile.lock` is committed, so CI and the web image install the locked gems (SPEC DP2)
* Data: `data/requirements.txt` pins the runtime dependencies with `==` and no longer installs black and pytest into the ingestor image; the dev tools move to `data/requirements-dev.txt` (SPEC DP3)
* Web: the web image and the Nix web app run Ruby 3.4, the version CI tests, instead of Ruby 3.3 (SPEC DP5)
* Data: the ingestor image runs Python 3.13, the version CI tests, instead of Python 3.12 (SPEC DP5)
* CI: the Python workflow tests Python 3.13 and 3.14; neither CI matrix fails fast (SPEC DP5)
* Web: a MeshCore channel message heard by two ingestors that resolved its sender to different node ids is stored once (#880)
* Web: the first boot after upgrading deletes duplicate MeshCore messages once; on instances with several MeshCore ingestors this can remove about a fifth of the stored MeshCore messages (#880)
* Data: each `INSTANCE_DOMAIN` target gets its own upload queue (10,000 records, 24 hours): a record an instance missed (connect failure, timeout, 5xx) is sent to it again once it answers, a failing instance pauses 5 s to 60 s and honours `Retry-After`, a 4xx other than 408, 425 and 429 is not retried, a record is dropped after 4 server errors or 8 attempts that got no reply, and an instance that is down no longer slows the others (SPEC UR1-UR5)
* Data: uploads no longer follow redirects; a record answered with a 3xx is dropped with a warning that names the status and the redirect host. Set `INSTANCE_DOMAIN` to the URL the instance answers on (SPEC UR6)
* Web: a heartbeat the ingestor resends after a lost reply no longer adds a second activity row (SPEC UR7)
* Data/Web: a Reticulum destination's placeholder name never replaces its real name or names its node; a node with no announced name reads its own placeholder; a nameless destination is named from its own hash (SPEC RA10, RE10)
* Web, bridge, app: Reticulum is named as its own protocol in the bridge tag (`[RT]`), the footer join strip (`RETICULUM_PRESET` and `RETICULUM_FREQ`), the `/nodes` page description and the client fallback label; the site description names no protocol; the app README states the app is Meshtastic only (SPEC RL8)
* Docs: Reticulum is marked experimental and announce-only; the README says what the RNS stack transmits and that the channel filters ignore it (SPEC RD7/RN5)
* Nix: the flake ingestor includes `rns`, so `PROTOCOL=reticulum` runs on NixOS; `flake.lock` moves nixpkgs to nixos-unstable of 2026-10-03, which brings Python 3.14 and Ruby 3.4.9 (SPEC DP8)
* Docs: `CONTRACTS.md` states the Reticulum placeholder and headline name rules as amended (SPEC RA10, RE10)
### Features
* Data/Web: Reticulum protocol support - `PROTOCOL=reticulum` ingests announces as `protocol="reticulum"` nodes end-to-end (ingest, stats, federation wire, UI) (SPEC S6/FS2/MA5/MA-F2/F2-2 as amended)
## v0.7.5
### Features
* Make the activity announcement opt-in via ANNOUNCE (default off) by @seybsen in <https://github.com/l5yth/potato-mesh/pull/886>
### Fixes
* Ingestor: every mesh transmission is now off by default (`TX_ENABLED`, default `0`), covering the activity announcement *and* the MeshCore on-air contact telemetry polls; announcements take a second opt-in (`TX_ANNOUNCE=1`) on top by @l5yth in <https://github.com/l5yth/potato-mesh/pull/891>
* Ingestor: `RX_ONLY` no longer fails open - it was compared as an exact `== "1"`, so `RX_ONLY=true`, `TRUE`, `yes`, `on` and `" 1"` all silently resolved to *off* and a receive-only listening post transmitted anyway; all TX flags now accept the common spellings, strip whitespace, and resolve an unrecognized value toward silence by @l5yth in <https://github.com/l5yth/potato-mesh/pull/891>
* Ingestor: the `TX_*` flags reach every packaged deployment - `docker-compose.yml`, `data/Dockerfile`, the NixOS module (`txEnabled`/`txAnnounce`) and `.env.example`; the previous `ANNOUNCE` opt-in reached none of them, so setting it in `.env` had no effect at all by @l5yth in <https://github.com/l5yth/potato-mesh/pull/891>
* Ingestor: the announcement's 24 h anti-spam delay is measured on a monotonic clock - it used wall clock, so on an RTC-less host a post-boot NTP step cleared it instantly by @l5yth in <https://github.com/l5yth/potato-mesh/pull/891>
* Ingestor: transmit policy is stated in the log at startup without `DEBUG=1`, and each suppressed announcement names the gate that closed by @l5yth in <https://github.com/l5yth/potato-mesh/pull/891>
* App: await the in-flight request before closing its HTTP client in `loadMessages`/`loadNodes` - the unawaited return let `finally` close the client mid-request by @l5yth
* App: raise the iOS deployment target to 15.0 (from 14.0) in the Podfile, Xcode project and `AppFrameworkInfo.plist` - Flutter 3.47's `Flutter` pod requires it, and `pod install` could not resolve against the old target. Drops support for iOS 14 devices. by @l5yth
* CI: the Mobile workflow matrix no longer fails fast, so an iOS failure stops hiding whether the Android build passed by @l5yth
* Docs: README gains a Transmitting on the mesh section - what each flag turns on, the truth table, the literal announcement text, and what is never gated by @l5yth in <https://github.com/l5yth/potato-mesh/pull/891>
## v0.7.4
### Features
* web: remediate frontend design & UX audit by @l5yth in <https://github.com/l5yth/potato-mesh/pull/855>
* data,web: mesh activity reporting & announcements by @l5yth in <https://github.com/l5yth/potato-mesh/pull/859>
* web: mesh activity map card by @l5yth in <https://github.com/l5yth/potato-mesh/pull/860>
* data: count every MeshCore RX-log frame; stamp position radio metadata by @l5yth in <https://github.com/l5yth/potato-mesh/pull/861>
* web: frontent performance fixes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/864>
* docs: document reverse-proxy deployment, asset caching, and scaling by @l5yth in <https://github.com/l5yth/potato-mesh/pull/872>
* web: cap the node table render to the top N with a show all control by @l5yth in <https://github.com/l5yth/potato-mesh/pull/869>
* web: immutable cache-control for versioned assets + configurable stats TTL by @l5yth in <https://github.com/l5yth/potato-mesh/pull/868>
* perf(web): index-seek /api/stats telemetry umbrella to end GVL stall by @l5yth in <https://github.com/l5yth/potato-mesh/pull/873>
* data,web: Meshtastic waypoints as a first-class POI layer by @l5yth in <https://github.com/l5yth/potato-mesh/pull/874>
* web,docker: bake git APP_VERSION into image for immutable asset caching by @l5yth in <https://github.com/l5yth/potato-mesh/pull/875>
* web: LCP critical-path refinements (icon caching, preconnect, chat-tabs reflow) by @l5yth in <https://github.com/l5yth/potato-mesh/pull/877>
### Fixes
* fix(docker): bump ingestor base to python 3.12.10 for cryptography's Rust MSRV by @l5yth in <https://github.com/l5yth/potato-mesh/pull/858>
* matrix: tolerate compacted /api/messages rows by @l5yth in <https://github.com/l5yth/potato-mesh/pull/862>
* data: harden MeshCore subscribe against unknown EventType by @l5yth in <https://github.com/l5yth/potato-mesh/pull/867>
* web: bump sqlite3 to 2.9.5 for use-after-free advisory by @l5yth in <https://github.com/l5yth/potato-mesh/pull/876>
* web: fix waypoints shipped-review audit findings by @l5yth in <https://github.com/l5yth/potato-mesh/pull/878>
### Chores
* chore: bump version to 0.7.4 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/863>
* build(deps): bump quinn-proto from 0.11.14 to 0.11.16 in /matrix by @dependabot in <https://github.com/l5yth/potato-mesh/pull/865>
## v0.7.3
### Features
* web: HOT primary basemap (dark-filtered) with per-tile CARTO fallback by @l5yth in <https://github.com/l5yth/potato-mesh/pull/844>
* Bump all packages to 0.7.3 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/845>
* data: meshcore rf metrics rssi/snr/hops/path by @l5yth in <https://github.com/l5yth/potato-mesh/pull/849>
* data,web: ingest every telemetry family from both protocols by @l5yth in <https://github.com/l5yth/potato-mesh/pull/850>
* web: live relative-time tick - last-seen/updated ages count up in place by @l5yth in <https://github.com/l5yth/potato-mesh/pull/851>
* web: stack HOT over CARTO basemap layers, drop per-tile timeout by @l5yth in <https://github.com/l5yth/potato-mesh/pull/856>
### Fixes
* web: blend basemap providers to fix HOT/CARTO tile checkerboard by @l5yth in <https://github.com/l5yth/potato-mesh/pull/846>
* web: node table keeps latest telemetry of every type (per-field merge) by @l5yth in <https://github.com/l5yth/potato-mesh/pull/847>
* web,data: reconcile MeshCore stale same-name node duplicates by @l5yth in <https://github.com/l5yth/potato-mesh/pull/857>
## v0.7.2
### Features
* feat(ingestor): passive UDP transport (Mesh via UDP), primary-channel only by @tjpaulsondev in <https://github.com/l5yth/potato-mesh/pull/838>
### Fixes
* fix: security review - dashboard XSS, private-mode federation gap, Matrix bridge resilience by @tjpaulsondev in <https://github.com/l5yth/potato-mesh/pull/839>
* fix: UDP-transport hardening, bridge tracker coverage, CI repairs; release 0.7.2 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/840>
* web: fix MeshCore ghost nodes (stale contact enrichment discarded) by @l5yth in <https://github.com/l5yth/potato-mesh/pull/841>
* fix(docker): armv7 ingestor build toolchain; disable matrix fail-fast by @l5yth in <https://github.com/l5yth/potato-mesh/pull/842>
## v0.7.1
### Features
* web: change tiles to carto by @l5yth in <https://github.com/l5yth/potato-mesh/pull/831>
* web: progressively backfill all bulk collections by @l5yth in <https://github.com/l5yth/potato-mesh/pull/835>
### Fixes
* web: fix pubsub thread budget to not block entire app by @l5yth in <https://github.com/l5yth/potato-mesh/pull/828>
* web: initial-load module-graph waterfall by @l5yth in <https://github.com/l5yth/potato-mesh/pull/832>
* web: fix MeshCore chat duplicates and warm-cache message gap by @l5yth in <https://github.com/l5yth/potato-mesh/pull/834>
* improve log wiring and capture all meshcore adverts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/836>
* web: chat-log retention and vertical scroll fix by @l5yth in <https://github.com/l5yth/potato-mesh/pull/837>
### Chores
* chore: bump version 0.7.1 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/833>
## v0.7.0
### Features
* web: add node opt-out marker and data retention policies by @l5yth in <https://github.com/l5yth/potato-mesh/pull/793>
* web: breaking change on stats api by @l5yth in <https://github.com/l5yth/potato-mesh/pull/801>
* web: federation signature v2 migration by @l5yth in <https://github.com/l5yth/potato-mesh/pull/805>
* web: progressively load messages in batches by @l5yth in <https://github.com/l5yth/potato-mesh/pull/807>
* web: render chat incrementally to reduce page load time by @l5yth in <https://github.com/l5yth/potato-mesh/pull/813>
* web: implement local storage caching by @l5yth in <https://github.com/l5yth/potato-mesh/pull/814>
* web: version JS/CSS assets to bust stale browser caches by @l5yth in <https://github.com/l5yth/potato-mesh/pull/815>
* web: add pagination to all APIs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/820>
* web: implement pubsub by @l5yth in <https://github.com/l5yth/potato-mesh/pull/821>
* web: mark message author nodes as seen by @l5yth in <https://github.com/l5yth/potato-mesh/pull/822>
* web: add visual feedback to pubsub events by @l5yth in <https://github.com/l5yth/potato-mesh/pull/824>
### Fixes
* data: move connected event to after ensure_contacts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/789>
* fix sentinel position data by @l5yth in <https://github.com/l5yth/potato-mesh/pull/792>
* Fix regression where Meshcore chat senders show as Meshtastic by @l5yth in <https://github.com/l5yth/potato-mesh/pull/794>
* web: fix chat by @l5yth in <https://github.com/l5yth/potato-mesh/pull/800>
* web: fix apis to use consistently use camel case by @l5yth in <https://github.com/l5yth/potato-mesh/pull/802>
* web: fix meshcore node synthisation, merging, and deduplication by @l5yth in <https://github.com/l5yth/potato-mesh/pull/808>
* web: fix federation dns status 500 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/809>
* web: fix federation cycle and http response error handler by @l5yth in <https://github.com/l5yth/potato-mesh/pull/810>
* fix: report custom LoRa config as "Custom SF{sf}/BW{bw}/CR{cr}" when use_preset=False (#811) by @giannoug in <https://github.com/l5yth/potato-mesh/pull/812>
* fix: unify Meshtastic custom LoRa label with MeshCore format by @l5yth in <https://github.com/l5yth/potato-mesh/pull/818>
* web: fix meshcore message duplication regression by @l5yth in <https://github.com/l5yth/potato-mesh/pull/825>
* web: fix live-update DOM handling by @l5yth in <https://github.com/l5yth/potato-mesh/pull/826>
* web: fix api/events hang on shutdown by @l5yth in <https://github.com/l5yth/potato-mesh/pull/827>
### Chores
* chore: bump to 0.6.4 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/785>
* build(deps): bump openssl from 0.10.78 to 0.10.79 in /matrix by @dependabot in <https://github.com/l5yth/potato-mesh/pull/787>
* build(deps): bump openssl from 0.10.79 to 0.10.80 in /matrix by @dependabot in <https://github.com/l5yth/potato-mesh/pull/795>
## v0.6.3
### Features
* web: rework map spider-net feature by @l5yth in <https://github.com/l5yth/potato-mesh/pull/769>
* web: add seo improvements by @l5yth in <https://github.com/l5yth/potato-mesh/pull/771>
* data: refactor 3/7 protocols by @l5yth in <https://github.com/l5yth/potato-mesh/pull/774>
* web: refactor 1/7 data processing by @l5yth in <https://github.com/l5yth/potato-mesh/pull/772>
* web: refactor 2/7 federation by @l5yth in <https://github.com/l5yth/potato-mesh/pull/773>
* web: refactor 5/7 node page charts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/776>
* web: refactor 7/7 main js by @l5yth in <https://github.com/l5yth/potato-mesh/pull/778>
* data: refactor 4/7 interfaces by @l5yth in <https://github.com/l5yth/potato-mesh/pull/775>
* web: refactor 6/7 node page by @l5yth in <https://github.com/l5yth/potato-mesh/pull/777>
### Fixes
* matrix: fix matrix preset labels by @l5yth in <https://github.com/l5yth/potato-mesh/pull/770>
* web: fix liveliness of api data hydration bug by @l5yth in <https://github.com/l5yth/potato-mesh/pull/783>
### Chores
* build(deps): bump rustls-webpki from 0.103.10 to 0.103.13 in /matrix by @dependabot in <https://github.com/l5yth/potato-mesh/pull/764>
* build(deps): bump openssl from 0.10.75 to 0.10.78 in /matrix by @dependabot in <https://github.com/l5yth/potato-mesh/pull/766>
* chore: bump version to 0.6.3 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/779>
## v0.6.2
### Features
* Matrix: enable meshcore by @l5yth in <https://github.com/l5yth/potato-mesh/pull/761>
* Web: show colocated nodes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/753>
### Fixes
* Web: fix emoji pattern render in short names by @l5yth in <https://github.com/l5yth/potato-mesh/pull/760>
* Data: catch packet handler errors by @l5yth in <https://github.com/l5yth/potato-mesh/pull/759>
* Web: fix meshcore message duplication with 120s dupe protection by @l5yth in <https://github.com/l5yth/potato-mesh/pull/758>
* Web: fix node duplication through message synthetization by @l5yth in <https://github.com/l5yth/potato-mesh/pull/757>
* Ingestor: deduplicate meshcore messages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/752>
* Fix reaction handling and classification by @l5yth in <https://github.com/l5yth/potato-mesh/pull/750>
* Web: fix federation node counts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/749>
## v0.6.1
### Features
* Web: per protocol active node counts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/735>
* Web: optimize caching by @l5yth in <https://github.com/l5yth/potato-mesh/pull/744>
* Data: better lora frequency handling for meshtastic by @l5yth in <https://github.com/l5yth/potato-mesh/pull/733>
### Fixes
* Web: fix meshcore node misclassification by @l5yth in <https://github.com/l5yth/potato-mesh/pull/748>
* Web: fix federation resolver issue with multi addresses by @l5yth in <https://github.com/l5yth/potato-mesh/pull/743>
* Web: restore refresh and protocol buttons by @l5yth in <https://github.com/l5yth/potato-mesh/pull/742>
* Ingestor: fix serial connection failures by @l5yth in <https://github.com/l5yth/potato-mesh/pull/736>
### Chores
* Chore: bump version to 0.6.1 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/726>
* Build(deps): bump rand from 0.9.2 to 0.9.4 in /matrix by @dependabot in <https://github.com/l5yth/potato-mesh/pull/741>
## v0.6.0
### Features
* Web: add markdown static pages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/723>
* Data: trace analysus multi ingestor support by @l5yth in <https://github.com/l5yth/potato-mesh/pull/721>
* Web: facelift by @l5yth in <https://github.com/l5yth/potato-mesh/pull/716>
* Web: sort channels by activity not index by @l5yth in <https://github.com/l5yth/potato-mesh/pull/711>
* Data: derive meshcore channel probe bound from device max_channels by @l5yth in <https://github.com/l5yth/potato-mesh/pull/701>
* Web: define meshcore modem presets by @l5yth in <https://github.com/l5yth/potato-mesh/pull/696>
* Data: register meshcore channel mappings by @l5yth in <https://github.com/l5yth/potato-mesh/pull/695>
* Data: provide frequency and modem preset for meshcore by @l5yth in <https://github.com/l5yth/potato-mesh/pull/694>
* Web: distinguish meshcore from meshtastic in frontend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/688>
* [Meshcore] fix: get meshcore protocol icon displaying correctly by @benallfree in <https://github.com/l5yth/potato-mesh/pull/681>
### Fixes
* Web: fix federation for multi protocol by @l5yth in <https://github.com/l5yth/potato-mesh/pull/722>
* Data: fix position time updates by @l5yth in <https://github.com/l5yth/potato-mesh/pull/715>
* Data: fix meshcore ingestor self reporting by @l5yth in <https://github.com/l5yth/potato-mesh/pull/713>
* Web: reference meshcore nodes in chat by @l5yth in <https://github.com/l5yth/potato-mesh/pull/709>
* Web: fix node disappearance role reset by @l5yth in <https://github.com/l5yth/potato-mesh/pull/707>
* Web: protect real node names from fallback by @l5yth in <https://github.com/l5yth/potato-mesh/pull/702>
* Web: add proper short names for meshcore companions by @l5yth in <https://github.com/l5yth/potato-mesh/pull/693>
* Fix: address review comments from PRs #676 and #681 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/689>
* [Meshcore] fix: race condition by @benallfree in <https://github.com/l5yth/potato-mesh/pull/676>
### Chores
* Release: v0.6.0 - remove deprecated env var aliases by @l5yth in <https://github.com/l5yth/potato-mesh/pull/704>
* Chore: prepare codebase for breaking release by @l5yth in <https://github.com/l5yth/potato-mesh/pull/718>
## v0.5.12
* Enh: surface meshcore role types (#680) by @l5yth in https://github.com/l5yth/potato-mesh/pull/685
* Chore: refactor codebase before meshcore release by @l5yth in https://github.com/l5yth/potato-mesh/pull/682
* [Meshcore] enh: short name should be 1st 4 hex digits of public key by @benallfree in https://github.com/l5yth/potato-mesh/pull/679
* Chore: update xcode deps by @benallfree in https://github.com/l5yth/potato-mesh/pull/674
* Chore: update mesh.sh to use requirements file by @benallfree in https://github.com/l5yth/potato-mesh/pull/675
* Data/meshcore: fix ble and enable tcp by @l5yth in https://github.com/l5yth/potato-mesh/pull/669
* Data: handle store_forward and router_heartbeat portnum by @l5yth in https://github.com/l5yth/potato-mesh/pull/667
* Feat: implement meshcore provider by @l5yth in https://github.com/l5yth/potato-mesh/pull/663
* Ci: update dependabot and codecov settings by @l5yth in https://github.com/l5yth/potato-mesh/pull/666
* Web: prepare release by @l5yth in https://github.com/l5yth/potato-mesh/pull/665
* App: only query meshtastic provider by @l5yth in https://github.com/l5yth/potato-mesh/pull/664
* Data: prepare ingestor for meshcore by @l5yth in https://github.com/l5yth/potato-mesh/pull/658
* Web: fix css issues by @l5yth in https://github.com/l5yth/potato-mesh/pull/659
* Web: prepare frontend for multi protocol by @l5yth in https://github.com/l5yth/potato-mesh/pull/657
* Feat: split device and power-sensor telemetry charts (#643) by @l5yth in https://github.com/l5yth/potato-mesh/pull/656
* Web: implement a 'protocol' field across systems by @l5yth in https://github.com/l5yth/potato-mesh/pull/655
* Fix upsert clearing node coordinates bug by @l5yth in https://github.com/l5yth/potato-mesh/pull/654
* Data: resolve circular dependency of deamon.py by @l5yth in https://github.com/l5yth/potato-mesh/pull/653
* Proposal: mesh provider pattern refactor by @benallfree in https://github.com/l5yth/potato-mesh/pull/651
* Build(deps): bump rustls-webpki from 0.103.8 to 0.103.10 in /matrix by @dependabot[bot] in https://github.com/l5yth/potato-mesh/pull/649
* Build(deps): bump quinn-proto from 0.11.13 to 0.11.14 in /matrix by @dependabot[bot] in https://github.com/l5yth/potato-mesh/pull/646
## v0.5.11
* Chore: bump version to 0.5.11 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/645>
* Web: limit horizontal size of dropdown by @l5yth in <https://github.com/l5yth/potato-mesh/pull/644>
## v0.5.10
* Web: expose node stats in distinct api by @l5yth in <https://github.com/l5yth/potato-mesh/pull/641>
* Web: do merge channels by name by @l5yth in <https://github.com/l5yth/potato-mesh/pull/640>
* Web: do not merge channels by ID in frontend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/637>
* Web: do not touch neighbor last seen on neighbor info by @l5yth in <https://github.com/l5yth/potato-mesh/pull/636>
* Ingestor: report self id per packet by @l5yth in <https://github.com/l5yth/potato-mesh/pull/635>
* Ci: fix docker compose and docs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/634>
* Web: supress encrypted text messages in frontend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/633>
* Federation: ensure requests timeout properly and can be terminated by @l5yth in <https://github.com/l5yth/potato-mesh/pull/631>
* Build(deps): bump bytes from 1.11.0 to 1.11.1 in /matrix by @dependabot[bot]< in https://github.com/l5yth/potato-mesh/pull/627>
* Matrix: config loading now merges optional TOML with CLI/env/secret inputs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/617>
* Matrix: logs only non-sensitive config fields by @l5yth in <https://github.com/l5yth/potato-mesh/pull/616>
* Web: decrypted takes precedence by @l5yth in <https://github.com/l5yth/potato-mesh/pull/614>
* Add Apache 2.0 license headers to missing sources by @l5yth in <https://github.com/l5yth/potato-mesh/pull/615>
* Web: decrypt PSK-1 unencrypted messages on arrival by @l5yth in <https://github.com/l5yth/potato-mesh/pull/611>
* Web: daemonize federation worker pool to avoid deadlocks on stuck announcments by @l5yth in <https://github.com/l5yth/potato-mesh/pull/610>
* Web: add announcement banner by @l5yth in <https://github.com/l5yth/potato-mesh/pull/609>
* L5Y chore version 0510 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/608>
## v0.5.9
* Matrix: listen for synapse on port 41448 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/607>
* Web: collapse federation map ledgend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/604>
* Web: fix stale node queries by @l5yth in <https://github.com/l5yth/potato-mesh/pull/603>
* Matrix: move short name to display name by @l5yth in <https://github.com/l5yth/potato-mesh/pull/602>
* Ci: update ruby to 4 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/601>
* Web: display traces of last 28 days if available by @l5yth in <https://github.com/l5yth/potato-mesh/pull/599>
* Web: establish menu structure by @l5yth in <https://github.com/l5yth/potato-mesh/pull/597>
* Matrix: fixed the text-message checkpoint regression by @l5yth in <https://github.com/l5yth/potato-mesh/pull/595>
* Matrix: cache seen messages by rx_time not id by @l5yth in <https://github.com/l5yth/potato-mesh/pull/594>
* Web: hide the default '0' tab when not active by @l5yth in <https://github.com/l5yth/potato-mesh/pull/593>
* Matrix: fix empty bridge state json by @l5yth in <https://github.com/l5yth/potato-mesh/pull/592>
* Web: allow certain charts to overflow upper bounds by @l5yth in <https://github.com/l5yth/potato-mesh/pull/585>
* Ingestor: support ROUTING_APP messages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/584>
* Ci: run nix flake check on ci by @l5yth in <https://github.com/l5yth/potato-mesh/pull/583>
* Web: hide legend by default by @l5yth in <https://github.com/l5yth/potato-mesh/pull/582>
* Nix flake by @benjajaja in <https://github.com/l5yth/potato-mesh/pull/577>
* Support BLE UUID format for macOS Bluetooth devices by @apo-mak in <https://github.com/l5yth/potato-mesh/pull/575>
* Web: add mesh.qrp.ro as seed node by @l5yth in <https://github.com/l5yth/potato-mesh/pull/573>
* Web: ensure unknown nodes for messages and traces by @l5yth in <https://github.com/l5yth/potato-mesh/pull/572>
* Chore: bump version to 0.5.9 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/569>
## v0.5.8
* Web: add secondary seed node jmrp.io by @l5yth in <https://github.com/l5yth/potato-mesh/pull/568>
* Data: implement whitelist for ingestor by @l5yth in <https://github.com/l5yth/potato-mesh/pull/567>
* Web: add ?since= parameter to all apis by @l5yth in <https://github.com/l5yth/potato-mesh/pull/566>
* Matrix: fix docker build by @l5yth in <https://github.com/l5yth/potato-mesh/pull/565>
* Matrix: fix docker build by @l5yth in <https://github.com/l5yth/potato-mesh/pull/564>
* Web: fix federation signature validation and create fallback by @l5yth in <https://github.com/l5yth/potato-mesh/pull/563>
* Chore: update readme by @l5yth in <https://github.com/l5yth/potato-mesh/pull/561>
* Matrix: add docker file for bridge by @l5yth in <https://github.com/l5yth/potato-mesh/pull/556>
* Matrix: add health checks to startup by @l5yth in <https://github.com/l5yth/potato-mesh/pull/555>
* Matrix: omit the api part in base url by @l5yth in <https://github.com/l5yth/potato-mesh/pull/554>
* App: add utility coverage tests for main.dart by @l5yth in <https://github.com/l5yth/potato-mesh/pull/552>
* Data: add thorough daemon unit tests by @l5yth in <https://github.com/l5yth/potato-mesh/pull/553>
* Chore: bump version to 0.5.8 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/551>
## v0.5.7
* Data: track ingestors heartbeat by @l5yth in <https://github.com/l5yth/potato-mesh/pull/549>
* Harden instance selector navigation URLs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/550>
* Data: hide channels that have been flag for ignoring by @l5yth in <https://github.com/l5yth/potato-mesh/pull/548>
* Web: fix limit when counting remote nodes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/547>
* Web: improve instances map and table view by @l5yth in <https://github.com/l5yth/potato-mesh/pull/546>
* Web: fix traces submission with optional fields on udp by @l5yth in <https://github.com/l5yth/potato-mesh/pull/545>
* Chore: bump version to 0.5.7 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/542>
* Handle zero telemetry aggregates by @l5yth in <https://github.com/l5yth/potato-mesh/pull/538>
* Web: fix telemetry api to return current in amperes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/541>
* Web: fix traces rendering by @l5yth in <https://github.com/l5yth/potato-mesh/pull/535>
* Normalize numeric node roles to canonical labels by @l5yth in <https://github.com/l5yth/potato-mesh/pull/539>
* Use INSTANCE_DOMAIN env for ingestor by @l5yth in <https://github.com/l5yth/potato-mesh/pull/536>
* Web: further refine the federation page by @l5yth in <https://github.com/l5yth/potato-mesh/pull/534>
* Add Federation Map by @apo-mak in <https://github.com/l5yth/potato-mesh/pull/532>
* Add contact link to the instance data by @apo-mak in <https://github.com/l5yth/potato-mesh/pull/533>
* Matrix: create potato-matrix-bridge by @l5yth in <https://github.com/l5yth/potato-mesh/pull/528>
## v0.5.6
* Web: display sats in view by @l5yth in <https://github.com/l5yth/potato-mesh/pull/523>
* Web: display air quality in separate chart by @l5yth in <https://github.com/l5yth/potato-mesh/pull/521>
* Ci: Add macOS and Ubuntu builds to Flutter workflow by @l5yth in <https://github.com/l5yth/potato-mesh/pull/519>
* Web: add current to charts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/520>
* App: fix notification icon by @l5yth in <https://github.com/l5yth/potato-mesh/pull/518>
* Spec: update test fixtures by @l5yth in <https://github.com/l5yth/potato-mesh/pull/517>
* App: generate proper icons by @l5yth in <https://github.com/l5yth/potato-mesh/pull/516>
* Web: fix favicon by @l5yth in <https://github.com/l5yth/potato-mesh/pull/515>
* Web: add ?since= parameter to api/messages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/512>
* App: implement notifications by @l5yth in <https://github.com/l5yth/potato-mesh/pull/511>
* App: add theme selector by @l5yth in <https://github.com/l5yth/potato-mesh/pull/507>
* App: further harden refresh logic and prefer local first by @l5yth in <https://github.com/l5yth/potato-mesh/pull/506>
* Ci: fix app artifacts for tags by @l5yth in <https://github.com/l5yth/potato-mesh/pull/504>
* Ci: build app artifacts for tags by @l5yth in <https://github.com/l5yth/potato-mesh/pull/503>
* App: add persistance by @l5yth in <https://github.com/l5yth/potato-mesh/pull/501>
* App: instance and chat mvp by @l5yth in <https://github.com/l5yth/potato-mesh/pull/498>
* App: add instance selector to settings by @l5yth in <https://github.com/l5yth/potato-mesh/pull/497>
* App: add scaffholding gitignore by @l5yth in <https://github.com/l5yth/potato-mesh/pull/496>
* Handle reaction app packets without reply id by @l5yth in <https://github.com/l5yth/potato-mesh/pull/495>
* Render reaction multiplier counts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/494>
* Add comprehensive tests for Flutter reader by @l5yth in <https://github.com/l5yth/potato-mesh/pull/491>
* Map numeric role ids to canonical Meshtastic roles by @l5yth in <https://github.com/l5yth/potato-mesh/pull/489>
* Update node detail hydration for traces by @l5yth in <https://github.com/l5yth/potato-mesh/pull/490>
* Add mobile Flutter CI workflow by @l5yth in <https://github.com/l5yth/potato-mesh/pull/488>
* Align OCI labels in docker workflow by @l5yth in <https://github.com/l5yth/potato-mesh/pull/487>
* Add Meshtastic reader Flutter app by @l5yth in <https://github.com/l5yth/potato-mesh/pull/483>
* Handle pre-release Docker tagging by @l5yth in <https://github.com/l5yth/potato-mesh/pull/486>
* Web: remove range from charts labels by @l5yth in <https://github.com/l5yth/potato-mesh/pull/485>
* Floor override frequencies to MHz integers by @l5yth in <https://github.com/l5yth/potato-mesh/pull/476>
* Prevent message ids from being treated as node identifiers by @l5yth in <https://github.com/l5yth/potato-mesh/pull/475>
* Fix 1 after emojis in reply. by @Alexkurd in <https://github.com/l5yth/potato-mesh/pull/464>
* Add frequency and preset to node table by @l5yth in <https://github.com/l5yth/potato-mesh/pull/472>
* Subscribe to traceroute app pubsub topic by @l5yth in <https://github.com/l5yth/potato-mesh/pull/471>
* Aggregate telemetry over the last 7 days by @l5yth in <https://github.com/l5yth/potato-mesh/pull/470>
* Address missing id field ingestor bug by @l5yth in <https://github.com/l5yth/potato-mesh/pull/469>
* Merge secondary channels by name by @l5yth in <https://github.com/l5yth/potato-mesh/pull/468>
* Rate limit host device telemetry by @l5yth in <https://github.com/l5yth/potato-mesh/pull/467>
* Add traceroutes to frontend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/466>
* Feat: implement traceroute app packet handling across the stack by @l5yth in <https://github.com/l5yth/potato-mesh/pull/463>
* Bump version and update changelog by @l5yth in <https://github.com/l5yth/potato-mesh/pull/462>
## v0.5.5
* Added comprehensive helper unit tests by @l5yth in <https://github.com/l5yth/potato-mesh/pull/457>
* Added reaction-aware handling by @l5yth in <https://github.com/l5yth/potato-mesh/pull/455>
* Env: add map zoom by @l5yth in <https://github.com/l5yth/potato-mesh/pull/454>
* Charts: render aggregated telemetry charts for all nodes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/453>
* Nodes: render charts detail pages as overlay by @l5yth in <https://github.com/l5yth/potato-mesh/pull/452>
* Fix telemetry parsing for charts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/451>
* Nodes: improve charts on detail pages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/450>
* Nodes: add charts to detail pages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/449>
* Aggregate frontend snapshots across views by @l5yth in <https://github.com/l5yth/potato-mesh/pull/447>
* Remove added 1 if reply with emoji by @Alexkurd in <https://github.com/l5yth/potato-mesh/pull/443>
* Refine node detail view layout by @l5yth in <https://github.com/l5yth/potato-mesh/pull/442>
* Enable map centering from node table coordinates by @l5yth in <https://github.com/l5yth/potato-mesh/pull/439>
* Add node detail route and page by @l5yth in <https://github.com/l5yth/potato-mesh/pull/441>
* Ensure Meshtastic nodeinfo patch runs before importing interfaces by @l5yth in <https://github.com/l5yth/potato-mesh/pull/440>
* Filter zero-valued fields from API responses by @l5yth in <https://github.com/l5yth/potato-mesh/pull/438>
* Add debug payload tracing and ignored packet logging by @l5yth in <https://github.com/l5yth/potato-mesh/pull/437>
* Tighten map auto-fit behaviour by @l5yth in <https://github.com/l5yth/potato-mesh/pull/435>
* Fetch encrypted chat log entries for log tab by @l5yth in <https://github.com/l5yth/potato-mesh/pull/434>
* Add encrypted filter to messages API by @l5yth in <https://github.com/l5yth/potato-mesh/pull/432>
* Guard NodeInfo handler against missing IDs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/431>
* Add standalone full-screen map, chat, and nodes views by @l5yth in <https://github.com/l5yth/potato-mesh/pull/429>
* Ensure chat history fetches full message limit by @l5yth in <https://github.com/l5yth/potato-mesh/pull/428>
* Fix ingestion of nodeinfo packets missing ids (#426) by @l5yth in <https://github.com/l5yth/potato-mesh/pull/427>
* Chore: update license headers by @l5yth in <https://github.com/l5yth/potato-mesh/pull/424>
* Chore: bump version to 0.5.5 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/423>
## v0.5.4
* Handle naming when primary channel has a name by @l5yth in <https://github.com/l5yth/potato-mesh/pull/422>
* Handle edge case when primary channel has a name by @l5yth in <https://github.com/l5yth/potato-mesh/pull/421>
* Add preset mode to logs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/420>
* Parallelize federation tasks with worker pool by @l5yth in <https://github.com/l5yth/potato-mesh/pull/419>
* Allow filtering chat and logs by node name by @l5yth in <https://github.com/l5yth/potato-mesh/pull/417>
* Gem: Add erb as dependency removed from std by @l5yth in <https://github.com/l5yth/potato-mesh/pull/416>
* Implement support for replies and reactions app by @l5yth in <https://github.com/l5yth/potato-mesh/pull/411>
* Ingestor: Ignore direct messages on default channel by @l5yth in <https://github.com/l5yth/potato-mesh/pull/414>
* Agents: Add instructions by @l5yth in <https://github.com/l5yth/potato-mesh/pull/410>
* Display encrypted messages in frontend log window by @l5yth in <https://github.com/l5yth/potato-mesh/pull/409>
* Add chat log entries for telemetry, position, and neighbor events by @l5yth in <https://github.com/l5yth/potato-mesh/pull/408>
* Handle missing instance domain outside production by @l5yth in <https://github.com/l5yth/potato-mesh/pull/405>
* Add tabbed chat panel with channel grouping by @l5yth in <https://github.com/l5yth/potato-mesh/pull/404>
* Normalize numeric client roles using Meshtastic CLI enums by @l5yth in <https://github.com/l5yth/potato-mesh/pull/402>
* Ensure Docker images publish versioned tags by @l5yth in <https://github.com/l5yth/potato-mesh/pull/403>
* Document environment configuration variables by @l5yth in <https://github.com/l5yth/potato-mesh/pull/400>
* Document federation refresh cadence by @l5yth in <https://github.com/l5yth/potato-mesh/pull/401>
* Add Prometheus monitoring documentation by @l5yth in <https://github.com/l5yth/potato-mesh/pull/399>
* Config: Read PROM_REPORT_IDS from environment by @nicjansma in <https://github.com/l5yth/potato-mesh/pull/398>
* Feat: Mesh-Ingestor: Ability to provide already-existing interface instance by @KenADev in <https://github.com/l5yth/potato-mesh/pull/395>
* Fix: Mesh-Ingestor: Fix error for non-existing datetime.UTC reference by @KenADev in <https://github.com/l5yth/potato-mesh/pull/396>
* Chore: bump version to 0.5.4 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/388>
## v0.5.3
* Add telemetry formatting utilities and extend node overlay by @l5yth in <https://github.com/l5yth/potato-mesh/pull/387>
* Prune blank values from API responses by @l5yth in <https://github.com/l5yth/potato-mesh/pull/386>
* Add full support to telemetry schema and API by @l5yth in <https://github.com/l5yth/potato-mesh/pull/385>
* Respect PORT environment override by @l5yth in <https://github.com/l5yth/potato-mesh/pull/384>
* Add instance selector dropdown for federation deployments by @l5yth in <https://github.com/l5yth/potato-mesh/pull/382>
* Harden federation announcements by @l5yth in <https://github.com/l5yth/potato-mesh/pull/381>
* Ensure private mode disables federation by @l5yth in <https://github.com/l5yth/potato-mesh/pull/380>
* Ensure private mode disables chat messaging by @l5yth in <https://github.com/l5yth/potato-mesh/pull/378>
* Disable federation features when FEDERATION=0 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/379>
* Expose PRIVATE environment configuration across tooling by @l5yth in <https://github.com/l5yth/potato-mesh/pull/377>
* Fix frontend coverage export for Codecov by @l5yth in <https://github.com/l5yth/potato-mesh/pull/376>
* Restrict /api/instances results to recent records by @l5yth in <https://github.com/l5yth/potato-mesh/pull/374>
* Expose FEDERATION environment option across tooling by @l5yth in <https://github.com/l5yth/potato-mesh/pull/375>
* Chore: bump version to 0.5.3 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/372>
## v0.5.2
* Align theme and info controls by @l5yth in <https://github.com/l5yth/potato-mesh/pull/371>
* Fixes POST request 403 errors on instances behind Cloudflare proxy by @varna9000 in <https://github.com/l5yth/potato-mesh/pull/368>
* Delay initial federation announcements by @l5yth in <https://github.com/l5yth/potato-mesh/pull/366>
* Ensure well-known document stays in sync on startup by @l5yth in <https://github.com/l5yth/potato-mesh/pull/365>
* Guard federation DNS resolution against restricted networks by @l5yth in <https://github.com/l5yth/potato-mesh/pull/362>
* Add federation ingestion limits and tests by @l5yth in <https://github.com/l5yth/potato-mesh/pull/364>
* Prefer reported primary channel names by @l5yth in <https://github.com/l5yth/potato-mesh/pull/363>
* Decouple message API node hydration by @l5yth in <https://github.com/l5yth/potato-mesh/pull/360>
* Fix ingestor reconnection detection by @l5yth in <https://github.com/l5yth/potato-mesh/pull/361>
* Harden instance domain validation by @l5yth in <https://github.com/l5yth/potato-mesh/pull/359>
* Ensure INSTANCE_DOMAIN propagates to containers by @l5yth in <https://github.com/l5yth/potato-mesh/pull/358>
* Chore: bump version to 0.5.2 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/356>
* Gracefully retry federation announcements over HTTP by @l5yth in <https://github.com/l5yth/potato-mesh/pull/355>
## v0.5.1
* Recursively ingest federated instances by @l5yth in <https://github.com/l5yth/potato-mesh/pull/353>
* Remove federation timeout environment overrides by @l5yth in <https://github.com/l5yth/potato-mesh/pull/352>
* Close unrelated short info overlays when opening short info by @l5yth in <https://github.com/l5yth/potato-mesh/pull/351>
* Improve federation instance error diagnostics by @l5yth in <https://github.com/l5yth/potato-mesh/pull/350>
* Harden federation domain validation and tests by @l5yth in <https://github.com/l5yth/potato-mesh/pull/347>
* Handle malformed instance records gracefully by @l5yth in <https://github.com/l5yth/potato-mesh/pull/348>
* Fix ingestor device mounting for non-serial connections by @l5yth in <https://github.com/l5yth/potato-mesh/pull/346>
* Ensure Docker deployments persist keyfile and well-known assets by @l5yth in <https://github.com/l5yth/potato-mesh/pull/345>
* Add modem preset display to node overlay by @l5yth in <https://github.com/l5yth/potato-mesh/pull/340>
* Display message frequency and channel in chat log by @l5yth in <https://github.com/l5yth/potato-mesh/pull/339>
* Bump fallback version string to v0.5.1 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/338>
* Docs: update changelog for 0.5.0 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/337>
* Fix ingestor docker import path by @l5yth in <https://github.com/l5yth/potato-mesh/pull/336>
## v0.5.0
* Ensure node overlays appear above fullscreen map by @l5yth in <https://github.com/l5yth/potato-mesh/pull/333>
* Adjust node table columns responsively by @l5yth in <https://github.com/l5yth/potato-mesh/pull/332>
* Add LoRa metadata fields to nodes and messages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/331>
* Add channel metadata capture for message tagging by @l5yth in <https://github.com/l5yth/potato-mesh/pull/329>
* Capture radio metadata for ingestor payloads by @l5yth in <https://github.com/l5yth/potato-mesh/pull/327>
* Fix FrozenError when filtering node query results by @l5yth in <https://github.com/l5yth/potato-mesh/pull/324>
* Ensure frontend reports git-aware version strings by @l5yth in <https://github.com/l5yth/potato-mesh/pull/321>
* Ensure web Docker image ships application sources by @l5yth in <https://github.com/l5yth/potato-mesh/pull/322>
* Refine stacked short info overlays on the map by @l5yth in <https://github.com/l5yth/potato-mesh/pull/319>
* Refine environment configuration defaults by @l5yth in <https://github.com/l5yth/potato-mesh/pull/318>
* Fix legacy configuration migration to XDG directories by @l5yth in <https://github.com/l5yth/potato-mesh/pull/317>
* Adopt XDG base directories for app data and config by @l5yth in <https://github.com/l5yth/potato-mesh/pull/316>
* Refactor: streamline ingestor environment variables by @l5yth in <https://github.com/l5yth/potato-mesh/pull/314>
* Adjust map auto-fit padding and default zoom by @l5yth in <https://github.com/l5yth/potato-mesh/pull/315>
* Ensure APIs filter stale data and refresh node details from latest sources by @l5yth in <https://github.com/l5yth/potato-mesh/pull/312>
* Improve offline tile fallback initialization by @l5yth in <https://github.com/l5yth/potato-mesh/pull/307>
* Add fallback for offline tile rendering errors by @l5yth in <https://github.com/l5yth/potato-mesh/pull/306>
* Fix map auto-fit handling and add controller by @l5yth in <https://github.com/l5yth/potato-mesh/pull/311>
* Fix map initialization bounds and add coverage by @l5yth in <https://github.com/l5yth/potato-mesh/pull/305>
* Increase coverage for configuration and sanitizer helpers by @l5yth in <https://github.com/l5yth/potato-mesh/pull/303>
* Add comprehensive theme and background front-end tests by @l5yth in <https://github.com/l5yth/potato-mesh/pull/302>
* Document sanitization and helper modules by @l5yth in <https://github.com/l5yth/potato-mesh/pull/301>
* Add in-repo Meshtastic protobuf stubs for tests by @l5yth in <https://github.com/l5yth/potato-mesh/pull/300>
* Handle CRL lookup failures during federation TLS by @l5yth in <https://github.com/l5yth/potato-mesh/pull/299>
* Ensure JavaScript workflow runs frontend tests by @l5yth in <https://github.com/l5yth/potato-mesh/pull/298>
* Unify structured logging across application and ingestor by @l5yth in <https://github.com/l5yth/potato-mesh/pull/296>
* Add Apache license headers to missing sources by @l5yth in <https://github.com/l5yth/potato-mesh/pull/297>
* Update workflows for ingestor, sinatra, and frontend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/295>
* Fix IPv6 instance domain canonicalization by @l5yth in <https://github.com/l5yth/potato-mesh/pull/294>
* Handle federation HTTPS CRL verification failures by @l5yth in <https://github.com/l5yth/potato-mesh/pull/293>
* Adjust federation announcement interval to eight hours by @l5yth in <https://github.com/l5yth/potato-mesh/pull/292>
* Restore modular app functionality by @l5yth in <https://github.com/l5yth/potato-mesh/pull/291>
* Refactor config and metadata helpers into PotatoMesh modules by @l5yth in <https://github.com/l5yth/potato-mesh/pull/290>
* Update default site configuration defaults by @l5yth in <https://github.com/l5yth/potato-mesh/pull/288>
* Add regression test for queue drain concurrency by @l5yth in <https://github.com/l5yth/potato-mesh/pull/287>
* Ensure Docker config directories are created for non-root user by @l5yth in <https://github.com/l5yth/potato-mesh/pull/286>
* Clarify numeric address requirement for network target parsing by @l5yth in <https://github.com/l5yth/potato-mesh/pull/285>
* Ensure mesh ingestor queue resets active flag when idle by @l5yth in <https://github.com/l5yth/potato-mesh/pull/284>
* Clarify BLE connection description in README by @l5yth in <https://github.com/l5yth/potato-mesh/pull/283>
* Configure web container for production mode by @l5yth in <https://github.com/l5yth/potato-mesh/pull/282>
* Normalize INSTANCE_DOMAIN configuration to require hostnames by @l5yth in <https://github.com/l5yth/potato-mesh/pull/280>
* Avoid blocking startup on federation announcements by @l5yth in <https://github.com/l5yth/potato-mesh/pull/281>
* Fix production Docker builds for web and ingestor images by @l5yth in <https://github.com/l5yth/potato-mesh/pull/279>
* Improve instance domain detection logic by @l5yth in <https://github.com/l5yth/potato-mesh/pull/278>
* Implement federation announcements and instances API by @l5yth in <https://github.com/l5yth/potato-mesh/pull/277>
* Fix federation signature handling and IP guard by @l5yth in <https://github.com/l5yth/potato-mesh/pull/276>
* Add persistent federation metadata endpoint by @l5yth in <https://github.com/l5yth/potato-mesh/pull/274>
* Add configurable instance domain with reverse DNS fallback by @l5yth in <https://github.com/l5yth/potato-mesh/pull/272>
* Document production deployment configuration by @l5yth in <https://github.com/l5yth/potato-mesh/pull/273>
* Add targeted API endpoints and expose version metadata by @l5yth in <https://github.com/l5yth/potato-mesh/pull/271>
* Prometheus metrics updates on startup and for position/telemetry by @nicjansma in <https://github.com/l5yth/potato-mesh/pull/270>
* Add hourly reconnect handling for inactive mesh interface by @l5yth in <https://github.com/l5yth/potato-mesh/pull/267>
* Dockerfile fixes by @nicjansma in <https://github.com/l5yth/potato-mesh/pull/268>
* Added prometheus /metrics endpoint by @nicjansma in <https://github.com/l5yth/potato-mesh/pull/262>
* Add fullscreen toggle to map view by @l5yth in <https://github.com/l5yth/potato-mesh/pull/263>
* Relocate JS coverage export script into web directory by @l5yth in <https://github.com/l5yth/potato-mesh/pull/266>
* V0.4.0 version string in web UI by @nicjansma in <https://github.com/l5yth/potato-mesh/pull/265>
* Add energy saving cycle to ingestor daemon by @l5yth in <https://github.com/l5yth/potato-mesh/pull/256>
* Chore: restore apache headers by @l5yth in <https://github.com/l5yth/potato-mesh/pull/260>
* Docs: add matrix to readme by @l5yth in <https://github.com/l5yth/potato-mesh/pull/259>
* Force dark theme default based on sanitized cookie by @l5yth in <https://github.com/l5yth/potato-mesh/pull/252>
* Document mesh ingestor modules with PDoc-style docstrings by @l5yth in <https://github.com/l5yth/potato-mesh/pull/255>
* Handle missing node IDs in Meshtastic nodeinfo packets by @l5yth in <https://github.com/l5yth/potato-mesh/pull/251>
* Document Ruby helper methods with RDoc comments by @l5yth in <https://github.com/l5yth/potato-mesh/pull/254>
* Add JSDoc documentation across client scripts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/253>
* Fix mesh ingestor telemetry and neighbor handling by @l5yth in <https://github.com/l5yth/potato-mesh/pull/249>
* Refactor front-end assets into external modules by @l5yth in <https://github.com/l5yth/potato-mesh/pull/245>
* Add tests for helper utilities and asset routes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/243>
* Docs: add ingestor inline docstrings by @l5yth in <https://github.com/l5yth/potato-mesh/pull/244>
* Add comprehensive coverage tests for mesh ingestor by @l5yth in <https://github.com/l5yth/potato-mesh/pull/241>
* Add inline documentation to config helpers and frontend scripts by @l5yth in <https://github.com/l5yth/potato-mesh/pull/240>
* Update changelog by @l5yth in <https://github.com/l5yth/potato-mesh/pull/238>
## v0.4.0
* Reformat neighbor overlay layout by @l5yth in <https://github.com/l5yth/potato-mesh/pull/237>
* Add legend toggle for neighbor lines by @l5yth in <https://github.com/l5yth/potato-mesh/pull/236>
* Hide Air Util Tx column on mobile by @l5yth in <https://github.com/l5yth/potato-mesh/pull/235>
* Add overlay for clickable neighbor links on map by @l5yth in <https://github.com/l5yth/potato-mesh/pull/234>
* Hide humidity and pressure columns on mobile by @l5yth in <https://github.com/l5yth/potato-mesh/pull/232>
* Remove last position timestamp from map info overlay by @l5yth in <https://github.com/l5yth/potato-mesh/pull/233>
* Improve live node positions and expose precision metadata by @l5yth in <https://github.com/l5yth/potato-mesh/pull/231>
* Show neighbor short names in info overlays by @l5yth in <https://github.com/l5yth/potato-mesh/pull/228>
* Add telemetry environment metrics to node UI by @l5yth in <https://github.com/l5yth/potato-mesh/pull/227>
* Reduce neighbor line opacity by @l5yth in <https://github.com/l5yth/potato-mesh/pull/226>
* Visualize neighbor connections on map canvas by @l5yth in <https://github.com/l5yth/potato-mesh/pull/224>
* Add clear control to filter input by @l5yth in <https://github.com/l5yth/potato-mesh/pull/225>
* Handle Bluetooth shutdown hangs gracefully by @l5yth in <https://github.com/l5yth/potato-mesh/pull/221>
* Adjust mesh priorities and receive topics by @l5yth in <https://github.com/l5yth/potato-mesh/pull/220>
* Add BLE and fallback mesh interface handling by @l5yth in <https://github.com/l5yth/potato-mesh/pull/219>
* Add neighbor info ingestion and API endpoints by @l5yth in <https://github.com/l5yth/potato-mesh/pull/218>
* Add debug logs for unknown node creation and last-heard updates by @l5yth in <https://github.com/l5yth/potato-mesh/pull/214>
* Update node last seen when events are received by @l5yth in <https://github.com/l5yth/potato-mesh/pull/212>
* Improve debug logging for node and telemetry data by @l5yth in <https://github.com/l5yth/potato-mesh/pull/213>
* Normalize stored message debug output by @l5yth in <https://github.com/l5yth/potato-mesh/pull/211>
* Stop repeating ingestor node info snapshot and timestamp debug logs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/210>
* Add telemetry API and ingestion support by @l5yth in <https://github.com/l5yth/potato-mesh/pull/205>
* Add private mode to hide chat and message APIs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/204>
* Handle offline-ready map fallback by @l5yth in <https://github.com/l5yth/potato-mesh/pull/202>
* Add linux/armv7 container builds and configuration options by @l5yth in <https://github.com/l5yth/potato-mesh/pull/201>
* Update Docker documentation by @l5yth in <https://github.com/l5yth/potato-mesh/pull/200>
* Update node last seen when ingesting encrypted messages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/198>
* Fix api in readme by @l5yth in <https://github.com/l5yth/potato-mesh/pull/197>
## v0.3.0
* Add connection recovery for TCP interface by @l5yth in <https://github.com/l5yth/potato-mesh/pull/186>
* Bump version to 0.3 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/191>
* Pgrade styles and fix interface issues by @l5yth in <https://github.com/l5yth/potato-mesh/pull/190>
* Some updates in the front by @dkorotkih2014-hub in <https://github.com/l5yth/potato-mesh/pull/188>
* Update last heard on node entry change by @l5yth in <https://github.com/l5yth/potato-mesh/pull/185>
* Populate chat metadata for unknown nodes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/182>
* Update role color theme to latest palette by @l5yth in <https://github.com/l5yth/potato-mesh/pull/183>
* Add placeholder nodes for unknown senders by @l5yth in <https://github.com/l5yth/potato-mesh/pull/181>
* Update role colors and ordering for firmware 2.7.10 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/180>
* Handle plain IP addresses in mesh TCP detection by @l5yth in <https://github.com/l5yth/potato-mesh/pull/154>
* Handle encrypted messages by @l5yth in <https://github.com/l5yth/potato-mesh/pull/173>
* Add fallback display names for unnamed nodes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/171>
* Ensure routers render above other node types by @l5yth in <https://github.com/l5yth/potato-mesh/pull/169>
* Move lint checks after tests in CI by @l5yth in <https://github.com/l5yth/potato-mesh/pull/168>
* Handle proto values in nodeinfo payloads by @l5yth in <https://github.com/l5yth/potato-mesh/pull/167>
* Remove raw payload storage from database schema by @l5yth in <https://github.com/l5yth/potato-mesh/pull/166>
* Add POSITION_APP ingestion and API support by @l5yth in <https://github.com/l5yth/potato-mesh/pull/160>
* Add support for NODEINFO_APP packets by @l5yth in <https://github.com/l5yth/potato-mesh/pull/159>
* Derive SEO metadata from existing config values by @l5yth in <https://github.com/l5yth/potato-mesh/pull/153>
* Tests: create helper script to dump all mesh data from serial by @l5yth in <https://github.com/l5yth/potato-mesh/pull/152>
* Limit chat log to recent entries by @l5yth in <https://github.com/l5yth/potato-mesh/pull/151>
* Require time library before formatting ISO timestamps by @l5yth in <https://github.com/l5yth/potato-mesh/pull/149>
* Define docker compose network by @l5yth in <https://github.com/l5yth/potato-mesh/pull/148>
* Fix sqlite3 native extension on Alpine by @l5yth in <https://github.com/l5yth/potato-mesh/pull/146>
* Fix web app startup binding by @l5yth in <https://github.com/l5yth/potato-mesh/pull/147>
* Ensure sqlite3 builds from source on Alpine by @l5yth in <https://github.com/l5yth/potato-mesh/pull/145>
* Support mock serial interface in CI by @l5yth in <https://github.com/l5yth/potato-mesh/pull/143>
* Fix Docker workflow matrix for supported platforms by @l5yth in <https://github.com/l5yth/potato-mesh/pull/142>
* Add clickable role filters to the map legend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/140>
* Rebuild chat log on each refresh by @l5yth in <https://github.com/l5yth/potato-mesh/pull/139>
* Fix: retain alpine runtime libs after removing build deps by @l5yth in <https://github.com/l5yth/potato-mesh/pull/138>
* Fix: support windows ingestor build by @l5yth in <https://github.com/l5yth/potato-mesh/pull/136>
* Fix: use supported ruby image by @l5yth in <https://github.com/l5yth/potato-mesh/pull/135>
* Feat: Add comprehensive Docker support by @trose in <https://github.com/l5yth/potato-mesh/pull/122>
* Chore: bump version to 0.2.1 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/134>
* Fix dark mode tile styling on new map tiles by @l5yth in <https://github.com/l5yth/potato-mesh/pull/132>
* Switch map tiles to OSM HOT and add theme filters by @l5yth in <https://github.com/l5yth/potato-mesh/pull/130>
* Add footer version display by @l5yth in <https://github.com/l5yth/potato-mesh/pull/128>
* Add responsive controls for map legend by @l5yth in <https://github.com/l5yth/potato-mesh/pull/129>
* Update changelog by @l5yth in <https://github.com/l5yth/potato-mesh/pull/119>
## v0.2.0
* Update readme for 0.2 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/118>
* Add PotatoMesh logo to header and favicon by @l5yth in <https://github.com/l5yth/potato-mesh/pull/117>
* Harden API auth and request limits by @l5yth in <https://github.com/l5yth/potato-mesh/pull/116>
* Add client-side sorting to node table by @l5yth in <https://github.com/l5yth/potato-mesh/pull/114>
* Add short name overlay for node details by @l5yth in <https://github.com/l5yth/potato-mesh/pull/111>
* Adjust python ingestor interval to 60 seconds by @l5yth in <https://github.com/l5yth/potato-mesh/pull/112>
* Hide location columns on medium screens by @l5yth in <https://github.com/l5yth/potato-mesh/pull/109>
* Handle message updates based on sender info by @l5yth in <https://github.com/l5yth/potato-mesh/pull/108>
* Prioritize node posts in queued API updates by @l5yth in <https://github.com/l5yth/potato-mesh/pull/107>
* Add auto-refresh toggle to UI by @l5yth in <https://github.com/l5yth/potato-mesh/pull/105>
* Adjust Leaflet popup styling for dark mode by @l5yth in <https://github.com/l5yth/potato-mesh/pull/104>
* Add site info overlay by @l5yth in <https://github.com/l5yth/potato-mesh/pull/103>
* Add long name tooltip to short name badge by @l5yth in <https://github.com/l5yth/potato-mesh/pull/102>
* Ensure node numeric aliases are derived from canonical IDs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/101>
* Chore: clean up repository by @l5yth in <https://github.com/l5yth/potato-mesh/pull/96>
* Handle SQLite busy errors when upserting nodes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/100>
* Configure Sinatra logging level from DEBUG flag by @l5yth in <https://github.com/l5yth/potato-mesh/pull/97>
* Add penetration tests for authentication and SQL injection by @l5yth in <https://github.com/l5yth/potato-mesh/pull/95>
* Document Python and Ruby source modules by @l5yth in <https://github.com/l5yth/potato-mesh/pull/94>
* Add tests covering mesh helper edge cases by @l5yth in <https://github.com/l5yth/potato-mesh/pull/93>
* Fix py code cov by @l5yth in <https://github.com/l5yth/potato-mesh/pull/92>
* Add Codecov reporting to Python CI by @l5yth in <https://github.com/l5yth/potato-mesh/pull/91>
* Skip null identifiers when selecting packet fields by @l5yth in <https://github.com/l5yth/potato-mesh/pull/88>
* Create python yml ga by @l5yth in <https://github.com/l5yth/potato-mesh/pull/90>
* Add unit tests for mesh ingestor script by @l5yth in <https://github.com/l5yth/potato-mesh/pull/89>
* Add coverage for debug logging on messages without sender by @l5yth in <https://github.com/l5yth/potato-mesh/pull/86>
* Handle concurrent node snapshot updates by @l5yth in <https://github.com/l5yth/potato-mesh/pull/85>
* Fix ingestion mapping for message sender IDs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/84>
* Add coverage for API authentication and payload edge cases by @l5yth in <https://github.com/l5yth/potato-mesh/pull/83>
* Add JUnit test reporting to Ruby CI by @l5yth in <https://github.com/l5yth/potato-mesh/pull/82>
* Configure SimpleCov reporting for Codecov by @l5yth in <https://github.com/l5yth/potato-mesh/pull/81>
* Update codecov job by @l5yth in <https://github.com/l5yth/potato-mesh/pull/80>
* Fix readme badges by @l5yth in <https://github.com/l5yth/potato-mesh/pull/79>
* Add Codecov upload step to Ruby workflow by @l5yth in <https://github.com/l5yth/potato-mesh/pull/78>
* Add Apache license headers to source files by @l5yth in <https://github.com/l5yth/potato-mesh/pull/77>
* Add integration specs for node and message APIs by @l5yth in <https://github.com/l5yth/potato-mesh/pull/76>
* Docs: update for 0.2.0 release by @l5yth in <https://github.com/l5yth/potato-mesh/pull/75>
* Create ruby workflow by @l5yth in <https://github.com/l5yth/potato-mesh/pull/74>
* Add RSpec smoke tests for app boot and database init by @l5yth in <https://github.com/l5yth/potato-mesh/pull/73>
* Align refresh controls with status text by @l5yth in <https://github.com/l5yth/potato-mesh/pull/72>
* Improve mobile layout by @l5yth in <https://github.com/l5yth/potato-mesh/pull/68>
* Normalize message sender IDs using node numbers by @l5yth in <https://github.com/l5yth/potato-mesh/pull/67>
* Style: condense node table by @l5yth in <https://github.com/l5yth/potato-mesh/pull/65>
* Log debug details for messages without sender by @l5yth in <https://github.com/l5yth/potato-mesh/pull/64>
* Fix nested dataclass serialization for node snapshots by @l5yth in <https://github.com/l5yth/potato-mesh/pull/63>
* Log node object on snapshot update failure by @l5yth in <https://github.com/l5yth/potato-mesh/pull/62>
* Initialize database on startup by @l5yth in <https://github.com/l5yth/potato-mesh/pull/61>
* Send mesh data to Potatomesh API by @l5yth in <https://github.com/l5yth/potato-mesh/pull/60>
* Convert boolean flags for SQLite binding by @l5yth in <https://github.com/l5yth/potato-mesh/pull/59>
* Use packet id as message primary key by @l5yth in <https://github.com/l5yth/potato-mesh/pull/58>
* Add message ingestion API and stricter auth by @l5yth in <https://github.com/l5yth/potato-mesh/pull/56>
* Feat: parameterize community info by @l5yth in <https://github.com/l5yth/potato-mesh/pull/55>
* Feat: add dark mode toggle by @l5yth in <https://github.com/l5yth/potato-mesh/pull/54>
## v0.1.0
* Show daily node count in title and header by @l5yth in <https://github.com/l5yth/potato-mesh/pull/49>
* Add daily date separators to chat log by @l5yth in <https://github.com/l5yth/potato-mesh/pull/47>
* Feat: make frontend responsive for mobile by @l5yth in <https://github.com/l5yth/potato-mesh/pull/46>
* Harden mesh utilities by @l5yth in <https://github.com/l5yth/potato-mesh/pull/45>
* Filter out distant nodes from Berlin map view by @l5yth in <https://github.com/l5yth/potato-mesh/pull/43>
* Display filtered active node counts in #MediumFast subheading by @l5yth in <https://github.com/l5yth/potato-mesh/pull/44>
* Limit chat log and highlight short names by role by @l5yth in <https://github.com/l5yth/potato-mesh/pull/42>
* Fix string/integer comparison in node query by @l5yth in <https://github.com/l5yth/potato-mesh/pull/40>
* Escape chat message and node entries by @l5yth in <https://github.com/l5yth/potato-mesh/pull/39>
* Sort chat entries by timestamp by @l5yth in <https://github.com/l5yth/potato-mesh/pull/38>
* Feat: append messages to chat log by @l5yth in <https://github.com/l5yth/potato-mesh/pull/36>
* Normalize future timestamps for nodes by @l5yth in <https://github.com/l5yth/potato-mesh/pull/35>
* Optimize web frontend and Ruby app by @l5yth in <https://github.com/l5yth/potato-mesh/pull/32>
* Add messages API endpoint with node details by @l5yth in <https://github.com/l5yth/potato-mesh/pull/33>
* Clamp node timestamps and sync last_heard with position time by @l5yth in <https://github.com/l5yth/potato-mesh/pull/31>
* Refactor: replace deprecated utcfromtimestamp by @l5yth in <https://github.com/l5yth/potato-mesh/pull/30>
* Add optional debug logging for node and message operations by @l5yth in <https://github.com/l5yth/potato-mesh/pull/29>
* Data: enable serial collection of messages on channel 0 by @l5yth in <https://github.com/l5yth/potato-mesh/pull/25>
* Add first_heard timestamp by @l5yth in <https://github.com/l5yth/potato-mesh/pull/23>
* Add persistent footer with contact information by @l5yth in <https://github.com/l5yth/potato-mesh/pull/22>
* Sort initial chat entries by last-heard by @l5yth in <https://github.com/l5yth/potato-mesh/pull/20>
* Display position time in relative 'time ago' format by @l5yth in <https://github.com/l5yth/potato-mesh/pull/19>
* Adjust marker size and map tile opacity by @l5yth in <https://github.com/l5yth/potato-mesh/pull/18>
* Add chat box for node notifications by @l5yth in <https://github.com/l5yth/potato-mesh/pull/17>
* Color markers by role with grayscale map by @l5yth in <https://github.com/l5yth/potato-mesh/pull/16>
* Default missing node role to client by @l5yth in <https://github.com/l5yth/potato-mesh/pull/15>
* Show live node count in nodes page titles by @l5yth in <https://github.com/l5yth/potato-mesh/pull/14>
* Filter stale nodes and add live search by @l5yth in <https://github.com/l5yth/potato-mesh/pull/13>
* Remove raw node JSON column by @l5yth in <https://github.com/l5yth/potato-mesh/pull/12>
* Add JSON ingest API for node updates by @l5yth in <https://github.com/l5yth/potato-mesh/pull/11>
* Ignore Python __pycache__ directories by @l5yth in <https://github.com/l5yth/potato-mesh/pull/10>
* Feat: load nodes from json for tests by @l5yth in <https://github.com/l5yth/potato-mesh/pull/8>
* Handle dataclass fields in node snapshots by @l5yth in <https://github.com/l5yth/potato-mesh/pull/6>
* Add index page and /nodes route for node map by @l5yth in <https://github.com/l5yth/potato-mesh/pull/4>