Files
meshcore-analyzer/docs/user-guide/configuration.md
T
efiten 317b59ab10 feat: area-based visual node filter — attribute packets by transmitter GPS (#804) (#839)
## Summary

- Adds configurable GPS polygon areas to `config.json`; nodes are
attributed to an area if their last-known position falls inside the
polygon
- New `Area: …` dropdown filter (matching the existing region filter
style) appears on all analytics, nodes, packets, map, and live screens
when areas are configured
- Backend resolves area membership with a 30s TTL cache; area filter
bypasses the 500-node cap on `/api/bulk-health` so all area nodes are
always returned
- Includes a polygon builder tool (`/area-map.html`) for drawing and
exporting area boundaries

## Changes

**Backend**
- `AreaEntry` type + `Areas` config field
- `GetNodePubkeysInArea` DB query + `resolveAreaNodes` (30s TTL,
`areaNodeMu` RWMutex)
- `PacketQuery.Area` + `filterPackets` polygon check
- `?area=` param propagated through all analytics, topology,
clock-health, and bulk-health routes
- `/api/config/areas` endpoint

**Frontend**
- `area-filter.js`: single-select dropdown, persists to localStorage,
cleans up stale keys on load
- Wired into analytics, nodes, packets, channels, map, and live pages
- Live map clears node markers on area change

**Docs & tools**
- `docs/user-guide/area-filter.md` — configuration and usage guide
- `docs/api-spec.md` — updated with new endpoint and `?area=` param
table
- `tools/area-map.html` — polygon builder for defining area boundaries
- Demo areas added to `config.example.json`

## Test plan

- [x] No areas configured → filter dropdown does not appear on any page
- [x] Areas configured → dropdown appears, "All" selected by default
- [x] Selecting an area filters nodes/packets/topology/map correctly
- [x] Selecting "All" restores unfiltered view
- [x] Selection persists across page reloads (localStorage)
- [x] Stale localStorage key (area removed from config) is cleared on
load
- [x] `/api/bulk-health?area=X` returns all nodes in area (no 500-node
cap)
- [x] `/api/config/areas` returns correct list

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Kpa-clawbot <kpaclawbot@outlook.com>
Co-authored-by: openclaw-bot <bot@openclaw.local>
2026-05-21 14:00:15 -07:00

7.3 KiB
Raw Blame History

Configuration

CoreScope is configured via config.json in the server's working directory. Copy config.example.json to get started.

Core settings

Field Default Description
port 3000 HTTP server port
apiKey Secret key for admin API endpoints (POST/PUT routes)
dbPath Path to SQLite database file (optional, defaults to meshcore.db)

MQTT

"mqtt": {
  "broker": "mqtt://localhost:1883",
  "topic": "meshcore/+/+/packets"
}

The ingestor connects to this MQTT broker and subscribes to the topic pattern.

Multiple MQTT sources

Use mqttSources for multiple brokers:

"mqttSources": [
  {
    "name": "local",
    "broker": "mqtt://localhost:1883",
    "topics": ["meshcore/#"]
  },
  {
    "name": "remote",
    "broker": "mqtts://mqtt.example.com:8883",
    "username": "user",
    "password": "pass",
    "topics": ["meshcore/SJC/#"]
  }
]

Branding

Field Description
branding.siteName Site title shown in the nav bar
branding.tagline Subtitle on the home page
branding.logoUrl URL to a custom logo image
branding.faviconUrl URL to a custom favicon

Theme

Colors used throughout the UI. All values are hex color codes.

Field Description
theme.accent Primary accent color (links, buttons)
theme.navBg Navigation bar background
theme.navBg2 Secondary nav background
theme.statusGreen Healthy status color
theme.statusYellow Degraded status color
theme.statusRed Silent/error status color

See Customization for the full list — the theme customizer exposes every color.

Node colors

Default marker colors by role:

"nodeColors": {
  "repeater": "#dc2626",
  "companion": "#2563eb",
  "room": "#16a34a",
  "sensor": "#d97706",
  "observer": "#8b5cf6"
}

Health thresholds

How long (in hours) before a node is marked degraded or silent:

Field Default Description
healthThresholds.infraDegradedHours 24 Repeaters/rooms → degraded after this many hours
healthThresholds.infraSilentHours 72 Repeaters/rooms → silent after this many hours
healthThresholds.nodeDegradedHours 1 Companions/others → degraded
healthThresholds.nodeSilentHours 24 Companions/others → silent

Retention

Field Default Description
retention.nodeDays 7 Nodes not seen in N days move to inactive
retention.packetDays 30 Packets older than N days are deleted daily

Note: Lowering retention does not immediately shrink the database file. SQLite marks deleted pages as free but does not return them to the filesystem unless incremental auto-vacuum is enabled. New databases created after v0.x.x have auto-vacuum enabled automatically. Existing databases require a one-time migration — see the Database guide.

Database

Field Default Description
db.vacuumOnStartup false Run a one-time full VACUUM on startup to enable incremental auto-vacuum (blocks for minutes on large DBs)
db.incrementalVacuumPages 1024 Free pages returned to the OS after each retention reaper cycle

See Database for details on SQLite auto-vacuum, WAL, and manual maintenance. See #919 for background.

Channel decryption

Field Description
channelKeys Object of "label": "hex-key" pairs for decrypting channel messages
hashChannels Array of channel names (e.g., "#LongFast") to match by hash

See Channels for details.

Map defaults

"mapDefaults": {
  "center": [37.45, -122.0],
  "zoom": 9
}

Initial map center and zoom level.

Regions

"regions": {
  "SJC": "San Jose, US",
  "SFO": "San Francisco, US"
}

Named regions for the region filter dropdown. The defaultRegion field sets which region is selected by default.

Cache TTL

All values in seconds. Controls how long the server caches API responses:

"cacheTTL": {
  "stats": 10,
  "nodeList": 90,
  "nodeDetail": 300,
  "analyticsRF": 1800
}

Lower values = fresher data but more server load.

Packet store

Field Default Description
packetStore.maxMemoryMB 1024 Maximum RAM for in-memory packet store
packetStore.estimatedPacketBytes 450 Estimated bytes per packet (for memory budgeting)
packetStore.retentionHours 0 Only load packets younger than N hours on startup and keep them in memory. Set this on any instance with a large DB. 0 = unlimited (loads full DB history — causes OOM on cold start when the DB has hundreds of thousands of paths). Recommended: same as retention.packetDays × 24 (e.g. 168 for 7 days).

Warning: Leaving retentionHours at 0 on a large database will cause the server to OOM-kill itself on every cold start. The full packet history is loaded into the subpath index at startup; a DB with ~280K paths produces ~13M index entries before the process is killed.

Timestamps

Field Default Description
timestamps.defaultMode "ago" Display mode: "ago" (relative) or "absolute"
timestamps.timezone "local" "local" or "utc"
timestamps.formatPreset "iso" Date format preset

Live map

Field Default Description
liveMap.propagationBufferMs 5000 How long to buffer observations before animating

HTTPS

"https": {
  "cert": "/path/to/cert.pem",
  "key": "/path/to/key.pem"
}

Provide cert and key paths to enable HTTPS.

Geographic filtering

"geo_filter": {
  "polygon": [[51.55, 3.80], [51.55, 5.90], [50.65, 5.90], [50.65, 3.80]],
  "bufferKm": 20
}

Restricts ingestion and API responses to nodes within the polygon plus a buffer margin. Remove the block to disable filtering. Nodes with no GPS fix always pass through.

Can also be configured live via the 🗺️ GeoFilter tab in the Customizer (requires apiKey).

See Geographic Filtering for the full guide.

Areas

"areas": {
  "BAY": {
    "label": "Bay Area",
    "polygon": [[37.90, -122.55], [37.90, -121.75], [37.25, -121.75], [37.25, -122.55]]
  },
  "SJC": {
    "label": "San Jose",
    "latMin": 37.20, "latMax": 37.45, "lonMin": -122.05, "lonMax": -121.75
  }
}

GPS-based display filter. When configured, a pill bar appears in the dashboard letting users scope packets, nodes, and analytics to nodes physically located within a named area. Attribution is based on the transmitting node's own GPS coordinates — not the observer's location.

Each entry supports a polygon (array of [lat, lon] pairs) or a bounding box (latMin/latMax/lonMin/lonMax). Remove the block to disable the area filter UI.

See Area Filter for the full guide including the visual builder tool.

Home page

The home section customizes the onboarding experience. See config.example.json for the full structure including steps, checklist, and footerLinks.