Comprehensive guide to deploying and operating CoreScope. For a quick start, see DEPLOY.md.
- System Requirements
- Docker Deployment
- Configuration Reference
- MQTT Setup
- TLS / HTTPS
- Monitoring & Health Checks
- Backup & Restore
- Troubleshooting
| 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 run -d --name corescope \
-p 80:80 \
-v corescope-data:/app/data \
ghcr.io/kpa-clawbot/corescope:latestOpen 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:1883automatically - SQLite database at
/app/data/meshcore.db
The bare docker run command is the primary deployment method. One image, documented parameters — run it however you want.
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| 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 |
Instead of passing -e flags, you can drop a .env file in your data volume:
# /your/data/.env
DISABLE_MOSQUITTO=true
DISABLE_CADDY=true
MQTT_BROKER=mqtt://my-broker:1883The entrypoint sources this file before starting services. This works with any launch method (docker run, compose, or manage.sh).
Docker Compose files are maintained for backward compatibility but are no longer the recommended approach.
curl -sL https://raw.githubusercontent.com/Kpa-clawbot/CoreScope/master/docker-compose.example.yml \
-o docker-compose.yml
docker compose up -d| 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 |
The manage.sh wrapper script provides a setup wizard and convenience commands. It uses Docker Compose internally. See DEPLOY.md for usage. New deployments should prefer bare docker run.
| 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 |
docker compose pull
docker compose up -dFor docker run users:
docker pull ghcr.io/kpa-clawbot/corescope:latest
docker stop corescope && docker rm corescope
docker run -d --name corescope ... # same flags as beforeData is preserved in the volume — updates are non-destructive.
CoreScope uses a layered configuration system (highest priority wins):
- Environment variables —
MQTT_BROKER,DB_PATH, etc. /app/data/config.json— full config file (volume-mounted)- Built-in defaults — work out of the box with no config
| 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 |
For advanced configuration, create a config.json and mount it at /app/data/config.json:
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:latestSee 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
CoreScope receives MeshCore packets via MQTT. The container ships with an internal Mosquitto broker — no setup needed for basic use.
The built-in Mosquitto broker listens on port 1883 inside the container. Point your MeshCore gateways at it:
# 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:latestTo use your own MQTT broker (Mosquitto, EMQX, HiveMQ, etc.):
-
Disable the internal broker:
-e DISABLE_MOSQUITTO=true
-
Point the ingestor at your broker:
-e MQTT_BROKER=mqtt://your-broker:1883
Or via
config.json:{ "mqttSources": [ { "name": "my-broker", "broker": "mqtt://your-broker:1883", "username": "user", "password": "pass", "topics": ["meshcore/#"] } ] }
CoreScope can connect to multiple MQTT brokers simultaneously:
{
"mqttSources": [
{
"name": "local",
"broker": "mqtt://localhost:1883",
"topics": ["meshcore/#"]
},
{
"name": "remote",
"broker": "mqtts://remote-broker:8883",
"username": "reader",
"password": "secret",
"topics": ["meshcore/+/+/packets"]
}
]
}MeshCore gateways typically publish to meshcore/<gateway>/<region>/packets. The default subscription meshcore/# catches all of them.
Run CoreScope behind nginx, Traefik, or Cloudflare Tunnel for TLS termination:
# 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.
The container includes Caddy for automatic Let's Encrypt certificates:
-
Create a Caddyfile:
corescope.example.com { reverse_proxy localhost:3000 } -
Mount it and expose TLS ports:
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.
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.
| URL | Description |
|---|---|
/api/spec |
OpenAPI 3.0 JSON schema — machine-readable API definition |
/api/docs |
Interactive Swagger UI — browse and test all 40+ endpoints |
Browse the API interactively:
http://your-instance/api/docs
Fetch the spec programmatically:
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.
The live instance at analyzer.00id.net has all API endpoints publicly accessible:
- Spec: analyzer.00id.net/api/spec
- Docs: analyzer.00id.net/api/docs
The container includes a built-in health check that hits /api/stats:
docker inspect --format='{{.State.Health.Status}}' corescopeDocker reports healthy or unhealthy automatically. The check runs every 30 seconds.
curl -f http://localhost/api/statsReturns JSON with packet counts, node counts, and version info:
{
"totalPackets": 56234,
"totalNodes": 142,
"totalObservers": 12,
"packetsLastHour": 830,
"packetsLast24h": 19644,
"engine": "go",
"version": "v3.4.1"
}# All logs
docker compose logs -f
# Server only
docker compose logs -f | grep '\[server\]'
# Ingestor only
docker compose logs -f | grep '\[ingestor\]'docker stats corescopeAll persistent data lives in /app/data. The critical file is the SQLite database:
# 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).dbOptional files to back up:
config.json— custom configurationtheme.json— custom theme/branding
# Stop the container
docker stop corescope
# Replace the database
docker cp ./backup.db corescope:/app/data/meshcore.db
# Restart
docker start corescope# 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 -deleteThis 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
The ingestor couldn't connect to any MQTT broker. Check:
- Is the internal Mosquitto running? (
DISABLE_MOSQUITTOshould befalse) - Is the external broker reachable? Test with
mosquitto_sub -h broker -t meshcore/# - Are credentials correct in
config.json?
If behind a reverse proxy, ensure WebSocket upgrade headers are forwarded:
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.
The in-memory packet store grows with retained packets. Configure retention limits in config.json:
{
"packetStore": {
"retentionHours": 24,
"maxMemoryMB": 512
},
"retention": {
"nodeDays": 7,
"packetDays": 30
}
}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.
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
- Use
linux/arm64images (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.maxMemoryMBto limit RAM usage.