docs: use ConfigurationTab that allows switching between yaml, env and cmd.

This commit is contained in:
Miroslav Šedivý 2025-04-05 18:25:04 +02:00
parent 0e3bcedcd4
commit 3a8a5c30ef
8 changed files with 430 additions and 390 deletions

View file

@ -1,4 +1,6 @@
import { Def, Opt } from '@site/src/components/Anchor';
import { ConfigurationTab } from '@site/src/components/Configuration';
import configOptions from './help.json';
# Configuration
@ -291,17 +293,16 @@ import TabItem from '@theme/TabItem';
This is the initial configuration of the room that can be modified by an admin in real-time.
```yaml title="config.yaml"
session:
private_mode: false
locked_logins: false
locked_controls: false
control_protection: false
implicit_hosting: true
inactive_cursors: false
merciful_reconnect: true
heartbeat_interval: 120
```
<ConfigurationTab options={configOptions} filter={[
'session.private_mode',
'session.locked_logins',
'session.locked_controls',
'session.control_protection',
'session.implicit_hosting',
'session.inactive_cursors',
'session.merciful_reconnect',
'session.heartbeat_interval',
]} comments={false} />
- <Def id="session.private_mode" /> whether private mode is enabled, users do not receive the room video or audio.
- <Def id="session.locked_logins" /> whether logins are locked for users, admins can still login.
@ -316,18 +317,17 @@ session:
This is the configuration of the neko server.
```yaml title="config.yaml"
server:
bind: "127.0.0.1:8080"
cert: "/path/to/cert.pem"
key: "/path/to/key.pem"
cors: [ "*" ]
metrics: true
path_prefix: "/neko"
pprof: true
proxy: true
static: "/var/www/neko"
```
<ConfigurationTab options={configOptions} filter={[
'server.bind',
'server.cert',
'server.key',
'server.cors',
'server.metrics',
'server.path_prefix',
'server.pprof',
'server.proxy',
'server.static',
]} comments={false} />
- <Def id="server.bind" /> address/port/socket to serve neko. For docker you might want to bind to `0.0.0.0` to allow connections from outside the container.
- <Def id="server.cert" /> and <Def id="server.key" /> paths to the SSL cert and key used to secure the neko server. If both are empty, the server will run in plain HTTP.
@ -345,14 +345,13 @@ server:
This is the configuration of the logging system.
```yaml title="config.yaml"
log:
dir: <string>
json: true
level: "info"
nocolor: true
time: "unix"
```
<ConfigurationTab options={configOptions} filter={[
'log.dir',
'log.json',
'log.level',
'log.nocolor',
'log.time',
]} comments={false} />
- <Def id="log.dir" /> directory to store logs. If empty, logs are written to stdout. This is useful when running neko in a container.
- <Def id="log.json" /> when true, logs are written in JSON format.
@ -368,10 +367,7 @@ Shortcut environment variable to enable DEBUG mode: `NEKO_DEBUG=true`
Here is a full configuration with default values as shown in the help command. Please refer to the sub-sections for more details.
import Configuration from '@site/src/components/Configuration';
import configOptions from './help.json';
<Configuration configOptions={configOptions} />
<ConfigurationTab options={configOptions} heading={true} />
## Next Steps {#next}

View file

@ -3,6 +3,8 @@ description: Configuration related to the Authentication and Sessions in Neko.
---
import { Def, Opt } from '@site/src/components/Anchor';
import { ConfigurationTab } from '@site/src/components/Configuration';
import configOptions from './help.json';
# Authentication
@ -86,21 +88,25 @@ This provider allows you to define two types of users: **regular** users and **a
Profiles for regular users and admins are optional, if not provided, the default profiles are used (see below in the example configuration).
```yaml title="config.yaml"
member:
provider: multiuser
multiuser:
# Password for admins, in plain text.
admin_password: "adminPassword"
# Profile fields as described above
admin_profile:
...
# Password for regular users, in plain text.
user_password: "userPassword"
# Profile fields as described above
user_profile:
...
```
<ConfigurationTab options={{
"member.provider": 'multiuser',
"member.multiuser.admin_password": {
defaultValue: "admin",
description: "Password for admins, in plain text.",
},
"member.multiuser.admin_profile": {
defaultValue: {},
description: "Profile fields as described above",
},
"member.multiuser.user_password": {
defaultValue: "neko",
description: "Password for regular users, in plain text.",
},
"member.multiuser.user_profile": {
defaultValue: {},
description: "Profile fields as described above",
},
}} />
<details>
<summary>See example configuration</summary>
@ -152,8 +158,8 @@ For easier configuration, you can specify only passwords using environment varia
```yaml title="docker-compose.yaml"
environment:
NEKO_MEMBER_MULTIUSER_USER_PASSWORD: "neko"
NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: "admin"
NEKO_MEMBER_MULTIUSER_USER_PASSWORD: "neko"
```
:::
@ -161,15 +167,17 @@ environment:
This provider reads the user's credentials from a file. It is useful for small deployments where you don't want to set up a database or LDAP server and still want to have persistent users.
```yaml title="config.yaml"
member:
provider: file
file:
# Absolute path to the file containing the users and their passwords.
path: /opt/neko/members.json
# Whether the passwords are hashed using sha256 or not.
hash: true
```
<ConfigurationTab options={{
"member.provider": 'file',
"member.file.path": {
defaultValue: "/opt/neko/members.json",
description: "Absolute path to the file containing the users and their passwords.",
},
"member.file.hash": {
defaultValue: false,
description: "Whether the passwords are hashed using sha256 or not.",
},
}} />
It allows you to store the user's credentials in a JSON file. The JSON structure maps user logins to their passwords and profiles.
@ -239,18 +247,13 @@ You can leave the file empty and add users later using the HTTP API.
This provider is the same as the file provider, but it saves the users only in memory. That means that the users are lost when the server is restarted. However, the default users can be set in the configuration file. The difference from the multi-user provider is that the users are not generated on demand and we define exactly which users with their passwords and profiles are allowed to log in. They cannot be logged in twice with the same username.
```yaml title="config.yaml"
member:
provider: object
object:
# List of users with their passwords and profiles
- username: "admin"
# Password in plain text
password: "admin"
# Profile fields as described above
profile:
...
```
<ConfigurationTab options={{
"member.provider": 'object',
"member.object.users": {
defaultValue: [],
description: "List of users with their passwords and profiles.",
},
}} />
<details>
<summary>See example configuration</summary>
@ -296,10 +299,9 @@ member:
This provider allows any user to log in without any authentication. It is useful for testing and development purposes.
```yaml title="config.yaml"
member:
provider: noauth
```
<ConfigurationTab options={configOptions} filter={{
"member.provider": 'noauth',
}} comments={false} />
:::danger
Do not use this provider in production environments unless you know exactly what you are doing. It allows anyone to log in and control neko as an admin.
@ -311,10 +313,9 @@ Currently, there are only two providers available for sessions: **memory** and *
Simply by specifying the `session.file` to a file path, the session provider will store the sessions in a file. Otherwise, the sessions are stored in memory and are lost when the server is restarted.
```yaml title="config.yaml"
session:
file: /opt/neko/sessions.json
```
<ConfigurationTab options={configOptions} filter={{
"session.file": '/opt/neko/sessions.json',
}} comments={false} />
:::info
In the future, we plan to add more session providers, such as Redis, PostgreSQL, etc. So the Configuration Options may change.
@ -324,10 +325,9 @@ In the future, we plan to add more session providers, such as Redis, PostgreSQL,
The API User is a special user that is used to authenticate the HTTP API requests. It cannot connect to the room, but it can perform administrative tasks. The API User does not have a password but only a token that is used to authenticate the requests. If the token is not set, the API User is disabled.
```yaml title="config.yaml"
session:
api_token: "apiToken"
```
<ConfigurationTab options={configOptions} filter={{
"session.api_token": '<secret_token>',
}} comments={false} />
:::tip
This user is useful in some situations when the rooms are generated by the server and the token is guaranteed to be random every time a short-lived room is run. It is not a good idea to define this token for long-lived rooms, as it can be stolen and used to perform administrative tasks.
@ -347,17 +347,15 @@ The authentication between the client and the server can be done using cookies o
If you disable the cookies, the token will be sent to the client in the login response and saved in local storage. This is less secure than using cookies, as the token **can be stolen using XSS attacks**. Therefore, it is recommended to use cookies.
:::
```yaml title="config.yaml"
session:
cookie:
enabled: true
name: "NEKO_SESSION"
expiration: "24h"
secure: true
http_only: true
domain: ""
path: ""
```
<ConfigurationTab options={configOptions} filter={[
'session.cookie.enabled',
'session.cookie.name',
'session.cookie.expiration',
'session.cookie.secure',
'session.cookie.http_only',
'session.cookie.domain',
'session.cookie.path'
]} comments={false} />
- <Def id="session.cookie.enabled" /> - Whether the cookies are enabled or not.
- <Def id="session.cookie.name" /> - Name of the cookie used to store the session.

View file

@ -3,6 +3,8 @@ description: Configuration related to Gstreamer capture in Neko.
---
import { Def, Opt } from '@site/src/components/Anchor';
import { ConfigurationTab } from '@site/src/components/Configuration';
import configOptions from './help.json';
# Audio & Video Capture
@ -27,22 +29,19 @@ All video pipelines must use the same video codec (defined in the <Opt id="video
The Gstreamer pipeline is started when the first client requests the video stream and is stopped after the last client disconnects.
```yaml title="config.yaml"
capture:
video:
display: "<display_name>"
codec: "vp8" # default video codec
ids: [ <pipeline_id1>, <pipeline_id2>, ... ]
pipelines:
<pipeline_id1>: <pipeline_config>
<pipeline_id2>: <pipeline_config>
...
```
<ConfigurationTab options={configOptions} filter={[
"capture.video.display",
"capture.video.codec",
"capture.video.ids",
"capture.video.pipeline",
"capture.video.pipelines",
]} comments={false} />
- <Def id="video.display" /> is the name of the [X display](https://www.x.org/wiki/) that you want to capture. If not specified, the environment variable `DISPLAY` will be used.
- <Def id="video.codec" /> available codecs are `vp8`, `vp9`, `av1`, `h264`. [Supported video codecs](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/WebRTC_codecs#supported_video_codecs) are dependent on the WebRTC implementation used by the client, `vp8` and `h264` are supported by all WebRTC implementations.
- <Def id="video.ids" /> is a list of pipeline ids that are defined in the <Opt id="video.pipelines" /> section. The first pipeline in the list will be the default pipeline.
- <Def id="video.pipelines" /> is a dictionary of pipeline configurations. Each pipeline configuration is defined by a unique pipeline id. They can be defined in two ways: either by building the pipeline dynamically using [Expression-Driven Configuration](#video.expression) or by defining the pipeline using a [Gstreamer Pipeline Description](#video.pipeline).
- <Def id="video.pipeline" /> is a shorthand for defining [Gstreamer pipeline description](#video.gst_pipeline) for a single pipeline. This is option is ignored if <Opt id="video.pipelines" /> is defined.
- <Def id="video.pipelines" /> is a dictionary of pipeline configurations. Each pipeline configuration is defined by a unique pipeline id. They can be defined in two ways: either by building the pipeline dynamically using [Expression-Driven Configuration](#video.expression) or by defining the pipeline using a [Gstreamer Pipeline Description](#video.gst_pipeline).
### Expression-Driven Configuration {#video.expression}
@ -151,7 +150,7 @@ import TabItem from '@theme/TabItem';
</details>
### Gstreamer Pipeline Description {#video.pipeline}
### Gstreamer Pipeline Description {#video.gst_pipeline}
If you want to define the pipeline using a [Gstreamer pipeline description](https://gstreamer.freedesktop.org/documentation/tools/gst-launch.html?gi-language=c#pipeline-description), you can do so by setting the <Def id="video.pipelines.gst_pipeline" /> parameter.
@ -169,7 +168,7 @@ Since now you have to define the whole pipeline, you need to specify the src ele
Your typical pipeline string would look like this:
```
ximagesrc display-name={display} show-pointer=true use-damage=false ! <your_elements> ! appsink name=appsink"
ximagesrc display-name={display} show-pointer=true use-damage=false ! <your_elements> ! appsink name=appsink
```
See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentation/ximagesrc/index.html) and [appsink](https://gstreamer.freedesktop.org/documentation/app/appsink.html) for more information.
@ -258,13 +257,11 @@ Only one audio pipeline can be defined in neko. The audio pipeline is used to ca
The Gstreamer pipeline is started when the first client requests the video stream and is stopped after the last client disconnects.
```yaml title="config.yaml"
capture:
audio:
device: "audio_output.monitor" # default audio device
codec: "opus" # default audio codec
pipeline: "<gstreamer_pipeline>"
```
<ConfigurationTab options={configOptions} filter={[
"capture.audio.device",
"capture.audio.codec",
"capture.audio.pipeline",
]} comments={false} />
- <Def id="audio.device" /> is the name of the [pulseaudio device](https://wiki.archlinux.org/title/PulseAudio/Examples) that you want to capture. If not specified, the default audio device will be used.
- <Def id="audio.codec" /> available codecs are `opus`, `g722`, `pcmu`, `pcma`. [Supported audio codecs](https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/WebRTC_codecs#supported_audio_codecs) are dependent on the WebRTC implementation used by the client, `opus` is supported by all WebRTC implementations.
@ -293,23 +290,21 @@ Neko allows you to broadcast out-of-the-box the display and audio capture to a t
The Gstreamer pipeline is started when the broadcast is started and is stopped when the broadcast is stopped regardless of the clients connected.
```yaml title="config.yaml"
capture:
broadcast:
audio_bitrate: 128 # in KB/s
video_bitrate: 4096 # in KB/s
preset: "veryfast"
pipeline: "<gstreamer_pipeline>"
url: "rtmp://<server>/<application>/<stream_key>"
autostart: true
```
<ConfigurationTab options={configOptions} filter={[
"capture.broadcast.audio_bitrate",
"capture.broadcast.video_bitrate",
"capture.broadcast.preset",
"capture.broadcast.pipeline",
"capture.broadcast.url",
"capture.broadcast.autostart",
]} comments={false} />
The default encoder uses `h264` for video and `aac` for audio, muxed in the `flv` container and sent over the `rtmp` protocol. You can change the encoder settings by setting a custom Gstreamer pipeline description in the <Opt id="broadcast.pipeline" /> parameter.
- <Def id="broadcast.audio_bitrate" /> and <Def id="broadcast.video_bitrate" /> are the bitrate settings for the default audio and video encoders expressed in kilobits per second.
- <Def id="broadcast.preset" /> is the encoding speed preset for the default video encoder. See available presets [here](https://gstreamer.freedesktop.org/documentation/x264/index.html?gi-language=c#GstX264EncPreset).
- <Def id="broadcast.pipeline" /> when set, encoder settings above are ignored and the custom Gstreamer pipeline description is used. In the pipeline, you can use `{display}`, `{device}` and `{url}` as placeholders for the X display name, pulseaudio audio device name, and broadcast URL respectively.
- <Def id="broadcast.url" /> is the URL of the RTMP server where the broadcast will be sent. This can be set later using the API if the URL is not known at the time of configuration or is expected to change.
- <Def id="broadcast.url" /> is the URL of the RTMP server where the broadcast will be sent e.g. `rtmp://<server>/<application>/<stream_key>`. This can be set later using the API if the URL is not known at the time of configuration or is expected to change.
- <Def id="broadcast.autostart" /> is a boolean value that determines whether the broadcast should start automatically when neko starts, works only if the URL is set.
<details>
@ -380,14 +375,12 @@ This is a fallback mechanism and should not be used as a primary video stream be
The Gstreamer pipeline is started in the background when the first client requests the screencast and is stopped after a period of inactivity.
```yaml title="config.yaml"
capture:
screencast:
enabled: true
rate: "10/1"
quality: 60
pipeline: "<gstreamer_pipeline>"
```
<ConfigurationTab options={configOptions} filter={[
"capture.screencast.enabled",
"capture.screencast.rate",
"capture.screencast.quality",
"capture.screencast.pipeline",
]} comments={false} />
- <Def id="screencast.enabled" /> is a boolean value that determines whether the screencast is enabled or not.
- <Def id="screencast.rate" /> is the framerate of the screencast. It is expressed as a fraction of frames per second, for example, `10/1` means 10 frames per second.
@ -422,14 +415,12 @@ Neko allows you to capture the webcam on the client machine and send it to the s
The Gstreamer pipeline is started when the client shares their webcam and is stopped when the client stops sharing the webcam. Maximum one webcam pipeline can be active at a time.
```yaml title="config.yaml"
capture:
webcam:
enabled: true
device: "/dev/video0" # default webcam device
width: 640
height: 480
```
<ConfigurationTab options={configOptions} filter={[
"capture.webcam.enabled",
"capture.webcam.device",
"capture.webcam.width",
"capture.webcam.height",
]} comments={false} />
- <Def id="webcam.enabled" /> is a boolean value that determines whether the webcam capture is enabled or not.
- <Def id="webcam.device" /> is the name of the [video4linux device](https://www.kernel.org/doc/html/v4.12/media/v4l-drivers/index.html) that will be used as a virtual webcam.
@ -463,12 +454,10 @@ Neko allows you to capture the microphone on the client machine and send it to t
The Gstreamer pipeline is started when the client shares their microphone and is stopped when the client stops sharing the microphone. Maximum one microphone pipeline can be active at a time.
```yaml title="config.yaml"
capture:
microphone:
enabled: true
device: "audio_input"
```
<ConfigurationTab options={configOptions} filter={[
"capture.microphone.enabled",
"capture.microphone.device",
]} comments={false} />
- <Def id="microphone.enabled" /> is a boolean value that determines whether the microphone capture is enabled or not.
- <Def id="microphone.device" /> is the name of the [pulseaudio device](https://wiki.archlinux.org/title/PulseAudio/Examples) that will be used as a virtual microphone.

View file

@ -3,6 +3,8 @@ description: Configuration related to the Desktop Environment in Neko.
---
import { Def, Opt } from '@site/src/components/Anchor';
import { ConfigurationTab } from '@site/src/components/Configuration';
import configOptions from './help.json';
# Desktop Environment
@ -10,18 +12,15 @@ This section describes how to configure the desktop environment inside neko.
Neko uses the [X Server](https://www.x.org/archive/X11R7.6/doc/man/man1/Xserver.1.xhtml) as the display server with [Openbox](http://openbox.org/wiki/Main_Page) as the default window manager. For audio, [PulseAudio](https://www.freedesktop.org/wiki/Software/PulseAudio/) is used.
```yaml title="config.yaml"
desktop:
display: "<display>"
screen: "1280x720@30" # default
```
<ConfigurationTab options={configOptions} filter={[
'desktop.display',
'desktop.screen'
]} comments={false} />
- <Def id="display" /> refers to the X server that is running on the system. If it is not specified, the environment variable `DISPLAY` is used. The same display is referred to in the [Capture](capture#video.display) configuration to capture the screen. In most cases, we want to use the same display for both.
- <Def id="screen" /> refers to the screen resolution and refresh rate. The format is `<width>x<height>@<refresh rate>`. If not specified, the default is `1280x720@30`.
:::tip
You can specify the screen resolution using the environment variable `NEKO_DESKTOP_SCREEN`.
Admin can change the resolution in the GUI.
:::
@ -33,12 +32,10 @@ Neko uses the [XTEST Extension Library](https://www.x.org/releases/X11R7.7/doc/l
Currently, only touchscreens are supported through the custom driver.
:::
```yaml title="config.yaml"
desktop:
input:
enabled: true # default
socket: "/tmp/xf86-input-neko.sock" # default
```
<ConfigurationTab options={configOptions} filter={[
'desktop.input.enabled',
'desktop.input.socket'
]} comments={false} />
- <Def id="input.enabled" /> enables the input device support. If not specified, the default is `false`.
- <Def id="input.socket" /> refers to the socket file that the custom driver creates. If not specified, the default is `/tmp/xf86-input-neko.sock`.
@ -51,10 +48,9 @@ When using Docker, the custom driver is already included in the image and the so
Most of the time, only a single application is used in the minimal desktop environment without any taskbar or desktop icons. It could happen that the user accidentally minimizes the application and then it is not possible to restore it. To prevent this, we can use the `unminimize` feature that simply listens for the minimize event and restores the window back to the original state.
```yaml title="config.yaml"
desktop:
unminimize: true # default
```
<ConfigurationTab options={configOptions} filter={[
'desktop.unminimize'
]} comments={false} />
## Upload Drop {#upload_drop}
@ -62,10 +58,9 @@ The upload drop is a feature that allows the user to upload files to the applica
The current approach is to catch the drag and drop events on the client side, upload them to the server along with the coordinates of the drop event, and then open an invisible overlay window on the server that has set the file path to the uploaded file and allows it to be dragged and dropped into the application. Then the mouse events are simulated to drag the file from the overlay window to the application window.
```yaml title="config.yaml"
desktop:
upload_drop: true # default
```
<ConfigurationTab options={configOptions} filter={[
'desktop.upload_drop'
]} comments={false} />
## File Chooser Dialog {#file_chooser_dialog}
@ -77,7 +72,6 @@ The file chooser dialog is a feature that allows handling the file chooser dialo
The current approach is to put the file chooser dialog in the background as soon as it is displayed, prompt the user to upload the file, and then select this file in the file chooser dialog by simulating the keyboard events to navigate to the file and press the open button. **This is very error-prone and may not work as expected.**
```yaml title="config.yaml"
desktop:
file_chooser_dialog: false # default
```
<ConfigurationTab options={configOptions} filter={[
'desktop.file_chooser_dialog'
]} comments={false} />

View file

@ -3,17 +3,18 @@ description: Configuration related to the Neko plugins.
---
import { Def, Opt } from '@site/src/components/Anchor';
import { ConfigurationTab } from '@site/src/components/Configuration';
import configOptions from './help.json';
# Plugins Configuration
Neko allows you to extend its functionality by using [plugins](https://pkg.go.dev/plugin). Go plugins come with a lot of benefits as well as some limitations. The main advantage is that you can extend the functionality of the application without recompiling the main application. But the main limitation is that you need to use the same Go version and all dependencies with the same version as the main application.
```yaml title="config.yaml"
plugins:
enabled: true
required: true
dir: "./bin/plugins"
```
<ConfigurationTab options={configOptions} filter={[
'plugins.enabled',
'plugins.required',
'plugins.dir',
]} comments={false} />
- <Def id="enabled" /> enables the plugin support. If set to `false`, the plugins are not loaded.
- <Def id="required" /> makes the plugin loading mandatory, meaning that if a plugin fails to load, the application will not start.
@ -29,10 +30,9 @@ There exist a few pre-loaded internal plugins that are shipped with Neko:
The chat plugin is a simple pre-loaded internal plugin that allows you to chat with other users in the same session. The chat messages are sent to the server and then broadcasted to all users in the same session.
```yaml title="config.yaml"
chat:
enabled: true
```
<ConfigurationTab options={{
'chat.enabled': true
}} />
- <Def id="chat.enabled" /> enables the chat support. If set to `false`, the chat is disabled.
@ -51,12 +51,15 @@ plugins:
The file transfer plugin is a simple pre-loaded internal plugin that allows you to transfer files between the client and the server. The files are uploaded to the server and then downloaded by the client.
```yaml title="config.yaml"
filetransfer:
enabled: true
dir: "./uploads"
refresh_interval: 30s
```
<ConfigurationTab options={{
'filetransfer.enabled': true,
'filetransfer.dir': './uploads',
'filetransfer.refresh_interval': {
type: 'duration',
defaultValue: '30s',
},
}} />
- <Def id="filetransfer.enabled" /> enables the file transfer support. If set to `false`, the file transfer is disabled.
- <Def id="filetransfer.dir" /> refers to the directory where the files are stored.
@ -70,4 +73,3 @@ plugins:
```
- `filetransfer.enabled` in the room settings context controls whether the file transfer is enabled for any user in the room, and in the user's profile context controls whether the user can transfer files.

View file

@ -3,6 +3,8 @@ description: Configuration related to the WebRTC and Networking in Neko.
---
import { Def, Opt } from '@site/src/components/Anchor';
import { ConfigurationTab } from '@site/src/components/Configuration';
import configOptions from './help.json';
# WebRTC Configuration
@ -18,19 +20,17 @@ ICE, which stands for Interactive Connectivity Establishment, is a protocol used
ICE Trickle is a feature that allows ICE candidates to be sent as they are discovered, rather than waiting for all candidates to be discovered before sending them. It means that the ICE connection can be established faster as the server can start connecting to the client as soon as it has a few ICE candidates and doesn't have to wait for all of them to be discovered.
```yaml title="config.yaml"
webrtc:
icetrickle: false
```
<ConfigurationTab options={configOptions} filter={[
'webrtc.icetrickle'
]} comments={false} />
### ICE Lite {#icelite}
ICE Lite is a minimal implementation of the ICE protocol intended for servers running on a public IP address. It is not enabled by default to allow more complex ICE configurations out of the box.
```yaml title="config.yaml"
webrtc:
icelite: false
```
<ConfigurationTab options={configOptions} filter={[
'webrtc.icelite'
]} comments={false} />
:::info
When using ICE Servers, ICE Lite must be disabled.
@ -100,16 +100,10 @@ import TabItem from '@theme/TabItem';
The ICE servers are divided into two groups:
```yaml title="config.yaml"
webrtc:
iceservers:
frontend:
# List of ICE Server configurations as described above
- urls: "stun:stun.l.google.com:19302"
backend:
# List of ICE Server configurations as described above
- urls: "stun:stun.l.google.com:19302"
```
<ConfigurationTab options={configOptions} filter={[
'webrtc.iceservers.frontend',
'webrtc.iceservers.backend'
]} />
- <Def id="iceservers.frontend" /> - ICE servers that are sent to the client and used to establish a connection between the client and the server.
- <Def id="iceservers.backend" /> - ICE servers that are used by the server to gather ICE candidates. They might contain private IP addresses or other sensitive information that should not be sent to the client.
@ -162,15 +156,14 @@ There exist two types of connections:
The ephemeral UDP port range can be configured using the following configuration:
```yaml title="config.yaml"
webrtc:
epr: "59000-59100"
```
<ConfigurationTab options={configOptions} filter={{
'webrtc.epr': "59000-59100"
}} comments={false} />
The range `59000-59100` contains 101 ports, which should be open on the server's firewall. The server uses these ports to establish a connection with the client. You can specify a different range of ports if needed, with fewer or more ports, depending on the number of simultaneous connections you expect.
:::tip
You can specify the ephemeral UDP port range as an environment variable in the `docker-compose.yaml` file using the `NEKO_WEBRTC_EPR` environment variable. When using docker, make sure to expose the ports in the `docker-compose.yaml`.
:::tip Make sure
When specifying the ephemeral UDP port range in `docker-compose.yaml`, make sure to use the same range for ports **as UDP**.
```yaml title="docker-compose.yaml"
environment:
@ -186,19 +179,18 @@ It is important to expose the same ports to the host machine, without any remapp
The UDP/TCP multiplexing port can be configured using the following configuration:
```yaml title="config.yaml"
webrtc:
udpmux: 59000
tcpmux: 59000
```
<ConfigurationTab options={configOptions} filter={{
'webrtc.udpmux': 59000,
'webrtc.tcpmux': 59000
}} comments={false} />
- <Def id="udpmux" /> - The port used for UDP connections.
- <Def id="tcpmux" /> - The port used for TCP connections.
The server uses only port `59000` for both UDP and TCP connections. This port should be open on the server's firewall. You can specify a different port if needed, or specify only one of the two protocols. UDP is generally better for latency, but some networks block UDP so it is good to have TCP available as a fallback.
:::tip
You can specify the UDP/TCP multiplexing port as an environment variable in the `docker-compose.yaml` file using the `NEKO_WEBRTC_TCPMUX` and `NEKO_WEBRTC_UDPMUX` environment variables. When using docker, make sure to expose the ports in the `docker-compose.yaml`.
:::tip Make sure
When specifying the UDP/TCP multiplexing port in `docker-compose.yaml`, make sure to correctly specify the protocol in the ports section.
```yaml title="docker-compose.yaml"
environment:
@ -217,42 +209,20 @@ It is important to expose the same ports to the host machine, without any remapp
The server IP address is sent to the client in ICE candidates so that the client can establish a connection with the server. By default, the server IP address is automatically resolved by the server to the public IP address of the server. If the server is behind a NAT, you want to specify a different IP address or use neko only in a local network, you can specify the server IP address manually.
#### NAT 1-to-1 {#nat1to1}
<ConfigurationTab options={configOptions} filter={{
'webrtc.nat1to1': '10.10.0.5'
}} comments={false} />
```yaml title="config.yaml"
webrtc:
nat1to1:
# IPv4 address of the server
- 10.10.0.5
# IPv6 address of the server
- 2001:db8:85a3::8a2e:370:7334
```
Currently, only one IPv4 and one IPv6 address can be specified. Therefore if you want to access your instance from both local and public networks, your router must support [NAT loopback (hairpinning)](https://en.wikipedia.org/wiki/Network_address_translation#NAT_hairpinning).
:::tip
You can specify the server IP address as an environment variable in the `docker-compose.yaml` file using the `NEKO_WEBRTC_NAT1TO1` environment variable.
```yaml title="docker-compose.yaml"
environment:
NEKO_WEBRTC_NAT1TO1: "10.10.0.5"
```
If you want to specify also an IPv6 address, use whitespace to separate the addresses.
```yaml title="docker-compose.yaml"
environment:
NEKO_WEBRTC_NAT1TO1: "10.10.0.5 2001:db8:85a3::8a2e:370:7334"
```
:::
Currently, only one address can be specified. Therefore if you want to access your instance from both local and public networks, your router must support [NAT loopback (hairpinning)](https://en.wikipedia.org/wiki/Network_address_translation#NAT_hairpinning).
#### IP Retrieval URL {#ip_retrieval_url}
If you do not specify the server IP address, the server will try to resolve the public IP address of the server automatically.
```yaml title="config.yaml"
webrtc:
ip_retrieval_url: "https://checkip.amazonaws.com"
```
<ConfigurationTab options={configOptions} filter={[
'webrtc.ip_retrieval_url'
]} comments={false} />
The server will send an HTTP GET request to the specified URL to retrieve the public IP address of the server.
## Bandwidth Estimator {#estimator}
@ -263,29 +233,6 @@ The bandwidth estimator is an experimental feature and might not work as expecte
The bandwidth estimator is a feature that allows the server to estimate the available bandwidth between the client and the server. It is used to switch between different video qualities based on the available bandwidth. The bandwidth estimator is disabled by default.
```yaml title="config.yaml"
webrtc:
estimator:
# Whether to enable the bandwidth estimator
enabled: false
# Whether the bandwidth estimator is passive - only used for logging and not for actual decisions
passive: false
# Enable debug logging for the bandwidth estimator (will print the current state and decisions)
debug: false
# Initial bitrate for the bandwidth estimator to start with (in bps)
initial_bitrate: 1000000
# How often to read and process bandwidth estimation reports
read_interval: "2s"
# How long to wait for a stable connection (upward or neutral trend) before upgrading
stable_duration: "12s"
# How long to wait for a stalled connection (neutral trend with low bandwidth) before downgrading
unstable_duration: "6s"
# How long to wait for stalled bandwidth estimation before downgrading
stalled_duration: "24s"
# How long to wait before downgrading again after the previous downgrade
downgrade_backoff: "10s"
# How long to wait before upgrading again after the previous upgrade
upgrade_backoff: "5s"
# How much bigger the difference between estimated and stream bitrate must be to trigger a change
diff_threshold: 0.15
```
<ConfigurationTab options={configOptions} filter={[
'webrtc.estimator'
]} comments={true} />

View file

@ -58,9 +58,9 @@ See the V3 configuration options for the [WebRTC Video](/docs/v3/configuration/c
| `NEKO_VP8=true` *deprecated* | `NEKO_CAPTURE_VIDEO_CODEC=vp8` |
| `NEKO_VP9=true` *deprecated* | `NEKO_CAPTURE_VIDEO_CODEC=vp9` |
| `NEKO_VIDEO` | `NEKO_CAPTURE_VIDEO_PIPELINE`, V3 allows multiple video pipelines |
| `NEKO_VIDEO_BITRATE` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.pipeline) instead |
| `NEKO_HWENC` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.pipeline) instead |
| `NEKO_MAX_FPS` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.pipeline) instead |
| `NEKO_VIDEO_BITRATE` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.gst_pipeline) instead |
| `NEKO_HWENC` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.gst_pipeline) instead |
| `NEKO_MAX_FPS` | **removed**, use [custom pipeline](/docs/v3/configuration/capture#video.gst_pipeline) instead |
:::warning Limitation
@ -133,10 +133,10 @@ See the V3 configuration options for the [WebRTC](/docs/v3/configuration/webrtc)
Here is a full list of all the configuration options available in Neko V2 that are still available in Neko V3 with legacy support enabled.
import Configuration from '@site/src/components/Configuration';
import { ConfigurationTab } from '@site/src/components/Configuration';
import configOptions from './help.json';
<Configuration configOptions={configOptions} />
<ConfigurationTab options={configOptions} heading={true} />
See the full [V3 configuration reference](/docs/v3/configuration/#full) for more details.

View file

@ -3,130 +3,244 @@ import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
interface ConfigOption {
key: string[];
description: string;
defaultValue?: string;
interface ConfigOptionValue {
type?: string;
description?: string;
defaultValue?: string;
}
interface ConfigurationTabProps {
configOptions: ConfigOption[];
interface ConfigOption extends ConfigOptionValue {
key: string[];
}
const ConfigurationTab: React.FC<ConfigurationTabProps> = ({ configOptions }) => {
const environmentVariables = () => {
let code = '';
configOptions.forEach(option => {
let value = ""
if (option.defaultValue) {
value = `"${option.defaultValue}"`
} else if (option.type) {
value = `<${option.type}>`
}
code += `# ${option.description}\n`;
code += `NEKO_${option.key.join('_').toUpperCase()}: ${value}\n`;
});
return (
<CodeBlock language="yaml">
{code}
</CodeBlock>
);
function configKey(key: string | string[], value: ConfigOptionValue | any): ConfigOption {
if (typeof key === 'string') {
key = key.split('.');
}
if (typeof value === 'object') {
return {
key,
type: value.type || getType(value.defaultValue),
description: value.description,
defaultValue: value.defaultValue,
}
} else {
return {
key,
type: getType(value),
defaultValue: value,
}
}
}
function configKeys(optValues: Record<string, ConfigOptionValue | any>): ConfigOption[] {
let options: ConfigOption[] = [];
Object.entries(optValues).forEach(([key, value]) => {
options.push(configKey(key, value));
});
return options;
}
function filterKeys(options: ConfigOption[], filter: string): ConfigOption[] {
return options.filter(option => {
const key = option.key.join('.');
return key.startsWith(filter)
});
}
function defaultValue(value: ConfigOptionValue): string {
switch (value.type) {
case 'boolean':
return `${value.defaultValue || false}`;
case 'int':
case 'float':
case 'number':
return `${value.defaultValue || 0}`;
case 'duration':
case 'string':
return `${value.defaultValue ? `"${value.defaultValue}"` : '<string>'}`;
case 'strings':
return '<comma-separated list of strings>';
case 'object':
return '<json encoded object>';
case 'array':
return '<json encoded array>';
default:
return value.type ? `<${value.type}>` : '';
}
}
function getType(value: any): string {
if (Array.isArray(value)) {
return 'array';
}
return typeof value;
}
export function EnvironmentVariables({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) {
if (typeof comments === 'undefined') {
comments = true;
}
const cmdArguments = () => {
let code = '';
configOptions.forEach(option => {
code += `# ${option.description}\n`;
code += `--${option.key.join('.')}`;
if (option.type) {
code += ` <${option.type}>`;
}
code += '\n';
});
return (
<CodeBlock language="shell">
{code}
</CodeBlock>
);
let code = '';
options.forEach(option => {
const description = option.description ? option.description : '';
const type = option.type ? ` (${option.type})` : '';
if (comments && description) {
code += `# ${description}${type}\n`;
}
code += `NEKO_${option.key.join('_').toUpperCase()}=${defaultValue(option)}\n`;
});
return (
<CodeBlock language="shell" {...props}>
{code}
</CodeBlock>
);
}
export function CommandLineArguments({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) {
if (typeof comments === 'undefined') {
comments = true;
}
const yamlFile = () => {
const final = Symbol('final');
const buildYaml = (obj, prefix = '') => {
let code = '';
Object.keys(obj).forEach(key => {
const value = obj[key];
if (typeof value === 'object' && !Array.isArray(value) && !value[final]) {
code += prefix+`${key}:\n`;
code += buildYaml(value, prefix + ' ');
let code = '';
options.forEach(option => {
const description = option.description ? option.description : '';
const type = option.type ? ` (${option.type})` : '';
if (comments && description) {
code += `# ${description}${type}\n`;
}
code += `--${option.key.join('.')} ${defaultValue(option)}\n`;
});
return (
<CodeBlock language="shell" {...props}>
{code}
</CodeBlock>
);
}
export function YamlFileContent({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) {
if (typeof comments === 'undefined') {
comments = true;
}
const final = Symbol('final');
const buildYaml = (obj: Record<string, any>, prefix = '') => {
let code = '';
Object.entries(obj).forEach(([key, option]) => {
if (typeof option === 'object' && !Array.isArray(option) && !option[final]) {
code += prefix+`${key}:\n`;
code += buildYaml(option, prefix + ' ');
} else {
const description = option.description ? option.description : '';
const type = option.type ? ` (${option.type})` : '';
if (comments && description) {
code += `${prefix}# ${description}${type}\n`;
}
let value: string;
if (option.type === 'strings') {
value = option.defaultValue ? `[ "${option.defaultValue}" ]` : '[ <string> ]';
} else if (option.type === 'object') {
value = "{}"
} else if (option.type === 'array') {
value = "[]"
} else {
let val = '';
switch (value.type) {
case 'boolean':
val = `${value.defaultValue || false}`;
break;
case 'int':
case 'float':
val = `${value.defaultValue || 0}`;
break;
case 'strings':
val = `[ ${value.defaultValue ? value.defaultValue.map(v => `"${v}"`).join(', ') : '<string>'} ]`;
break;
case 'duration':
case 'string':
val = `${value.defaultValue ? `"${value.defaultValue}"` : '<string>'}`;
break;
default:
val = `<${value.type}>`;
break;
}
code += prefix+`# ${value.description || ''}\n`;
code += prefix+`${key}: ${val}\n`;
value = defaultValue(option);
}
});
return code;
};
code += `${prefix}${key}: ${value}\n`;
}
});
return code;
};
const yamlCode = buildYaml(configOptions.reduce((acc, option) => {
const keys = option.key;
let current = acc;
keys.forEach((key, index) => {
if (!current[key]) {
current[key] = index === keys.length - 1 ? option : {};
}
current = current[key];
});
current[final] = true;
return acc;
}, {}));
const yamlCode = buildYaml(options.reduce((acc, option) => {
const keys = option.key;
let current = acc;
keys.forEach((key, index) => {
if (!current[key]) {
current[key] = index === keys.length - 1 ? option : {};
}
current = current[key];
});
current[final] = true;
return acc;
}, {}));
return (
<CodeBlock language="yaml">
{yamlCode}
</CodeBlock>
);
return (
<CodeBlock language="yaml" {...props}>
{yamlCode}
</CodeBlock>
);
}
type ConfigurationTabProps = {
options?: ConfigOption[] | Record<string, ConfigOptionValue | any>;
heading?: boolean;
comments?: boolean;
filter?: string | string[] | Record<string, ConfigOptionValue | any>;
};
export function ConfigurationTab({ options, heading, comments, filter, ...props }: ConfigurationTabProps) {
var configOptions: ConfigOption[] = [];
if (Array.isArray(options)) {
configOptions = options;
} else {
configOptions = configKeys(options)
}
if (typeof comments === 'undefined') {
comments = true;
}
if (typeof heading === 'undefined') {
heading = false;
}
if (Array.isArray(filter)) {
let filteredOptions: ConfigOption[] = [];
for (const f of filter) {
filteredOptions = [ ...filteredOptions, ...filterKeys(configOptions, f) ];
}
configOptions = filteredOptions;
} else if (typeof filter === 'string') {
configOptions = filterKeys(configOptions, filter);
} else if (typeof filter === 'object') {
let filteredOptions: ConfigOption[] = [];
for (const k in filter) {
let filtered = configOptions.find(option => {
return option.key.join('.') === k;
});
let replaced = configKey(k, filter[k]);
filteredOptions = [ ...filteredOptions, { ...filtered, ...replaced } ];
}
configOptions = filteredOptions;
}
return (
<div>
<Tabs>
<TabItem value="env" label="Environment Variables">
<Tabs groupId="configuration" defaultValue="yaml" values={[
{ label: 'YAML Configuration File', value: 'yaml' },
{ label: 'Environment Variables', value: 'env' },
{ label: 'Command Line Arguments', value: 'args' },
]} {...props}>
<TabItem value="env" label="Environment Variables">
{heading && (
<p>You can set the following environment variables in your <code>docker-compose.yaml</code> file or in your shell environment.</p>
{environmentVariables()}
</TabItem>
<TabItem value="args" label="Command Line Arguments">
)}
{EnvironmentVariables({ options: configOptions, comments })}
</TabItem>
<TabItem value="args" label="Command Line Arguments">
{heading && (
<p>You can list the following command line arguments using <code>neko serve --help</code>.</p>
{cmdArguments()}
</TabItem>
<TabItem value="yaml" label="YAML Configuration File">
)}
{CommandLineArguments({ options: configOptions, comments })}
</TabItem>
<TabItem value="yaml" label="YAML Configuration File">
{heading && (
<p>You can create a <code>/etc/neko/neko.yaml</code> file with the following configuration options.</p>
{yamlFile()}
</TabItem>
</Tabs>
</div>
)}
{YamlFileContent({ options: configOptions, comments })}
</TabItem>
</Tabs>
);
};
export default ConfigurationTab;
}