A lightweight MQTT-based map and chat viewer for Meshtastic for small regional communities https://mesh.skobk.in
  • Go 58.9%
  • TypeScript 37%
  • CSS 3.5%
  • JavaScript 0.5%
Find a file
Alexey Skobkin e81c80bd50
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/tag/ci Pipeline was successful
style: fix golangci-lint issues in S&F migration (#97)
CI's `backend-lint` step flagged four issues in the v18 migration
file touched by the previous commit:

- three `nlreturn` warnings (missing blank line before `return` in
  error branches and the function-tail returns)
- one `nilerr` warning on the non-JSON-object pass-through branch in
  `rewriteStoreForwardDetails`. The error is intentionally swallowed
  (any non-object payload should pass through to the generic fallback
  renderer), so the swallow is now explicit via a `//nolint:nilerr`
  directive with a comment.

No behavioural change. `golangci-lint run ./cmd/... ./internal/...`
is now clean and the S&F migration tests still pass.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-17 19:32:12 +03:00
.woodpecker build(ci): run push pipeline only on master 2026-06-16 03:29:09 +03:00
cmd feat(debug): inspect low-level protobuf traceroute packet fields in cmd/meshtastic-debug 2026-02-28 21:21:04 +03:00
docs feat(apidocs): follow-up cleanup and optimization for API docs 2026-03-14 01:34:09 +03:00
internal style: fix golangci-lint issues in S&F migration (#97) 2026-06-17 19:32:12 +03:00
web test: close S&F feature test coverage gaps (#97) 2026-06-17 19:22:34 +03:00
.gitignore fix(config): load environment overrides through koanf (#89) 2026-06-16 03:11:24 +03:00
.golangci.yml chore(lint): exclude web from golangci-lint and fix lint issues 2026-03-11 21:55:51 +03:00
.goreleaser.yml feat(apidocs): follow-up cleanup and optimization for API docs (part 2) 2026-03-14 01:49:02 +03:00
AGENTS.md docs: adding agentic instructions regarding UPGRADE.md 2026-06-16 03:13:26 +03:00
config.example.yaml feat(updatecheck): markdown post-processing for release descriptions 2026-06-16 19:26:27 +03:00
Dockerfile build(frontend): embed SPA assets in all server builds 2026-03-14 02:19:03 +03:00
Dockerfile.release feat(apidocs): follow-up cleanup and optimization for API docs (part 2) 2026-03-14 01:49:02 +03:00
go.mod fix(config): load environment overrides through koanf (#89) 2026-06-16 03:11:24 +03:00
go.sum fix(config): load environment overrides through koanf (#89) 2026-06-16 03:11:24 +03:00
LICENSE docs: year changed in the LICENSE 2026-02-25 21:31:41 +03:00
README.md feat(updatecheck): markdown post-processing for release descriptions 2026-06-16 19:26:27 +03:00
redocly.yaml feat(apidocs): initial API documentation support 2026-03-14 01:22:50 +03:00
UPGRADE.md docs: introducing UPGRADE.md for BC breaks information for operators 2026-06-16 03:13:11 +03:00

MeshMap Lite

status-badge latest release

Lightweight read-only Meshtastic regional map and chat viewer.

Screenshots

Map screenshot

Node details

Node details screenshot

Event log

Event log screenshot

Run locally

  1. go run ./cmd/server --config ./config.example.yaml
  2. Open http://localhost:8080

Local builds report version dev by default. Tagged release builds can embed the exact Git tag into the binary via:

go build -ldflags "-X meshmap-lite/internal/buildinfo.Version=$(git describe --exact-match --tags 2>/dev/null || echo dev)" ./cmd/server

Run in Docker

Docker images are available for deploy from these registries:

Docker Compose example.

Frontend dev

  1. cd web
  2. npm install
  3. npm run dev

For backend builds, npm run build writes the SPA bundle into internal/frontend/assets/dist, and go build ./cmd/server embeds that bundle into the binary. If those assets are missing, the Go build fails.

Config

YAML and MML_ environment variables are supported. ENV keys use __ as nesting separator.

Example:

MML_MQTT__ROOT_TOPIC=msh/RU/ARKH
MML_CHANNELS__LONGFAST__PRIMARY=true

Reference:

YAML key ENV key Default Description
mqtt.host MML_MQTT__HOST "" MQTT broker host.
mqtt.port MML_MQTT__PORT 1883 MQTT broker port.
mqtt.tls MML_MQTT__TLS false Enable TLS for MQTT connection.
mqtt.client_id MML_MQTT__CLIENT_ID auto-generated stable ID MQTT client ID. Set to keep broker session identity explicit.
mqtt.username MML_MQTT__USERNAME "" MQTT username.
mqtt.password MML_MQTT__PASSWORD "" MQTT password.
mqtt.root_topic MML_MQTT__ROOT_TOPIC "" Root Meshtastic topic prefix. Required.
mqtt.protocol_version MML_MQTT__PROTOCOL_VERSION "3.1.1" MQTT protocol version.
mqtt.subscribe_qos MML_MQTT__SUBSCRIBE_QOS 1 MQTT subscription QoS.
mqtt.clean_session MML_MQTT__CLEAN_SESSION false MQTT clean session flag.
mqtt.reconnect_timeout MML_MQTT__RECONNECT_TIMEOUT 10s Reconnect timeout.
mqtt.connect_timeout MML_MQTT__CONNECT_TIMEOUT 10s Connection timeout.
mqtt.keepalive MML_MQTT__KEEPALIVE 60s MQTT keepalive interval.
ingest.traceroute.timeout MML_INGEST__TRACEROUTE__TIMEOUT 60s Timeout window for ingest-side traceroute lifecycle synthesis.
ingest.traceroute.max_entries MML_INGEST__TRACEROUTE__MAX_ENTRIES 1000 Max active traceroute tracker entries kept in memory.
ingest.traceroute.final_retention MML_INGEST__TRACEROUTE__FINAL_RETENTION 60s How long final traceroute tracker entries stay in memory for duplicate suppression.
ingest.map_reports.enabled MML_INGEST__MAP_REPORTS__ENABLED true Enable map report ingest.
ingest.map_reports.topic_suffix MML_INGEST__MAP_REPORTS__TOPIC_SUFFIX "2/map" Topic suffix for map reports under root topic.
storage.kv.driver MML_STORAGE__KV__DRIVER "memory" Dedup KV backend driver. Only memory is supported.
storage.kv.size MML_STORAGE__KV__SIZE 100000 Dedup KV max entries.
storage.kv.ttl MML_STORAGE__KV__TTL 6h Dedup KV entry TTL.
storage.sql.driver MML_STORAGE__SQL__DRIVER "sqlite" SQL backend driver. Only sqlite is supported.
storage.sql.url MML_STORAGE__SQL__URL "/data/db.sqlite" SQL connection URL/path.
storage.sql.auto_migrate MML_STORAGE__SQL__AUTO_MIGRATE true Run DB migrations on startup.
storage.sql.log_max_rows MML_STORAGE__SQL__LOG_MAX_ROWS 50000 Max number of log rows to keep. 0 disables pruning.
storage.sql.log_prune_batch_rows MML_STORAGE__SQL__LOG_PRUNE_BATCH_ROWS 1000 Extra rows allowed above cap before prune runs; prune then shrinks back to max rows.
channels[] MML_CHANNELS__<CHANNEL_NAME>__... {} Channel map keyed by channel name. At least one channel is required.
channels[].ChannelName.psk MML_CHANNELS__<CHANNEL_NAME>__PSK "AQ==" Channel PSK (applied during normalization if empty).
channels[].ChannelName.events MML_CHANNELS__<CHANNEL_NAME>__EVENTS ["text_message","node_info","position","telemetry"] Enabled event families. In ENV format use CSV (for example text_message,node_info).
channels[].ChannelName.primary MML_CHANNELS__<CHANNEL_NAME>__PRIMARY false Marks primary channel. At most one channel can be primary.
web.listen_addr MML_WEB__LISTEN_ADDR ":8080" HTTP listen address.
web.base_path MML_WEB__BASE_PATH "/" Base path for web/API routing.
web.info.file MML_WEB__INFO__FILE "" Optional Markdown file loaded at startup and shown as a site information modal. Relative paths resolve from process working directory; prefer absolute paths in deployments.
web.chat.enabled MML_WEB__CHAT__ENABLED true Enable chat API/UI features.
web.chat.default_channel MML_WEB__CHAT__DEFAULT_CHANNEL first channel name (sorted) Default channel for chat UI/API.
web.chat.show_recent_messages MML_WEB__CHAT__SHOW_RECENT_MESSAGES 50 Initial recent messages count.
web.chat.history_window MML_WEB__CHAT__HISTORY_WINDOW 168h Oldest chat history age visible through the chat API/UI.
web.ws.heartbeat_interval MML_WEB__WS__HEARTBEAT_INTERVAL 30s WebSocket heartbeat interval.
web.ws.stats_interval MML_WEB__WS__STATS_INTERVAL 60s WebSocket runtime stats emission interval.
web.map.clustering MML_WEB__MAP__CLUSTERING false Enable marker clustering on map. Disabled by default.
web.map.disconnected_threshold MML_WEB__MAP__DISCONNECTED_THRESHOLD 60m Node stale/disconnected threshold.
web.map.topology_cache_ttl MML_WEB__MAP__TOPOLOGY_CACHE_TTL 10m Shared TTL for the per-node topology/details cache on the frontend and for the global topology snapshot cache on the backend.
web.map.topology_max_edges MML_WEB__MAP__TOPOLOGY_MAX_EDGES 2000 Maximum number of topology edges returned by GET /api/v1/topology/edges before the response sets truncated: true.
web.map.precision_circles_mode MML_WEB__MAP__PRECISION_CIRCLES_MODE "selected" Precision circle mode: none, selected, or always.
web.map.default_view.latitude MML_WEB__MAP__DEFAULT_VIEW__LATITUDE 64.5 Initial map center latitude.
web.map.default_view.longitude MML_WEB__MAP__DEFAULT_VIEW__LONGITUDE 40.6 Initial map center longitude.
web.map.default_view.zoom MML_WEB__MAP__DEFAULT_VIEW__ZOOM 13 Initial map zoom.
web.relevance.telemetry_max_age MML_WEB__RELEVANCE__TELEMETRY_MAX_AGE 24h Hide telemetry snapshots older than this API/UI relevance window.
web.relevance.topology_evidence_max_age MML_WEB__RELEVANCE__TOPOLOGY_EVIDENCE_MAX_AGE 72h Hide topology evidence older than this API/UI relevance window.
web.relevance.map_position_max_age MML_WEB__RELEVANCE__MAP_POSITION_MAX_AGE 336h Hide map positions and positioned nodes older than this API/UI relevance window.
web.log.live_updates MML_WEB__LOG__LIVE_UPDATES true Enable live log updates over WebSocket.
web.log.page_size_default MML_WEB__LOG__PAGE_SIZE_DEFAULT 100 Default log page size (normalized to 1..500).
web.stats.activity.daily.bucket MML_WEB__STATS__ACTIVITY__DAILY__BUCKET 5m Stats tab daily (24h) activity bucket size; values under 1m are raised to 1m.
web.stats.activity.weekly.bucket MML_WEB__STATS__ACTIVITY__WEEKLY__BUCKET 1h Stats tab weekly (168h) activity bucket size; values under 7m are raised to 7m.
logging.level MML_LOGGING__LEVEL "info" Log level.
update_check.enabled MML_UPDATE_CHECK__ENABLED true Enable release checks shown in the UI.
update_check.interval MML_UPDATE_CHECK__INTERVAL 12h Refresh interval for configured release sources.
update_check.timeout MML_UPDATE_CHECK__TIMEOUT 15s Per-source release request timeout.
update_check.sources[].name MML_UPDATE_CHECK__SOURCES__0__NAME "meshmap-lite" Stable update source identifier. Increase the numeric index for additional ENV-defined sources.
update_check.sources[].label MML_UPDATE_CHECK__SOURCES__0__LABEL "Map" User-facing label for the update source.
update_check.sources[].type MML_UPDATE_CHECK__SOURCES__0__TYPE "forgejo" Release source type. Supported values: forgejo, github.
update_check.sources[].base_url MML_UPDATE_CHECK__SOURCES__0__BASE_URL "https://git.skobk.in" API base URL. Required for forgejo; optional for github, where it defaults to https://api.github.com. For GitHub Enterprise use a compatible REST base such as https://github.example.com/api/v3.
update_check.sources[].repository MML_UPDATE_CHECK__SOURCES__0__REPOSITORY "skobkin/meshmap-lite" Repository in owner/repo form.
update_check.sources[].current_version_source MML_UPDATE_CHECK__SOURCES__0__CURRENT_VERSION_SOURCE "buildinfo" Current-version source. Use buildinfo for the running binary version or none.
update_check.sources[].limit MML_UPDATE_CHECK__SOURCES__0__LIMIT 15 Number of releases fetched per source. GitHub values are sent as per_page and clamped to 1..100.
update_check.sources[].pre_releases MML_UPDATE_CHECK__SOURCES__0__PRE_RELEASES false Include platform-flagged unstable releases (alpha/beta/RC). For forgejo, stable-only mode sends pre-release=false; when enabled, the filter is omitted so stable releases remain included. For github the per-release prerelease flag is honored instead of filtered.
update_check.sources[].post_process MML_UPDATE_CHECK__SOURCES__0__POST_PROCESS true Post-process release Markdown before caching it: 40-character commit hashes become commit links, #123 references become repository issue links, full issue/PR URLs collapse to owner/repo#123, and @user references become profile links. Set to false to keep upstream release bodies unchanged.

Notes:

  • Channel names are preserved as configured.
  • ENV overrides are parsed as: bool (true/false), int, float, time.Duration (10s, 60m, 6h), or string.
  • ENV list entries use numeric path segments, for example MML_UPDATE_CHECK__SOURCES__0__TYPE=github.
  • Unknown ENV keys are ignored.
  • If web.info.file is set and the file cannot be read or rendered, startup fails. Changing the file requires restarting the app; clients see changed content again because dismissal is keyed to the Markdown source hash.
  • Stats activity periods are normalized to positive durations and capped at 1440 buckets per period. The minimum effective bucket is therefore 1m for daily and 7m for weekly; any smaller value is silently raised to the minimum.
  • mqtt.root_topic must be set and at least one channel must be configured.
  • PSK shorthand behavior is documented in docs/keys.md.

API

The canonical API contract source lives in internal/apidocs/assets/openapi.yaml.

Notes

MQTT ingest decodes real Meshtastic protobuf payloads (ServiceEnvelope/MapReport/MeshPacket), with JSON fallback kept for synthetic local tests.