commit 31ea273235f010814effdbb1b40c6b4e2d6c795e Author: Joerg Date: Tue May 19 11:11:17 2026 +0200 Initial diff --git a/README.md b/README.md new file mode 100644 index 0000000..c3e59fa --- /dev/null +++ b/README.md @@ -0,0 +1,161 @@ +# DJ7NTs QO100 Webconsole + +> **Disclaimer:** This is a proof-of-concept project. Use at your own risk — no guarantees or warranties of any kind. The source code is not fully open-source at this time. + +## Prerequisites + +- Docker (any recent version) and Docker Compose +- **Analog Devices PlutoSDR** (original rev.B/C) — this image does **not** work with Pluto Plus, Hamgeek AD9363, LibreSDR, or other AD9363-based clones +- **USB-to-Ethernet adapter** (100 Mbit) connected to the Pluto's USB OTG port — Gigabit adapters are not supported and may cause issues +- LNB connected to the PlutoSDR +- The Pluto and the host running Docker must be on the same network + +The prebuilt image is available for **x86_64** (PCs/servers) and **arm64** (Raspberry Pi 4+, Rock 5 ITX). Docker automatically pulls the correct variant for your hardware. + +## Quick Start with Docker Compose + +Create a `docker-compose.yml`: + +```yaml +services: + sdrc: + image: git.dj7nt.de/dj7nt/qo100wc:latest + ports: + - "3004:3000" + volumes: + - ./data:/app/data + environment: + SESSION_SECRET: ${SESSION_SECRET} + DATABASE_PATH: /app/data/qo100.db + PLUTO_CONNECTED: "1" + restart: unless-stopped +``` + +Generate a session secret and create a `.env` file: + +```bash +echo "SESSION_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32)" > .env +``` + +Create the data directory before starting (Docker would create it as root otherwise): + +```bash +mkdir -p data +``` + +Start: + +```bash +docker compose up -d +``` + +## First-Time Setup + +1. Open `http://:3004` in a browser +2. Log in with **admin** / **admin** (you will be forced to change the password) +3. Navigate to **Setup** (or go to `/setup`) +4. Configure: + - **PlutoSDR IP** — the IP address of your Pluto (default: `192.168.6.122`) + - **LNB LO Frequency** — the actual local oscillator frequency of your LNB in Hz. This is *not* the nominal 9750 MHz — e.g. my Bullseye TCXO LNB runs at `9749971700` Hz (9750 MHz minus ~28.3 kHz offset). If you use a different LNB, measure or look up its exact LO frequency. +5. Click **Save and Connect** + +To change these later, go to **Setup** as admin. + +## Networking: Reaching the Pluto + +By default Docker uses bridge networking. If the Pluto is on a separate Ethernet interface the container can't route to, use host networking (Linux only): + +```yaml +services: + sdrc: + image: git.dj7nt.de/dj7nt/qo100wc:latest + network_mode: host + # no ports: block needed — app listens on 3000 directly + volumes: + - ./data:/app/data + environment: + SESSION_SECRET: ${SESSION_SECRET} + DATABASE_PATH: /app/data/qo100.db + PLUTO_CONNECTED: "1" + restart: unless-stopped +``` + +## HTTPS / Microphone Access + +The browser's `getUserMedia()` API (used for TX microphone capture) requires a **secure context**. This means one of: + +- Access via `http://localhost` (works for local testing only) +- Access via **HTTPS** (required for remote access) + +The Docker image does **not** include HTTPS. You need a reverse proxy. + +### HAProxy Example + +Put HAProxy in front to terminate TLS (e.g. with Let's Encrypt via certbot): + +```yaml +services: + sdrc: + image: git.dj7nt.de/dj7nt/qo100wc:latest + # no ports exposed publicly — only haproxy talks to it + volumes: + - ./data:/app/data + environment: + SESSION_SECRET: ${SESSION_SECRET} + DATABASE_PATH: /app/data/qo100.db + PLUTO_CONNECTED: "1" + restart: unless-stopped + networks: + - internal + + haproxy: + image: haproxy:3 + ports: + - "80:80" + - "443:443" + volumes: + - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro + - ./certs:/usr/local/etc/haproxy/certs:ro + restart: unless-stopped + networks: + - internal + +networks: + internal: +``` + +`haproxy/haproxy.cfg`: + +``` +frontend http + bind *:80 + http-request redirect scheme https unless { ssl_fc } + +frontend https + bind *:443 ssl crt /usr/local/etc/haproxy/certs/sdrc.pem + + default_backend sdrc + +backend sdrc + server sdrc sdrc:3000 +``` + +Place your fullchain + privkey as `certs/sdrc.pem` (concatenated PEM). With Let's Encrypt: + +```bash +certbot certonly --standalone -d sdrc.example.com +cat /etc/letsencrypt/live/sdrc.example.com/fullchain.pem \ + /etc/letsencrypt/live/sdrc.example.com/privkey.pem > certs/sdrc.pem +``` + +After this setup, open `https://sdrc.example.com` — the browser will allow microphone access for TX. + +### Alternative: Chrome Flag (Not Recommended for Production) + +For testing without HTTPS, launch Chrome with: + +``` +chrome --unsafely-treat-insecure-origin-as-secure=http://:3004 +``` + +This grants media permissions on that HTTP origin. Only use for development.