Overlord

# Overlord # [TELEGRAM SERVER JOIN NOW NO EXCUSES WE GIVE SUPPORT AND IT'S FUN](https://t.me/Onimai) Hello, I made this project for fun. The server is TypeScript on Node/Bun. The client is Go. Operators talk to the server through a web panel or the Electron desktop app, and agents connect over encrypted WebSockets. Docker is the easiest way to run it. --- - [Quick Start (Docker)](#quick-start-docker) - [Windows](#windows) - [Linux](#linux) - [macOS](#macos) - [No Docker (.bat / .sh)](#no-docker-bat--sh) - [Production Package Scripts](#production-package-scripts) - [WebRTC Streaming](#webrtc-streaming) - [Docker Notes (TLS, reverse proxy, cache)](#docker-notes-tls-reverse-proxy-cache) --- ## Quick Start (Docker) Pick your OS below. Each section is self-contained: install Docker, get the project, start it. > Windows and macOS use `docker-compose.windows.yml`. Linux uses the default `docker-compose.yml` (host networking). After the first start, open `https://localhost:5173`. Default login is `admin` / `admin` unless you set `OVERLORD_USER` / `OVERLORD_PASS`. First startup writes generated secrets to `data/save.json` (inside the container: `/app/data/save.json`) — keep that file private and back it up. --- ### Windows
Step-by-step: Windows
**1. Install Docker Desktop** Either from the website: - https://docs.docker.com/desktop/setup/install/windows-install/ Or with winget: ```powershell winget install -e --id Docker.DockerDesktop ``` Start Docker Desktop once, then verify: ```powershell docker --version docker compose version ``` **2. Get the project** ```powershell git clone https://github.com/vxaboveground/Overlord.git cd Overlord ``` **3. Start it** ```powershell docker compose -f docker-compose.windows.yml up -d ``` **4. Open the panel** ```text https://localhost:5173 ``` **5. Update later** ```powershell docker compose -f docker-compose.windows.yml down docker compose -f docker-compose.windows.yml pull docker compose -f docker-compose.windows.yml up -d ``` **6. Stop** ```powershell docker compose -f docker-compose.windows.yml down ```
--- ### Linux
Step-by-step: Linux (Debian / Ubuntu / Kali)
**1. Install Docker** Official docs: https://docs.docker.com/engine/install/debian/ Set up Docker's apt repository: ```bash # Add Docker's official GPG key: sudo apt update sudo apt install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: sudo tee /etc/apt/sources.list.d/docker.sources < --- ### macOS
Step-by-step: macOS
**1. Install Docker Desktop** Either from the website: - https://docs.docker.com/desktop/setup/install/mac-install/ Or with Homebrew: ```bash brew install --cask docker ``` Start Docker Desktop once, then verify: ```bash docker --version docker compose version ``` **2. Get the project** ```bash git clone https://github.com/vxaboveground/Overlord.git cd Overlord ``` **3. Start it** macOS uses the same compose file as Windows: ```bash docker compose -f docker-compose.windows.yml up -d ``` **4. Open the panel** ```text https://localhost:5173 ``` **5. Update later** ```bash docker compose -f docker-compose.windows.yml down docker compose -f docker-compose.windows.yml pull docker compose -f docker-compose.windows.yml up -d ``` **6. Stop** ```bash docker compose -f docker-compose.windows.yml down ```
--- ## No Docker (.bat / .sh) If you don't want Docker, use the included scripts. Prerequisites: - Bun in PATH - Go 1.21+ in PATH ### Windows Development mode (starts server + client): ```bat start-dev.bat ``` Production mode (build + run server executable): ```bat start-prod.bat ``` Build client binaries (adds client builds to the build queue): ```bat build-clients.bat ``` ### Linux / macOS Make scripts executable once: ```bash chmod +x start-dev.sh start-dev-server.sh start-dev-client.sh start-prod.sh build-prod-package.sh ``` Development mode (server in background, client in foreground): ```bash ./start-dev.sh ``` Only server, or only client: ```bash ./start-dev.sh server ./start-dev.sh client ``` Production mode: ```bash ./start-prod.sh ``` --- ## Production Package Scripts Build a production-ready package where the server can still build client binaries at runtime. Windows: ```bat build-prod-package.bat ``` Output: `release/` Linux / macOS: ```bash ./build-prod-package.sh ``` Output: `release/prod-package/` --- ## WebRTC Streaming The remote desktop viewer has a **Transport** dropdown with three modes: - **Canvas** (default): H.264 / JPEG / block frames over the existing WebSocket, decoded into a ``. Highest latency, works anywhere the WS does. - **WebRTC P2P**: browser ↔ agent direct. The server only relays SDP and ICE candidates over the existing WS — MediaMTX is not involved. Lowest latency. Fails when both sides are behind aggressive symmetric NAT. - **WebRTC Relayed**: agent publishes to a MediaMTX sidecar via WHIP, browser plays via WHEP. The server proxies signaling so the existing JWT auth + per-client RBAC still apply. Lowest-effort fallback when P2P can't punch through. ### Building agents with WebRTC WebRTC is **opt-in per agent build**. In the builder UI, tick the **WebRTC** checkbox before clicking Build. Without it, the Pion stack (~6 MB of Go modules) is not compiled in — any WebRTC start attempt from the operator returns `webrtc support not compiled in` and the viewer falls back to Canvas. The Canvas path always works regardless of the build setting. The build tag (`overlord_webrtc`) is also available if you build agents outside the UI: ```bash go build -tags overlord_webrtc ./cmd/agent ``` ### MediaMTX sidecar Compose starts an `overlord-mediamtx` service for Relayed mode. It needs: - Port `8189/udp` and `8189/tcp` reachable from operators (WebRTC ICE traffic). The Windows / macOS compose publishes these; Linux uses host networking and shares the host's interfaces directly. - No auth config — the Overlord server proxies every WHIP/WHEP request through `/api/webrtc/...` and enforces the existing operator JWT + RBAC there. If you only ever want P2P, you can comment out the `mediamtx:` service in your compose file — only Relayed mode depends on it. ### LAN / public access By default MediaMTX advertises `127.0.0.1` as an ICE candidate, which is enough for operators running their browser on the same machine as Docker. For viewers on a different machine: - **Linux** (host networking): nothing to configure — MediaMTX sees the host's eth0/wlan0 and gathers those candidates automatically. - **Windows / macOS** (bridge networking): set `OVERLORD_WEBRTC_ADDITIONAL_HOSTS` to a comma-separated list including the server's reachable LAN or public IP. Either export it in your shell, or add it to a `.env` file next to the compose file: ``` OVERLORD_WEBRTC_ADDITIONAL_HOSTS=127.0.0.1,192.168.1.42 ``` Then `docker compose -f docker-compose.windows.yml up -d mediamtx` to apply. ### Advanced MediaMTX customization The compose file passes a minimal set of `MTX_*` environment variables to MediaMTX — enough for Overlord to work. To change anything else (codecs, paths, ICE servers, etc.), either: - Add more `MTX_*` variables to the `mediamtx` service's `environment:` block (every option in MediaMTX's docs is supported as an env var), or - Provide a full `mediamtx.yml` via a bind mount: ```yaml mediamtx: # ...existing config... volumes: - ./mediamtx.yml:/mediamtx.yml:ro ``` Make sure the file exists on the host *before* the container starts — Docker will otherwise auto-create an empty directory at that path and fail with "not a directory". --- ## Docker Notes (TLS, reverse proxy, cache) Notes on configs and workarounds. ### BuildKit cache for faster rebuilds `docker-compose.yml` ships with `build.cache_from` and `build.cache_to` pointing at `.docker-cache/buildx`. Local builds reuse it automatically — no extra setup. ### Runtime client build cache The compose setup uses a persistent volume for runtime client builds: - Volume: `overlord-client-build-cache` - Mount: `/app/client-build-cache` - Env: `OVERLORD_CLIENT_BUILD_CACHE_DIR` (default `/app/client-build-cache`) ### Certbot TLS To use Let's Encrypt certificates in production Docker: 1. Set `OVERLORD_TLS_CERTBOT_ENABLED=true` 2. Set `OVERLORD_TLS_CERTBOT_DOMAIN=your-domain.com` 3. Mount letsencrypt into the container read-only, e.g. `/etc/letsencrypt:/etc/letsencrypt:ro` Default cert paths: ``` cert: /etc/letsencrypt/live//fullchain.pem key: /etc/letsencrypt/live//privkey.pem ca: /etc/letsencrypt/live//chain.pem ``` Override with: - `OVERLORD_TLS_CERTBOT_LIVE_PATH` - `OVERLORD_TLS_CERTBOT_CERT_FILE` - `OVERLORD_TLS_CERTBOT_KEY_FILE` - `OVERLORD_TLS_CERTBOT_CA_FILE` ### Reverse proxy TLS offload If your platform terminates TLS before traffic reaches Overlord (Render, Caddy, nginx, etc.), set: ``` OVERLORD_TLS_OFFLOAD=true OVERLORD_HEALTHCHECK_URL=http://localhost:5173/health OVERLORD_PUBLISH_HOST=127.0.0.1 ``` When enabled: - Container serves internal HTTP on `0.0.0.0:$PORT` - External URL stays `https://...` through your platform proxy - Health checks should use `http://localhost:$PORT/health` inside the container - Don't expose the internal container HTTP port directly to the internet ### Source IP behind a domain / reverse proxy If the dashboard, audit log, or IP bans show all agents as `172.x.x.x` (or some other proxy/bridge IP), something between the agent and Bun is rewriting the source IP. Two independent flags govern this: - `OVERLORD_TLS_OFFLOAD` — TLS terminates at the proxy; Overlord runs plain HTTP internally. - `OVERLORD_TRUST_PROXY` — honor `X-Forwarded-For` / `X-Real-IP` / `CF-Connecting-IP` so dashboard/audit/IP-bans see the real client. Auto-enabled when `TLS_OFFLOAD=true`. Common shapes: | Setup | `TLS_OFFLOAD` | `TRUST_PROXY` | |---|---|---| | Domain, no proxy (Linux host networking; certbot inside Overlord; Cloudflare DNS-only) | `false` | `false` | | Domain, proxy does TLS (Render, nginx terminating TLS → http upstream) | `true` | auto (`true`) | | Domain, proxy in front but Overlord still does TLS (Cloudflare orange-cloud Full Strict; nginx with `proxy_pass https://`) | `false` | `true` | Only enable `OVERLORD_TRUST_PROXY` when a trusted reverse proxy is in front. If Overlord is directly exposed and you enable it, agents can spoof their source IP by sending their own `X-Forwarded-For` header, breaking IP bans and audit accuracy. The upstream proxy also has to be configured to inject the header (Cloudflare does by default; nginx/Caddy/Traefik need explicit directives). If you're using `docker-compose.windows.yml` or `docker-compose.quickstart.yml` (Docker Desktop bridge networking) with **no** reverse proxy, Docker itself rewrites source IPs to the bridge gateway and there is no header to recover from — `TRUST_PROXY` cannot help. Either switch to Linux host networking or put a real reverse proxy in front. ### Notes - Keep `HOST=0.0.0.0` inside the container. Limit exposure with `OVERLORD_PUBLISH_HOST`, not the bind host. - If your `.env` secret/password contains `$`, escape it as `$$` to avoid Docker Compose variable-expansion warnings.