Skip to content

Make dual-mode supervisor port configurable#237

Closed
DL6ER wants to merge 2 commits intoselkies-project:mainfrom
DL6ER:fix/supervisor-port-configurable
Closed

Make dual-mode supervisor port configurable#237
DL6ER wants to merge 2 commits intoselkies-project:mainfrom
DL6ER:fix/supervisor-port-configurable

Conversation

@DL6ER
Copy link
Copy Markdown
Contributor

@DL6ER DL6ER commented Apr 25, 2026

Reference the issue numbers and reviewers

(no existing issue I'd be aware of)

Explain relevant issues and how this pull request solves them

When SELKIES_ENABLE_DUAL_MODE=true, run() in src/selkies/__main__.py starts the supervisor API server (the /switch and /status endpoints that drive the dashboard's runtime mode switch) on a hard-coded port:

api_task = asyncio.create_task(create_api_server(manager, host='localhost', port=8082))
logger.info("Dual mode enabled: Supervisor API server started on port 8082")

That hardcoded 8082 collides with the default of the port setting (SELKIES_PORT, the data websocket server), which is also 8082.

Result: turning dual mode on out of the box races two listeners against the same port. In the typical container deployment (e.g. linuxserver/baseimage-selkies, where CUSTOM_WS_PORT defaults to 8082) users have to know to override CUSTOM_WS_PORT=8081 (or similar) or one of the two services fails silently. There is no env var today to move the supervisor instead.

This PR makes the supervisor port a first-class setting so it can be moved without changing the data port:

  • new supervisor_port setting (SELKIES_SUPERVISOR_PORT), default 8083
  • run() reads settings.supervisor_port instead of the hardcoded 8082
  • log line includes the actual port

8083 matches the existing control_port default and removes the collision out of the box, while leaving every other default unchanged.

Describe the changes in code and its dependencies and justify that they work as intended after testing

Two files touched, both in src/selkies/:

settings.py — one new entry in the settings list:

{'name': 'supervisor_port', 'type': 'int', 'default': 8083,
 'env_var': 'SELKIES_SUPERVISOR_PORT',
 'help': 'Port for the dual-mode supervisor API (handles /switch and /status endpoints when enable_dual_mode is true).'},

__main__.py — replace the hardcoded port in the dual-mode branch:

if settings.enable_dual_mode[0]:
    supervisor_port = settings.supervisor_port
    api_task = asyncio.create_task(create_api_server(manager, host='localhost', port=supervisor_port))
    logger.info(f"Dual mode enabled: Supervisor API server started on port {supervisor_port}")

No new dependencies. No change to the supervisor server itself, only where it binds.

Tested manually inside a linuxserver/baseimage-selkies-derived container (debian-trixie + xfce):

Default (no env var):

$ docker run -d -e SELKIES_ENABLE_DUAL_MODE=true ... webtop:patched
$ docker exec <id> ss -ltn | grep -E '808[0-9]'
LISTEN 0 128 127.0.0.1:8082 0.0.0.0:*    # data ws (selkies --port default)
LISTEN 0 128 127.0.0.1:8083 0.0.0.0:*    # supervisor API (new default)

Override:

$ docker run -d -e SELKIES_ENABLE_DUAL_MODE=true -e SELKIES_SUPERVISOR_PORT=9099 ...
$ docker exec <id> ss -ltn | grep 9099
LISTEN 0 128 127.0.0.1:9099 0.0.0.0:*

$ curl -s http://127.0.0.1:9099/status
{"current_mode": "websockets", "status": "running"}

Single-mode (enable_dual_mode=false, default): the supervisor task is not started either way, so the new setting has no effect — confirmed by running the previous default config and observing only the data ws listener.

Describe alternatives you've considered

  • Move the data port instead and keep the supervisor at 8082. Rejected: the data port is far more widely referenced in downstream nginx configs, deployment scripts and docs (CUSTOM_WS_PORT, proxy_pass http://127.0.0.1:8082), so changing it would be the more disruptive break.
  • Make the supervisor port follow port + 1. Rejected as kinda unintuitive
  • Keep 8082 as the supervisor default and document that CUSTOM_WS_PORT must be moved. Rejected: this is the current state and the source of the bug report from downstream.

Additional context

This unblocks the corresponding nginx routing in linuxserver/docker-baseimage-selkies (separate PR) which adds
location ~ ^/(switch|status)$ and a configurable SELKIES_SUPERVISOR_PORT env var on the nginx side; with that PR plus
this one, linuxserver/webtop users get working runtime mode switching without any custom port juggling.

  • I confirm that this pull request is relevant to the scope of this project.
  • I confirm that this pull request has been tested thoroughly.
  • I confirm that the style of the changed code conforms to the overall style of the project.
  • I confirm that I have read other open and closed pull requests and that duplicates do not exist.
  • I confirm that I have justified the need for this pull request.
  • I confirm that no portion of this pull request contains credentials or other private information.
  • I confirm that this pull request does not willfully infringe trademarks, patents, or other IP, and adheres to applicable software license terms.

The supervisor API server (/switch, /status) was hardcoded to port 8082,
which collides with the data websocket server's default port (also
8082). Users enabling SELKIES_ENABLE_DUAL_MODE=true would either get a
bind error in selkies, or had to manually set CUSTOM_WS_PORT to a
different value to avoid the collision.

This adds a `supervisor_port` setting (default 8083) with an
env-var override `SELKIES_SUPERVISOR_PORT`, and uses it in
`run()` instead of the hardcoded value.

Default 8083 matches the existing control_port default and avoids the
collision out of the box. Users who currently rely on
`CUSTOM_WS_PORT=8081` to work around the conflict can keep that, or
switch to the cleaner default. No backwards-incompatible behaviour for
single-mode setups.
Copilot AI review requested due to automatic review settings April 25, 2026 15:37
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the dual-mode supervisor API server port configurable via settings, so dual-mode can bind the /switch and /status endpoints on a non-hardcoded port.

Changes:

  • Add a new supervisor_port setting intended to control the dual-mode supervisor API bind port.
  • Update run() in src/selkies/__main__.py to use settings.supervisor_port instead of a hardcoded port.
  • Update logging to print the configured supervisor port.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
src/selkies/settings.py Adds a supervisor_port setting definition.
src/selkies/main.py Uses settings.supervisor_port when starting the supervisor API task and logs the chosen port.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/selkies/__main__.py
if settings.enable_dual_mode[0]:
api_task = asyncio.create_task(create_api_server(manager, host='localhost', port=8082))
logger.info("Dual mode enabled: Supervisor API server started on port 8082")
supervisor_port = settings.supervisor_port
Comment thread src/selkies/settings.py Outdated
# Server Startup & Operational Settings
{'name': 'port', 'type': 'int', 'default': 8081, 'env_var': 'CUSTOM_WS_PORT', 'help': 'Port for the data websocket server.'},
{'name': 'control_port', 'type': 'int', 'default': 8083, 'help': 'Port for the internal control plane API.'},
{'name': 'supervisor_port', 'type': 'int', 'default': 8083, 'env_var': 'SELKIES_SUPERVISOR_PORT', 'help': 'Port for the dual-mode supervisor API (handles /switch and /status endpoints when enable_dual_mode is true).'},
Comment thread src/selkies/settings.py Outdated
# Server Startup & Operational Settings
{'name': 'port', 'type': 'int', 'default': 8081, 'env_var': 'CUSTOM_WS_PORT', 'help': 'Port for the data websocket server.'},
{'name': 'control_port', 'type': 'int', 'default': 8083, 'help': 'Port for the internal control plane API.'},
{'name': 'supervisor_port', 'type': 'int', 'default': 8083, 'env_var': 'SELKIES_SUPERVISOR_PORT', 'help': 'Port for the dual-mode supervisor API (handles /switch and /status endpoints when enable_dual_mode is true).'},
Comment thread src/selkies/settings.py Outdated
# Server Startup & Operational Settings
{'name': 'port', 'type': 'int', 'default': 8081, 'env_var': 'CUSTOM_WS_PORT', 'help': 'Port for the data websocket server.'},
{'name': 'control_port', 'type': 'int', 'default': 8083, 'help': 'Port for the internal control plane API.'},
{'name': 'supervisor_port', 'type': 'int', 'default': 8083, 'env_var': 'SELKIES_SUPERVISOR_PORT', 'help': 'Port for the dual-mode supervisor API (handles /switch and /status endpoints when enable_dual_mode is true).'},
Comment thread src/selkies/settings.py Outdated
Comment on lines +115 to +117
{'name': 'port', 'type': 'int', 'default': 8081, 'env_var': 'CUSTOM_WS_PORT', 'help': 'Port for the data websocket server.'},
{'name': 'control_port', 'type': 'int', 'default': 8083, 'help': 'Port for the internal control plane API.'},
{'name': 'supervisor_port', 'type': 'int', 'default': 8083, 'env_var': 'SELKIES_SUPERVISOR_PORT', 'help': 'Port for the dual-mode supervisor API (handles /switch and /status endpoints when enable_dual_mode is true).'},
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request replaces the hardcoded port for the dual-mode supervisor API with a configurable supervisor_port setting. Feedback indicates that the current implementation will cause an AttributeError at runtime because the setting was added to the websocket-specific configuration instead of common settings. Additionally, the default port value of 8083 conflicts with the existing control_port, which would lead to binding failures if both features are enabled.

Comment thread src/selkies/__main__.py
if settings.enable_dual_mode[0]:
api_task = asyncio.create_task(create_api_server(manager, host='localhost', port=8082))
logger.info("Dual mode enabled: Supervisor API server started on port 8082")
supervisor_port = settings.supervisor_port
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This access will raise an AttributeError at runtime because supervisor_port was only added to the websockets settings definition (SETTING_DEFINITIONS_WEBSOCKETS), but __main__.py imports and uses settings_webrtc. The definition in src/selkies/settings.py must be moved to COMMON_SETTING_DEFINITIONS to be available in both modes.

Comment thread src/selkies/settings.py Outdated
# Server Startup & Operational Settings
{'name': 'port', 'type': 'int', 'default': 8081, 'env_var': 'CUSTOM_WS_PORT', 'help': 'Port for the data websocket server.'},
{'name': 'control_port', 'type': 'int', 'default': 8083, 'help': 'Port for the internal control plane API.'},
{'name': 'supervisor_port', 'type': 'int', 'default': 8083, 'env_var': 'SELKIES_SUPERVISOR_PORT', 'help': 'Port for the dual-mode supervisor API (handles /switch and /status endpoints when enable_dual_mode is true).'},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are two issues with this addition:

  1. Port Collision: The default value 8083 is identical to the default control_port (line 116). If a user enables both the dual-mode supervisor and the secure control plane (by providing a master_token), the two API servers will attempt to bind to the same port, causing a startup failure. I suggest using 8084 as the default.
  2. Incorrect Scope: As noted in __main__.py, this setting should be moved to COMMON_SETTING_DEFINITIONS (around line 98) because it is accessed by the supervisor logic in __main__.py, which uses the WebRTC settings object.
Suggested change
{'name': 'supervisor_port', 'type': 'int', 'default': 8083, 'env_var': 'SELKIES_SUPERVISOR_PORT', 'help': 'Port for the dual-mode supervisor API (handles /switch and /status endpoints when enable_dual_mode is true).'},
{'name': 'supervisor_port', 'type': 'int', 'default': 8084, 'env_var': 'SELKIES_SUPERVISOR_PORT', 'help': 'Port for the dual-mode supervisor API (handles /switch and /status endpoints when enable_dual_mode is true).'},

Three issues raised in review:

1. supervisor_port was added to SETTING_DEFINITIONS_WEBSOCKETS only, but
   __main__.run() reads it from `settings_webrtc` (imported as
   `settings`). In dual mode this would AttributeError at runtime. Move
   the definition into COMMON_SETTING_DEFINITIONS so it appears in both
   settings_ws and settings_webrtc.

2. Default 8083 collided with control_port (also default 8083). Bump to
   8084 so the dual-mode supervisor and the secure-mode control plane
   can coexist on default ports.

3. The explicit env_var='SELKIES_SUPERVISOR_PORT' was redundant: the
   AppSettings auto-derives `SELKIES_<UPPER>` already, so the help line
   would render "Env: SELKIES_SUPERVISOR_PORT (or SELKIES_SUPERVISOR_PORT)".
   Drop the explicit env_var; the auto-derived name is identical.
@DL6ER
Copy link
Copy Markdown
Contributor Author

DL6ER commented Apr 25, 2026

/gemini review

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the dual-mode supervisor API server port configurable via settings to avoid binding collisions when enable_dual_mode is enabled, replacing the previously hard-coded supervisor port in __main__.py.

Changes:

  • Add a new supervisor_port integer setting (intended to back SELKIES_SUPERVISOR_PORT) for configuring the supervisor API server port.
  • Update run() to read settings.supervisor_port and log the actual configured port instead of using a hard-coded value.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/selkies/settings.py Adds the supervisor_port setting definition and default value.
src/selkies/main.py Uses settings.supervisor_port when starting the supervisor API server and updates logging accordingly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/selkies/settings.py
Comment on lines 97 to 99
{'name': 'enable_dual_mode', 'type': 'bool', 'default': False, 'help': 'Enable switching Streaming modes from UI'},
{'name': 'supervisor_port', 'type': 'int', 'default': 8084, 'help': 'Port for the dual-mode supervisor API (/switch and /status endpoints) when enable_dual_mode is true.'},
{'name': 'audio_device_name', 'type': 'str', 'default': 'output.monitor', 'help': 'Audio device name for pcmflux capture.'},
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request makes the supervisor API port configurable by adding a new supervisor_port setting in settings.py and updating main.py to use it instead of a hardcoded value. I have no feedback to provide.

@PMohanJ
Copy link
Copy Markdown
Member

PMohanJ commented Apr 27, 2026

Thanks for the PR. But we have an ongoing effort to unify all ports into a single port #236.
To give you some context: Selkies images and LSIO images are different in how the streaming services are started, event though both containers use same piece of code. Currently LSIO doesn't support dual streaming mode in their images (check out the discord server for info) and do not seem to intend to do so for the foreseeable future.
You are right the port should be configurable, that's what #236 trying to address. Please consider reaching out to the maintainers in the discord for more info.

@ehfd
Copy link
Copy Markdown
Member

ehfd commented Apr 27, 2026

Related to #236.

@ehfd
Copy link
Copy Markdown
Member

ehfd commented Apr 30, 2026

Superseded by #236.

@ehfd ehfd closed this Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants