Selkies
Developer Reference

settings

Centralized configuration schema and resolution for the Selkies server.

Every setting lives in one declarative list (SETTING_DEFINITIONS); the AppSettings singleton resolves each entry with precedence CLI flag > SELKIES_\<NAME> env > fallback env_var(s) > built-in default. CLI and env names derive from name: my_setting becomes --my-setting / SELKIES_MY_SETTING. This module also owns everything both transports must agree on about settings: the client-facing settings payload (build_client_settings_payload), the shared per-setting sanitizer for client-proposed values (sanitize_client_setting), and the WebSocket message size ceiling with its bounded gzip inflater.

The ceiling (WS_MAX_MESSAGE_BYTES) is one value for both directions: enforced on receive (aiohttp max_msg_size), advertised to clients so multipart chunk sizing (clipboard, uploads) fills the frame on either end, and the bound on inflating a client 0x05 gzip frame. It is clamped under WS_MESSAGE_SIZE_HARD_CAP, above which the server also refuses to emit any single frame.

Override value syntax, by setting type:

  • List/enum (e.g. SELKIES_ENCODER="jpeg,h264enc"): first item is the default, the full list is the allowed options; a single value locks the choice. Invalid items are dropped; an entirely-invalid override keeps the full built-in menu and default.
  • Bool (case-insensitive): "true"/"1" is on, anything else off; a "|locked" suffix (e.g. "true|locked") forbids the client changing it.
  • Range: "8-240" restricts the allowed span (initial value = built-in default, clamped in); a bare value "60" keeps the built-in span and makes it the initial value, widening the span if it falls outside (so legacy fixed-value configs still resolve); "60,8-240" sets initial and span in one value; a degenerate span "60-60" locks the setting.
  • An override set to "" means "use the built-in default"; list types keep their explicit ""/"none" = disable semantics.
attributeWS_MESSAGE_SIZE_HARD_CAP
= 32 * 1024 * 1024
attributeWS_MAX_MESSAGE_BYTES
= min(8 * 1024 * 1024, WS_MESSAGE_SIZE_HARD_CAP)
attributeSETTING_DEFINITIONSList[Dict[str, Any]]
= [{'name': 'addr', 'type': 'str', 'default': '0.0.0.0', 'help': 'Host address to start the streaming service, default: "0.0.0.0"'}, {'name': 'port', 'type': 'int', 'default': 8080, 'min': 1, 'max': 65535, 'env_var': 'CUSTOM_WS_PORT', 'help': 'Port to start the streaming service, default: "8080"'}, {'name': 'unix_socket', 'type': 'str', 'default': '', 'env_var': 'SELKIES_UNIX_SOCKET', 'help': 'Unix socket path to start the streaming service; when set, a Unix domain socket is bound instead of the TCP addr/port pair.'}, {'name': 'web_root', 'type': 'str', 'default': '', 'help': 'Path to directory containing web application files. Defaults to web files packaged with Selkies application'}, {'name': 'audio_enabled', 'type': 'bool', 'default': True, 'help': 'Enable server-to-client audio streaming. Disabling this will also disable microphone support.'}, {'name': 'microphone_enabled', 'type': 'bool', 'default': False, 'help': 'Enable client-to-server microphone forwarding.'}, {'name': 'gamepad_enabled', 'type': 'bool', 'default': True, 'help': 'Enable gamepad support.'}, {'name': 'webcam_enabled', 'type': 'bool', 'default': False, 'help': 'Enable client-to-server webcam forwarding to the virtual V4L2 device.'}, {'name': 'enable_clipboard', 'type': 'str', 'default': 'true', 'help': 'Clipboard policy for both transports: "true" (both directions), "in" (client-to-server only), "out" (server-to-client only), "false" (disabled).'}, {'name': 'command_enabled', 'type': 'bool', 'default': False, 'help': 'Enable parsing of command websocket messages. Disabled by default for security; opt in with SELKIES_COMMAND_ENABLED=true (or --command-enabled true).'}, {'name': 'webrtc_pacer', 'type': 'bool', 'default': True, 'help': 'Pace outgoing WebRTC packets per transport with strict priorities (audio/RTCP > data-channel > video), an IDR-aware video queue budget and GOP-reset recovery, so audio and interactive signaling are protected from video bursts on congested links. Enabled by default; set SELKIES_WEBRTC_PACER=false to disable. SELKIES_WEBRTC_PACER_STALE_MS sets the stale-GOP purge deadline in milliseconds (0 = disabled).'}, {'name': 'file_transfers', 'type': 'list', 'default': 'upload,download', 'meta': {'allowed': ['upload', 'download']}, 'help': 'Allowed file transfer directions (comma-separated: "upload,download"). Set to "" or "none" to disable.'}, {'name': 'file_transfer_limit_mbps', 'type': 'float', 'default': 0.0, 'min': 0.0, 'help': 'Static file-transfer throttle in Mbit/s, one allowance shared by all downloads and uploads, for links whose rate the operator knows. 0 disables. The congestion-control pacing protects the video stream without it; the cap is chiefly for links its gauges cannot see, e.g. behind a reverse proxy.'}, {'name': 'file_transfer_cc', 'type': 'bool', 'default': True, 'help': "Congestion-control pacing for file transfers: a greedy transfer otherwise queues ahead of the video stream (bufferbloat) and the session stalls. Downloads are held inside a shared allowance that adapts from kernel queue depth (and RTT off-Linux); uploads back off the moment the uploader's own session round trip inflates past its floor, so they take whatever the uplink has spare and yield to the stream. Neither needs a link estimate. Behind a reverse proxy the download gauge measures only the hop to the proxy, so the static cap is the download lever there; the upload gauge times the client end to end and keeps working."}, {'name': 'framerate', 'type': 'range', 'default': '8-240', 'meta': {'default_value': 60}, 'help': 'Framerate: allowed range (e.g., "8-240"), initial value (e.g., "60"), or both ("60,8-240"); "60-60" locks.'}, {'name': 'video_crf', 'type': 'range', 'default': '5-50', 'meta': {'default_value': 25}, 'help': 'Video CRF (constant quality): allowed range (e.g., "5-50"), initial value (e.g., "25"), or both ("25,5-50"); "25-25" locks.'}, {'name': 'video_bitrate', 'type': 'range', 'default': '100-1000000', 'meta': {'default_value': 8000}, 'help': 'Video bitrate aka CBR, in kilobits per second (kbps): allowed range (e.g., "100-1000000"), initial value (e.g., "8000" for 8 Mbps, "250" for 250 kbps), or both ("8000,100-1000000"); "8000-8000" locks.'}, {'name': 'rate_control_mode', 'type': 'enum', 'default': 'crf', 'meta': {'allowed': ['cbr', 'crf']}, 'help': 'Rate control mode for the H.264 encoders (crf = constant quality/QP, cbr = constant bitrate). Honored for every H.264 encoder when enable_rate_control is true (the default).'}, {'name': 'enable_rate_control', 'type': 'bool', 'default': True, 'help': 'Honor the client-selected rate_control_mode (crf/cbr). Enabled by default so both modes are selectable; set false to lock the encoder to its built-in default.'}, {'name': 'keyframe_interval', 'type': 'float', 'default': 0.0, 'min': 0.0, 'max': 300.0, 'help': 'Seconds between scheduled video recovery keyframes (any video codec). 0 (default) keeps the GOP infinite: keyframes are sent only on demand (client join/reset, keyframe requests), which keeps bitrate and quality steady.'}, {'name': 'video_min_qp', 'type': 'int', 'default': 0, 'min': 0, 'max': 51, 'help': 'CBR-mode minimum H.264 QP (0 = encoder default). Raising it caps bit spend on easy content when the bitrate budget is generous.'}, {'name': 'video_max_qp', 'type': 'int', 'default': 0, 'min': 0, 'max': 51, 'help': 'CBR-mode maximum H.264 QP (0 = encoder default). Lowering it keeps screen text legible under motion at the cost of overshooting the bitrate target on hard content (measured at 720p60 scrolling text: 35 lifts x264 by ~19 dB at ~2.5x the target).'}, {'name': 'audio_frame_duration_ms', 'type': 'enum', 'default': '10', 'meta': {'allowed': ['2.5', '5', '10', '20', '40', '60']}, 'help': 'Opus frame duration in milliseconds for server-to-client audio. Lower values cut audio latency (each frame must fill before it can be sent, and the client buffers a fixed number of frames) at a small bitrate-efficiency and packet-rate cost. On WebRTC the SDP ptime/minptime follow this value.'}, {'name': 'audio_bitrate', 'type': 'enum', 'default': '128000', 'meta': {'allowed': ['32000', '48000', '64000', '96000', '128000', '192000', '256000', '320000', '384000', '510000'], 'value_range': [6000, 510000]}, 'help': 'The default audio bitrate.'}, {'name': 'audio_redundancy', 'type': 'bool', 'default': True, 'help': 'Enable Opus RED (RFC 2198) audio redundancy to cut dropouts/concealment under packet loss. On by default; carries prior frames as redundancy on WebRTC (browsers de-RED natively, plain-opus fallback for peers that decline) and, on WebSocket, is gated on every client supporting it.'}, {'name': 'audio_redundancy_distance', 'type': 'int', 'default': 2, 'min': 0, 'max': 4, 'help': 'Number of prior Opus frames carried as RED redundancy when audio_redundancy is enabled (0-4; higher survives longer loss bursts at proportionally more bandwidth).'}, {'name': 'manual_resolution', 'type': 'bool', 'default': False, 'help': 'Lock the resolution to the manual width/height values.'}, {'name': 'manual_width', 'type': 'int', 'default': 0, 'meta': {'min': 0, 'max': 16384}, 'help': 'Lock width to a fixed value. Setting this forces manual resolution mode.'}, {'name': 'manual_height', 'type': 'int', 'default': 0, 'meta': {'min': 0, 'max': 16384}, 'help': 'Lock height to a fixed value. Setting this forces manual resolution mode.'}, {'name': 'scaling_dpi', 'type': 'enum', 'default': '96', 'meta': {'allowed': ['96', '120', '144', '168', '192', '216', '240', '264', '288']}, 'help': 'The default DPI for UI scaling.'}, {'name': 'force_aligned_resolution', 'type': 'bool', 'default': False, 'help': 'Forces the display resolution to be a multiple of 16 pixels.'}, {'name': 'enable_binary_clipboard', 'type': 'bool', 'default': True, 'help': 'Allow binary data (e.g., images) on the clipboard.'}, {'name': 'use_browser_cursors', 'type': 'bool', 'default': True, 'help': 'Use browser CSS cursors instead of rendering to canvas.'}, {'name': 'use_css_scaling', 'type': 'bool', 'default': False, 'help': 'HiDPI when false, if true a lower resolution is sent from the client and the canvas is stretched.'}, {'name': 'ui_title', 'type': 'str', 'default': 'Selkies', 'help': 'Title in top left corner of sidebar.'}, {'name': 'ui_show_logo', 'type': 'bool', 'default': True, 'help': 'Show the Selkies logo in the sidebar.'}, {'name': 'ui_show_core_buttons', 'type': 'bool', 'default': True, 'help': 'Show the core components buttons display, audio, microphone, webcam, and gamepad.'}, {'name': 'ui_show_sidebar', 'type': 'bool', 'default': True, 'help': 'Show the main sidebar UI.'}, {'name': 'ui_sidebar_show_video_settings', 'type': 'bool', 'default': True, 'help': 'Show the video settings section in the sidebar.'}, {'name': 'ui_sidebar_show_screen_settings', 'type': 'bool', 'default': True, 'help': 'Show the screen settings section in the sidebar.'}, {'name': 'ui_sidebar_show_audio_settings', 'type': 'bool', 'default': True, 'help': 'Show the audio settings section in the sidebar.'}, {'name': 'ui_sidebar_show_stats', 'type': 'bool', 'default': True, 'help': 'Show the stats section in the sidebar.'}, {'name': 'ui_sidebar_show_shortcuts', 'type': 'bool', 'default': True, 'help': 'Show the keyboard shortcuts section in the sidebar (both dashboards honor this).'}, {'name': 'ui_sidebar_show_clipboard', 'type': 'bool', 'default': True, 'help': 'Show the clipboard section in the sidebar.'}, {'name': 'ui_sidebar_show_files', 'type': 'bool', 'default': True, 'help': 'Show the file transfer section in the sidebar.'}, {'name': 'ui_sidebar_show_apps', 'type': 'bool', 'default': True, 'help': 'Show the applications section in the sidebar.'}, {'name': 'ui_sidebar_show_sharing', 'type': 'bool', 'default': True, 'help': 'Show the sharing section in the sidebar.'}, {'name': 'ui_sidebar_show_gamepads', 'type': 'bool', 'default': True, 'help': 'Show the gamepads section in the sidebar.'}, {'name': 'ui_sidebar_show_webcam', 'type': 'bool', 'default': True, 'help': 'Show the webcam toggle among the core buttons (classic sidebar) and stream controls (wish top menu). Hides the control only; webcam_enabled governs whether the server accepts webcam frames.'}, {'name': 'ui_sidebar_show_fullscreen', 'type': 'bool', 'default': True, 'help': 'Show the fullscreen button in the sidebar.'}, {'name': 'ui_sidebar_show_gaming_mode', 'type': 'bool', 'default': True, 'help': 'Show the gaming mode button in the sidebar.'}, {'name': 'ui_sidebar_show_trackpad', 'type': 'bool', 'default': True, 'help': 'Show the virtual trackpad button in the sidebar.'}, {'name': 'ui_sidebar_show_keyboard_button', 'type': 'bool', 'default': True, 'help': 'Show the on-screen keyboard button in the display area.'}, {'name': 'ui_sidebar_show_soft_buttons', 'type': 'bool', 'default': True, 'help': 'Show the soft buttons section in the sidebar.'}, {'name': 'enable_sharing', 'type': 'bool', 'default': True, 'help': 'Master toggle for all sharing features.'}, {'name': 'enable_collab', 'type': 'bool', 'default': True, 'help': 'Enable collaborative (read-write) sharing link.'}, {'name': 'enable_shared', 'type': 'bool', 'default': True, 'help': 'Enable view-only sharing links.'}, {'name': 'enable_player2', 'type': 'bool', 'default': True, 'help': 'Enable sharing link for gamepad player 2.'}, {'name': 'enable_player3', 'type': 'bool', 'default': True, 'help': 'Enable sharing link for gamepad player 3.'}, {'name': 'enable_player4', 'type': 'bool', 'default': True, 'help': 'Enable sharing link for gamepad player 4.'}, {'name': 'debug', 'type': 'bool', 'default': False, 'help': 'Enable debug logging.'}, {'name': 'mode', 'type': 'str', 'default': 'websockets', 'help': "Specify the mode: 'webrtc' or 'websockets'; defaults to websockets"}, {'name': 'enable_dual_mode', 'type': 'bool', 'default': True, 'help': 'Enable switching Streaming modes from UI'}, {'name': 'audio_device_name', 'type': 'str', 'default': 'output.monitor', 'help': 'Audio device name for pcmflux capture.'}, {'name': 'master_token', 'type': 'str', 'default': '', 'help': 'Master token to enable secure mode and protect the control plane API.'}, {'name': 'enable_https', 'type': 'bool', 'default': False, 'help': 'Enable or disable HTTPS for the web application, specifying a valid server certificate is recommended'}, {'name': 'https_cert', 'type': 'str', 'default': '/etc/ssl/certs/ssl-cert-snakeoil.pem', 'help': 'Path to the TLS server certificate file when HTTPS is enabled'}, {'name': 'https_key', 'type': 'str', 'default': '/etc/ssl/private/ssl-cert-snakeoil.key', 'help': 'Path to the TLS server private key file when HTTPS is enabled, set to an empty value if the private key is included in the certificate'}, {'name': 'cert_reload_interval', 'type': 'int', 'default': 30, 'min': 0, 'help': 'Seconds between checks for SSL certificate file changes when HTTPS is enabled, set to 0 to disable automatic certificate reloading'}, {'name': 'enable_basic_auth', 'type': 'bool', 'default': True, 'help': 'Enable basic authentication on the server. On by default, and the server refuses to start until a password is set through --basic-auth-password, SELKIES_BASIC_AUTH_PASSWORD, PASSWORD or PASSWD; pass --enable-basic-auth=false to serve without a login instead.'}, {'name': 'basic_auth_user', 'type': 'str', 'default': 'ubuntu', 'env_var': ['CUSTOM_USER', 'USERNAME', 'USER'], 'help': 'Username for basic authentication; resolves from the CUSTOM_USER, then USERNAME, then USER environment variables, and defaults to "ubuntu" when none is set.'}, {'name': 'basic_auth_password', 'type': 'str', 'default': '', 'env_var': ['PASSWORD', 'PASSWD'], 'help': 'Password used when basic authentication is set; resolves from SELKIES_BASIC_AUTH_PASSWORD, then PASSWORD, then PASSWD, so an image that already names a container account password does not have to repeat it. There is no default: the server will not start with basic authentication enabled until one of these is set.'}, {'name': 'basic_auth_viewonly_password', 'type': 'str', 'default': '', 'env_var': 'VIEWONLY_PASSWORD', 'help': 'Optional second basic-auth password that grants view-only access. Clients authenticating with it are capped at the viewer role (no keyboard, mouse, clipboard, gamepad, or command input) regardless of the role they request, while the main password authorizes full control. Empty disables the split. Ignored in secure mode, where the master token governs roles.'}, {'name': 'subfolder', 'type': 'str', 'default': '', 'env_var': 'SUBFOLDER', 'help': 'URL path prefix the server is reverse-proxied under; prepended to every route (websockets, tokens, metrics, static files). Slashes are optional, so "desk", "/desk" and "/desk/" are the same prefix and "/" is the root. The web client reads its own prefix from the URL it was loaded from, so only the server needs telling.'}, {'name': 'run_after_connect', 'type': 'str', 'default': '', 'help': "Shell command run after the first client has connected ('' = off); runs again each time a client connects while no others are connected."}, {'name': 'run_after_disconnect', 'type': 'str', 'default': '', 'help': "Shell command run after the last client has disconnected ('' = off), including on server shutdown while clients are connected."}, {'name': 'encoder', 'type': 'enum', 'default': 'h264enc', 'meta': {'allowed': ['h264enc', 'h264enc-striped', 'jpeg']}, 'help': 'The default video encoder: h264enc is full-frame H.264 on NVENC or VA-API, falling back to the software encoder pixelflux was built with (x264, or OpenH264 in a GPL-free build); h264enc-striped is CPU-striped H.264 on that same software encoder; jpeg is CPU-striped JPEG. Only h264enc streams over WebRTC.'}, {'name': 'jpeg_quality', 'type': 'range', 'default': '1-100', 'meta': {'default_value': 40}, 'help': 'JPEG quality: allowed range (e.g., "1-100"), initial value (e.g., "40"), or both ("40,1-100"); "40-40" locks.'}, {'name': 'video_fullcolor', 'type': 'bool', 'default': False, 'help': 'Enable H.264 full color range for pixelflux encoders.'}, {'name': 'video_streaming_mode', 'type': 'bool', 'default': True, 'help': 'Enable H.264 streaming mode (Turbo: encode every frame like a traditional video encoder) for pixelflux encoders.'}, {'name': 'use_cpu', 'type': 'bool', 'default': False, 'help': 'Force CPU-based encoding for pixelflux.'}, {'name': 'use_paint_over_quality', 'type': 'bool', 'default': True, 'help': 'Enable high-quality paint-over for static scenes.'}, {'name': 'paint_over_jpeg_quality', 'type': 'range', 'default': '1-100', 'meta': {'default_value': 90}, 'help': 'JPEG paint-over quality: allowed range, initial value, or both ("90,1-100"); "90-90" locks.'}, {'name': 'video_paintover_crf', 'type': 'range', 'default': '5-50', 'meta': {'default_value': 18}, 'help': 'H.264 paint-over CRF: allowed range, initial value, or both ("18,5-50"); "18-18" locks.'}, {'name': 'video_paintover_burst_frames', 'type': 'range', 'default': '1-30', 'meta': {'default_value': 5}, 'help': 'H.264 paint-over burst frames: allowed range, initial value, or both ("5,1-30"); "5-5" locks.'}, {'name': 'second_screen', 'type': 'bool', 'default': True, 'help': 'Enable support for a second monitor/display.'}, {'name': 'encode_dri', 'type': 'str', 'default': '', 'env_var': 'DRI_NODE', 'help': 'Path to the DRI render node the ENCODER uses (VA-API/NVENC device selection).'}, {'name': 'render_dri', 'type': 'str', 'default': '', 'env_var': 'DRINODE', 'help': 'Path to the DRI render node the Wayland compositor RENDERS on (defaults to auto_gpu selection, else software rendering).'}, {'name': 'auto_gpu', 'type': 'str', 'default': 'true', 'env_var': 'AUTO_GPU', 'help': 'GPU auto-selection for rendering, enabled by default: "true" picks the first GPU; "false" disables it; otherwise a case-insensitive token picks the first GPU it matches — a vendor name (nvidia, amd/ati, intel, arm/mali, qualcomm/adreno, broadcom/videocore, apple, imagination/powervr, vmware, virtio, ...), a kernel driver name (amdgpu, i915, xe, nouveau, panfrost, msm, v3d, ...), a devicetree vendor prefix (qcom, rockchip, brcm, ...), or a raw PCI vendor ID (0x10de).'}, {'name': 'wayland', 'type': 'bool', 'default': False, 'env_var': 'PIXELFLUX_WAYLAND', 'help': 'Run the Wayland (headless compositor) backend instead of X11 capture/input (default: X11); PIXELFLUX_WAYLAND remains as a legacy fallback.'}, {'name': 'app_wayland_display', 'type': 'str', 'default': '', 'help': 'Wayland socket where applications run — the target for input injection and clipboard — when it differs from the pixelflux capture compositor (e.g. a nested session that pixelflux captures). Empty auto-detects the nested compositor socket in XDG_RUNTIME_DIR, falling back to the capture compositor when nothing is nested.'}, {'name': 'computer_use_bind', 'type': 'str', 'default': '', 'help': "Start pixelflux's Computer-Use HTTP server: a bare port listens on all interfaces, host:port scopes it (e.g. 127.0.0.1:9500). Empty leaves it off; the PIXELFLUX_CU environment variable remains the standalone fallback."}, {'name': 'wayland_host_display', 'type': 'str', 'default': '', 'help': 'Wayland socket of an EXTERNAL compositor (e.g. labwc started with WLR_BACKENDS=headless) that pixelflux captures and injects into as a client, instead of compositing its own. Frames arrive by screencopy into pixelflux-allocated GPU buffers (zero-copy into the encoder); input rides virtual-keyboard/pointer devices. Empty keeps the built-in compositor.'}, {'name': 'recording_socket', 'type': 'str', 'default': '', 'env_var': 'PIXELFLUX_RECORDING_SOCKET', 'help': "Unix socket path for the out-of-band H.264 recording tap ('' = off); pixelflux binds it and multiplexes the elementary stream to connected clients."}, {'name': 'file_manager_path', 'type': 'str', 'default': '~/Desktop', 'env_var': 'FILE_MANAGER_PATH', 'help': 'Directory for client file transfers on both transports: uploads land here and the file-browser/download API serves it (created at startup if missing).'}, {'name': 'watermark_path', 'type': 'str', 'default': '', 'env_var': 'WATERMARK_PNG', 'help': 'Absolute path to the watermark PNG file.'}, {'name': 'watermark_location', 'type': 'int', 'default': -1, 'env_var': 'WATERMARK_LOCATION', 'help': 'Watermark location enum (0-6).'}, {'name': 'wayland_socket_index', 'type': 'int', 'default': 0, 'min': 0, 'help': 'Index for the Wayland command socket (e.g. 0 for wayland-0).'}, {'name': 'rtc_config_json', 'type': 'str', 'default': '/tmp/rtc.json', 'help': 'JSON file with WebRTC configuration to use, checked periodically, overriding all other STUN/TURN settings'}, {'name': 'turn_rest_uri', 'type': 'str', 'default': '', 'help': 'URI for TURN REST API service, example: http://localhost:8008'}, {'name': 'turn_rest_api_key', 'type': 'str', 'default': '', 'help': 'API key to pass to the TURN REST API service'}, {'name': 'turn_rest_username', 'type': 'str', 'default': '', 'help': "Username sent to the TURN REST API service (x-auth-user header); the service embeds it in the HMAC credential. Empty (default) uses the generic 'selkies'."}, {'name': 'turn_rest_username_auth_header', 'type': 'str', 'default': 'x-auth-user', 'help': 'Header to pass user to TURN REST API service'}, {'name': 'turn_rest_protocol_header', 'type': 'str', 'default': 'x-turn-protocol', 'help': 'Header to pass desired TURN protocol to TURN REST API service'}, {'name': 'turn_rest_tls_header', 'type': 'str', 'default': 'x-turn-tls', 'help': 'Header to pass TURN (D)TLS usage to TURN REST API service'}, {'name': 'turn_host', 'type': 'str', 'default': 'staticauth.openrelay.metered.ca', 'help': 'TURN host when generating RTC config from shared secret or using long-term credentials, IPv6 addresses must be enclosed with square brackets such as [::1]'}, {'name': 'turn_port', 'type': 'int', 'default': 443, 'min': 1, 'max': 65535, 'help': 'TURN port when generating RTC config from shared secret or using long-term credentials'}, {'name': 'turn_protocol', 'type': 'str', 'default': 'udp', 'help': 'TURN protocol for the client to use ("udp" or "tcp"), set to "tcp" without the quotes if "udp" is blocked on the network, "udp" is otherwise strongly recommended'}, {'name': 'turn_tls', 'type': 'bool', 'default': False, 'help': 'Enable or disable TURN over TLS (for the TCP protocol) or TURN over DTLS (for the UDP protocol), valid TURN server certificate required'}, {'name': 'turn_shared_secret', 'type': 'str', 'default': 'openrelayprojectsecret', 'help': 'Shared TURN secret used to generate HMAC credentials, also requires --turn-host and --turn-port'}, {'name': 'turn_username', 'type': 'str', 'default': '', 'help': 'Legacy non-HMAC TURN credential username, also requires --turn-host and --turn-port'}, {'name': 'turn_password', 'type': 'str', 'default': '', 'help': 'Legacy non-HMAC TURN credential password, also requires --turn-host and --turn-port'}, {'name': 'stun_host', 'type': 'str', 'default': 'stun.l.google.com', 'help': 'STUN host for NAT hole punching with WebRTC, change to your internal STUN/TURN server for local networks without internet, defaults to "stun.l.google.com"'}, {'name': 'stun_port', 'type': 'int', 'default': 19302, 'min': 1, 'max': 65535, 'help': 'STUN port for NAT hole punching with WebRTC, change to your internal STUN/TURN server for local networks without internet, defaults to "19302"'}, {'name': 'webrtc_public_ip', 'type': 'str', 'default': '', 'help': 'Public IP address(es) to advertise in WebRTC host ICE candidates (Pion-style NAT1TO1), for a host behind static 1:1 NAT such as a cloud instance whose private address maps to a fixed public/elastic IP with the WebRTC UDP ports forwarded. Accepts one IPv4 and/or one IPv6 address (comma- or space-separated); each replaces the private address of host candidates in its own family, while server-reflexive (STUN) and relay (TURN) candidates are left untouched so hole-punching and TURN fallback still work. Empty (default) keeps the gathered addresses unchanged.'}, {'name': 'enable_cloudflare_turn', 'type': 'bool', 'default': False, 'help': 'Enable Cloudflare TURN service, requires SELKIES_CLOUDFLARE_TURN_TOKEN_ID, and SELKIES_CLOUDFLARE_TURN_API_TOKEN'}, {'name': 'cloudflare_turn_token_id', 'type': 'str', 'default': '', 'help': 'The Cloudflare TURN App token ID.'}, {'name': 'cloudflare_turn_api_token', 'type': 'str', 'default': '', 'help': 'The Cloudflare TURN API token.'}, {'name': 'app_wait_ready', 'type': 'bool', 'default': False, 'help': 'Waits for --app-ready-file to exist before starting stream if set to "true"'}, {'name': 'app_ready_file', 'type': 'str', 'default': '/tmp/selkies-appready', 'help': 'File set by sidecar used to indicate that app is initialized and ready'}, {'name': 'uinput_mouse_socket', 'type': 'str', 'default': '', 'help': 'Path to the uinput mouse socket, if not provided uinput is used directly'}, {'name': 'js_socket_path', 'type': 'str', 'default': '/tmp', 'help': 'Directory to write the Selkies Joystick Interposer communication sockets to, default: /tmp, results in socket files: /tmp/selkies_js{0-3}.sock'}, {'name': 'webcam_socket_path', 'type': 'str', 'default': '/tmp', 'help': 'Directory to write the Selkies V4L2 Interposer webcam socket to, default: /tmp, results in socket file: /tmp/selkies_webcam0.sock'}, {'name': 'webcam_width', 'type': 'int', 'default': 1280, 'help': 'Width of the virtual webcam device; client camera frames are scaled and letterboxed to fit.'}, {'name': 'webcam_height', 'type': 'int', 'default': 720, 'help': 'Height of the virtual webcam device; client camera frames are scaled and letterboxed to fit.'}, {'name': 'webcam_pixel_format', 'type': 'str', 'default': 'auto', 'help': 'Pixel format of the virtual webcam device. "auto" follows the uplink: a browser sending JPEG (no WebCodecs) gets an MJPEG device that carries its frames as received, any other uplink an I420 device, and a later uplink of the other kind re-creates the device for itself while no application is reading it. Or pin "I420" (planar 4:2:0, the browsers\' preference), "NV12", "YUYV" or "MJPEG", which is then kept whatever arrives.'}, {'name': 'webcam_encoder', 'type': 'enum', 'default': 'auto', 'meta': {'allowed': ['auto', 'h264', 'vp8', 'mjpeg']}, 'help': 'Codec WebSocket clients encode the webcam uplink with. "auto" runs the measured ladder (H.264, else VP8, JPEG when neither keeps up) on engines that stream camera frames through MediaStreamTrackProcessor, and JPEG on the `<video>`-element path (Firefox): its software encoders can hold the camera rate while costing a full core, which no client-side probe can price. "h264" or "vp8" run that one codec on every path, trading client CPU for a fraction of the uplink bandwidth, still falling to JPEG where it cannot keep up or encodes the wrong colours; "mjpeg" pins JPEG everywhere. Clients may override per user unless the value is locked; the WebRTC transport encodes in the browser and ignores this.'}, {'name': 'webcam_device', 'type': 'str', 'default': 'auto', 'help': 'Also mirror the webcam into a v4l2loopback kernel device, which applications find without the V4L2 Interposer: "auto" uses the first v4l2loopback output device found (typically a desktop host or privileged container), a path such as "/dev/video10" uses that device, and "false" never does. The interposer socket is always served.'}, {'name': 'webcam_pipewire', 'type': 'bool', 'default': True, 'help': 'Also publish the webcam as a PipeWire Video/Source node when a PipeWire daemon is reachable, for PipeWire-native applications and the pipewire-v4l2 wrapper. The interposer socket is always served.'}, {'name': 'uinput_gamepad', 'type': 'str', 'default': 'auto', 'help': 'Register gamepads as kernel devices through /dev/uinput, which applications (Steam, Proton, in-desktop browsers) find without the Joystick Interposer or fake-udev: "auto" does so only where the interposer is not configured for the session and /dev/uinput is writable — typically a desktop host rather than a container — while "true" always attempts it and "false" never does.'}, {'name': 'gpu_id', 'type': 'str', 'default': '', 'help': 'GPU ID for hardware video encoders: selects /dev/dri/renderD{128 + n} and the GPU-stats index. Empty (default) sets no explicit pick, encoding on ID 0 — the first GPU — or on the GPU chosen by --auto-gpu; -1 disables hardware encoding. Ignored when --encode-dri specifies a device path.'}, {'name': 'congestion_control', 'type': 'bool', 'default': False, 'help': 'Adapt the video bitrate to the transport-wide-cc (GCC-style) bandwidth estimate from WebRTC receiver feedback. Effective in CBR rate-control mode; may trade quality/stability for congestion responsiveness.'}, {'name': 'audio_channels', 'type': 'int', 'default': 2, 'min': 1, 'help': 'Number of audio channels, defaults to stereo (2 channels)'}, {'name': 'enable_resize', 'type': 'bool', 'default': True, 'help': 'Enable dynamic resizing to match browser size'}, {'name': 'enable_cursors', 'type': 'bool', 'default': True, 'help': 'Enable passing remote cursors to client'}, {'name': 'debug_cursors', 'type': 'bool', 'default': False, 'help': 'Enable cursor debug logging'}, {'name': 'cursor_size', 'type': 'int', 'default': -1, 'env_var': 'XCURSOR_SIZE', 'help': 'Cursor size in points at 96 DPI (scaled with the session DPI). Applies to the X11 server cursor, the Wayland compositor cursor theme, and the remote-cursor capture cap on both transports; -1 uses the platform default (32 on X11, 24 on Wayland).'}, {'name': 'enable_webrtc_statistics', 'type': 'bool', 'default': False, 'help': 'Enable WebRTC Statistics CSV dumping to the directory --webrtc-statistics-dir with filenames selkies-stats-video-[timestamp].csv and selkies-stats-audio-[timestamp].csv'}, {'name': 'webrtc_statistics_dir', 'type': 'str', 'default': '/tmp', 'help': 'Directory to save WebRTC Statistics CSV from client with filenames selkies-stats-video-[timestamp].csv and selkies-stats-audio-[timestamp].csv'}, {'name': 'enable_metrics_http', 'type': 'bool', 'default': False, 'help': 'Enable the Prometheus HTTP /metrics endpoint.'}, {'name': 'backpressure_queue_size', 'type': 'int', 'default': 120, 'min': 1, 'max': 100000, 'help': 'Max frames/audio chunks buffered per stream before dropping under backpressure (WebSockets mode). Higher tolerates larger client hiccups at the cost of latency.'}, {'name': 'allowed_origins', 'type': 'str', 'default': '', 'help': "Comma-separated browser Origins allowed to open the streaming WebSocket (cross-site WebSocket-hijacking guard). Empty (default) allows only same-origin plus non-browser clients that send no Origin; use '*' to allow any origin."}]
attributeSENSITIVE_SETTING_NAMES
= frozenset({'master_token', 'https_key', 'basic_auth_user', 'basic_auth_password', 'basic_auth_viewonly_password', 'turn_rest_api_key', 'turn_shared_secret', 'turn_password', 'cloudflare_turn_token_id', 'cloudflare_turn_api_token'})
attributeWEBRTC_ENCODER_CHOICES
= ('h264enc',)
attributeENCODER_ALIASES
= {'x264enc': 'h264enc', 'openh264enc': 'h264enc'}
attributesettings
= AppSettings(SETTING_DEFINITIONS)
attributeOPERATOR_LOCKED_WHEN_OVERRIDDEN
= ('scaling_dpi',)
attributeCPU_ONLY_ENCODERS
= ('jpeg', 'h264enc-striped')
attributeCLIENT_PAYLOAD_EXCLUDED
= ['port', 'addr', 'unix_socket', 'web_root', 'encode_dri', 'render_dri', 'debug', 'audio_device_name', 'watermark_path', 'recording_socket', 'file_manager_path', 'run_after_connect', 'run_after_disconnect', 'https_cert', 'rtc_config_json', 'app_ready_file', 'js_socket_path', 'webcam_socket_path', 'webcam_device', 'uinput_mouse_socket', 'webrtc_statistics_dir', 'computer_use_bind', 'wayland_host_display', 'app_wayland_display']
attributeINT_SETTING_DEFAULT_MAX
= 1000000
attributeINT_SETTING_DEFAULT_MIN
= -1000000

On this page

No Headings
Edit on GitHub