From a1216e74982e2d4dc5ffbc4a00f9d532cd24778b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 25 Apr 2026 17:13:35 +0200 Subject: [PATCH 1/4] nginx: route /switch and /status to selkies supervisor (dual-mode) When `SELKIES_ENABLE_DUAL_MODE=true` selkies starts a small aiohttp supervisor on `localhost:8082` exposing two endpoints: POST /switch - switch the active streaming mode (websockets <-> webrtc) GET /status - report which mode is currently running The selkies-dashboard sidebar uses these to drive the "Streaming Mode" selector. Without nginx routes the browser only sees an HTML 404 from nginx and the dashboard's mode selector silently disappears (the related dual-mode dropdown is gated on a successful /status response in some downstream forks). This adds: - a new init-nginx env var `SELKIES_SUPERVISOR_PORT` (default 8082) and a sed substitution for `SUPERVISOR_PORT` in default.conf, so the port becomes configurable; - a regex location `^/(switch|status)$` that proxies both endpoints to 127.0.0.1:SUPERVISOR_PORT in both server blocks. Note the existing `CWS` default (`CUSTOM_WS_PORT=8082`) conflicts with the supervisor's hardcoded port in selkies (also 8082). Users enabling dual mode currently need to set `CUSTOM_WS_PORT=8081`. Tracking the supervisor-port-default question in selkies-project/selkies separately; this PR only adds the nginx-side plumbing so the routes exist as soon as selkies grows a configurable port. --- root/defaults/default.conf | 22 ++++++++++++++++++++++ root/etc/s6-overlay/s6-rc.d/init-nginx/run | 2 ++ 2 files changed, 24 insertions(+) diff --git a/root/defaults/default.conf b/root/defaults/default.conf index 2b6258af..b749f5b9 100644 --- a/root/defaults/default.conf +++ b/root/defaults/default.conf @@ -48,6 +48,17 @@ server { add_header X-Content-Type-Options "nosniff"; } } + # Selkies dual-mode supervisor (only listening when + # SELKIES_ENABLE_DUAL_MODE=true). Exposes: + # POST /switch - select stream mode (websockets|webrtc) + # GET /status - currently active mode + run state + # See selkies-project/selkies src/selkies/__main__.py. + location ~ ^/(switch|status)$ { + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_buffering off; + proxy_pass http://127.0.0.1:SUPERVISOR_PORT; + } error_page 500 502 503 504 /50x.html; location = SUBFOLDER50x.html { root /usr/share/selkies/web/; @@ -106,6 +117,17 @@ server { add_header X-Content-Type-Options "nosniff"; } } + # Selkies dual-mode supervisor (only listening when + # SELKIES_ENABLE_DUAL_MODE=true). Exposes: + # POST /switch - select stream mode (websockets|webrtc) + # GET /status - currently active mode + run state + # See selkies-project/selkies src/selkies/__main__.py. + location ~ ^/(switch|status)$ { + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_buffering off; + proxy_pass http://127.0.0.1:SUPERVISOR_PORT; + } error_page 500 502 503 504 /50x.html; location = SUBFOLDER50x.html { root /usr/share/selkies/web/; diff --git a/root/etc/s6-overlay/s6-rc.d/init-nginx/run b/root/etc/s6-overlay/s6-rc.d/init-nginx/run index e1eaed6f..39f385a8 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-nginx/run +++ b/root/etc/s6-overlay/s6-rc.d/init-nginx/run @@ -7,6 +7,7 @@ NGINX_CONFIG=/etc/nginx/sites-available/default CPORT="${CUSTOM_PORT:-3000}" CHPORT="${CUSTOM_HTTPS_PORT:-3001}" CWS="${CUSTOM_WS_PORT:-8082}" +SUP="${SELKIES_SUPERVISOR_PORT:-8082}" CUSER="${CUSTOM_USER:-abc}" SFOLDER="${SUBFOLDER:-/}" FILE_MANAGER_PATH="${FILE_MANAGER_PATH:-$HOME/Desktop}" @@ -31,6 +32,7 @@ cp /defaults/default.conf ${NGINX_CONFIG} sed -i "s/3000/$CPORT/g" ${NGINX_CONFIG} sed -i "s/3001/$CHPORT/g" ${NGINX_CONFIG} sed -i "s/CWS/$CWS/g" ${NGINX_CONFIG} +sed -i "s/SUPERVISOR_PORT/$SUP/g" ${NGINX_CONFIG} sed -i "s|SUBFOLDER|$SFOLDER|g" ${NGINX_CONFIG} sed -i "s|REPLACE_DOWNLOADS_PATH|$FILE_MANAGER_PATH|g" ${NGINX_CONFIG} s6-setuidgid abc mkdir -p ${FILE_MANAGER_PATH} From a835c89db23d341851a0a7bd4d322966e63cc444 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 25 Apr 2026 18:07:48 +0200 Subject: [PATCH 2/4] nginx: forward client headers to supervisor proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copilot caught that the new /switch + /status location forwards only Host, while every other proxied location in this config (/devmode, SUBFOLDERwebsocket) also passes X-Real-IP, X-Forwarded-For, and X-Forwarded-Proto. The supervisor logs the requesting peer for the mode-switch audit trail; without these headers everything looks like 127.0.0.1. Mirror the existing forwarding-header set in both server blocks (no timeouts added — these are short HTTP requests, not WS). --- root/defaults/default.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/root/defaults/default.conf b/root/defaults/default.conf index b749f5b9..21ce5174 100644 --- a/root/defaults/default.conf +++ b/root/defaults/default.conf @@ -55,6 +55,9 @@ server { # See selkies-project/selkies src/selkies/__main__.py. location ~ ^/(switch|status)$ { proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_buffering off; proxy_pass http://127.0.0.1:SUPERVISOR_PORT; @@ -124,6 +127,9 @@ server { # See selkies-project/selkies src/selkies/__main__.py. location ~ ^/(switch|status)$ { proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_buffering off; proxy_pass http://127.0.0.1:SUPERVISOR_PORT; From 7c23db78c67bab47774327e2a54c75c630e0cdb9 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 25 Apr 2026 18:13:30 +0200 Subject: [PATCH 3/4] nginx: subfolder-aware supervisor route, drop comments PASSWORD-sed strips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three follow-up issues from review: 1. The `# Selkies dual-mode supervisor ...` comment block above each location would be destroyed by the existing `if [ ! -z ${PASSWORD+x} ]; then sed -i 's/#//g' ...; fi` in init-nginx/run, which strips ALL `#` characters from the rendered config to uncomment the auth_basic lines. After the strip, our comment lines would parse as nginx directives and break startup. Drop the explanatory comments — the PR body already documents the intent, and nginx has no out-of-band comment syntax that survives the strip. 2. The regex `^/(switch|status)$` matched only at the document root, so a non-default SUBFOLDER deployment (e.g. SUBFOLDER=/foo/) would miss `/foo/switch` entirely. Switch to `^SUBFOLDER(switch|status)$` (rendered to `^/(switch|status)$` for the default `/` case) and `proxy_pass http://127.0.0.1:SUPERVISOR_PORT/$1` so the selkies supervisor — which serves at the root — sees the original `/switch` or `/status` regardless of nginx-side prefix. 3. Add an inline doc comment in init-nginx/run explaining what SELKIES_SUPERVISOR_PORT does (nginx proxy target only, mirrors the selkies-side port when SELKIES_ENABLE_DUAL_MODE is on). --- root/defaults/default.conf | 18 ++++-------------- root/etc/s6-overlay/s6-rc.d/init-nginx/run | 3 +++ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/root/defaults/default.conf b/root/defaults/default.conf index 21ce5174..8d4a0b08 100644 --- a/root/defaults/default.conf +++ b/root/defaults/default.conf @@ -48,19 +48,14 @@ server { add_header X-Content-Type-Options "nosniff"; } } - # Selkies dual-mode supervisor (only listening when - # SELKIES_ENABLE_DUAL_MODE=true). Exposes: - # POST /switch - select stream mode (websockets|webrtc) - # GET /status - currently active mode + run state - # See selkies-project/selkies src/selkies/__main__.py. - location ~ ^/(switch|status)$ { + location ~ ^SUBFOLDER(switch|status)$ { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_buffering off; - proxy_pass http://127.0.0.1:SUPERVISOR_PORT; + proxy_pass http://127.0.0.1:SUPERVISOR_PORT/$1; } error_page 500 502 503 504 /50x.html; location = SUBFOLDER50x.html { @@ -120,19 +115,14 @@ server { add_header X-Content-Type-Options "nosniff"; } } - # Selkies dual-mode supervisor (only listening when - # SELKIES_ENABLE_DUAL_MODE=true). Exposes: - # POST /switch - select stream mode (websockets|webrtc) - # GET /status - currently active mode + run state - # See selkies-project/selkies src/selkies/__main__.py. - location ~ ^/(switch|status)$ { + location ~ ^SUBFOLDER(switch|status)$ { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_buffering off; - proxy_pass http://127.0.0.1:SUPERVISOR_PORT; + proxy_pass http://127.0.0.1:SUPERVISOR_PORT/$1; } error_page 500 502 503 504 /50x.html; location = SUBFOLDER50x.html { diff --git a/root/etc/s6-overlay/s6-rc.d/init-nginx/run b/root/etc/s6-overlay/s6-rc.d/init-nginx/run index 39f385a8..6056929e 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-nginx/run +++ b/root/etc/s6-overlay/s6-rc.d/init-nginx/run @@ -7,6 +7,9 @@ NGINX_CONFIG=/etc/nginx/sites-available/default CPORT="${CUSTOM_PORT:-3000}" CHPORT="${CUSTOM_HTTPS_PORT:-3001}" CWS="${CUSTOM_WS_PORT:-8082}" +# Port of the selkies dual-mode supervisor API (/switch and /status). +# Only consulted when SELKIES_ENABLE_DUAL_MODE=true on the selkies side; +# this value drives nginx routing only, not the selkies bind port. SUP="${SELKIES_SUPERVISOR_PORT:-8082}" CUSER="${CUSTOM_USER:-abc}" SFOLDER="${SUBFOLDER:-/}" From de8f481ec257fc277781ed3987ff06f8ce76ae89 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 25 Apr 2026 18:21:29 +0200 Subject: [PATCH 4/4] nginx: split supervisor route into two exact-match locations Follow-up to review feedback: the previous attempt used a regex location with `$1` capture and SUBFOLDER substituted into the regex itself. Two real risks with that approach: 1. SUBFOLDER is treated as literal text by sed but as regex once substituted, so a subfolder containing regex metacharacters (e.g. `.`) would change match semantics. 2. proxy_pass with a variable in the URI ($1) switches nginx out of prefix-strip mode and drops the query string unless explicitly appended via `\$is_args\$args`. /switch and /status do not use query strings today, but the trap is real. Replace the single regex location with two `location =` exact-match blocks per server (switch and status), each with a static proxy_pass URL. SUBFOLDER stays a pure prefix substitution, no regex involved; the static path means full URI rewriting works with all request shapes; `=` is the highest-priority match in nginx so dispatch is also fastest. Pattern matches the existing SUBFOLDERwebsocket / SUBFOLDERwebsockets / SUBFOLDERfiles convention in this same file. --- root/defaults/default.conf | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/root/defaults/default.conf b/root/defaults/default.conf index 8d4a0b08..8abe29e2 100644 --- a/root/defaults/default.conf +++ b/root/defaults/default.conf @@ -48,14 +48,23 @@ server { add_header X-Content-Type-Options "nosniff"; } } - location ~ ^SUBFOLDER(switch|status)$ { + location = SUBFOLDERswitch { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_buffering off; - proxy_pass http://127.0.0.1:SUPERVISOR_PORT/$1; + proxy_pass http://127.0.0.1:SUPERVISOR_PORT/switch; + } + location = SUBFOLDERstatus { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_buffering off; + proxy_pass http://127.0.0.1:SUPERVISOR_PORT/status; } error_page 500 502 503 504 /50x.html; location = SUBFOLDER50x.html { @@ -115,14 +124,23 @@ server { add_header X-Content-Type-Options "nosniff"; } } - location ~ ^SUBFOLDER(switch|status)$ { + location = SUBFOLDERswitch { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_http_version 1.1; + proxy_buffering off; + proxy_pass http://127.0.0.1:SUPERVISOR_PORT/switch; + } + location = SUBFOLDERstatus { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_buffering off; - proxy_pass http://127.0.0.1:SUPERVISOR_PORT/$1; + proxy_pass http://127.0.0.1:SUPERVISOR_PORT/status; } error_page 500 502 503 504 /50x.html; location = SUBFOLDER50x.html {