From 85b4b4cd91f79526e040193847166c17e07aec0e Mon Sep 17 00:00:00 2001 From: Admin <1+admin@noreply.git.dcntrlized.org> Date: Sun, 23 Aug 2026 00:39:53 +0100 Subject: [PATCH] feat(transport): loopback-only default port binding + reverse-proxy docs (onboarding M3) --- README.md | 51 ++++++++++++++++++++++++++++++++++++++- docker-compose.debug.yaml | 3 ++- docker-compose.yaml | 5 +++- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 907884d..a4b563c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,52 @@ # Maestro -Dcntrlized project coordinator service \ No newline at end of file +Dcntrlized project coordinator service + +## Access & transport + +The dashboard ships session cookies (instance-owner gate), so the deployment +default is **loopback-only**: `docker-compose.yaml` binds +`127.0.0.1:8080:8080`, making the listener unreachable from the LAN unless you +deliberately opt out. Maestro itself stays plaintext HTTP; TLS terminates in a +reverse proxy (D6 — no TLS machinery inside the process). + +### Single host + +Reach the UI over SSH from your workstation: + +```sh +ssh -L 8080:127.0.0.1:8080 +# then open http://localhost:8080 +``` + +### Reverse proxy (LAN / public) + +Put Caddy, Traefik or nginx in front of `http://127.0.0.1:8080` and terminate +TLS there: + +```text +maestro.example.org { + reverse_proxy 127.0.0.1:8080 +} +``` + +- **WebSocket:** the UI uses `/api/ws`. Caddy and Traefik proxy upgrades + automatically; nginx needs explicit headers: + ```nginx + location / { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + ``` +- **Cookies behind the proxy:** set `COOKIE_SECURE=true` in Maestro's + environment so both session cookies (`maestro_access` / `maestro_refresh`) + are marked `Secure`. +- **Host header:** mutating requests carry an Origin/Referer CSRF check that + compares hosts. A proxy that preserves `Host` works unchanged; if you rewrite + the host, keep forwarding `X-Forwarded-*` and serve the UI on the same host + you proxy to. diff --git a/docker-compose.debug.yaml b/docker-compose.debug.yaml index b947040..2c211b7 100644 --- a/docker-compose.debug.yaml +++ b/docker-compose.debug.yaml @@ -3,7 +3,8 @@ services: image: maestro container_name: maestro ports: - - "8080:8080" + # Loopback-only default (dev is localhost anyway); see docker-compose.yaml + - "127.0.0.1:8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock" - "${XDG_DATA_HOME:-${HOME}}/.dcntrlized:/data/.dcntrlized" diff --git a/docker-compose.yaml b/docker-compose.yaml index 75cf657..1f3a980 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,7 +3,10 @@ services: image: maestro container_name: maestro ports: - - "8080:8080" + # Loopback-only by default (owner gate ships session cookies). Reach it + # via `ssh -L 8080:127.0.0.1:8080 ` or put a TLS reverse proxy in + # front — see README "Access & transport". + - "127.0.0.1:8080:8080" volumes: - "/var/run/docker.sock:/var/run/docker.sock" - "${XDG_DATA_HOME:-${HOME}}/.dcntrlized:/data/.dcntrlized"