mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-06-04 19:02:04 +00:00
317b59ab10
## 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>
237 lines
7.3 KiB
Markdown
237 lines
7.3 KiB
Markdown
# 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
|
||
|
||
```json
|
||
"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:
|
||
|
||
```json
|
||
"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](customization.md) for the full list — the theme customizer exposes every color.
|
||
|
||
## Node colors
|
||
|
||
Default marker colors by role:
|
||
|
||
```json
|
||
"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](database.md) is enabled. New databases created
|
||
> after v0.x.x have auto-vacuum enabled automatically. Existing databases require
|
||
> a one-time migration — see the [Database](database.md) 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](database.md) for details on SQLite auto-vacuum, WAL, and manual maintenance.
|
||
See [#919](https://github.com/Kpa-clawbot/CoreScope/issues/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](channels.md) for details.
|
||
|
||
## Map defaults
|
||
|
||
```json
|
||
"mapDefaults": {
|
||
"center": [37.45, -122.0],
|
||
"zoom": 9
|
||
}
|
||
```
|
||
|
||
Initial map center and zoom level.
|
||
|
||
## Regions
|
||
|
||
```json
|
||
"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:
|
||
|
||
```json
|
||
"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
|
||
|
||
```json
|
||
"https": {
|
||
"cert": "/path/to/cert.pem",
|
||
"key": "/path/to/key.pem"
|
||
}
|
||
```
|
||
|
||
Provide cert and key paths to enable HTTPS.
|
||
|
||
## Geographic filtering
|
||
|
||
```json
|
||
"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](geofilter.md) for the full guide.
|
||
|
||
## Areas
|
||
|
||
```json
|
||
"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](area-filter.md) 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`.
|