mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-09-25 22:23:50 +00:00
trackedBytes undercounted the packet store by about 2.5x, so packetStore.maxMemoryMB never triggered: a tx was charged at creation, before pickBestObservation set its path, so the byPathHop and spTxIndex costs were never added, and eviction then re-estimated with the path known and subtracted more than had been added, drifting the total downwards. The ParsedDecoded cache, the obsKeys dedup key and several per-observation strings were not estimated at all. StoreTx.accountedBytes now records what was charged, rechargeTx returns the delta after every pickBestObservation, and eviction subtracts accountedBytes instead of re-estimating. Measured by the author on a production database copy: trackedMB 151 against 402 MB of heap in use before, 351 against 396 MB after, with GC cycles dropping from ~0.71/s to ~0.011/s over 12 h on their instance. Reviewed by auditing the accounting rather than the arithmetic: all six production pickBestObservation sites recharge, all three subtraction sites read accountedBytes, every recharge site holds s.mu (Load from :857, the two ingest paths at :2778 and :3142 with deferred unlocks), observations are charged only after acceptance, and no charged tx is discarded during the chunk merge. That lock audit is the independent check, because CI's race job covers the ingestor only. Operator impact, both from the estimate growing rather than any limit moving: where maxMemoryMB is set, eviction now caps the real store size, and the cold load clamp drops about a third of the boot walk (124420 to 84374 packets at 650 MB). Where it is unset, which is the default, the change is inert. docs/go-migration.md claimed the Go server ignored the setting, which was never true, and is corrected here. Merged by the interim maintainer without a second human reviewer: CI (run 35258839322) plus the review above are the independent checks.
646 lines
19 KiB
Markdown
646 lines
19 KiB
Markdown
# CoreScope Deployment Guide
|
|
|
|
Comprehensive guide to deploying and operating CoreScope. For a quick start, see [DEPLOY.md](../DEPLOY.md).
|
|
|
|
## Table of Contents
|
|
|
|
- [System Requirements](#system-requirements)
|
|
- [Docker Deployment](#docker-deployment)
|
|
- [Configuration Reference](#configuration-reference)
|
|
- [MQTT Setup](#mqtt-setup)
|
|
- [TLS / HTTPS](#tls--https)
|
|
- [Behind a CDN (Cloudflare, Fastly)](#behind-a-cdn-cloudflare-fastly)
|
|
- [Monitoring & Health Checks](#monitoring--health-checks)
|
|
- [Backup & Restore](#backup--restore)
|
|
- [Troubleshooting](#troubleshooting)
|
|
|
|
---
|
|
|
|
## System Requirements
|
|
|
|
| Resource | Minimum | Recommended |
|
|
|----------|---------|-------------|
|
|
| RAM | 256 MB | 512 MB+ |
|
|
| Disk | 500 MB (image + DB) | 2 GB+ for long-term data |
|
|
| CPU | 1 core | 2+ cores |
|
|
| Architecture | `linux/amd64`, `linux/arm64` | — |
|
|
| Docker | 20.10+ | Latest stable |
|
|
|
|
CoreScope runs well on Raspberry Pi 4/5 (ARM64). The Go server uses ~300 MB RAM for 56K+ packets.
|
|
|
|
---
|
|
|
|
## Docker Deployment
|
|
|
|
### Quick Start (one command)
|
|
|
|
```bash
|
|
docker run -d --name corescope \
|
|
-p 80:80 \
|
|
-v corescope-data:/app/data \
|
|
ghcr.io/kpa-clawbot/corescope:latest
|
|
```
|
|
|
|
Open `http://localhost` — you'll see an empty dashboard ready to receive packets.
|
|
|
|
No `config.json` is required. The server starts with sensible defaults:
|
|
- HTTP on port 3000 (Caddy proxies port 80 → 3000 internally)
|
|
- Internal Mosquitto MQTT broker on port 1883
|
|
- Ingestor connects to `mqtt://localhost:1883` automatically
|
|
- SQLite database at `/app/data/meshcore.db`
|
|
|
|
### Full `docker run` Reference (recommended)
|
|
|
|
The bare `docker run` command is the primary deployment method. One image, documented parameters — run it however you want.
|
|
|
|
```bash
|
|
docker run -d --name corescope \
|
|
--restart=unless-stopped \
|
|
-p 80:80 -p 443:443 -p 1883:1883 \
|
|
-e DISABLE_MOSQUITTO=false \
|
|
-e DISABLE_CADDY=false \
|
|
-v /your/data:/app/data \
|
|
-v /your/Caddyfile:/etc/caddy/Caddyfile:ro \
|
|
-v /your/caddy-data:/data/caddy \
|
|
ghcr.io/kpa-clawbot/corescope:latest
|
|
```
|
|
|
|
#### Parameters
|
|
|
|
| Parameter | Required | Description |
|
|
|-----------|----------|-------------|
|
|
| `-p 80:80` | Yes | HTTP web UI |
|
|
| `-p 443:443` | No | HTTPS (only if using built-in Caddy with a domain) |
|
|
| `-p 1883:1883` | No | MQTT broker (expose if external gateways connect directly) |
|
|
| `-v /your/data:/app/data` | Yes | Persistent data: SQLite DB, config.json, theme.json |
|
|
| `-v /your/Caddyfile:/etc/caddy/Caddyfile:ro` | No | Custom Caddyfile for HTTPS |
|
|
| `-v /your/caddy-data:/data/caddy` | No | Caddy TLS certificate storage |
|
|
| `-e DISABLE_MOSQUITTO=true` | No | Skip the internal Mosquitto broker (use your own) |
|
|
| `-e DISABLE_CADDY=true` | No | Skip the built-in Caddy reverse proxy |
|
|
| `-e MQTT_BROKER=mqtt://host:1883` | No | Override MQTT broker URL |
|
|
|
|
#### `/app/data/.env` convenience file
|
|
|
|
Instead of passing `-e` flags, you can drop a `.env` file in your data volume:
|
|
|
|
```bash
|
|
# /your/data/.env
|
|
DISABLE_MOSQUITTO=true
|
|
DISABLE_CADDY=true
|
|
MQTT_BROKER=mqtt://my-broker:1883
|
|
```
|
|
|
|
The entrypoint sources this file before starting services. This works with any launch method (`docker run`, compose, or manage.sh).
|
|
|
|
### Docker Compose (legacy alternative)
|
|
|
|
Docker Compose files are maintained for backward compatibility but are no longer the recommended approach.
|
|
|
|
```bash
|
|
curl -sL https://raw.githubusercontent.com/Kpa-clawbot/CoreScope/master/docker-compose.example.yml \
|
|
-o docker-compose.yml
|
|
docker compose up -d
|
|
```
|
|
|
|
#### Compose environment variables
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `HTTP_PORT` | `80` | Host port for the web UI |
|
|
| `DATA_DIR` | `./data` | Host path for persistent data |
|
|
| `DISABLE_MOSQUITTO` | `false` | Set `true` to use an external MQTT broker |
|
|
| `DISABLE_CADDY` | `false` | Set `true` to skip the built-in Caddy proxy |
|
|
|
|
### manage.sh (legacy alternative)
|
|
|
|
The `manage.sh` wrapper script provides a setup wizard and convenience commands. It uses Docker Compose internally. See [DEPLOY.md](../DEPLOY.md) for usage. New deployments should prefer bare `docker run`.
|
|
|
|
### Image tags
|
|
|
|
| Tag | Use case |
|
|
|-----|----------|
|
|
| `v3.4.1` | Pinned release — recommended for production |
|
|
| `v3.4` | Latest patch in the v3.4.x series |
|
|
| `v3` | Latest minor+patch in v3.x |
|
|
| `latest` | Latest release tag |
|
|
| `edge` | Built from master on every push — unstable |
|
|
|
|
### Updating
|
|
|
|
```bash
|
|
docker compose pull
|
|
docker compose up -d
|
|
```
|
|
|
|
For `docker run` users:
|
|
|
|
```bash
|
|
docker pull ghcr.io/kpa-clawbot/corescope:latest
|
|
docker stop corescope && docker rm corescope
|
|
docker run -d --name corescope ... # same flags as before
|
|
```
|
|
|
|
Data is preserved in the volume — updates are non-destructive.
|
|
|
|
---
|
|
|
|
## Configuration Reference
|
|
|
|
CoreScope uses a layered configuration system (highest priority wins):
|
|
|
|
1. **Environment variables** — `MQTT_BROKER`, `DB_PATH`, etc.
|
|
2. **`/app/data/config.json`** — full config file (volume-mounted)
|
|
3. **Built-in defaults** — work out of the box with no config
|
|
|
|
### Environment variable overrides
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `MQTT_BROKER` | `mqtt://localhost:1883` | MQTT broker URL (overrides config file) |
|
|
| `MQTT_TOPIC` | `meshcore/#` | MQTT topic subscription pattern |
|
|
| `DB_PATH` | `data/meshcore.db` | SQLite database path |
|
|
| `DISABLE_MOSQUITTO` | `false` | Skip the internal Mosquitto broker |
|
|
| `DISABLE_CADDY` | `false` | Skip the built-in Caddy reverse proxy |
|
|
|
|
### config.json
|
|
|
|
For advanced configuration, create a `config.json` and mount it at `/app/data/config.json`:
|
|
|
|
```bash
|
|
docker run -d --name corescope \
|
|
-p 80:80 \
|
|
-v corescope-data:/app/data \
|
|
-v ./config.json:/app/data/config.json:ro \
|
|
ghcr.io/kpa-clawbot/corescope:latest
|
|
```
|
|
|
|
See `config.example.json` in the repository for all available options including:
|
|
- MQTT sources (multiple brokers)
|
|
- Channel encryption keys
|
|
- Branding and theming
|
|
- Health thresholds
|
|
- Region filters
|
|
- Retention policies
|
|
- Geo-filtering
|
|
- Map tile providers (OSM, Stamen, Carto, etc.)
|
|
|
|
### Map Tile Providers
|
|
|
|
Map tile providers are enabled and configured via the `config.json` file. You can provide your custom API credentials (e.g. `osm_url`, `stamen_api_key`, `mapbox_api_key`) to activate external tile services. Once configured on the server, users can select their preferred tile provider from the Customizer UI on the client, and their choice will be persisted automatically.
|
|
|
|
#### CARTO now requires an API key
|
|
|
|
CARTO's raster basemaps (`carto-dark`, `carto-light`, `carto-voyager`,
|
|
`carto-voyager-dark`, `positron-dark`) require an API key. Without one, tiles
|
|
are still returned with a **200 status** but every one of them is stamped
|
|
`API KEY REQUIRED` — so nothing errors, no healthcheck fires, and the map just
|
|
quietly looks broken.
|
|
|
|
Get a free key at <https://carto.com/basemaps/apikey> — no CARTO account, no
|
|
credit card, emailed back immediately, 5 million tile requests per calendar
|
|
month across raster and vector. Then set it as `key`:
|
|
|
|
```json
|
|
"map": {
|
|
"tiles": {
|
|
"providers": {
|
|
"carto": { "enabled": true, "key": "YOUR_CARTO_KEY" }
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Notes:
|
|
|
|
- The query parameter is `key`. **`api_key` is silently ignored** and still
|
|
serves the watermarked tile.
|
|
- The key is sent to the browser, so treat it as public. CARTO asks for a domain
|
|
when the key is issued, but whether that restriction is enforced on every
|
|
request has not been verified here. Assume anyone can read the key from
|
|
devtools and spend your quota. The 5M/month fair-use ceiling makes that
|
|
low-impact for most deployments, but it is worth knowing.
|
|
- A wrong or expired key fails the same silent way as no key at all. Verify by
|
|
**looking at a tile**, not by checking for a 200.
|
|
- CARTO has said it is considering freezing data updates to the raster
|
|
basemaps. If you would rather not depend on them, set
|
|
`"carto": { "enabled": false }` and use another provider — `esri` needs no
|
|
key at all, and `osm`/`stamen` accept their own tokens.
|
|
|
|
---
|
|
|
|
## MQTT Setup
|
|
|
|
CoreScope receives MeshCore packets via MQTT. The container ships with an internal Mosquitto broker — no setup needed for basic use.
|
|
|
|
### Internal broker (default)
|
|
|
|
The built-in Mosquitto broker listens on port 1883 inside the container. Point your MeshCore gateways at it:
|
|
|
|
```bash
|
|
# Expose MQTT port for external gateways
|
|
docker run -d --name corescope \
|
|
-p 80:80 -p 1883:1883 \
|
|
-v corescope-data:/app/data \
|
|
ghcr.io/kpa-clawbot/corescope:latest
|
|
```
|
|
|
|
### External broker
|
|
|
|
To use your own MQTT broker (Mosquitto, EMQX, HiveMQ, etc.):
|
|
|
|
1. Disable the internal broker:
|
|
```bash
|
|
-e DISABLE_MOSQUITTO=true
|
|
```
|
|
|
|
2. Point the ingestor at your broker:
|
|
```bash
|
|
-e MQTT_BROKER=mqtt://your-broker:1883
|
|
```
|
|
|
|
Or via `config.json`:
|
|
```json
|
|
{
|
|
"mqttSources": [
|
|
{
|
|
"name": "my-broker",
|
|
"broker": "mqtt://your-broker:1883",
|
|
"username": "user",
|
|
"password": "pass",
|
|
"topics": ["meshcore/#"]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Multiple brokers
|
|
|
|
CoreScope can connect to multiple MQTT brokers simultaneously:
|
|
|
|
```json
|
|
{
|
|
"mqttSources": [
|
|
{
|
|
"name": "local",
|
|
"broker": "mqtt://localhost:1883",
|
|
"topics": ["meshcore/#"]
|
|
},
|
|
{
|
|
"name": "remote",
|
|
"broker": "mqtts://remote-broker:8883",
|
|
"username": "reader",
|
|
"password": "secret",
|
|
"topics": ["meshcore/+/+/packets"]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### MQTT topic format
|
|
|
|
MeshCore gateways typically publish to `meshcore/<gateway>/<region>/packets`. The default subscription `meshcore/#` catches all of them.
|
|
|
|
---
|
|
|
|
## TLS / HTTPS
|
|
|
|
### Option 1: External reverse proxy (recommended)
|
|
|
|
Run CoreScope behind nginx, Traefik, or Cloudflare Tunnel for TLS termination:
|
|
|
|
```nginx
|
|
# nginx example
|
|
server {
|
|
listen 443 ssl;
|
|
server_name corescope.example.com;
|
|
|
|
ssl_certificate /etc/ssl/certs/corescope.pem;
|
|
ssl_certificate_key /etc/ssl/private/corescope.key;
|
|
|
|
location / {
|
|
proxy_pass http://localhost:80;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host $host;
|
|
}
|
|
}
|
|
```
|
|
|
|
The `Upgrade` and `Connection` headers are required for WebSocket support.
|
|
|
|
### Option 2: Built-in Caddy (auto-TLS)
|
|
|
|
The container includes Caddy for automatic Let's Encrypt certificates:
|
|
|
|
1. Create a Caddyfile:
|
|
```
|
|
corescope.example.com {
|
|
reverse_proxy localhost:3000
|
|
}
|
|
```
|
|
|
|
2. Mount it and expose TLS ports:
|
|
```bash
|
|
docker run -d --name corescope \
|
|
-p 80:80 -p 443:443 \
|
|
-v corescope-data:/app/data \
|
|
-v caddy-certs:/data/caddy \
|
|
-v ./Caddyfile:/etc/caddy/Caddyfile:ro \
|
|
ghcr.io/kpa-clawbot/corescope:latest
|
|
```
|
|
|
|
Caddy handles certificate issuance and renewal automatically.
|
|
|
|
---
|
|
|
|
## API Documentation
|
|
|
|
CoreScope auto-generates an OpenAPI 3.0 specification from its route definitions. The spec is always in sync with the running server — no manual maintenance required.
|
|
|
|
### Endpoints
|
|
|
|
| URL | Description |
|
|
|-----|-------------|
|
|
| `/api/spec` | OpenAPI 3.0 JSON schema — machine-readable API definition |
|
|
| `/api/docs` | Interactive Swagger UI — browse and test all 40+ endpoints |
|
|
|
|
### Usage
|
|
|
|
**Browse the API interactively:**
|
|
```
|
|
http://your-instance/api/docs
|
|
```
|
|
|
|
**Fetch the spec programmatically:**
|
|
```bash
|
|
curl http://your-instance/api/spec | jq .
|
|
```
|
|
|
|
**For bot/integration developers:** The spec includes all request parameters, response schemas, and example values. Import it into Postman, Insomnia, or any OpenAPI-compatible tool.
|
|
|
|
### Public instance
|
|
The live instance at [analyzer.00id.net](https://analyzer.00id.net) has all API endpoints publicly accessible:
|
|
- Spec: [analyzer.00id.net/api/spec](https://analyzer.00id.net/api/spec)
|
|
- Docs: [analyzer.00id.net/api/docs](https://analyzer.00id.net/api/docs)
|
|
|
|
---
|
|
|
|
## Behind a CDN (Cloudflare, Fastly)
|
|
|
|
If you front CoreScope with a CDN — Cloudflare, Fastly, Akamai, or
|
|
similar — you **must** configure the CDN to bypass cache for `/api/*`.
|
|
The server emits `Cache-Control: no-store` on every API response
|
|
(see #1551), but Cloudflare's zone-level Cache Rules and legacy Page
|
|
Rules can override origin headers. When that happens, observers, packets,
|
|
stats and other API responses get cached at the edge for minutes to hours,
|
|
producing observer-flap, stale dashboards and inconsistent state across
|
|
viewers.
|
|
|
|
### 1. Verify whether your CDN is caching `/api/*`
|
|
|
|
From **outside** the CDN (a different network than your origin), run:
|
|
|
|
```sh
|
|
curl -sI 'https://<your-domain>/api/observers' | grep -iE 'cf-cache|age|cache-control'
|
|
```
|
|
|
|
Healthy output (cache is bypassed):
|
|
|
|
```
|
|
cache-control: no-store
|
|
cf-cache-status: BYPASS
|
|
age: 0
|
|
```
|
|
|
|
Unhealthy output (CDN is caching despite `no-store`):
|
|
|
|
```
|
|
cache-control: no-store
|
|
cf-cache-status: HIT
|
|
age: 4732
|
|
```
|
|
|
|
`HIT` or `age > 0` means an intermediary is serving cached JSON. Fix it
|
|
before relying on the dashboard.
|
|
|
|
You can also run the bundled helper, which exits non-zero with a precise
|
|
diagnostic when caching is detected:
|
|
|
|
```sh
|
|
scripts/check-cdn-bypass.sh https://<your-domain>
|
|
```
|
|
|
|
### 2. Cloudflare: add a Cache Rule (recommended)
|
|
|
|
Cloudflare Dashboard → your zone → **Caching → Cache Rules → Create rule**:
|
|
|
|
- **When incoming requests match:** Field = `URI Path`, Operator = `starts with`, Value = `/api/`
|
|
- **Then:** Cache eligibility → **Bypass cache**
|
|
|
|
Save and deploy. Re-run the curl above; you should now see
|
|
`cf-cache-status: BYPASS`.
|
|
|
|
Legacy Page Rules equivalent (if your zone has no Cache Rules):
|
|
|
|
- URL pattern: `*your-domain*/api/*`
|
|
- Setting: **Cache Level → Bypass**
|
|
|
|
### 3. Fastly / other CDNs
|
|
|
|
Apply the equivalent bypass-cache rule for the `/api/` path prefix.
|
|
The key invariant is: any response from `/api/*` must reach the
|
|
browser uncached (no shared-cache HIT, no positive `Age` header).
|
|
|
|
### 4. Re-verify
|
|
|
|
After applying the rule, run step 1's curl from outside the CDN again
|
|
and confirm `cf-cache-status: BYPASS` (or absence of HIT) and `age: 0`.
|
|
|
|
### 5. Watch the server log
|
|
|
|
The server logs a one-shot warning at the first request bearing a
|
|
CDN-specific header (`CF-Connecting-IP`, `CF-Ray`, `Fastly-Client-IP`,
|
|
or `True-Client-IP`):
|
|
|
|
Generic reverse-proxy headers (`X-Forwarded-For`, `X-Real-IP`) are
|
|
deliberately NOT used as the signal — every nginx/Caddy/Traefik/k8s
|
|
deploy sets them, so they'd produce false positives on every
|
|
reverse-proxied install.
|
|
|
|
```
|
|
[security] WARNING: detected request via CDN (CF-Ray header present).
|
|
Ensure /api/* is bypassed in your CDN config — see docs/deployment-behind-cdn.md.
|
|
Cached API responses cause observer-flap and incorrect dashboards.
|
|
```
|
|
|
|
This is informational — the request is not blocked. Treat it as a
|
|
prompt to run the verification curl above. The warning logs at most
|
|
once per process boot, regardless of how many CDN-fronted requests
|
|
arrive.
|
|
|
|
### Why this can't be fixed server-side
|
|
|
|
CDN cache policy is operator-controlled. The application emits the
|
|
most conservative cache header it can (`Cache-Control: no-store`),
|
|
but Cloudflare Cache Rules / Page Rules have higher precedence than
|
|
origin headers in many zone configurations. The only durable fix is
|
|
the operator-side bypass rule.
|
|
|
|
---
|
|
|
|
## Monitoring & Health Checks
|
|
|
|
### Docker health check
|
|
|
|
The container includes a built-in health check that hits `/api/stats`:
|
|
|
|
```bash
|
|
docker inspect --format='{{.State.Health.Status}}' corescope
|
|
```
|
|
|
|
Docker reports `healthy` or `unhealthy` automatically. The check runs every 30 seconds.
|
|
|
|
### Manual health check
|
|
|
|
```bash
|
|
curl -f http://localhost/api/stats
|
|
```
|
|
|
|
Returns JSON with packet counts, node counts, and version info:
|
|
|
|
```json
|
|
{
|
|
"totalPackets": 56234,
|
|
"totalNodes": 142,
|
|
"totalObservers": 12,
|
|
"packetsLastHour": 830,
|
|
"packetsLast24h": 19644,
|
|
"engine": "go",
|
|
"version": "v3.4.1"
|
|
}
|
|
```
|
|
|
|
### Log monitoring
|
|
|
|
```bash
|
|
# All logs
|
|
docker compose logs -f
|
|
|
|
# Server only
|
|
docker compose logs -f | grep '\[server\]'
|
|
|
|
# Ingestor only
|
|
docker compose logs -f | grep '\[ingestor\]'
|
|
```
|
|
|
|
### Resource monitoring
|
|
|
|
```bash
|
|
docker stats corescope
|
|
```
|
|
|
|
---
|
|
|
|
## Backup & Restore
|
|
|
|
### Backup
|
|
|
|
All persistent data lives in `/app/data`. The critical file is the SQLite database:
|
|
|
|
```bash
|
|
# Copy from the Docker volume
|
|
docker cp corescope:/app/data/meshcore.db ./backup-$(date +%Y%m%d).db
|
|
|
|
# Or if using a bind mount
|
|
cp ./data/meshcore.db ./backup-$(date +%Y%m%d).db
|
|
```
|
|
|
|
Optional files to back up:
|
|
- `config.json` — custom configuration
|
|
- `theme.json` — custom theme/branding
|
|
|
|
### Restore
|
|
|
|
```bash
|
|
# Stop the container
|
|
docker stop corescope
|
|
|
|
# Replace the database
|
|
docker cp ./backup.db corescope:/app/data/meshcore.db
|
|
|
|
# Restart
|
|
docker start corescope
|
|
```
|
|
|
|
### Automated backups
|
|
|
|
```bash
|
|
# cron: daily backup at 3 AM, keep 7 days
|
|
0 3 * * * docker cp corescope:/app/data/meshcore.db /backups/corescope-$(date +\%Y\%m\%d).db && find /backups -name "corescope-*.db" -mtime +7 -delete
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Container starts but dashboard is empty
|
|
|
|
This is normal on first start with no MQTT sources configured. The dashboard shows data once packets arrive via MQTT. Either:
|
|
- Point a MeshCore gateway at the container's MQTT broker (port 1883)
|
|
- Configure an external MQTT source in `config.json`
|
|
|
|
### "no MQTT connections established" in logs
|
|
|
|
The ingestor couldn't connect to any MQTT broker. Check:
|
|
1. Is the internal Mosquitto running? (`DISABLE_MOSQUITTO` should be `false`)
|
|
2. Is the external broker reachable? Test with `mosquitto_sub -h broker -t meshcore/#`
|
|
3. Are credentials correct in `config.json`?
|
|
|
|
### WebSocket disconnects / real-time updates stop
|
|
|
|
If behind a reverse proxy, ensure WebSocket upgrade headers are forwarded:
|
|
```nginx
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
```
|
|
|
|
Also check proxy timeouts — set them to at least 300s for long-lived WebSocket connections.
|
|
|
|
### High memory usage
|
|
|
|
The in-memory packet store grows with retained packets. Configure retention limits in `config.json`:
|
|
|
|
```json
|
|
{
|
|
"packetStore": {
|
|
"retentionHours": 24,
|
|
"maxMemoryMB": 512
|
|
},
|
|
"retention": {
|
|
"nodeDays": 7,
|
|
"packetDays": 30
|
|
}
|
|
}
|
|
```
|
|
|
|
`packetStore.maxMemoryMB` bounds the store **and the caches that belong to it** — the decoded-packet cache and per-packet index entries, not just the stored rows. It is enforced in two places: the startup load stops at the budget, and the store evicts oldest-first when it exceeds it. Leaving it unset means no limit. Actual usage is on `/api/perf` as `packetStore.trackedMB`, next to `maxMB`.
|
|
|
|
### Database locked errors
|
|
|
|
SQLite doesn't support concurrent writers well. Ensure only one CoreScope instance accesses the database file. If running multiple containers, each needs its own database.
|
|
|
|
### Container unhealthy
|
|
|
|
Check logs: `docker compose logs --tail 50`. Common causes:
|
|
- Port 3000 already in use inside the container
|
|
- Database file permissions (must be writable by the container user)
|
|
- Corrupted database — restore from backup
|
|
|
|
### ARM / Raspberry Pi issues
|
|
|
|
- Use `linux/arm64` images (Pi 4 and 5). Pi 3 (armv7) is not supported.
|
|
- First pull may be slow — the multi-arch manifest selects the right image automatically.
|
|
- If memory is tight, set `packetStore.maxMemoryMB` to limit RAM usage.
|