Files
neko/webpage/docs/configuration/plugins.md
T
SamAcctX c75a2f60b5 server: sanitised session ids, chat length limit, websocket read limit, leaky frame queues (#712)
* server: generated video pipelines keep at most one raw frame queued

GetPipeline used plain `queue` elements, which buffer up to 1s / 200
buffers. When the encoder cannot keep up, frames pile up there and the
stream (and every reaction to input) falls behind by up to that much.
The queues now hold only the newest frame (leaky=downstream): a slow
encoder drops frames instead of adding latency.

Measured input-to-display latency (Alt+F2/Escape opens/closes a dialog,
time to the first changed received frame; xfce image built with ./build,
1920x1080, default VP8 pipeline, 30 trials per run, runs interleaved):

                  before p50/p90 ms     after p50/p90 ms
  no CPU limit    268/303, 188/225      183/217, 157/226
  --cpus=1        270/422, 362/673      155/237, 200/428

fps unchanged without a limit (25), slightly higher with one (22.5 ->
23.5-24). Pipelines given as raw gst strings are unchanged.

Tests: TestVideoConfigGetPipelineQueues; client e2e on the built image.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* server: session ids no longer carry the login name verbatim

The multiuser and noauth providers built the session id as
"<username>-<token>". The v3 API addresses sessions and members by id
in the URL path (DELETE /api/sessions/{id}, POST /api/members/{id},
POST /api/room/control/give/{id}), so a login name like
"x/../../logout?" produced an id that sent an admin's unencoded
request to another endpoint, and even when the client encodes it, an
id with "/" never matches the chi route: such a member could not be
kicked, muted or banned.

utils.SafeID maps a name to letters and digits of any script plus
".", "_" and "-": every run of other characters becomes one "-",
leading and trailing "-" and "." are trimmed, the result is cut to 64
characters, and an empty result becomes "user". Nothing with a meaning
in a URL path survives; letters outside ASCII are kept so names such as
"Müller" stay recognisable (clients percent-encode ids in paths). Both
providers now use SafeID(username) + "-" + token; the profile name
keeps the name as typed, so nothing changes on screen.

The file and object providers use the username itself as the member
id. Members there are created explicitly by an admin, so
POST /api/members now rejects a name SafeID would change with a 400
instead of silently altering it. The oauth provider derives ids from
the IdP subject ("oauth:" + sub) and is unchanged.

Tests: TestSafeID (pkg/utils).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* server: chat plugin limits message length (chat.max_length)

The chat plugin accepted text of any size, over the websocket
(chat/message) and over REST (POST /api/chat), and broadcast it to
every session. The web client caps its input box at 512 characters,
but any client can send more, and one large message stalls every
viewer's renderer.

New option chat.max_length (default 512, counted in characters with
utf8.RuneCountInString, not bytes; 0 disables the limit). Both paths
go through one check: REST answers 400 with the length and the limit,
the websocket handler logs at warn level and drops the message, as it
already does for a sender without permission. The default never
rejects what the web client can type: its maxlength counts UTF-16
units, which is never fewer than runes.

Docs: plugins.md and release notes. help.txt/help.json list no plugin
options (no chat.*, filetransfer.*, openinapp.* entries), so nothing
was added there.

Tests: TestCheckLength (internal/plugins/chat).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* server: limit the size of websocket frames a client may send

There was no read limit on the websocket, so a client could push frames
of any size; they were read and decoded in full before a handler (such
as the chat length check) could reject them. Frames above 1 MiB now end
the connection, as gorilla's SetReadLimit does. The largest legitimate
messages, an SDP answer and pasted clipboard text, stay far below that.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* server: video RTP timestamps follow the time between captured frames

ximagesrc gives a frame it captures late a buffer duration of only the
time left until the next frame slot ("Frame duration is from now until
the next expected capture time"), and frames dropped before the encoder
leave gaps. pion advances the RTP clock by Sample.Duration, so whenever
the encoder falls behind, the stream's clock runs slower than real
time. Browsers grow their jitter buffer to make up for it and lip sync
drifts. This happens with any queue in front of the encoder.

Each video sample's duration is now the gap since the previous sample's
PTS. The gap is known only when the next frame arrives, so it is
applied one frame late; the clock itself stays right. The sink also
counts frames in neko_capture_streamsink_frames: a rate below the
configured fps means the encoder falls behind.

Measured on the firefox image built with ./build, 1080p25 VP8, the
container limited to 0.5 CPU: RTP clock 0.2-0.9x real time before,
1.00x after; browser video jitter buffer 0.24-1.9 s before, 60-220 ms
after; audio up to 0.4 s (default queue) or 0.74 s (one-frame leaky
queue) ahead of the video before, within -70..+150 ms after.

Tests: TestSampleDuration; go test -race ./internal/capture.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:09:43 +02:00

4.7 KiB

description
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. 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.

<ConfigurationTab options={configOptions} filter={[ 'plugins.enabled', 'plugins.required', 'plugins.dir', ]} comments={false} />

  • enables the plugin support. If set to false, the plugins are not loaded.
  • makes the plugin loading mandatory, meaning that if a plugin fails to load, the application will not start.
  • refers to the directory where the plugins are stored.

:::danger External plugins are experimental and may not work as expected. They will be replaced with hashicorp/go-plugin in the future. :::

There exist a few pre-loaded internal plugins that are shipped with Neko:

Chat Plugin

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.

<ConfigurationTab options={{ 'chat.enabled': true, 'chat.max_length': 512, }} />

  • enables the chat support. If set to false, the chat is disabled.
  • is the longest accepted chat message, counted in characters (not bytes). Longer messages are rejected with 400 over the API and dropped over the websocket. Set to 0 to disable the limit.

The chat plugin extends user profile and room settings by adding the following fields:

plugins:
  chat.can_send: true
  chat.can_receive: true
  • chat.can_send in the room settings context controls whether the chat messages can be sent by any user in the room, and in the user's profile context controls whether the user can send chat messages.
  • chat.can_receive in the room settings context controls whether the chat messages can be received by any user in the room, and in the user's profile context controls whether the user can receive chat messages.

File Transfer Plugin

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.

<ConfigurationTab options={{ 'filetransfer.enabled': true, 'filetransfer.dir': './uploads', 'filetransfer.refresh_interval': { type: 'duration', defaultValue: '30s', }, 'filetransfer.user_download': false, 'filetransfer.user_upload': false, 'filetransfer.user_delete': false, }} />

  • enables the file transfer support. If set to false, the file transfer is disabled.
  • refers to the directory where the files are stored.
  • refers to the interval at which the file list is refreshed.
  • allows non-admin users to download files. If set to false (default), only admins can download files.
  • allows non-admin users to upload files. If set to false (default), only admins can upload files.
  • allows non-admin users to delete files. If set to false (default), only admins can delete files.

The file transfer plugin extends user profile and room settings by adding the following fields:

plugins:
  filetransfer.enabled: true
  • 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.

Open In App Plugin

The open in app plugin allows users to open links directly from the chat inside the shared neko instance.

<ConfigurationTab options={{ 'openinapp.enabled': true, 'openinapp.open_command': 'xdg-open', }} />

  • enables the open in app support. If set to false, the feature is hidden from all clients.
  • is the shell command used to open URLs inside the neko instance. Defaults to xdg-open. Can be set to a browser-specific command such as firefox -P default depending on the app image in use.