Files
overlord/README.md
T

657 lines
22 KiB
Markdown
Raw Normal View History

2026-03-05 15:25:52 -06:00
<p align="center">
2026-05-07 21:06:09 -05:00
<img src="https://raw.githubusercontent.com/vxaboveground/Overlord/refs/heads/main/Overlord-Server/public/assets/overlord.png" alt="Overlord" width="280" />
2026-03-05 15:25:52 -06:00
</p>
2026-03-05 00:04:26 -06:00
# Overlord
# [TELEGRAM SERVER JOIN NOW NO EXCUSES WE GIVE SUPPORT AND IT'S FUN](https://t.me/Onimai)
2026-03-05 00:04:26 -06:00
Hello, I made this project for fun.
2026-05-07 21:06:09 -05:00
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.
2026-03-10 10:02:50 -05:00
---
2026-03-07 11:35:31 +00:00
2026-03-10 10:02:50 -05:00
- [Quick Start (Docker)](#quick-start-docker)
2026-05-07 21:06:09 -05:00
- [Windows](#windows)
- [Linux](#linux)
- [macOS](#macos)
2026-03-10 10:02:50 -05:00
- [No Docker (.bat / .sh)](#no-docker-bat--sh)
- [Production Package Scripts](#production-package-scripts)
2026-05-13 18:30:18 -05:00
- [WebRTC Streaming](#webrtc-streaming)
2026-07-08 20:34:25 -05:00
- [OIDC / SSO Login](#oidc--sso-login)
- [Login Branding](#login-branding)
2026-03-10 10:02:50 -05:00
- [Docker Notes (TLS, reverse proxy, cache)](#docker-notes-tls-reverse-proxy-cache)
2026-03-07 11:35:31 +00:00
2026-03-10 10:02:50 -05:00
---
2026-03-07 11:35:31 +00:00
2026-03-10 10:02:50 -05:00
## Quick Start (Docker)
2026-03-07 11:35:31 +00:00
2026-05-07 21:06:09 -05:00
Pick your OS below. Each section is self-contained: install Docker, get the project, start it.
2026-03-07 11:35:31 +00:00
2026-05-07 21:06:09 -05:00
> Windows and macOS use `docker-compose.windows.yml`. Linux uses the default `docker-compose.yml` (host networking).
2026-05-07 21:06:09 -05:00
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.
2026-05-07 21:06:09 -05:00
---
2026-03-07 15:14:00 -06:00
2026-03-10 10:02:50 -05:00
### Windows
2026-03-07 15:14:00 -06:00
<details>
2026-05-07 21:06:09 -05:00
<summary>Step-by-step: Windows</summary>
<br>
**1. Install Docker Desktop**
Either from the website:
2026-03-07 15:14:00 -06:00
2026-03-10 10:02:50 -05:00
- https://docs.docker.com/desktop/setup/install/windows-install/
2026-03-07 15:14:00 -06:00
2026-05-07 21:06:09 -05:00
Or with winget:
2026-03-07 15:14:00 -06:00
2026-03-10 10:02:50 -05:00
```powershell
winget install -e --id Docker.DockerDesktop
```
2026-03-05 00:04:26 -06:00
2026-05-07 21:06:09 -05:00
Start Docker Desktop once, then verify:
2026-03-07 16:19:04 -06:00
2026-03-10 10:02:50 -05:00
```powershell
docker --version
docker compose version
```
2026-03-07 16:19:04 -06:00
2026-05-07 21:06:09 -05:00
**2. Get the project**
2026-05-07 21:06:09 -05:00
```powershell
git clone https://github.com/vxaboveground/Overlord.git
cd Overlord
```
**3. Start it**
```powershell
docker compose -f docker-compose.windows.yml up -d
```
2026-05-07 21:06:09 -05:00
**4. Open the panel**
2026-05-07 21:06:09 -05:00
```text
https://localhost:5173
```
2026-05-07 21:06:09 -05:00
**5. Update later**
2026-03-07 16:19:04 -06:00
2026-05-07 21:06:09 -05:00
```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
```
2026-03-07 16:19:04 -06:00
2026-05-07 21:06:09 -05:00
**6. Stop**
```powershell
docker compose -f docker-compose.windows.yml down
```
</details>
---
### Linux
<details>
<summary>Step-by-step: Linux (Debian / Ubuntu / Kali)</summary>
<br>
**1. Install Docker**
Official docs: https://docs.docker.com/engine/install/debian/
2026-03-07 16:19:04 -06:00
2026-03-10 10:02:50 -05:00
Set up Docker's apt repository:
2026-03-07 16:19:04 -06:00
2026-03-10 10:02:50 -05:00
```bash
# Add Docker's official GPG key:
2026-03-10 10:02:50 -05:00
sudo apt update
sudo apt install ca-certificates curl
2026-03-10 10:02:50 -05:00
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
2026-03-07 16:19:04 -06:00
# Add the repository to Apt sources:
2026-03-10 10:02:50 -05:00
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
2026-03-10 10:02:50 -05:00
Signed-By: /etc/apt/keyrings/docker.asc
EOF
2026-03-05 00:04:26 -06:00
2026-03-10 10:02:50 -05:00
sudo apt update
```
2026-05-07 21:06:09 -05:00
On a derivative distro (e.g. Kali), replace the codename expansion with the matching Debian codename, e.g. `bookworm`.
2026-03-10 10:02:50 -05:00
2026-05-07 21:06:09 -05:00
Install Docker:
2026-03-10 10:02:50 -05:00
```bash
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
2026-03-10 10:02:50 -05:00
```
2026-05-07 21:06:09 -05:00
Make sure the daemon is running:
2026-03-10 10:02:50 -05:00
```bash
sudo systemctl status docker
2026-03-10 10:02:50 -05:00
```
2026-05-07 21:06:09 -05:00
**2. Grab the compose file**
Make a folder for it, drop in the file, and you're done:
```bash
mkdir overlord && cd overlord
wget https://raw.githubusercontent.com/vxaboveground/Overlord/refs/heads/main/docker-compose.yml
```
No `wget`? Use `curl`:
```bash
mkdir overlord && cd overlord
curl -O https://raw.githubusercontent.com/vxaboveground/Overlord/refs/heads/main/docker-compose.yml
```
**3. Start it**
```bash
docker compose up -d
```
The image is pulled automatically from `ghcr.io/vxaboveground/overlord:latest` on first run.
**4. Open the panel**
```text
https://localhost:5173
or
https://IP:5173
```
**5. Update later**
From the same folder:
```bash
docker compose down
docker compose pull
docker compose up -d
```
**6. Stop**
```bash
docker compose down
```
</details>
2026-05-07 21:06:09 -05:00
---
2026-03-10 10:02:50 -05:00
### macOS
<details>
2026-05-07 21:06:09 -05:00
<summary>Step-by-step: macOS</summary>
<br>
2026-05-07 21:06:09 -05:00
**1. Install Docker Desktop**
Either from the website:
2026-03-10 10:02:50 -05:00
- https://docs.docker.com/desktop/setup/install/mac-install/
2026-05-07 21:06:09 -05:00
Or with Homebrew:
2026-03-10 10:02:50 -05:00
```bash
brew install --cask docker
```
Start Docker Desktop once, then verify:
```bash
docker --version
docker compose version
```
2026-05-07 21:06:09 -05:00
**2. Get the project**
2026-05-07 21:06:09 -05:00
```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
```
2026-05-07 21:06:09 -05:00
**4. Open the panel**
```text
https://localhost:5173
```
**5. Update later**
```bash
2026-05-07 21:06:09 -05:00
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
```
</details>
2026-05-07 21:06:09 -05:00
---
2026-03-10 10:02:50 -05:00
## No Docker (.bat / .sh)
2026-05-07 21:06:09 -05:00
If you don't want Docker, use the included scripts.
2026-03-10 10:02:50 -05:00
2026-05-07 21:06:09 -05:00
Prerequisites:
2026-03-10 10:02:50 -05:00
- Bun in PATH
- Go 1.21+ in PATH
### Windows
Development mode (starts server + client):
```bat
2026-06-12 09:14:49 -05:00
scripts\start-dev.bat
2026-03-10 10:02:50 -05:00
```
Production mode (build + run server executable):
```bat
2026-06-12 09:14:49 -05:00
scripts\start-prod.bat
2026-03-10 10:02:50 -05:00
```
Build client binaries (adds client builds to the build queue):
2026-03-10 10:02:50 -05:00
```bat
2026-06-12 09:14:49 -05:00
scripts\build-clients.bat
2026-03-10 10:02:50 -05:00
```
### Linux / macOS
Make scripts executable once:
```bash
2026-06-12 09:14:49 -05:00
chmod +x scripts/*.sh scripts/*.command
2026-03-10 10:02:50 -05:00
```
2026-05-07 21:06:09 -05:00
Development mode (server in background, client in foreground):
2026-03-10 10:02:50 -05:00
```bash
2026-06-12 09:14:49 -05:00
./scripts/start-dev.sh
2026-03-10 10:02:50 -05:00
```
2026-05-07 21:06:09 -05:00
Only server, or only client:
2026-03-10 10:02:50 -05:00
```bash
2026-06-12 09:14:49 -05:00
./scripts/start-dev.sh server
./scripts/start-dev.sh client
2026-03-10 10:02:50 -05:00
```
Production mode:
```bash
2026-06-12 09:14:49 -05:00
./scripts/start-prod.sh
2026-03-10 10:02:50 -05:00
```
2026-05-07 21:06:09 -05:00
---
2026-03-10 10:02:50 -05:00
## Production Package Scripts
Build a production-ready package where the server can still build client binaries at runtime.
Windows:
2026-03-05 00:04:26 -06:00
```bat
2026-06-12 09:14:49 -05:00
scripts\build-prod-package.bat
2026-03-05 00:04:26 -06:00
```
2026-05-07 21:06:09 -05:00
Output: `release/`
Linux / macOS:
2026-03-05 00:04:26 -06:00
```bash
2026-06-12 09:14:49 -05:00
./scripts/build-prod-package.sh
2026-03-05 00:04:26 -06:00
```
2026-05-07 21:06:09 -05:00
Output: `release/prod-package/`
2026-03-05 00:04:26 -06:00
2026-05-07 21:06:09 -05:00
---
2026-03-10 10:02:50 -05:00
2026-05-13 18:30:18 -05:00
## 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 `<canvas>`. Highest latency, works anywhere the WS does.
2026-07-17 16:01:48 -05:00
- **WebRTC P2P**: browser ↔ agent direct when possible, with the bundled Coturn server as a fallback for restrictive or symmetric NAT. MediaMTX is not involved. Lowest latency on a direct route.
2026-05-13 18:30:18 -05:00
- **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
```
2026-07-17 16:01:48 -05:00
### Coturn STUN / TURN
Compose starts `overlord-coturn` for ICE traversal in both P2P and MediaMTX Relayed modes. A one-time init service generates a random master secret in the private `overlord-turn-secret` Docker volume. Overlord uses that secret to issue one-hour Coturn REST credentials independently to the authenticated P2P viewer and agent. MediaMTX reads the secret inside Docker and also generates expiring credentials for its WHIP/WHEP clients. No Google STUN server is used, and the master secret is never sent directly to a peer.
The defaults work for same-machine testing. For LAN or internet access, set the address that both the browser and agent can reach in a `.env` file next to the compose file:
```env
OVERLORD_TURN_HOST=turn.example.com
OVERLORD_TURN_EXTERNAL_IP=203.0.113.10
```
`OVERLORD_TURN_HOST` is placed in the ICE URLs and may be a public DNS name or IP. `OVERLORD_TURN_EXTERNAL_IP` tells Coturn which public address to advertise when the Docker host is behind NAT; omit it when Coturn binds the public interface directly. Do not use the Docker service name because peers outside the Compose network cannot resolve or reach it.
Allow and forward these inbound ports to the Coturn host:
- `3478/udp` and `3478/tcp` for STUN and TURN client connections.
- `49160-49200/udp` for TURN relay allocations.
The bundled setup supports ordinary TURN over UDP and TCP. It intentionally does not enable `turns:` until a trusted Coturn TLS certificate and public hostname are configured.
2026-05-13 18:30:18 -05:00
### 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.
2026-07-17 16:01:48 -05:00
If you only ever want P2P, you can comment out the `mediamtx:` service in your compose file — only Relayed mode depends on it. MediaMTX is configured to use the same Coturn service as a client-only ICE fallback, but it is not itself a STUN/TURN server; Coturn handles all relay allocations.
2026-05-13 18:30:18 -05:00
### LAN / public access
2026-07-12 23:14:29 -05:00
MediaMTX automatically discovers addresses assigned directly to its network interfaces. This covers ordinary Linux host-network and many LAN deployments, but it cannot reliably infer a public address created by NAT, port forwarding, a cloud load balancer, or a reverse proxy. It also cannot discover which public DNS name you intend operators to use.
2026-05-13 18:30:18 -05:00
2026-07-12 23:14:29 -05:00
Before using **WebRTC Relayed** from another machine, make sure MediaMTX advertises an address that both agents and operators can reach:
2026-05-13 18:30:18 -05:00
2026-07-12 23:14:29 -05:00
- **Linux with host networking and no public NAT:** normally nothing is required; MediaMTX sees the host's interfaces automatically.
- **Public server behind NAT, port forwarding, or a cloud public-IP mapping:** set the public IP or a DNS name that resolves to it.
- **Windows / macOS Docker Desktop:** set the host's reachable LAN/public address because automatic interface discovery sees the container network, not necessarily the Docker host.
- **Same-machine testing on Docker Desktop:** retain `127.0.0.1` in the list.
2026-05-13 18:30:18 -05:00
2026-07-12 23:14:29 -05:00
Set `OVERLORD_WEBRTC_ADDITIONAL_HOSTS` as a comma-separated list in the shell or in a `.env` file next to the compose file:
2026-05-13 18:30:18 -05:00
2026-07-12 23:14:29 -05:00
```env
# LAN example
OVERLORD_WEBRTC_ADDITIONAL_HOSTS=192.168.1.42
# Public DNS plus LAN access
OVERLORD_WEBRTC_ADDITIONAL_HOSTS=stream.example.com,192.168.1.42
# Docker Desktop: same host plus other LAN machines
OVERLORD_WEBRTC_ADDITIONAL_HOSTS=127.0.0.1,192.168.1.42
```
Ensure inbound UDP `8189` is forwarded to the MediaMTX host/container; TCP `8189` is the slower fallback. Recreate the sidecar after changing the setting:
```bash
# Linux
docker compose up -d --force-recreate mediamtx
# Windows / macOS Docker Desktop
docker compose -f docker-compose.windows.yml up -d --force-recreate mediamtx
```
To verify the selected route, open `chrome://webrtc-internals` during a relayed stream and confirm that the chosen remote candidate points to the expected server address and preferably uses UDP.
Automatic public-IP discovery is possible with STUN, but it is not a complete replacement for this setting: it can require random UDP ports, does not discover your intended domain, and can fail behind restrictive or symmetric NAT. For predictable production deployments, explicitly setting the reachable IP/domain and forwarding fixed UDP port `8189` is recommended.
2026-05-13 18:30:18 -05:00
2026-07-17 13:23:12 -05:00
### Remote desktop network impairment tests
On a Linux test host, `scripts/rd-network-impairment.sh` applies repeatable bandwidth, latency, jitter, and packet-loss profiles with `tc netem`. Give it the exact interface carrying the WebRTC traffic:
```bash
# Inspect interfaces first; do not guess the target.
ip link show
sudo bash scripts/rd-network-impairment.sh apply eth0 wan
bash scripts/rd-network-impairment.sh status eth0
# Always restore the interface after the test.
sudo bash scripts/rd-network-impairment.sh clear eth0
```
Available profiles are `wan` (20 Mbps), `constrained` (8 Mbps), and `harsh` (3 Mbps), with progressively higher delay, jitter, and loss. The rule affects all egress traffic on the selected interface, not only Overlord. The script refuses to touch loopback unless `OVERLORD_NETEM_ALLOW_LOOPBACK=1` is explicitly set.
For each profile, start Remote Desktop with both **Profile: Auto (best)** and **Bitrate: Auto**, then watch the Stats HUD. Verify that bitrate falls before the profile steps down, decode/processing queues remain bounded, and the profile slowly recovers after clearing the impairment.
2026-05-13 18:30:18 -05:00
### 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".
---
2026-07-08 20:34:25 -05:00
## OIDC / SSO Login
Overlord supports generic OIDC login for homelab identity providers such as Authentik, Authelia, Keycloak, Zitadel, and Dex. Local username/password login stays enabled as a fallback.
Configure your OIDC provider with this redirect URI:
```text
https://YOUR_OVERLORD_HOST/api/oidc/callback
```
Then set the relevant environment variables:
```env
OVERLORD_OIDC_ENABLED=true
OVERLORD_OIDC_LABEL=Sign in with SSO
OVERLORD_OIDC_ISSUER=https://auth.example.com/application/o/overlord/
OVERLORD_OIDC_CLIENT_ID=overlord
OVERLORD_OIDC_CLIENT_SECRET=change-me
OVERLORD_OIDC_REDIRECT_URI=https://overlord.example.com/api/oidc/callback
OVERLORD_OIDC_DEFAULT_ROLE=viewer
OVERLORD_OIDC_ALLOWED_DOMAINS=example.com
OVERLORD_OIDC_ADMIN_GROUPS=overlord-admins
OVERLORD_OIDC_OPERATOR_GROUPS=overlord-operators
OVERLORD_OIDC_VIEWER_GROUPS=overlord-viewers
```
New OIDC users are linked by the provider's `issuer + sub` identity. Email/username linking to an existing local account is disabled by default; enable `OVERLORD_OIDC_ALLOW_EMAIL_LINK=true` only if you trust your provider's verified email claims.
---
## Login Branding
Self-hosted and enterprise deployments can brand the login screen with environment variables:
```env
OVERLORD_LOGIN_BRAND_NAME=Acme SOC
2026-07-08 21:47:57 -05:00
OVERLORD_NAV_BRAND_NAME=Acme Console
OVERLORD_BRAND_ACCENT_COLOR=#14b8a6
2026-07-08 20:34:25 -05:00
OVERLORD_LOGIN_TITLE=Welcome to Acme Overlord
OVERLORD_LOGIN_SUBTITLE=Sign in with your Acme identity
OVERLORD_LOGIN_LOGO_URL=/assets/acme-logo.png
OVERLORD_LOGIN_LOGO_ALT=Acme logo
2026-07-08 21:47:57 -05:00
OVERLORD_NAV_LOGO_URL=/assets/acme-nav-logo.png
OVERLORD_NAV_LOGO_ALT=Acme logo
OVERLORD_LOGIN_HERO_IMAGE_URL=/assets/acme-login.jpg
OVERLORD_LOGIN_HERO_IMAGE_ALT=Acme operations center
OVERLORD_LOGIN_FOOTER_TEXT=Authorized Acme access only
OVERLORD_LOGIN_SUPPORT_TEXT=Need access help?
OVERLORD_LOGIN_SUPPORT_URL=https://help.example.com
2026-07-08 20:34:25 -05:00
OVERLORD_LOGIN_ICON_CLASS=fa-solid fa-shield-halved
```
2026-07-08 21:47:57 -05:00
Logo, hero, and support URLs accept absolute `http(s)` URLs or root-relative paths. If no logo URL is set, the UI uses `OVERLORD_LOGIN_ICON_CLASS`. The same options can also be managed from Settings → Branding.
2026-07-08 20:34:25 -05:00
---
2026-03-10 10:02:50 -05:00
## Docker Notes (TLS, reverse proxy, cache)
2026-05-07 21:06:09 -05:00
Notes on configs and workarounds.
2026-03-10 10:02:50 -05:00
### BuildKit cache for faster rebuilds
2026-05-07 21:06:09 -05:00
`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.
2026-03-10 10:02:50 -05:00
### Runtime client build cache
2026-03-05 00:04:26 -06:00
2026-03-10 10:02:50 -05:00
The compose setup uses a persistent volume for runtime client builds:
2026-03-05 00:04:26 -06:00
2026-05-07 21:06:09 -05:00
- Volume: `overlord-client-build-cache`
- Mount: `/app/client-build-cache`
- Env: `OVERLORD_CLIENT_BUILD_CACHE_DIR` (default `/app/client-build-cache`)
2026-07-15 23:26:41 -05:00
### macOS CGO builds on Linux/Docker
When a macOS target is selected with CGO enabled, the builder asks for a user-provided macOS SDK archive. Package the complete `MacOSX*.sdk` directory as `.tar.xz`, `.tar.gz`, `.tgz`, or `.tar` and upload it from the Build page. The archive must contain the SDK's `System/Library/Frameworks` and `usr` directories.
The upload is limited to 1 GB, belongs to the authenticated user, can be used for only one build, and is deleted after the build. Unused uploads expire after one hour. The Docker image supplies Clang and LLD; Apple SDK files are never bundled or downloaded by Overlord.
2026-03-10 10:02:50 -05:00
### Certbot TLS
2026-05-07 21:06:09 -05:00
To use Let's Encrypt certificates in production Docker:
2026-03-10 10:02:50 -05:00
2026-05-07 21:06:09 -05:00
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`
2026-03-10 10:02:50 -05:00
2026-05-07 21:06:09 -05:00
Default cert paths:
2026-03-10 10:02:50 -05:00
2026-05-07 21:06:09 -05:00
```
cert: /etc/letsencrypt/live/<domain>/fullchain.pem
key: /etc/letsencrypt/live/<domain>/privkey.pem
ca: /etc/letsencrypt/live/<domain>/chain.pem
```
2026-03-10 10:02:50 -05:00
2026-05-07 21:06:09 -05:00
Override with:
2026-03-10 10:02:50 -05:00
2026-05-07 21:06:09 -05:00
- `OVERLORD_TLS_CERTBOT_LIVE_PATH`
- `OVERLORD_TLS_CERTBOT_CERT_FILE`
- `OVERLORD_TLS_CERTBOT_KEY_FILE`
- `OVERLORD_TLS_CERTBOT_CA_FILE`
2026-03-10 10:02:50 -05:00
2026-05-07 21:06:09 -05:00
### Reverse proxy TLS offload
2026-03-10 10:02:50 -05:00
Overlord can serve HTTP only on loopback while nginx, Caddy, Traefik, IIS, or
another trusted reverse proxy owns the public HTTPS certificate. Browsers and
agents must still use the public `https://` / `wss://` address.
2026-03-10 10:02:50 -05:00
2026-07-22 22:53:44 -05:00
HTTP/3 is enabled automatically when Overlord terminates TLS itself. QUIC uses
UDP on the same port as HTTPS (5173 by default), so direct deployments must
allow both TCP and UDP on that port. HTTP/1.1 remains enabled for initial
`Alt-Svc` discovery, health checks, clients without HTTP/3, and WebSocket
upgrades. When `OVERLORD_TLS_OFFLOAD=true`, the reverse proxy owns public
HTTP/3 support because Bun's internal listener has no TLS.
To verify both advertisement and an actual QUIC transfer with an HTTP/3-capable
curl build:
```powershell
.\scripts\test-http3.ps1 https://YOUR_OVERLORD_HOST:5173/health
```
For a self-signed certificate, add `-AllowUntrustedCertificate`.
To compare cold HTTP/1.1 and HTTP/3 request timings:
```powershell
.\scripts\benchmark-http3.ps1 https://YOUR_OVERLORD_HOST:5173/health -Requests 30
```
The benchmark selects an HTTP/3-capable curl automatically, validates the
protocol used by every sample, prints average/p50/p95 timing, and saves the raw
measurements as CSV.
For a native install or Linux Docker with host networking, add this to `.env`:
```env
HOST=127.0.0.1
2026-03-13 15:37:18 -05:00
OVERLORD_TLS_OFFLOAD=true
OVERLORD_HEALTHCHECK_URL=http://127.0.0.1:5173/health
2026-03-13 15:37:18 -05:00
```
For Docker Desktop (`docker-compose.windows.yml`), the container must listen on
all of its own interfaces while Docker publishes the port on host loopback:
```env
HOST=0.0.0.0
OVERLORD_PUBLISH_HOST=127.0.0.1
OVERLORD_TLS_OFFLOAD=true
OVERLORD_HEALTHCHECK_URL=http://127.0.0.1:5173/health
```
Restart Overlord after changing `.env`. The reverse proxy upstream is then
`http://127.0.0.1:5173`. It must preserve `Host`, set
`X-Forwarded-Proto: https` and `X-Forwarded-For`, support WebSocket upgrades,
and use timeouts/body limits suitable for long sessions and file uploads.
Ready-to-copy Caddy and nginx configurations are in `deploy/reverse-proxy/`.
2026-05-07 21:06:09 -05:00
When enabled:
2026-03-13 15:37:18 -05:00
- Overlord skips certificate generation and serves internal HTTP.
- Authentication cookies remain `Secure` because public TLS terminates at the proxy.
- Proxy source-IP headers are trusted for auditing, bans, and rate limiting.
- The internal HTTP listener must never be exposed directly to the internet.
2026-03-13 15:37:18 -05:00
2026-05-13 23:49:17 -05:00
### 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.
2026-05-07 21:06:09 -05:00
### Notes
2026-05-07 21:06:09 -05:00
- 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.