Files
efiten 51f823bf7e feat: one-click prune nodes outside geofilter (#669 M4) (#738)
## Summary

- Adds `POST /api/admin/prune-geo-filter` endpoint — dry-run by default,
`?confirm=true` to permanently delete nodes outside the current
geofilter polygon + buffer. Requires `X-API-Key` header.
- Adds **Prune nodes** section inside the GeoFilter customizer tab
(write-access only, same `writeEnabled` gate as PUT). **Preview** lists
affected nodes; **Confirm delete** removes them.
- Adds `GetNodesForGeoPrune` and `DeleteNodesByPubkeys` DB helpers.
- Updates `docs/user-guide/geofilter.md` — documents the UI button as
primary workflow, CLI script as alternative.

> **Depends on M3** (`feat/geofilter-m3-customizer`, PR #736). Merge M3
first.

## Test plan

- [x] `cd cmd/server && go test ./...` — all pass
- [x] Customizer GeoFilter tab without `apiKey` — Prune section not
visible
- [x] With `apiKey` + polygon active — Prune section visible
- [x] **Preview** returns list of nodes outside polygon (no deletions)
- [x] **Confirm delete** removes nodes, list clears
- [x] `POST /api/admin/prune-geo-filter` without `X-API-Key` → 401
- [x] `POST /api/admin/prune-geo-filter` with no polygon configured →
400

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 03:19:31 +00:00

6.5 KiB
Raw Permalink 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.

Home page

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