Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# AGENTS.md

This file provides guidance to Codex and other coding agents when working in `tr-stack`.

## What This Project Is

Single Docker Compose stack for Trunk Reporter: trunk-recorder, tr-engine, tr-dashboard, imbe-asr, PostgreSQL, Mosquitto, and Caddy.

## Commands

```bash
cp sample.env .env
docker compose config
docker compose up -d
docker compose logs -f tr-engine
```

Use `docker compose -f docker-compose.yml -f docker-compose.pi.yml up -d` for Raspberry Pi / CPU-oriented deployments.

## Change Guidance

- Keep the default path friendly to first-time hobbyist installs.
- Do not require NVIDIA GPU support in the default compose file; GPU support should be an override or clearly optional.
- Auth docs must use the current tr-engine model: `open`, `token`, and `full` modes derived from `AUTH_TOKEN` and `ADMIN_PASSWORD`.
- Avoid exposing anonymous MQTT broadly by default; document LAN-only assumptions when ports are published.
5 changes: 0 additions & 5 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
{$SITE_ADDRESS::80} {
# When AUTH_ENABLED=true, inject read token for browser requests.
# Browsers that send their own Authorization header (e.g. with WRITE_TOKEN) pass through untouched.
@no_auth not header Authorization *
request_header @no_auth Authorization "Bearer {$AUTH_TOKEN:}"

handle /api/* {
reverse_proxy tr-engine:8080
}
Expand Down
25 changes: 11 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ docker compose logs -f tr-engine
**Dashboard:** http://YOUR_SERVER_IP (served by Caddy on port 80)
**API:** http://YOUR_SERVER_IP/api/v1

Auth is disabled by default — no login required for local installs. See [Securing for Public Access](#securing-for-public-access) if you want to expose this externally.
Auth is disabled by default — no login required for trusted local installs. See [Securing for Public Access](#securing-for-public-access) before exposing this externally.

## Configuration

Expand All @@ -54,20 +54,19 @@ POSTGRES_PASSWORD=your-secure-password
SITE_ADDRESS=http://192.168.1.100 # your server's IP or hostname
```

Auth is disabled by default (`AUTH_ENABLED=false`). The dashboard is accessible without login. See [Securing for Public Access](#securing-for-public-access) to enable auth.
Auth is in `open` mode by default when both `AUTH_TOKEN` and `ADMIN_PASSWORD` are unset. The dashboard is accessible without login. See [Securing for Public Access](#securing-for-public-access) to enable auth.

### Securing for Public Access

If you're exposing the stack to the internet, enable auth:
If you're exposing the stack to the internet, use full auth:

```env
AUTH_ENABLED=true
AUTH_TOKEN= # openssl rand -base64 32
WRITE_TOKEN= # openssl rand -base64 32
ADMIN_PASSWORD= # dashboard login password
# Optional public read token returned by /api/v1/auth-init:
# AUTH_TOKEN= # openssl rand -base64 32
```

With auth enabled, Caddy injects the read token for browser requests automatically. The dashboard will show a login prompt — use username `admin` and your `ADMIN_PASSWORD`.
When `ADMIN_PASSWORD` is set, tr-engine runs in full mode. The dashboard uses JWT login for writes and can use the optional `AUTH_TOKEN` as a public read token through `/api/v1/auth-init`. Caddy does not inject auth headers.

### config.json

Expand Down Expand Up @@ -141,14 +140,12 @@ CPU inference on Pi is significantly slower than GPU. Expect higher latency on t

## GPU

GPU is strongly recommended for imbe-asr. The stack assumes NVIDIA GPU with the Docker NVIDIA runtime installed. For CPU-only:
The default stack runs imbe-asr on CPU so first-run works without NVIDIA drivers. For GPU acceleration, use the GPU override:

```env
IMBE_ASR_DEVICE=cpu
```bash
docker compose -f docker-compose.yml -f docker-compose.gpu.yml up -d
```

And remove the `deploy.resources` section from the `imbe-asr` service in `docker-compose.yml`.

## Upgrading

```bash
Expand All @@ -161,9 +158,9 @@ docker compose pull && docker compose up -d
|---|---|---|
| tr-engine API | 8080 | Set `HTTP_PORT` in `.env` |
| Caddy (dashboard + API) | 80 | Set `HTTP_PORT` in `.env` |
| Mosquitto MQTT | 1883 | For trunk-recorder instances on other machines |
| Mosquitto MQTT | 1883 | Bound to localhost by default; set `MQTT_BIND_IP` for external trunk-recorder hosts |

All ports bind to `0.0.0.0` by default. Set `BIND_IP` in `.env` to restrict to a specific interface.
HTTP binds to `0.0.0.0` by default. Set `BIND_IP` in `.env` to restrict it to a specific interface. MQTT binds to `127.0.0.1` by default to avoid exposing anonymous Mosquitto on the LAN; set `MQTT_BIND_IP` only when remote trunk-recorder instances need to publish to this broker.

## Setup Guide

Expand Down
75 changes: 58 additions & 17 deletions config.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,58 @@
{
"ver": 2,
"audioArchive": true,
"callLog": true,
"logFile": true,
"broadcastSignals": false,
"audioOutputPath": "/app/audio",
"captureDir": "/app/audio",
"callTimeout": 1,
"captureDir": "/app/audio/",
"audioStreaming": true,
"instanceId": "eddie",

"sources": [
{
"center": 855000000,
"rate": 2400000,
"center": 855500000.0,
"rate": 10000000.0,
"error": 0,
"gain": 40,
"antenna": "SMA",
"digitalRecorders": 3,
"driver": "osmosdr",
"device": "rtl=0"
"gain": 12,
"mixGain": 12,
"ifGain": 8,
"lnaGain": 12,
"digitalRecorders": 20,
"device": "airspy,bias=1",
"driver": "osmosdr"
}
],

"systems": [
{
"control_channels": [851000000],
"shortName": "butco",
"control_channels": [853062500, 853037500, 853287500, 853537500],
"type": "p25",
"shortName": "mySys",
"talkgroupsFile": "/app/talkgroups/talkgroups.csv"
"modulation": "qpsk",
"talkgroupsFile": "/app/talkgroups/trs_tg_6643.csv",
"transmissionArchive": false,
"compressWav": true,
"digitalLevels": 3,
"minDuration": 0.3,
"enabled": true
},
{
"shortName": "warco",
"control_channels": [858237500, 858762500, 859237500, 859762500],
"type": "p25",
"modulation": "qpsk",
"talkgroupsFile": "/app/talkgroups/trs_tg_6643.csv",
"transmissionArchive": false,
"compressWav": true,
"digitalLevels": 3,
"minDuration": 0.3,
"enabled": false
},
{
"shortName": "conv",
"type": "conventional",
"channelFile": "/app/talkgroups/channels_conv.csv",
"squelch": -60,
"analogLevels": 8,
"minDuration": 1.0
}
],

Expand All @@ -36,11 +63,12 @@
"broker": "tcp://mosquitto:1883",
"topic": "tr/feeds",
"unit_topic": "tr/units",
"instanceId": "my-site",
"clientid": "tr-eddie",
"username": "",
"password": "",
"mqtt_audio": true,
"mqtt_audio_type": "m4a"
"mqtt_audio_type": "m4a",
"console_logs": true
},
{
"name": "mqtt_dvcf",
Expand All @@ -49,6 +77,19 @@
"mqtt_enabled": true,
"broker": "tcp://mosquitto:1883",
"topic": "tr/feeds",
"clientid": "dvcf-eddie",
"username": "",
"password": ""
},
{
"name": "mqtt_avcf",
"library": "libmqtt_avcf",
"write_enabled": true,
"mqtt_enabled": false,
"analog_only": true,
"broker": "tcp://mosquitto:1883",
"topic": "tr/feeds",
"clientid": "avcf-eddie",
"username": "",
"password": ""
}
Expand Down
15 changes: 15 additions & 0 deletions docker-compose.gpu.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Docker Compose override for NVIDIA GPU inference.
# Usage: docker compose -f docker-compose.yml -f docker-compose.gpu.yml up -d

services:
imbe-asr:
image: ghcr.io/trunk-reporter/imbe-asr-server:latest
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
environment:
IMBE_ASR_DEVICE: cuda
18 changes: 5 additions & 13 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
image: eclipse-mosquitto:2
restart: unless-stopped
ports:
- "${BIND_IP:-0.0.0.0}:1883:1883"
- "${MQTT_BIND_IP:-127.0.0.1}:1883:1883"
volumes:
- ./mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf
- mosquitto-data:/mosquitto/data
Expand Down Expand Up @@ -66,22 +66,15 @@ services:
# ---------------------------------------------------------------------------
# imbe-asr — IMBE vocoder → text transcription server
# Models are downloaded from HuggingFace on first run if not present.
# GPU strongly recommended; set IMBE_ASR_DEVICE=cpu for CPU-only.
# CPU by default; use docker-compose.gpu.yml for NVIDIA GPU acceleration.
# ---------------------------------------------------------------------------
imbe-asr:
image: ghcr.io/trunk-reporter/imbe-asr-server:latest
image: ghcr.io/trunk-reporter/imbe-asr-server:cpu
restart: unless-stopped
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
environment:
IMBE_ASR_MODEL: "${IMBE_ASR_MODEL:-trunk-reporter/imbe-asr-base-512d-p25}"
IMBE_ASR_BEAM_WIDTH: "${IMBE_ASR_BEAM_WIDTH:-100}"
IMBE_ASR_DEVICE: "${IMBE_ASR_DEVICE:-cuda}"
IMBE_ASR_DEVICE: "${IMBE_ASR_DEVICE:-cpu}"
volumes:
- ./data/models:/models
healthcheck:
Expand Down Expand Up @@ -111,7 +104,7 @@ services:
- imbe-asr

# ---------------------------------------------------------------------------
# caddy — Reverse proxy: single port for dashboard + API with auth injection
# caddy — Reverse proxy: single port for dashboard + API
# ---------------------------------------------------------------------------
caddy:
image: caddy:2-alpine
Expand All @@ -120,7 +113,6 @@ services:
- "${BIND_IP:-0.0.0.0}:${HTTP_PORT:-80}:80"
environment:
SITE_ADDRESS: ${SITE_ADDRESS:-:80}
AUTH_TOKEN: ${AUTH_TOKEN:-placeholder}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
Expand Down
13 changes: 13 additions & 0 deletions mosquitto/mosquitto.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
# =============================================================================
# tr-stack Mosquitto configuration
#
# Minimal development configuration — allows anonymous connections for
# local containers. The docker-compose.yml binds Mosquitto to 127.0.0.1:1883
# by default (via MQTT_BIND_IP), so anonymous access is only visible to
# other containers on the Docker network. To expose on the LAN for remote
# trunk-recorder instances, set MQTT_BIND_IP to your server's LAN address
# in .env and consider configuring broker auth.
#
# For production, set allow_anonymous false and configure password_file.
# =============================================================================

listener 1883
allow_anonymous true
20 changes: 12 additions & 8 deletions sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
# IP to bind external ports (default: all interfaces)
# BIND_IP=192.168.1.100

# MQTT broker host bind address. Defaults to localhost so anonymous MQTT is not
# exposed on the LAN. Set to your LAN IP only when external trunk-recorder
# hosts need to publish to this broker, and configure broker auth first.
# MQTT_BIND_IP=192.168.1.100

# Port Caddy listens on
HTTP_PORT=80

Expand All @@ -18,13 +23,12 @@ POSTGRES_USER=trengine
POSTGRES_PASSWORD=change-me
POSTGRES_DB=trengine

# Auth — disabled by default for local installs
# Set AUTH_ENABLED=true and configure tokens/password for public-facing deployments
AUTH_ENABLED=false

# API tokens (only needed when AUTH_ENABLED=true)
# AUTH_TOKEN= # generate with: openssl rand -base64 32
# WRITE_TOKEN=
# Auth modes are derived from AUTH_TOKEN and ADMIN_PASSWORD:
# - open: leave both unset (default for trusted local/LAN installs)
# - token: set AUTH_TOKEN only; dashboard users enter the shared token
# - full: set ADMIN_PASSWORD; optional AUTH_TOKEN becomes a public read token
# For public-facing deployments, set ADMIN_PASSWORD and use API keys for uploads.
# AUTH_TOKEN= # optional; generate with: openssl rand -base64 32
# ADMIN_PASSWORD=

# MQTT topics — must match your trunk-recorder plugin config
Expand All @@ -40,7 +44,7 @@ IMBE_ASR_URL=http://imbe-asr:8000
# trunk-reporter/imbe-asr-large-1024d — 290M model, best accuracy on clean speech
# trunk-reporter/imbe-asr-base-512d — base model, good for edge/Pi deployment
IMBE_ASR_MODEL=trunk-reporter/imbe-asr-base-512d-p25
IMBE_ASR_DEVICE=cuda # set to 'cpu' if no GPU (automatic with docker-compose.pi.yml)
IMBE_ASR_DEVICE=cpu # set to 'cuda' when using docker-compose.gpu.yml

# Raspberry Pi notes:
# - Use docker-compose.pi.yml override (sets cpu device + smaller model automatically)
Expand Down
3 changes: 3 additions & 0 deletions talkgroups/channels_conv.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TG Number,Frequency,Alpha Tag,Tone,Description
1,854.8125,TRACK REPAIR,118.8 PL,Track Repair
2,855.6375,RAILROAD,205 DPL,Intra-Plant Switching Railroad
Loading