-### Features
-
- * Text Chat (With basic markdown support, discord flavor)
- * Admin users (Kick, Ban & Force Give/Release Controls, Lock room)
- * Clipboard synchronization (on [supported browsers](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/readText))
- * Emote overlay
- * Ignore user (chat and emotes)
- * Persistent settings
- * Automatic Login with custom url args. (add `?usr=&pwd=` to the url.)
- * Broadcasting room content using RTMP (to e.g. twitch or youtube...)
- * Bidirectional file transfer (if enabled)
-
-
-
-With `NEKO_FILE_TRANSFER_ENABLED=true`:
-
-
-
-
-### Why n.eko?
+### Why neko?
I like cats π± (`Neko` is the Japanese word for cat), I'm a weeb/nerd.
@@ -131,28 +147,26 @@ I like cats π± (`Neko` is the Japanese word for cat), I'm a weeb/nerd.
## Multiple rooms
-For n.eko room management software, visit [neko-rooms](https://github.com/m1k1o/neko-rooms).
+For neko room management software, visit [neko-rooms](https://github.com/m1k1o/neko-rooms).
-It also offers zero-knowledge [installation script (with HTTPS and Traefik)](https://github.com/m1k1o/neko-rooms/#zero-knowledge-installation-with-https-and-traefik).
+It also offers [Zero-knowledge installation (with HTTPS)](https://github.com/m1k1o/neko-rooms/?tab=readme-ov-file#zero-knowledge-installation-with-https).
## Documentation
-* [Getting Started](https://neko.m1k1o.net/#/getting-started/)
- * [Quick Start](https://neko.m1k1o.net/#/getting-started/quick-start)
- * [Examples](https://neko.m1k1o.net/#/getting-started/examples)
- * [Reverse Proxy](https://neko.m1k1o.net/#/getting-started/reverse-proxy)
- * [Configuration](https://neko.m1k1o.net/#/getting-started/configuration)
- * [Troubleshooting](https://neko.m1k1o.net/#/getting-started/troubleshooting)
-* [Mobile Support](https://neko.m1k1o.net/#/mobile-support)
-* [Contributing](https://neko.m1k1o.net/#/contributing)
- * [Non Goals](https://neko.m1k1o.net/#/non-goals)
- * [Technologies](https://neko.m1k1o.net/#/technologies)
-* [Changelog](https://neko.m1k1o.net/#/changelog)
+Full documentation is available at [neko.m1k1o.net](https://neko.m1k1o.net/). Key sections include:
-## How to contribute? How to build?
+- [Migration from V2](https://neko.m1k1o.net/docs/v3/migration-from-v2)
+- [Getting Started](https://neko.m1k1o.net/docs/v3/quick-start)
+- [Installation](https://neko.m1k1o.net/docs/v3/installation)
+- [Examples](https://neko.m1k1o.net/docs/v3/installation/examples)
+- [Configuration](https://neko.m1k1o.net/docs/v3/configuration)
+- [Frequently Asked Questions](https://neko.m1k1o.net/docs/v3/faq)
+- [Troubleshooting](https://neko.m1k1o.net/docs/v3/troubleshooting)
-Navigate to [.docker](.docker) folder for further information.
+## How to Contribute
+
+Contributions are welcome! Check the [Contributing Guide](https://neko.m1k1o.net/contributing) for details.
## Support
-If you want to support this project, you can do it [here](https://github.com/sponsors/m1k1o).
+If you find Neko useful, consider supporting the project via [GitHub Sponsors](https://github.com/sponsors/m1k1o).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..7db0c453
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,19 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+If there are any vulnerabilities in **m1k1o/neko**, don't hesitate to _report them_.
+
+1. Send an email to `security@m1k1o.net`.
+
+2. Describe the vulnerability.
+
+ If you have a fix, that is most welcome -- please attach or summarize it in your message!
+
+3. We will evaluate the vulnerability and, if necessary, release a fix or mitigating steps to address it. We will contact you to let you know the outcome, and will credit you in the report.
+
+ Please **do not disclose the vulnerability publicly** until a fix is released!
+
+4. Once we have either a) published a fix, or b) declined to address the vulnerability for whatever reason, you are free to publicly disclose it.
+
+We appreciate your help in keeping Neko secure.
diff --git a/apps/brave/Dockerfile b/apps/brave/Dockerfile
index a0e2890d..53c4745b 100644
--- a/apps/brave/Dockerfile
+++ b/apps/brave/Dockerfile
@@ -1,12 +1,13 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
RUN set -eux; apt-get update; \
apt-get install -y --no-install-recommends apt-transport-https curl openbox; \
#
# install brave browser
+ ARCH=$(dpkg --print-architecture); \
curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg; \
- echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg arch=amd64] https://brave-browser-apt-release.s3.brave.com/ stable main" \
+ echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg arch=${ARCH}] https://brave-browser-apt-release.s3.brave.com/ stable main" \
| tee /etc/apt/sources.list.d/brave-browser-release.list; \
apt-get update; \
apt-get install -y --no-install-recommends brave-browser; \
diff --git a/apps/brave/Dockerfile.nvidia b/apps/brave/Dockerfile.nvidia
index 0ac80280..acd6da94 100644
--- a/apps/brave/Dockerfile.nvidia
+++ b/apps/brave/Dockerfile.nvidia
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:nvidia-base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest
FROM $BASE_IMAGE
RUN set -eux; apt-get update; \
diff --git a/apps/brave/supervisord.nvidia.conf b/apps/brave/supervisord.nvidia.conf
index 995886a9..ef25d415 100644
--- a/apps/brave/supervisord.nvidia.conf
+++ b/apps/brave/supervisord.nvidia.conf
@@ -12,9 +12,11 @@ command=/bin/entrypoint.sh /usr/bin/brave-browser
--enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization
--ignore-gpu-blocklist
--disable-seccomp-filter-sandbox
- --use-gl=egl
+ --use-angle=vulkan
--disable-software-rasterizer
--disable-dev-shm-usage
+ --disable-vulkan-surface
+ --enable-unsafe-webgpu
stopsignal=INT
autorestart=true
priority=800
diff --git a/apps/chromium/Dockerfile b/apps/chromium/Dockerfile
index 70f4085d..4a3171f5 100644
--- a/apps/chromium/Dockerfile
+++ b/apps/chromium/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
#
diff --git a/apps/chromium/Dockerfile.nvidia b/apps/chromium/Dockerfile.nvidia
index 752a7d98..8af30e5e 100644
--- a/apps/chromium/Dockerfile.nvidia
+++ b/apps/chromium/Dockerfile.nvidia
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest
FROM $BASE_IMAGE
#
diff --git a/apps/chromium/supervisord.nvidia.conf b/apps/chromium/supervisord.nvidia.conf
index 7eb6785d..2f83368d 100644
--- a/apps/chromium/supervisord.nvidia.conf
+++ b/apps/chromium/supervisord.nvidia.conf
@@ -12,9 +12,11 @@ command=/bin/entrypoint.sh /usr/bin/chromium
--enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization
--ignore-gpu-blocklist
--disable-seccomp-filter-sandbox
- --use-gl=egl
+ --use-angle=vulkan
--disable-software-rasterizer
--disable-dev-shm-usage
+ --disable-vulkan-surface
+ --enable-unsafe-webgpu
stopsignal=INT
autorestart=true
priority=800
diff --git a/apps/firefox/Dockerfile b/apps/firefox/Dockerfile
index 6a510434..b07b02b9 100644
--- a/apps/firefox/Dockerfile
+++ b/apps/firefox/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
#
@@ -10,7 +10,7 @@ RUN set -eux; apt-get update; \
#
# install firefox-esr for armhf
apt-get install -y --no-install-recommends firefox-esr; \
- ln -s /usr/bin/firefox-esr /usr/bin/firefox; \
+ ln -s /usr/lib/firefox-esr /usr/lib/firefox; \
#
# install extensions
mkdir -p /usr/lib/firefox-esr/distribution/extensions; \
diff --git a/apps/firefox/Dockerfile.nvidia b/apps/firefox/Dockerfile.nvidia
index a43c5279..62df72f0 100644
--- a/apps/firefox/Dockerfile.nvidia
+++ b/apps/firefox/Dockerfile.nvidia
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest
FROM $BASE_IMAGE
ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US"
diff --git a/apps/firefox/neko.js b/apps/firefox/neko.js
index a55e1e51..0552774a 100644
--- a/apps/firefox/neko.js
+++ b/apps/firefox/neko.js
@@ -15,8 +15,8 @@ lockPref("plugins.hide_infobar_for_missing_plugin", true);
lockPref("profile.allow_automigration", false);
lockPref("signon.prefillForms", false);
lockPref("signon.rememberSignons", false);
-lockPref("xpinstall.enabled", false);
-lockPref("xpinstall.whitelist.required", true);
+//lockPref("xpinstall.enabled", false);
+//lockPref("xpinstall.whitelist.required", true);
lockPref("browser.download.manager.retention", 0);
lockPref("browser.download.folderList", 2);
lockPref("browser.download.forbid_open_with", true);
diff --git a/apps/google-chrome/Dockerfile b/apps/google-chrome/Dockerfile
index 669c5ee1..04347401 100644
--- a/apps/google-chrome/Dockerfile
+++ b/apps/google-chrome/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
ARG SRC_URL="https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb"
diff --git a/apps/google-chrome/Dockerfile.nvidia b/apps/google-chrome/Dockerfile.nvidia
index f96645b5..9dc778ee 100644
--- a/apps/google-chrome/Dockerfile.nvidia
+++ b/apps/google-chrome/Dockerfile.nvidia
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:nvidia-base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/nvidia-base:latest
FROM $BASE_IMAGE
#Β latest working version with EGL: 111.0.5563.146, revert when resolved
diff --git a/apps/google-chrome/supervisord.nvidia.conf b/apps/google-chrome/supervisord.nvidia.conf
index f3821fd0..1e6ca2ed 100644
--- a/apps/google-chrome/supervisord.nvidia.conf
+++ b/apps/google-chrome/supervisord.nvidia.conf
@@ -12,9 +12,11 @@ command=/bin/entrypoint.sh /usr/bin/google-chrome
--enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization
--ignore-gpu-blocklist
--disable-seccomp-filter-sandbox
- --use-gl=egl
+ --use-angle=vulkan
--disable-software-rasterizer
--disable-dev-shm-usage
+ --disable-vulkan-surface
+ --enable-unsafe-webgpu
stopsignal=INT
autorestart=true
priority=800
diff --git a/apps/kde/Dockerfile b/apps/kde/Dockerfile
index a96820b6..3ca3ba38 100644
--- a/apps/kde/Dockerfile
+++ b/apps/kde/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
#
diff --git a/apps/microsoft-edge/Dockerfile b/apps/microsoft-edge/Dockerfile
index a291fad2..50724792 100644
--- a/apps/microsoft-edge/Dockerfile
+++ b/apps/microsoft-edge/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
ARG API_URL="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/"
diff --git a/apps/microsoft-edge/Dockerfile.nvidia b/apps/microsoft-edge/Dockerfile.nvidia
index 1ba67c18..177e8490 100644
--- a/apps/microsoft-edge/Dockerfile.nvidia
+++ b/apps/microsoft-edge/Dockerfile.nvidia
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
ARG API_URL="https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/"
diff --git a/apps/microsoft-edge/supervisord.nvidia.conf b/apps/microsoft-edge/supervisord.nvidia.conf
index 971bb3b7..beb2df4d 100644
--- a/apps/microsoft-edge/supervisord.nvidia.conf
+++ b/apps/microsoft-edge/supervisord.nvidia.conf
@@ -12,9 +12,11 @@ command=/bin/entrypoint.sh /usr/bin/microsoft-edge
--enable-features=Vulkan,UseSkiaRenderer,VaapiVideoEncoder,VaapiVideoDecoder,CanvasOopRasterization
--ignore-gpu-blocklist
--disable-seccomp-filter-sandbox
- --use-gl=egl
+ --use-angle=vulkan
--disable-software-rasterizer
--disable-dev-shm-usage
+ --disable-vulkan-surface
+ --enable-unsafe-webgpu
stopsignal=INT
autorestart=true
priority=800
diff --git a/apps/opera/Dockerfile b/apps/opera/Dockerfile
index e02697d6..dc333e08 100644
--- a/apps/opera/Dockerfile
+++ b/apps/opera/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
ARG API_URL="https://download5.operacdn.com/pub/opera/desktop/"
diff --git a/apps/remmina/Dockerfile b/apps/remmina/Dockerfile
index e527c218..fae54608 100644
--- a/apps/remmina/Dockerfile
+++ b/apps/remmina/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
# install remmina
diff --git a/apps/remmina/run-remmina.sh b/apps/remmina/run-remmina.sh
index a9817805..f2d22979 100755
--- a/apps/remmina/run-remmina.sh
+++ b/apps/remmina/run-remmina.sh
@@ -1,5 +1,4 @@
#!/bin/bash
-set -u
err() {
echo "ERROR: $*" >&2
@@ -16,23 +15,28 @@ if [[ -n "$REMMINA_PROFILE" ]]; then
exec remmina -c "$profile"
fi
-[[ -z "$REMMINA_URL" ]] && err "Neither 'REMMINA_PROFILE' nor 'REMMINA_URL' found in env vars"
+if [[ ! -z "$REMMINA_URL" ]]; then
+ readarray -t arr < <( echo -n "$REMMINA_URL" | perl -pe 's|^(\w+)\:\/\/(?:([^:]+)(?::([^@]+))?@)?(.*)$|\1\n\2\n\3\n\4|' )
+ proto="${arr[0]}"
+ user="${arr[1]}"
+ pw="${arr[2]}"
+ host="${arr[3]}"
+ echo "Parsed url in 'REMMINA_URL': proto:$proto username:$user host:$host"
-readarray -t arr < <( echo -n "$REMMINA_URL" | perl -pe 's|^(\w+)\:\/\/(?:([^:]+)(?::([^@]+))?@)?(.*)$|\1\n\2\n\3\n\4|' )
-proto="${arr[0]}"
-user="${arr[1]}"
-pw="${arr[2]}"
-host="${arr[3]}"
-echo "Parsed url in 'REMMINA_URL': proto:$proto username:$user host:$host"
+ [[ "$proto" != "vnc" && "$proto" != "rdp" && "$proto" != "spice" ]] && err "Unsupported protocol $proto in connection url 'REMMINA_URL'"
-[[ "$proto" != "vnc" && "$proto" != "rdp" && "$proto" != "spice" ]] && err "Unsupported protocol $proto in connection url 'REMMINA_URL'"
+ profile="$profile_dir"/"$proto".remmina
+ remmina --set-option username="$user" --update-profile "$profile"
+ remmina --set-option password="$pw" --update-profile "$profile"
+ remmina --set-option server="$host" --update-profile "$profile"
-profile="$profile_dir"/"$proto".remmina
-remmina --set-option username="$user" --update-profile "$profile"
-remmina --set-option password="$pw" --update-profile "$profile"
-remmina --set-option server="$host" --update-profile "$profile"
+ # remmina --set-option window_maximize=1 --update-profile "$profile"
+ # remmina --set-option scale=1 --update-profile "$profile"
-# remmina --set-option window_maximize=1 --update-profile "$profile"
-# remmina --set-option scale=1 --update-profile "$profile"
+ echo "Running remmina with URL $REMMINA_URL"
+ exec remmina -c "$profile"
+fi
-exec remmina -c "$profile"
+
+echo "Running remmina without connection profile"
+exec remmina
diff --git a/apps/tor-browser/Dockerfile b/apps/tor-browser/Dockerfile
index dd8c5bfc..8ded6895 100644
--- a/apps/tor-browser/Dockerfile
+++ b/apps/tor-browser/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
#
diff --git a/apps/ungoogled-chromium/Dockerfile b/apps/ungoogled-chromium/Dockerfile
index 97a6cac1..d7ab45a3 100644
--- a/apps/ungoogled-chromium/Dockerfile
+++ b/apps/ungoogled-chromium/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
ARG API_URL="https://api.github.com/repos/macchrome/linchrome/releases/latest"
diff --git a/apps/vivaldi/Dockerfile b/apps/vivaldi/Dockerfile
index b5fa22d6..6ce74a6b 100644
--- a/apps/vivaldi/Dockerfile
+++ b/apps/vivaldi/Dockerfile
@@ -1,17 +1,14 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
-ARG VIVALDI_VERSION="5.3.2679.34-1"
-# TODO: Get chromium version from vivaldi
-ARG CHROMIUM_VERSION="102.0.5005.72"
-
#
# install vivaldi
SHELL ["/bin/bash", "-c"]
RUN set -eux; apt-get update; \
- wget -O /tmp/vivaldi.deb "https://downloads.vivaldi.com/stable/vivaldi-stable_${VIVALDI_VERSION}_amd64.deb"; \
- apt-get install -y --no-install-recommends wget unzip xz-utils jq openbox /tmp/vivaldi.deb; \
- /opt/vivaldi/update-ffmpeg; \
+ ARCH=$(dpkg --print-architecture); \
+ wget -O /tmp/vivaldi.deb "https://downloads.vivaldi.com/stable/vivaldi-stable_${ARCH}.deb"; \
+ apt-get install -y --no-install-recommends wget unzip xz-utils jq openbox; \
+ apt install -y --no-install-recommends /tmp/vivaldi.deb; \
#
# install latest version of uBlock Origin and SponsorBlock for YouTube
EXTENSIONS_DIR="/usr/share/chromium/extensions"; \
@@ -22,7 +19,7 @@ RUN set -eux; apt-get update; \
mkdir -p "${EXTENSIONS_DIR}"; \
for EXT_ID in "${EXTENSIONS[@]}"; \
do \
- EXT_URL="https://clients2.google.com/service/update2/crx?response=redirect&nacl_arch=x86-64&prodversion=${CHROMIUM_VERSION}&acceptformat=crx2,crx3&x=id%3D${EXT_ID}%26installsource%3Dondemand%26uc"; \
+ EXT_URL="https://clients2.google.com/service/update2/crx?response=redirect&prodversion=100&acceptformat=crx2,crx3&x=id%3D${EXT_ID}%26installsource%3Dondemand%26uc"; \
EXT_PATH="${EXTENSIONS_DIR}/${EXT_ID}.crx"; \
wget -O "${EXT_PATH}" "${EXT_URL}"; \
EXT_VERSION="$(unzip -p "${EXT_PATH}" manifest.json 2>/dev/null | jq -r ".version")"; \
diff --git a/apps/vlc/Dockerfile b/apps/vlc/Dockerfile
index aea2e1d6..02572d2f 100644
--- a/apps/vlc/Dockerfile
+++ b/apps/vlc/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
#
diff --git a/apps/waterfox/Dockerfile b/apps/waterfox/Dockerfile
index 49f4c66b..7dbf9d80 100644
--- a/apps/waterfox/Dockerfile
+++ b/apps/waterfox/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
ARG SRC_URL="https://cdn1.waterfox.net/waterfox/releases/latest/linux"
@@ -10,7 +10,7 @@ RUN set -eux; apt-get update; \
xz-utils bzip2 libgtk-3-0 libdbus-glib-1-2; \
#
# fetch latest release
- wget -O /tmp/waterfox-setup.tar.bz2 "${SRC_URL}"; \
+ wget --user-agent="Mozilla/5.0" -O /tmp/waterfox-setup.tar.bz2 "${SRC_URL}"; \
mkdir /usr/lib/waterfox; \
tar -xjf /tmp/waterfox-setup.tar.bz2 -C /usr/lib; \
rm -f /tmp/waterfox-setup.tar.bz2; \
diff --git a/apps/waterfox/neko.js b/apps/waterfox/neko.js
index a55e1e51..0552774a 100644
--- a/apps/waterfox/neko.js
+++ b/apps/waterfox/neko.js
@@ -15,8 +15,8 @@ lockPref("plugins.hide_infobar_for_missing_plugin", true);
lockPref("profile.allow_automigration", false);
lockPref("signon.prefillForms", false);
lockPref("signon.rememberSignons", false);
-lockPref("xpinstall.enabled", false);
-lockPref("xpinstall.whitelist.required", true);
+//lockPref("xpinstall.enabled", false);
+//lockPref("xpinstall.whitelist.required", true);
lockPref("browser.download.manager.retention", 0);
lockPref("browser.download.folderList", 2);
lockPref("browser.download.forbid_open_with", true);
diff --git a/apps/xfce/Dockerfile b/apps/xfce/Dockerfile
index b1aa6a7d..b9f45b0c 100644
--- a/apps/xfce/Dockerfile
+++ b/apps/xfce/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=m1k1o/neko:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
#
diff --git a/build b/build
index 0e061a5e..9c311608 100755
--- a/build
+++ b/build
@@ -2,39 +2,57 @@
set -e
cd "$(dirname "$0")"
+#
+# This script builds the neko base image and all the applications
+#
+
# disable buildx because of https://github.com/docker/buildx/issues/847
+# if you want to use buildx, set USE_BUILDX=1
if [ -z "$USE_BUILDX" ]; then
USE_BUILDX=0
fi
# check if docker buildx is available, its not docker-buildx command but rather subcommand of docker
-if [ -z "$USE_BUILDX" ] && [ -x "$(command -v docker)" ]; then
- if docker buildx version >/dev/null 2>&1; then
- USE_BUILDX=1
- fi
-fi
-
-#
-# This script builds the neko base image and all the applications
-#
+#if [ -z "$USE_BUILDX" ] && [ -x "$(command -v docker)" ]; then
+# if docker buildx version >/dev/null 2>&1; then
+# USE_BUILDX=1
+# fi
+#fi
function log() {
echo "$(date +'%Y-%m-%d %H:%M:%S') - [NEKO] - $1" > /dev/stderr
}
function help() {
- echo "Usage: $0"
- echo " -p, --platform : The platform (default: linux/amd64)"
- echo " -r, --repository : The repository prefix (default: ghcr.io/m1k1o/neko)"
- echo " -t, --tag : The image tag, can be specified multiple times, if not specified"
- echo " uses 'latest' and if available, current git semver tag (v*.*.*)"
- echo " -f, --flavor : The flavor, if not specified, builds without flavor"
- echo " -b, --base : The base image name (default: [-]base:)"
- echo " -a, --app : The app to build, if not specified, builds the base image"
- echo " -y, --yes : Skip confirmation prompts"
- echo " --no-cache : Build without docker cache"
- echo " --push : Push the image to the registry after building"
- echo " -h, --help : Show this help message"
+ echo "Usage: $0 [options] [image]"
+ echo
+ echo "Options:"
+ echo " -p, --platform : The platform (default: system architecture)"
+ echo " -r, --repository : The repository prefix (default: ghcr.io/m1k1o/neko)"
+ echo " -t, --tag : The image tag, can be specified multiple times, if not specified"
+ echo " uses 'latest' and if available, current git semver tag (v*.*.*)"
+ echo " -f, --flavor : The flavor, if not specified, builds without flavor"
+ echo " -b, --base_image : The base image name (default: [-]base:)"
+ echo " -a, --application : The app to build, if not specified, builds the base image"
+ echo " -y, --yes : Skip confirmation prompts"
+ echo " --no-cache : Build without docker cache"
+ echo " --push : Push the image to the registry after building"
+ echo " -h, --help : Show this help message"
+ echo
+ echo "Positional arguments:"
+ echo " : The image name, if not specified, uses the full image name"
+ echo " in the format /-:"
+ echo " Example: ghcr.io/m1k1o/neko/nvidia-firefox:latest"
+ echo " You can override any of the above options by specifying them"
+ echo " after the image name."
+ echo
+ echo "Environment variables:"
+ echo " USE_BUILDX : Set to 1 to use docker buildx instead of docker build"
+ echo " (default: 0)"
+ echo " CLIENT_DIST : The client dist file to use, if not specified, builds them"
+ echo " from the source code."
+ echo " (options) : Options can be specified as environment variables, for example:"
+ echo " PLATFORM=linux/arm64 $0 --repository ghcr.io/m1k1o/neko"
}
FULL_IMAGE=""
@@ -44,8 +62,8 @@ while [[ "$#" -gt 0 ]]; do
--repository|-r) REPOSITORY="$2"; shift ;;
--tag|-t) TAGS+=("$2"); TAG="$2"; shift ;;
--flavor|-f) FLAVOR="$2"; shift ;;
- --base|-b) BASE_IMAGE="$2"; shift ;;
- --app|-a) APPLICATION="$2"; shift ;;
+ --base_image|-b) BASE_IMAGE="$2"; shift ;;
+ --application|-a) APPLICATION="$2"; shift ;;
--yes|-y) YES=1 ;;
--no-cache) NO_CACHE="--no-cache" log "Building without cache" ;;
--push) PUSH=1 ;;
@@ -131,17 +149,12 @@ function build_image() {
local APPLICATION_IMAGE="$1"
shift
+ # get list of tags in full format: :
+ local IMAGE_NO_TAG="${APPLICATION_IMAGE%:*}"
local FULL_TAGS=()
- # if the image name starts with local/, just use the tag as is
- if [[ "$APPLICATION_IMAGE" == *"local/"* ]]; then
- FULL_TAGS=("$APPLICATION_IMAGE")
- else
- # get list of tags in full format: :
- local IMAGE_NO_TAG="${APPLICATION_IMAGE%:*}"
- for T in "${TAGS[@]}"; do
- FULL_TAGS+=("$IMAGE_NO_TAG:$T")
- done
- fi
+ for T in "${TAGS[@]}"; do
+ FULL_TAGS+=("$IMAGE_NO_TAG:$T")
+ done
if [ -z "$USE_BUILDX" ] || [ "$USE_BUILDX" != "1" ]; then
# if buildx is not available, use docker build
@@ -197,7 +210,18 @@ else
fi
if [ -z "$PLATFORM" ]; then
- PLATFORM="linux/amd64"
+ # use system architecture if not specified
+ UNAME="$(uname -m)"
+ if [ "$UNAME" == "x86_64" ]; then
+ PLATFORM="linux/amd64"
+ elif [ "$UNAME" == "aarch64" ] || [ "$UNAME" == "arm64" ]; then
+ PLATFORM="linux/arm64"
+ elif [ "$UNAME" == "armv7l" ]; then
+ PLATFORM="linux/arm/v7"
+ else
+ log "Unknown architecture: $UNAME"
+ exit 1
+ fi
fi
log "Using platform: $PLATFORM"
@@ -290,10 +314,10 @@ fi
log "Building base image: $BASE_IMAGE"
docker run --rm -i \
- -v ./:/src \
+ -v "$(pwd)":/src \
-e "RUNTIME_DOCKERFILE=$RUNTIME_DOCKERFILE" \
--workdir /src \
--entrypoint go \
golang:1.24-bullseye \
- run ./docker/main.go \
- -i Dockerfile.tmpl -client $CLIENT_DIST | build_image $BASE_IMAGE -f - .
+ run utils/docker/main.go \
+ -i Dockerfile.tmpl -client "$CLIENT_DIST" | build_image $BASE_IMAGE -f - .
diff --git a/client/src/app.vue b/client/src/app.vue
index 668e7a42..e908ebd1 100644
--- a/client/src/app.vue
+++ b/client/src/app.vue
@@ -119,23 +119,43 @@
}
}
- @media only screen and (max-width: 600px) {
- #neko.expanded {
- .neko-main {
- transform: translateX(calc(-100% + 65px));
+ @media only screen and (max-width: 1024px) {
+ html,
+ body {
+ overflow-y: auto !important;
+ width: auto !important;
+ height: auto !important;
+ }
- video {
- display: none;
- }
+ body > p {
+ display: none;
+ }
+
+ #neko {
+ position: relative;
+ flex-direction: column;
+ max-height: initial !important;
+
+ .neko-main {
+ height: 100vh;
}
.neko-menu {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 65px;
- width: calc(100% - 65px);
+ height: 100vh;
+ width: 100% !important;
+ }
+ }
+ }
+
+ @media only screen and (max-width: 1024px) and (orientation: portrait) {
+ #neko {
+ &.expanded .neko-main {
+ height: 40vh;
+ }
+
+ &.expanded .neko-menu {
+ height: 60vh;
+ width: 100% !important;
}
}
}
@@ -217,6 +237,20 @@
}
}
+ @Watch('side')
+ onSide(side: boolean) {
+ if (side) {
+ console.log('side enabled')
+ // scroll to the side
+ this.$nextTick(() => {
+ const side = document.querySelector('aside')
+ if (side) {
+ side.scrollIntoView({ behavior: 'smooth', block: 'start' })
+ }
+ })
+ }
+ }
+
controlAttempt() {
if (this.shakeKbd || this.$accessor.remote.hosted) return
diff --git a/client/src/components/connect.vue b/client/src/components/connect.vue
index 92d04476..fbe35645 100644
--- a/client/src/components/connect.vue
+++ b/client/src/components/connect.vue
@@ -24,7 +24,7 @@
diff --git a/client/src/neko/events.ts b/client/src/neko/events.ts
index 45c264db..fc76e83c 100644
--- a/client/src/neko/events.ts
+++ b/client/src/neko/events.ts
@@ -15,7 +15,7 @@ export const EVENT = {
ERROR: 'system/error',
},
CLIENT: {
- HEARTBEAT: 'client/heartbeat'
+ HEARTBEAT: 'client/heartbeat',
},
SIGNAL: {
OFFER: 'signal/offer',
diff --git a/client/src/plugins/i18n.ts b/client/src/plugins/i18n.ts
index 6b199e86..7d563a15 100644
--- a/client/src/plugins/i18n.ts
+++ b/client/src/plugins/i18n.ts
@@ -1,11 +1,31 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import { messages } from '~/locale'
+import { get } from '~/utils/localstorage'
Vue.use(VueI18n)
+const fallbackLocale = 'en'
+
+function detectBrowserLanguage(): string {
+ const browserLang = navigator.language.toLowerCase()
+
+ const supportedLangs = Object.keys(messages)
+ if (supportedLangs.includes(browserLang)) {
+ return browserLang
+ }
+
+ const baseLang = browserLang.split('-')[0]
+ const matchingLang = supportedLangs.find((lang) => lang.startsWith(baseLang))
+ if (matchingLang) {
+ return matchingLang
+ }
+
+ return fallbackLocale
+}
+
export const i18n = new VueI18n({
- locale: 'en',
- fallbackLocale: 'en',
+ locale: get('lang', detectBrowserLanguage()),
+ fallbackLocale,
messages,
})
diff --git a/client/src/store/remote.ts b/client/src/store/remote.ts
index c8e9d282..5cd40cd0 100644
--- a/client/src/store/remote.ts
+++ b/client/src/store/remote.ts
@@ -18,6 +18,9 @@ export const state = () => ({
})
export const getters = getterTree(state, {
+ controlling: (state, getters, root) => {
+ return root.user.id === state.id
+ },
hosting: (state, getters, root) => {
return root.user.id === state.id || state.implicitHosting
},
@@ -89,7 +92,7 @@ export const actions = actionTree(
},
request({ getters }) {
- if (!accessor.connected || getters.hosting) {
+ if (!accessor.connected || getters.controlling) {
return
}
diff --git a/config.yml b/config.yml
index 42be43b0..8cb20f2c 100644
--- a/config.yml
+++ b/config.yml
@@ -15,6 +15,3 @@ session:
cookie:
# needed for legacy API
enabled: false
-
-webrtc:
- icelite: true
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 8738b2b8..c8f81530 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -1,15 +1,14 @@
-version: "3.4"
services:
neko:
- image: "m1k1o/neko:firefox"
+ image: "ghcr.io/m1k1o/neko/firefox:latest"
restart: "unless-stopped"
shm_size: "2gb"
ports:
- "8080:8080"
- "52000-52100:52000-52100/udp"
environment:
- NEKO_SCREEN: 1920x1080@30
- NEKO_PASSWORD: neko
- NEKO_PASSWORD_ADMIN: admin
- NEKO_EPR: 52000-52100
- NEKO_ICELITE: 1
+ NEKO_DESKTOP_SCREEN: 1920x1080@30
+ NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko
+ NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin
+ NEKO_WEBRTC_EPR: 52000-52100
+ NEKO_WEBRTC_ICELITE: 1
diff --git a/docker/go.mod b/docker/go.mod
deleted file mode 100644
index ee6c7af1..00000000
--- a/docker/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module github.com/m1k1o/neko/docker
-
-go 1.24.1
diff --git a/runtime/Dockerfile b/runtime/Dockerfile
index 880781be..b4c165ef 100644
--- a/runtime/Dockerfile
+++ b/runtime/Dockerfile
@@ -103,7 +103,9 @@ ENV NEKO_PLUGINS_DIR=/etc/neko/plugins/
#
# add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
- CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
+ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ exit 1
#
# run neko
diff --git a/runtime/Dockerfile.bookworm b/runtime/Dockerfile.bookworm
index 07e8e6b9..b53e1ad0 100644
--- a/runtime/Dockerfile.bookworm
+++ b/runtime/Dockerfile.bookworm
@@ -95,8 +95,9 @@ ENV NEKO_PLUGINS_DIR=/etc/neko/plugins/
#
# add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
- CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
-
+ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ exit 1
#
# run neko
CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"]
diff --git a/runtime/Dockerfile.intel b/runtime/Dockerfile.intel
index 657cd8af..bb44c189 100644
--- a/runtime/Dockerfile.intel
+++ b/runtime/Dockerfile.intel
@@ -115,8 +115,9 @@ ENV RENDER_GID=
#
# add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
- CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
-
+ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ exit 1
#
# run neko
CMD ["/usr/bin/supervisord", "-c", "/etc/neko/supervisord.conf"]
diff --git a/runtime/Dockerfile.nvidia b/runtime/Dockerfile.nvidia
index 88f1c1e0..1cf652c5 100644
--- a/runtime/Dockerfile.nvidia
+++ b/runtime/Dockerfile.nvidia
@@ -1,6 +1,6 @@
ARG UBUNTU_RELEASE=20.04
ARG CUDA_VERSION=11.4.3
-ARG VIRTUALGL_VERSION=3.1
+ARG VIRTUALGL_VERSION=3.1.3-20250409
ARG GSTREAMER_VERSION=1.20
#
@@ -61,13 +61,13 @@ ARG UBUNTU_RELEASE
ARG VIRTUALGL_VERSION
# Make all NVIDIA GPUs visible by default
-ENV NVIDIA_VISIBLE_DEVICES all
+ENV NVIDIA_VISIBLE_DEVICES=all
# All NVIDIA driver capabilities should preferably be used, check `NVIDIA_DRIVER_CAPABILITIES` inside the container if things do not work
-ENV NVIDIA_DRIVER_CAPABILITIES all
+ENV NVIDIA_DRIVER_CAPABILITIES=all
#
# set vgl-display to headless 3d gpu card/// correct values are egl[n] or /dev/dri/card0:if this is passed into container
-ENV VGL_DISPLAY egl
+ENV VGL_DISPLAY=egl
#
# set custom user
@@ -205,23 +205,22 @@ RUN VULKAN_API_VERSION=$(dpkg -s libvulkan1 | grep -oP 'Version: [0-9|\.]+' | gr
}" > /etc/vulkan/icd.d/nvidia_icd.json
#
-# install VirtualGL and make libraries available for preload
-RUN set -eux; \
+# install an up-to-date version of VirtualGL
+RUN apt-get update; \
+ apt-get install -y --no-install-recommends wget gpg ca-certificates; \
+ # Add VirtualGL GPG key
+ wget -q -O- https://packagecloud.io/dcommander/virtualgl/gpgkey | \
+ gpg --dearmor >/etc/apt/trusted.gpg.d/VirtualGL.gpg; \
+ # Download the official VirtualGL.list file
+ wget -q -O /etc/apt/sources.list.d/VirtualGL.list \
+ https://raw.githubusercontent.com/VirtualGL/repo/main/VirtualGL.list; \
+ # Install packages
apt-get update; \
- wget "https://sourceforge.net/projects/virtualgl/files/virtualgl_${VIRTUALGL_VERSION}_amd64.deb"; \
- wget "https://sourceforge.net/projects/virtualgl/files/virtualgl32_${VIRTUALGL_VERSION}_amd64.deb"; \
- apt-get install -y --no-install-recommends ./virtualgl_${VIRTUALGL_VERSION}_amd64.deb ./virtualgl32_${VIRTUALGL_VERSION}_amd64.deb; \
- rm -f "virtualgl_${VIRTUALGL_VERSION}_amd64.deb" "virtualgl32_${VIRTUALGL_VERSION}_amd64.deb"; \
- chmod u+s /usr/lib/libvglfaker.so; \
- chmod u+s /usr/lib/libdlfaker.so; \
- chmod u+s /usr/lib32/libvglfaker.so; \
- chmod u+s /usr/lib32/libdlfaker.so; \
- chmod u+s /usr/lib/i386-linux-gnu/libvglfaker.so; \
- chmod u+s /usr/lib/i386-linux-gnu/libdlfaker.so; \
+ apt-get install -y --no-install-recommends virtualgl=${VIRTUALGL_VERSION}; \
#
# clean up
apt-get clean -y; \
- rm -rf /var/lib/apt/lists/* /var/cache/apt/*;
+ rm -rf /var/lib/apt/lists/* /var/cache/apt/*
#
# copy runtime configs
@@ -262,7 +261,9 @@ COPY --from=gstreamer /usr/share/gstreamer /usr/share/gstreamer
#
# add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
- CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
+ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ exit 1
#
# run neko
diff --git a/runtime/Dockerfile.nvidia.bookworm b/runtime/Dockerfile.nvidia.bookworm
index 7798c6ff..0e3c3c1d 100644
--- a/runtime/Dockerfile.nvidia.bookworm
+++ b/runtime/Dockerfile.nvidia.bookworm
@@ -1,6 +1,6 @@
ARG UBUNTU_RELEASE=22.04
ARG CUDA_VERSION=12.2.0
-ARG VIRTUALGL_VERSION=3.1
+ARG VIRTUALGL_VERSION=3.1.3-20250409
ARG GSTREAMER_VERSION=1.22
#
@@ -61,13 +61,13 @@ ARG UBUNTU_RELEASE
ARG VIRTUALGL_VERSION
# Make all NVIDIA GPUs visible by default
-ENV NVIDIA_VISIBLE_DEVICES all
+ENV NVIDIA_VISIBLE_DEVICES=all
# All NVIDIA driver capabilities should preferably be used, check `NVIDIA_DRIVER_CAPABILITIES` inside the container if things do not work
-ENV NVIDIA_DRIVER_CAPABILITIES all
+ENV NVIDIA_DRIVER_CAPABILITIES=all
#
# set vgl-display to headless 3d gpu card/// correct values are egl[n] or /dev/dri/card0:if this is passed into container
-ENV VGL_DISPLAY egl
+ENV VGL_DISPLAY=egl
#
# set custom user
@@ -199,23 +199,22 @@ RUN VULKAN_API_VERSION=$(dpkg -s libvulkan1 | grep -oP 'Version: [0-9|\.]+' | gr
}" > /etc/vulkan/icd.d/nvidia_icd.json
#
-# install VirtualGL and make libraries available for preload
-RUN set -eux; \
+# install an up-to-date version of VirtualGL
+RUN apt-get update; \
+ apt-get install -y --no-install-recommends wget gpg ca-certificates; \
+ # Add VirtualGL GPG key
+ wget -q -O- https://packagecloud.io/dcommander/virtualgl/gpgkey | \
+ gpg --dearmor >/etc/apt/trusted.gpg.d/VirtualGL.gpg; \
+ # Download the official VirtualGL.list file
+ wget -q -O /etc/apt/sources.list.d/VirtualGL.list \
+ https://raw.githubusercontent.com/VirtualGL/repo/main/VirtualGL.list; \
+ # Install packages
apt-get update; \
- wget "https://sourceforge.net/projects/virtualgl/files/virtualgl_${VIRTUALGL_VERSION}_amd64.deb"; \
- wget "https://sourceforge.net/projects/virtualgl/files/virtualgl32_${VIRTUALGL_VERSION}_amd64.deb"; \
- apt-get install -y --no-install-recommends ./virtualgl_${VIRTUALGL_VERSION}_amd64.deb ./virtualgl32_${VIRTUALGL_VERSION}_amd64.deb; \
- rm -f "virtualgl_${VIRTUALGL_VERSION}_amd64.deb" "virtualgl32_${VIRTUALGL_VERSION}_amd64.deb"; \
- chmod u+s /usr/lib/libvglfaker.so; \
- chmod u+s /usr/lib/libdlfaker.so; \
- chmod u+s /usr/lib32/libvglfaker.so; \
- chmod u+s /usr/lib32/libdlfaker.so; \
- chmod u+s /usr/lib/i386-linux-gnu/libvglfaker.so; \
- chmod u+s /usr/lib/i386-linux-gnu/libdlfaker.so; \
+ apt-get install -y --no-install-recommends virtualgl=${VIRTUALGL_VERSION}; \
#
# clean up
apt-get clean -y; \
- rm -rf /var/lib/apt/lists/* /var/cache/apt/*;
+ rm -rf /var/lib/apt/lists/* /var/cache/apt/*
#
# copy runtime configs
@@ -254,7 +253,9 @@ COPY --from=gstreamer /usr/share/gstreamer /usr/share/gstreamer
#
# add healthcheck
HEALTHCHECK --interval=10s --timeout=5s --retries=8 \
- CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || exit 1
+ CMD wget -O - http://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ wget --no-check-certificate -O - https://localhost:${NEKO_SERVER_BIND#*:}/health || \
+ exit 1
#
# run neko
diff --git a/runtime/supervisord.conf b/runtime/supervisord.conf
index 950ddbb5..16431d13 100644
--- a/runtime/supervisord.conf
+++ b/runtime/supervisord.conf
@@ -33,7 +33,7 @@ redirect_stderr=true
[program:neko]
environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
-command=/usr/bin/neko serve --static "/var/www"
+command=/usr/bin/neko serve --server.static "/var/www"
stopsignal=INT
stopwaitsecs=3
autorestart=true
diff --git a/server/build b/server/build
index 6bf20bef..9d00766b 100755
--- a/server/build
+++ b/server/build
@@ -22,7 +22,7 @@ fi
#
# load server dependencies
-go get -v -t -d .
+go get -v -t .
#
# build server
diff --git a/server/dev/build b/server/dev/build
index c67c5acb..0522631c 100755
--- a/server/dev/build
+++ b/server/dev/build
@@ -9,9 +9,10 @@ GIT_COMMIT=`git rev-parse --short HEAD`
GIT_BRANCH=`git rev-parse --symbolic-full-name --abbrev-ref HEAD`
echo "Building server image"
-docker build -t neko_server --build-arg "GIT_COMMIT=$GIT_COMMIT" --build-arg "GIT_BRANCH=$GIT_BRANCH" -f ../Dockerfile ..
+docker build -t neko_server:src -f ../Dockerfile ..
-BUILD_IMAGE=neko_server FLAVOUR=$1 ../../build
+echo "Building base image"
+../../build -y -b neko_server:base -f "$1"
echo "Building app image"
docker build -t neko_server:app --build-arg "BASE_IMAGE=neko_server:base" -f ./runtime/Dockerfile ./runtime
diff --git a/server/dev/fmt b/server/dev/fmt
index f8b3130e..a294473e 100755
--- a/server/dev/fmt
+++ b/server/dev/fmt
@@ -1,12 +1,12 @@
#!/bin/bash
cd "$(dirname "$0")"
-if [ "$(docker images -q neko_server 2> /dev/null)" == "" ]; then
- echo "Image 'neko_server' not found. Run ./build first."
+if [ "$(docker images -q neko_server:src 2> /dev/null)" == "" ]; then
+ echo "Image 'neko_server:src' not found. Run ./build first."
exit 1
fi
docker run -it --rm \
--entrypoint="go" \
-v "${PWD}/../:/src" \
- neko_server fmt ./...
+ neko_server:src fmt ./...
diff --git a/server/dev/go b/server/dev/go
index 29a62e91..229a9365 100755
--- a/server/dev/go
+++ b/server/dev/go
@@ -1,8 +1,8 @@
#!/bin/bash
cd "$(dirname "$0")"
-if [ "$(docker images -q neko_server 2> /dev/null)" == "" ]; then
- echo "Image 'neko_server' not found. Run ./build first."
+if [ "$(docker images -q neko_server:src 2> /dev/null)" == "" ]; then
+ echo "Image 'neko_server:src' not found. Run ./build first."
exit 1
fi
@@ -10,7 +10,7 @@ docker run -it \
--name "neko_server_go" \
--entrypoint="go" \
-v "${PWD}/../:/src" \
- neko_server "$@";
+ neko_server:src "$@";
#
# copy package files
docker cp neko_server_go:/src/go.mod "../go.mod"
diff --git a/server/dev/lint b/server/dev/lint
index c0f0b5bb..0c9e9bed 100755
--- a/server/dev/lint
+++ b/server/dev/lint
@@ -1,8 +1,8 @@
#!/bin/bash
cd "$(dirname "$0")"
-if [ "$(docker images -q neko_server 2> /dev/null)" == "" ]; then
- echo "Image 'neko_server' not found. Run ./build first."
+if [ "$(docker images -q neko_server:src 2> /dev/null)" == "" ]; then
+ echo "Image 'neko_server:src' not found. Run ./build first."
exit 1
fi
@@ -11,4 +11,4 @@ fi
docker run --rm -it \
-v "${PWD}/../:/src" \
--entrypoint="/bin/bash" \
- neko_server -c '[ -f ./bin/golangci-lint ] || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.31.0;./bin/golangci-lint run';
+ neko_server:src -c '[ -f ./bin/golangci-lint ] || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.31.0;./bin/golangci-lint run';
diff --git a/server/dev/rebuild b/server/dev/rebuild
index d2132fe7..696af7aa 100755
--- a/server/dev/rebuild
+++ b/server/dev/rebuild
@@ -10,7 +10,7 @@ set -e
docker run --rm -it \
-v "${PWD}/../:/src" \
--entrypoint="/bin/bash" \
- neko_server "./build" "$@";
+ neko_server:src "./build" "$@";
#
# remove old plugins
diff --git a/server/dev/rebuild.input b/server/dev/rebuild.input
index 006ba9c8..bd6a97c6 100755
--- a/server/dev/rebuild.input
+++ b/server/dev/rebuild.input
@@ -1,6 +1,6 @@
#!/bin/bash
cd "$(dirname "$0")"
-cd ../../runtime/xorg-deps/xf86-input-neko
+cd ../../utils/xorg-deps/xf86-input-neko
#
# aborting if any command returns a non-zero value
diff --git a/server/dev/runtime/Dockerfile b/server/dev/runtime/Dockerfile
index 0be6746f..438acb2f 100644
--- a/server/dev/runtime/Dockerfile
+++ b/server/dev/runtime/Dockerfile
@@ -1,4 +1,4 @@
-ARG BASE_IMAGE=neko_server:base
+ARG BASE_IMAGE=ghcr.io/m1k1o/neko/base:latest
FROM $BASE_IMAGE
ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US"
@@ -8,13 +8,13 @@ ARG SRC_URL="https://download.mozilla.org/?product=firefox-latest&os=linux64&lan
RUN set -eux; apt-get update; \
apt-get install -y --no-install-recommends \
dbus-x11 xfce4 xfce4-terminal sudo \
- xz-utils bzip2 libgtk-3-0 libdbus-glib-1-2; \
+ xz-utils libgtk-3-0 libdbus-glib-1-2; \
#
# fetch latest firefox release
- wget -O /tmp/firefox-setup.tar.bz2 "${SRC_URL}"; \
+ wget -O /tmp/firefox-setup.tar.xz "${SRC_URL}"; \
mkdir /usr/lib/firefox; \
- tar -xjf /tmp/firefox-setup.tar.bz2 -C /usr/lib; \
- rm -f /tmp/firefox-setup.tar.bz2; \
+ tar -xvf /tmp/firefox-setup.tar.xz -C /usr/lib; \
+ rm -f /tmp/firefox-setup.tar.xz; \
ln -s /usr/lib/firefox/firefox /usr/bin/firefox; \
#
# add user to sudoers
@@ -22,7 +22,7 @@ RUN set -eux; apt-get update; \
echo "neko:neko" | chpasswd; \
echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers; \
# clean up
- apt-get --purge autoremove -y xz-utils bzip2; \
+ apt-get --purge autoremove -y xz-utils; \
apt-get clean -y; \
rm -rf /var/lib/apt/lists/* /var/cache/apt/*
diff --git a/server/internal/config/capture.go b/server/internal/config/capture.go
index 3d8025e0..298e69cd 100644
--- a/server/internal/config/capture.go
+++ b/server/internal/config/capture.go
@@ -90,12 +90,12 @@ func (Capture) Init(cmd *cobra.Command) error {
return err
}
- cmd.PersistentFlags().String("capture.video.pipelines", "[]", "pipelines config in JSON used for video streaming")
+ cmd.PersistentFlags().String("capture.video.pipelines", "{}", "pipelines config used for video streaming")
if err := viper.BindPFlag("capture.video.pipelines", cmd.PersistentFlags().Lookup("capture.video.pipelines")); err != nil {
return err
}
- cmd.PersistentFlags().String("capture.video.pipeline", "", "gstreamer pipeline used for video streaming; shortcut for having only a single video pipeline instead of multiple, ignored if capture.video.pipelines is set")
+ cmd.PersistentFlags().String("capture.video.pipeline", "", "shortcut for configuring only a single gstreamer pipeline, ignored if pipelines is set")
if err := viper.BindPFlag("capture.video.pipeline", cmd.PersistentFlags().Lookup("capture.video.pipeline")); err != nil {
return err
}
@@ -451,6 +451,7 @@ func (s *Capture) SetV2() {
enableLegacy = true
}
+ modifiedVideoCodec := false
if videoCodec := viper.GetString("video_codec"); videoCodec != "" {
s.VideoCodec, ok = codec.ParseStr(videoCodec)
if !ok || s.VideoCodec.Type != webrtc.RTPCodecTypeVideo {
@@ -459,24 +460,29 @@ func (s *Capture) SetV2() {
}
log.Warn().Msg("you are using v2 configuration 'NEKO_VIDEO_CODEC' which is deprecated, please use 'NEKO_CAPTURE_VIDEO_CODEC' instead")
enableLegacy = true
+ modifiedVideoCodec = true
}
if viper.GetBool("vp8") {
s.VideoCodec = codec.VP8()
log.Warn().Msg("you are using deprecated config setting 'NEKO_VP8=true', use 'NEKO_CAPTURE_VIDEO_CODEC=vp8' instead")
enableLegacy = true
+ modifiedVideoCodec = true
} else if viper.GetBool("vp9") {
s.VideoCodec = codec.VP9()
log.Warn().Msg("you are using deprecated config setting 'NEKO_VP9=true', use 'NEKO_CAPTURE_VIDEO_CODEC=vp9' instead")
enableLegacy = true
+ modifiedVideoCodec = true
} else if viper.GetBool("h264") {
s.VideoCodec = codec.H264()
log.Warn().Msg("you are using deprecated config setting 'NEKO_H264=true', use 'NEKO_CAPTURE_VIDEO_CODEC=h264' instead")
enableLegacy = true
+ modifiedVideoCodec = true
} else if viper.GetBool("av1") {
s.VideoCodec = codec.AV1()
log.Warn().Msg("you are using deprecated config setting 'NEKO_AV1=true', use 'NEKO_CAPTURE_VIDEO_CODEC=av1' instead")
enableLegacy = true
+ modifiedVideoCodec = true
}
videoHWEnc := HwEncUnset
@@ -498,7 +504,7 @@ func (s *Capture) SetV2() {
videoPipeline := viper.GetString("video")
// video pipeline
- if videoHWEnc != HwEncUnset || videoBitrate != 0 || videoMaxFPS != 0 || videoPipeline != "" {
+ if modifiedVideoCodec || videoHWEnc != HwEncUnset || videoBitrate != 0 || videoMaxFPS != 0 || videoPipeline != "" {
pipeline, err := NewVideoPipeline(s.VideoCodec, s.Display, videoPipeline, videoMaxFPS, videoBitrate, videoHWEnc)
if err != nil {
log.Warn().Err(err).Msg("unable to create video pipeline, using default")
@@ -534,6 +540,7 @@ func (s *Capture) SetV2() {
enableLegacy = true
}
+ modifiedAudioCodec := false
if audioCodec := viper.GetString("audio_codec"); audioCodec != "" {
s.AudioCodec, ok = codec.ParseStr(audioCodec)
if !ok || s.AudioCodec.Type != webrtc.RTPCodecTypeAudio {
@@ -542,31 +549,36 @@ func (s *Capture) SetV2() {
}
log.Warn().Msg("you are using v2 configuration 'NEKO_AUDIO_CODEC' which is deprecated, please use 'NEKO_CAPTURE_AUDIO_CODEC' instead")
enableLegacy = true
+ modifiedAudioCodec = true
}
if viper.GetBool("opus") {
s.AudioCodec = codec.Opus()
log.Warn().Msg("you are using deprecated config setting 'NEKO_OPUS=true', use 'NEKO_CAPTURE_AUDIO_CODEC=opus' instead")
enableLegacy = true
+ modifiedAudioCodec = true
} else if viper.GetBool("g722") {
s.AudioCodec = codec.G722()
log.Warn().Msg("you are using deprecated config setting 'NEKO_G722=true', use 'NEKO_CAPTURE_AUDIO_CODEC=g722' instead")
enableLegacy = true
+ modifiedAudioCodec = true
} else if viper.GetBool("pcmu") {
s.AudioCodec = codec.PCMU()
log.Warn().Msg("you are using deprecated config setting 'NEKO_PCMU=true', use 'NEKO_CAPTURE_AUDIO_CODEC=pcmu' instead")
enableLegacy = true
+ modifiedAudioCodec = true
} else if viper.GetBool("pcma") {
s.AudioCodec = codec.PCMA()
log.Warn().Msg("you are using deprecated config setting 'NEKO_PCMA=true', use 'NEKO_CAPTURE_AUDIO_CODEC=pcma' instead")
enableLegacy = true
+ modifiedAudioCodec = true
}
audioBitrate := viper.GetUint("audio_bitrate")
audioPipeline := viper.GetString("audio")
// audio pipeline
- if audioBitrate != 0 || audioPipeline != "" {
+ if modifiedAudioCodec || audioBitrate != 0 || audioPipeline != "" {
pipeline, err := NewAudioPipeline(s.AudioCodec, s.AudioDevice, audioPipeline, audioBitrate)
if err != nil {
log.Warn().Err(err).Msg("unable to create audio pipeline, using default")
@@ -604,7 +616,7 @@ func (s *Capture) SetV2() {
// set legacy flag if any V2 configuration was used
if !viper.IsSet("legacy") && enableLegacy {
- log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message")
+ log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details")
viper.Set("legacy", true)
}
}
diff --git a/server/internal/config/desktop.go b/server/internal/config/desktop.go
index 2814b697..8331035e 100644
--- a/server/internal/config/desktop.go
+++ b/server/internal/config/desktop.go
@@ -133,7 +133,7 @@ func (s *Desktop) SetV2() {
// set legacy flag if any V2 configuration was used
if !viper.IsSet("legacy") && enableLegacy {
- log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message")
+ log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details")
viper.Set("legacy", true)
}
}
diff --git a/server/internal/config/member.go b/server/internal/config/member.go
index c3a7d242..db9c5daa 100644
--- a/server/internal/config/member.go
+++ b/server/internal/config/member.go
@@ -22,45 +22,45 @@ type Member struct {
}
func (Member) Init(cmd *cobra.Command) error {
- cmd.PersistentFlags().String("member.provider", "multiuser", "choose member provider")
+ cmd.PersistentFlags().String("member.provider", "multiuser", "selected member provider")
if err := viper.BindPFlag("member.provider", cmd.PersistentFlags().Lookup("member.provider")); err != nil {
return err
}
// file provider
- cmd.PersistentFlags().String("member.file.path", "", "member file provider: storage path")
+ cmd.PersistentFlags().String("member.file.path", "", "member file provider: path to the file containing the users and their passwords")
if err := viper.BindPFlag("member.file.path", cmd.PersistentFlags().Lookup("member.file.path")); err != nil {
return err
}
- cmd.PersistentFlags().Bool("member.file.hash", true, "member file provider: whether to hash passwords using sha256 (recommended)")
+ cmd.PersistentFlags().Bool("member.file.hash", true, "member file provider: whether the passwords are hashed using sha256 or not (recommended)")
if err := viper.BindPFlag("member.file.hash", cmd.PersistentFlags().Lookup("member.file.hash")); err != nil {
return err
}
// object provider
- cmd.PersistentFlags().String("member.object.users", "[]", "member object provider: users in JSON format")
+ cmd.PersistentFlags().String("member.object.users", "[]", "member object provider: list of users with their passwords and profiles")
if err := viper.BindPFlag("member.object.users", cmd.PersistentFlags().Lookup("member.object.users")); err != nil {
return err
}
// multiuser provider
- cmd.PersistentFlags().String("member.multiuser.user_password", "neko", "member multiuser provider: user password")
+ cmd.PersistentFlags().String("member.multiuser.user_password", "neko", "member multiuser provider: password for regular users")
if err := viper.BindPFlag("member.multiuser.user_password", cmd.PersistentFlags().Lookup("member.multiuser.user_password")); err != nil {
return err
}
- cmd.PersistentFlags().String("member.multiuser.admin_password", "admin", "member multiuser provider: admin password")
+ cmd.PersistentFlags().String("member.multiuser.admin_password", "admin", "member multiuser provider: password for admin users")
if err := viper.BindPFlag("member.multiuser.admin_password", cmd.PersistentFlags().Lookup("member.multiuser.admin_password")); err != nil {
return err
}
- cmd.PersistentFlags().String("member.multiuser.user_profile", "{}", "member multiuser provider: user profile in JSON format")
+ cmd.PersistentFlags().String("member.multiuser.user_profile", "{}", "member multiuser provider: profile template for regular users")
if err := viper.BindPFlag("member.multiuser.user_profile", cmd.PersistentFlags().Lookup("member.multiuser.user_profile")); err != nil {
return err
}
- cmd.PersistentFlags().String("member.multiuser.admin_profile", "{}", "member multiuser provider: admin profile in JSON format")
+ cmd.PersistentFlags().String("member.multiuser.admin_profile", "{}", "member multiuser provider: profile template for admin users")
if err := viper.BindPFlag("member.multiuser.admin_profile", cmd.PersistentFlags().Lookup("member.multiuser.admin_profile")); err != nil {
return err
}
@@ -162,7 +162,7 @@ func (s *Member) SetV2() {
// set legacy flag if any V2 configuration was used
if !viper.IsSet("legacy") && enableLegacy {
- log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message")
+ log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details")
viper.Set("legacy", true)
}
}
diff --git a/server/internal/config/root.go b/server/internal/config/root.go
index ab6ddc8b..c8902087 100644
--- a/server/internal/config/root.go
+++ b/server/internal/config/root.go
@@ -139,7 +139,7 @@ func (s *Root) SetV2() {
// set legacy flag if any V2 configuration was used
if !viper.IsSet("legacy") && enableLegacy {
- log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message")
+ log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details")
viper.Set("legacy", true)
}
}
diff --git a/server/internal/config/server.go b/server/internal/config/server.go
index 8491d43c..99be15f4 100644
--- a/server/internal/config/server.go
+++ b/server/internal/config/server.go
@@ -172,7 +172,7 @@ func (s *Server) SetV2() {
// set legacy flag if any V2 configuration was used
if !viper.IsSet("legacy") && enableLegacy {
- log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message")
+ log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details")
viper.Set("legacy", true)
}
}
diff --git a/server/internal/config/session.go b/server/internal/config/session.go
index 4349ee9c..a38d05aa 100644
--- a/server/internal/config/session.go
+++ b/server/internal/config/session.go
@@ -206,7 +206,7 @@ func (s *Session) SetV2() {
// set legacy flag if any V2 configuration was used
if !viper.IsSet("legacy") && enableLegacy {
- log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message")
+ log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details")
viper.Set("legacy", true)
}
}
diff --git a/server/internal/config/webrtc.go b/server/internal/config/webrtc.go
index c28593df..bc202d70 100644
--- a/server/internal/config/webrtc.go
+++ b/server/internal/config/webrtc.go
@@ -67,17 +67,17 @@ func (WebRTC) Init(cmd *cobra.Command) error {
}
// Looks like this is conflicting with the frontend and backend ICE servers since latest versions
- //cmd.PersistentFlags().String("webrtc.iceservers", "[]", "Global STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys")
+ //cmd.PersistentFlags().String("webrtc.iceservers", "[]", "STUN and TURN servers used by the ICE agent")
//if err := viper.BindPFlag("webrtc.iceservers", cmd.PersistentFlags().Lookup("webrtc.iceservers")); err != nil {
// return err
//}
- cmd.PersistentFlags().String("webrtc.iceservers.frontend", "[]", "Frontend only STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys")
+ cmd.PersistentFlags().String("webrtc.iceservers.frontend", "[]", "STUN and TURN servers used by the frontend")
if err := viper.BindPFlag("webrtc.iceservers.frontend", cmd.PersistentFlags().Lookup("webrtc.iceservers.frontend")); err != nil {
return err
}
- cmd.PersistentFlags().String("webrtc.iceservers.backend", "[]", "Backend only STUN and TURN servers in JSON format with `urls`, `username` and `credential` keys")
+ cmd.PersistentFlags().String("webrtc.iceservers.backend", "[]", "STUN and TURN servers used by the backend")
if err := viper.BindPFlag("webrtc.iceservers.backend", cmd.PersistentFlags().Lookup("webrtc.iceservers.backend")); err != nil {
return err
}
@@ -217,14 +217,14 @@ func (s *WebRTC) Set() {
// parse frontend ice servers
if err := viper.UnmarshalKey("webrtc.iceservers.frontend", &s.ICEServersFrontend, viper.DecodeHook(
- utils.JsonStringAutoDecode([]types.ICEServer{}),
+ utils.JsonStringAutoDecode(s.ICEServersFrontend),
)); err != nil {
log.Warn().Err(err).Msgf("unable to parse frontend ICE servers")
}
// parse backend ice servers
if err := viper.UnmarshalKey("webrtc.iceservers.backend", &s.ICEServersBackend, viper.DecodeHook(
- utils.JsonStringAutoDecode([]types.ICEServer{}),
+ utils.JsonStringAutoDecode(s.ICEServersBackend),
)); err != nil {
log.Warn().Err(err).Msgf("unable to parse backend ICE servers")
}
@@ -238,7 +238,7 @@ func (s *WebRTC) Set() {
// parse global ice servers
var iceServers []types.ICEServer
if err := viper.UnmarshalKey("webrtc.iceservers", &iceServers, viper.DecodeHook(
- utils.JsonStringAutoDecode([]types.ICEServer{}),
+ utils.JsonStringAutoDecode(iceServers),
)); err != nil {
log.Warn().Err(err).Msgf("unable to parse global ICE servers")
}
@@ -409,7 +409,7 @@ func (s *WebRTC) SetV2() {
// set legacy flag if any V2 configuration was used
if !viper.IsSet("legacy") && enableLegacy {
- log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, or set 'NEKO_LEGACY=true' to acknowledge this message")
+ log.Warn().Msg("legacy configuration is enabled because at least one V2 configuration was used, please migrate to V3 configuration, visit https://neko.m1k1o.net/docs/v3/migration-from-v2 for more details")
viper.Set("legacy", true)
}
}
diff --git a/server/internal/desktop/clipboard.go b/server/internal/desktop/clipboard.go
index 49c2f00f..fff20d8b 100644
--- a/server/internal/desktop/clipboard.go
+++ b/server/internal/desktop/clipboard.go
@@ -10,14 +10,19 @@ import (
"github.com/m1k1o/neko/server/pkg/xevent"
)
+const (
+ ClipboardTextPlainTarget = "UTF8_STRING"
+ ClipboardTextHtmlTarget = "text/html"
+)
+
func (manager *DesktopManagerCtx) ClipboardGetText() (*types.ClipboardText, error) {
- text, err := manager.ClipboardGetBinary("STRING")
+ text, err := manager.ClipboardGetBinary(ClipboardTextPlainTarget)
if err != nil {
return nil, err
}
// Rich text must not always be available, can fail silently.
- html, _ := manager.ClipboardGetBinary("text/html")
+ html, _ := manager.ClipboardGetBinary(ClipboardTextHtmlTarget)
return &types.ClipboardText{
Text: string(text),
@@ -31,10 +36,10 @@ func (manager *DesktopManagerCtx) ClipboardSetText(data types.ClipboardText) err
// is set, if available. Otherwise plain text.
if data.HTML != "" {
- return manager.ClipboardSetBinary("text/html", []byte(data.HTML))
+ return manager.ClipboardSetBinary(ClipboardTextHtmlTarget, []byte(data.HTML))
}
- return manager.ClipboardSetBinary("STRING", []byte(data.Text))
+ return manager.ClipboardSetBinary(ClipboardTextPlainTarget, []byte(data.Text))
}
func (manager *DesktopManagerCtx) ClipboardGetBinary(mime string) ([]byte, error) {
@@ -53,6 +58,23 @@ func (manager *DesktopManagerCtx) ClipboardGetBinary(mime string) ([]byte, error
return stdout.Bytes(), nil
}
+func (manager *DesktopManagerCtx) replaceClipboardCommand(newCmd *exec.Cmd) {
+ // Swap the current clipboard command with the new one.
+ oldCmd := manager.clipboardCommand.Swap(newCmd)
+
+ // If the command is already running, we need to shutdown it properly.
+ if oldCmd == nil || oldCmd.ProcessState != nil {
+ return
+ }
+
+ // If there is a previous command running and it was not stopped yet, we need to kill it.
+ if err := oldCmd.Process.Kill(); err != nil {
+ manager.logger.Err(err).Msg("failed to kill previous clipboard command")
+ } else {
+ manager.logger.Debug().Msg("killed previous clipboard command")
+ }
+}
+
func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) error {
cmd := exec.Command("xclip", "-selection", "clipboard", "-in", "-target", mime)
@@ -64,7 +86,9 @@ func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) e
return err
}
- // TODO: Refactor.
+ // Shutdown previous command if it exists and replace it with the new one.
+ manager.replaceClipboardCommand(cmd)
+
// We need to wait until the data came to the clipboard.
wait := make(chan struct{})
xevent.Emmiter.Once("clipboard-updated", func(payload ...any) {
@@ -84,9 +108,23 @@ func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) e
stdin.Close()
- // TODO: Refactor.
- // cmd.Wait()
- <-wait
+ select {
+ case <-manager.shutdown:
+ return fmt.Errorf("clipboard manager is shutting down")
+ case <-wait:
+ }
+
+ manager.wg.Add(1)
+ go func() {
+ defer manager.wg.Done()
+
+ if err := cmd.Wait(); err != nil {
+ msg := strings.TrimSpace(stderr.String())
+ manager.logger.Err(err).Msgf("clipboard command finished with error: %s", msg)
+ } else {
+ manager.logger.Debug().Msg("clipboard command finished successfully")
+ }
+ }()
return nil
}
diff --git a/server/internal/desktop/manager.go b/server/internal/desktop/manager.go
index 0ffadd73..aca5fe1a 100644
--- a/server/internal/desktop/manager.go
+++ b/server/internal/desktop/manager.go
@@ -1,7 +1,9 @@
package desktop
import (
+ "os/exec"
"sync"
+ "sync/atomic"
"time"
"github.com/kataras/go-events"
@@ -25,6 +27,11 @@ type DesktopManagerCtx struct {
config *config.Desktop
screenSize types.ScreenSize // cached screen size
input xinput.Driver
+
+ // Clipboard process holding the most recent clipboard data.
+ // It must remain running to allow pasting clipboard data.
+ // The last command is kept running until it is replaced or shutdown.
+ clipboardCommand atomic.Pointer[exec.Cmd]
}
func New(config *config.Desktop) *DesktopManagerCtx {
@@ -131,6 +138,8 @@ func (manager *DesktopManagerCtx) Shutdown() error {
manager.logger.Info().Msgf("shutdown")
close(manager.shutdown)
+
+ manager.replaceClipboardCommand(nil)
manager.wg.Wait()
xorg.DisplayClose()
diff --git a/server/internal/http/legacy/handler.go b/server/internal/http/legacy/handler.go
index 65a0349b..bfd29f3a 100644
--- a/server/internal/http/legacy/handler.go
+++ b/server/internal/http/legacy/handler.go
@@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"strings"
+ "time"
"github.com/m1k1o/neko/server/internal/api"
oldEvent "github.com/m1k1o/neko/server/internal/http/legacy/event"
@@ -35,9 +36,6 @@ var (
return true
},
}
-
- // DefaultDialer is a dialer with all fields set to the default zero values.
- DefaultDialer = websocket.DefaultDialer
)
type LegacyHandler struct {
@@ -45,16 +43,21 @@ type LegacyHandler struct {
serverAddr string
bannedIPs map[string]struct{}
sessionIPs map[string]string
+ wsDialer *websocket.Dialer
}
-func New() *LegacyHandler {
+func New(serverAddr string) *LegacyHandler {
// Init
return &LegacyHandler{
logger: log.With().Str("module", "legacy").Logger(),
- serverAddr: "127.0.0.1:8080",
+ serverAddr: serverAddr,
bannedIPs: make(map[string]struct{}),
sessionIPs: make(map[string]string),
+ wsDialer: &websocket.Dialer{
+ Proxy: nil, // disable proxy for local requests
+ HandshakeTimeout: 45 * time.Second,
+ },
}
}
@@ -99,7 +102,7 @@ func (h *LegacyHandler) Route(r types.Router) {
defer s.destroy()
// dial to the remote backend
- connBackend, _, err := DefaultDialer.Dial("ws://"+h.serverAddr+"/api/ws?token="+url.QueryEscape(s.token), nil)
+ connBackend, _, err := h.wsDialer.Dial("ws://"+h.serverAddr+"/api/ws?token="+url.QueryEscape(s.token), nil)
if err != nil {
h.logger.Error().Err(err).Msg("couldn't dial to the remote backend")
@@ -142,10 +145,12 @@ func (h *LegacyHandler) Route(r types.Router) {
m = websocket.FormatCloseMessage(e.Code, e.Text)
}
}
- errc <- err
+ errc <- fmt.Errorf("src read message error: %w", err)
dst.WriteMessage(websocket.CloseMessage, m)
break
}
+
+ // handle text messages
if msgType == websocket.TextMessage {
err = rewriteTextMessage(msg)
@@ -162,12 +167,26 @@ func (h *LegacyHandler) Route(r types.Router) {
Message: strings.ReplaceAll(err.Error(), ErrBackendRespone.Error()+": ", ""),
})
continue
- } else if errors.Is(err, ErrWebsocketSend) {
+ }
+
+ if errors.Is(err, ErrWebsocketSend) {
+ errc <- fmt.Errorf("dst write message error: %w", err)
+ break
+ }
+
+ h.logger.Error().Err(err).Msg("couldn't rewrite text message")
+ continue
+ }
+
+ // forward ping pong messages
+ if msgType == websocket.PingMessage ||
+ msgType == websocket.PongMessage {
+ err = dst.WriteMessage(msgType, msg)
+ if err != nil {
errc <- err
break
- } else {
- h.logger.Error().Err(err).Msg("couldn't rewrite text message")
}
+ continue
}
}
}
@@ -181,9 +200,9 @@ func (h *LegacyHandler) Route(r types.Router) {
var message string
select {
case err = <-errClient:
- message = "websocketproxy: Error when copying from backend to client: %v"
+ message = "websocketproxy: Error when copying from backend to client"
case err = <-errBackend:
- message = "websocketproxy: Error when copying from client to backend: %v"
+ message = "websocketproxy: Error when copying from client to backend"
}
if e, ok := err.(*websocket.CloseError); !ok || e.Code == websocket.CloseAbnormalClosure {
diff --git a/server/internal/http/legacy/session.go b/server/internal/http/legacy/session.go
index bd28aa03..02f45ec0 100644
--- a/server/internal/http/legacy/session.go
+++ b/server/internal/http/legacy/session.go
@@ -55,13 +55,18 @@ type session struct {
}
func (h *LegacyHandler) newSession(r *http.Request) *session {
+ transport := http.DefaultTransport.(*http.Transport).Clone()
+ transport.Proxy = nil // disable proxy for local requests
+
return &session{
r: r,
h: h,
logger: h.logger,
serverAddr: h.serverAddr,
- client: http.DefaultClient,
- sessions: make(map[string]*memberStruct),
+ client: &http.Client{
+ Transport: transport,
+ },
+ sessions: make(map[string]*memberStruct),
}
}
diff --git a/server/internal/http/manager.go b/server/internal/http/manager.go
index 8aadd086..acea79db 100644
--- a/server/internal/http/manager.go
+++ b/server/internal/http/manager.go
@@ -2,6 +2,7 @@ package http
import (
"context"
+ "net"
"net/http"
"os"
@@ -56,11 +57,6 @@ func New(WebSocketManager types.WebSocketManager, ApiManager types.ApiManager, c
return config.AllowOrigin(r.Header.Get("Origin"))
}))
- // Legacy handler
- if viper.GetBool("legacy") {
- legacy.New().Route(router)
- }
-
batch := batchHandler{
Router: router,
PathPrefix: "/api",
@@ -122,6 +118,24 @@ func (manager *HttpManagerCtx) Start() {
}
}()
manager.logger.Info().Msgf("https listening on %s", manager.http.Addr)
+
+ // if we have legacy mode, we need to start local http server too
+ if viper.GetBool("legacy") {
+ // create a listener for the API server with a random port
+ listener, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ manager.logger.Panic().Err(err).Msg("unable to start legacy http proxy")
+ }
+
+ go func() {
+ if err := http.Serve(listener, manager.router); err != http.ErrServerClosed {
+ manager.logger.Panic().Err(err).Msg("unable to start http server")
+ }
+ }()
+ manager.logger.Info().Msgf("legacy proxy listening on %s", listener.Addr().String())
+
+ legacy.New(listener.Addr().String()).Route(manager.router)
+ }
} else {
go func() {
if err := manager.http.ListenAndServe(); err != http.ErrServerClosed {
@@ -129,6 +143,11 @@ func (manager *HttpManagerCtx) Start() {
}
}()
manager.logger.Info().Msgf("http listening on %s", manager.http.Addr)
+
+ // start legacy proxy if enabled
+ if viper.GetBool("legacy") {
+ legacy.New(manager.http.Addr).Route(manager.router)
+ }
}
}
diff --git a/server/internal/webrtc/legacyhandler.go b/server/internal/webrtc/legacyhandler.go
index e7d05c98..1185e6e0 100644
--- a/server/internal/webrtc/legacyhandler.go
+++ b/server/internal/webrtc/legacyhandler.go
@@ -78,7 +78,7 @@ func (manager *WebRTCManagerCtx) handleLegacy(
}
logger.
- Debug().
+ Trace().
Str("x", strconv.Itoa(int(payload.X))).
Str("y", strconv.Itoa(int(payload.Y))).
Msg("scroll")
@@ -97,7 +97,7 @@ func (manager *WebRTCManagerCtx) handleLegacy(
return nil
}
- logger.Debug().Msgf("button down %d", payload.Key)
+ logger.Trace().Msgf("button down %d", payload.Key)
} else {
err := manager.desktop.KeyDown(uint32(payload.Key))
if err != nil {
@@ -105,7 +105,7 @@ func (manager *WebRTCManagerCtx) handleLegacy(
return nil
}
- logger.Debug().Msgf("key down %d", payload.Key)
+ logger.Trace().Msgf("key down %d", payload.Key)
}
case OP_KEY_UP:
payload := &PayloadKey{}
@@ -121,7 +121,7 @@ func (manager *WebRTCManagerCtx) handleLegacy(
return nil
}
- logger.Debug().Msgf("button up %d", payload.Key)
+ logger.Trace().Msgf("button up %d", payload.Key)
} else {
err := manager.desktop.KeyUp(uint32(payload.Key))
if err != nil {
@@ -129,7 +129,7 @@ func (manager *WebRTCManagerCtx) handleLegacy(
return nil
}
- logger.Debug().Msgf("key up %d", payload.Key)
+ logger.Trace().Msgf("key up %d", payload.Key)
}
case OP_KEY_CLK:
// unused
diff --git a/server/internal/websocket/manager.go b/server/internal/websocket/manager.go
index 0f7baafd..9fc21f60 100644
--- a/server/internal/websocket/manager.go
+++ b/server/internal/websocket/manager.go
@@ -276,7 +276,9 @@ func (manager *WebSocketManagerCtx) connect(connection *websocket.Conn, r *http.
e, ok := err.(*websocket.CloseError)
if !ok {
- err = errors.Unwrap(err) // unwrap if possible
+ if e := errors.Unwrap(err); e != nil {
+ err = e // unwrap if possible
+ }
logger.Warn().Err(err).Msg("read message error")
// client is expected to reconnect soon
delayedDisconnect = true
diff --git a/server/internal/websocket/peer.go b/server/internal/websocket/peer.go
index 3861e3a0..0c2fd553 100644
--- a/server/internal/websocket/peer.go
+++ b/server/internal/websocket/peer.go
@@ -43,7 +43,9 @@ func (peer *WebSocketPeerCtx) Send(event string, payload any) {
})
if err != nil {
- err = errors.Unwrap(err) // unwrap if possible
+ if e := errors.Unwrap(err); e != nil {
+ err = e // unwrap if possible
+ }
peer.logger.Warn().Err(err).Str("event", event).Msg("send message error")
return
}
diff --git a/utils/docker/go.mod b/utils/docker/go.mod
new file mode 100644
index 00000000..62bde8c0
--- /dev/null
+++ b/utils/docker/go.mod
@@ -0,0 +1,3 @@
+module github.com/m1k1o/neko/utils/docker
+
+go 1.24.1
diff --git a/docker/main.go b/utils/docker/main.go
similarity index 100%
rename from docker/main.go
rename to utils/docker/main.go
diff --git a/runtime/xorg-deps/Dockerfile b/utils/xorg-deps/Dockerfile
similarity index 100%
rename from runtime/xorg-deps/Dockerfile
rename to utils/xorg-deps/Dockerfile
diff --git a/runtime/xorg-deps/xf86-input-neko/.gitignore b/utils/xorg-deps/xf86-input-neko/.gitignore
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/.gitignore
rename to utils/xorg-deps/xf86-input-neko/.gitignore
diff --git a/runtime/xorg-deps/xf86-input-neko/80-neko.conf b/utils/xorg-deps/xf86-input-neko/80-neko.conf
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/80-neko.conf
rename to utils/xorg-deps/xf86-input-neko/80-neko.conf
diff --git a/runtime/xorg-deps/xf86-input-neko/COPYING b/utils/xorg-deps/xf86-input-neko/COPYING
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/COPYING
rename to utils/xorg-deps/xf86-input-neko/COPYING
diff --git a/runtime/xorg-deps/xf86-input-neko/Dockerfile b/utils/xorg-deps/xf86-input-neko/Dockerfile
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/Dockerfile
rename to utils/xorg-deps/xf86-input-neko/Dockerfile
diff --git a/runtime/xorg-deps/xf86-input-neko/Makefile.am b/utils/xorg-deps/xf86-input-neko/Makefile.am
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/Makefile.am
rename to utils/xorg-deps/xf86-input-neko/Makefile.am
diff --git a/runtime/xorg-deps/xf86-input-neko/README.md b/utils/xorg-deps/xf86-input-neko/README.md
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/README.md
rename to utils/xorg-deps/xf86-input-neko/README.md
diff --git a/runtime/xorg-deps/xf86-input-neko/autogen-clean.sh b/utils/xorg-deps/xf86-input-neko/autogen-clean.sh
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/autogen-clean.sh
rename to utils/xorg-deps/xf86-input-neko/autogen-clean.sh
diff --git a/runtime/xorg-deps/xf86-input-neko/autogen.sh b/utils/xorg-deps/xf86-input-neko/autogen.sh
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/autogen.sh
rename to utils/xorg-deps/xf86-input-neko/autogen.sh
diff --git a/runtime/xorg-deps/xf86-input-neko/configure.ac b/utils/xorg-deps/xf86-input-neko/configure.ac
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/configure.ac
rename to utils/xorg-deps/xf86-input-neko/configure.ac
diff --git a/runtime/xorg-deps/xf86-input-neko/m4/.gitkeep b/utils/xorg-deps/xf86-input-neko/m4/.gitkeep
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/m4/.gitkeep
rename to utils/xorg-deps/xf86-input-neko/m4/.gitkeep
diff --git a/runtime/xorg-deps/xf86-input-neko/release.sh b/utils/xorg-deps/xf86-input-neko/release.sh
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/release.sh
rename to utils/xorg-deps/xf86-input-neko/release.sh
diff --git a/runtime/xorg-deps/xf86-input-neko/src/Makefile.am b/utils/xorg-deps/xf86-input-neko/src/Makefile.am
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/src/Makefile.am
rename to utils/xorg-deps/xf86-input-neko/src/Makefile.am
diff --git a/runtime/xorg-deps/xf86-input-neko/src/neko.c b/utils/xorg-deps/xf86-input-neko/src/neko.c
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/src/neko.c
rename to utils/xorg-deps/xf86-input-neko/src/neko.c
diff --git a/runtime/xorg-deps/xf86-input-neko/xorg-neko.pc.in b/utils/xorg-deps/xf86-input-neko/xorg-neko.pc.in
similarity index 100%
rename from runtime/xorg-deps/xf86-input-neko/xorg-neko.pc.in
rename to utils/xorg-deps/xf86-input-neko/xorg-neko.pc.in
diff --git a/runtime/xorg-deps/xf86-video-dummy/01_v0.3.8_xdummy-randr.patch b/utils/xorg-deps/xf86-video-dummy/01_v0.3.8_xdummy-randr.patch
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/01_v0.3.8_xdummy-randr.patch
rename to utils/xorg-deps/xf86-video-dummy/01_v0.3.8_xdummy-randr.patch
diff --git a/runtime/xorg-deps/xf86-video-dummy/README.md b/utils/xorg-deps/xf86-video-dummy/README.md
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/README.md
rename to utils/xorg-deps/xf86-video-dummy/README.md
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/COPYING b/utils/xorg-deps/xf86-video-dummy/v0.3.8/COPYING
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/COPYING
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/COPYING
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/ChangeLog b/utils/xorg-deps/xf86-video-dummy/v0.3.8/ChangeLog
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/ChangeLog
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/ChangeLog
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.am b/utils/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.am
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.am
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.am
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.in b/utils/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.in
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.in
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/Makefile.in
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/README b/utils/xorg-deps/xf86-video-dummy/v0.3.8/README
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/README
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/README
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/aclocal.m4 b/utils/xorg-deps/xf86-video-dummy/v0.3.8/aclocal.m4
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/aclocal.m4
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/aclocal.m4
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/compile b/utils/xorg-deps/xf86-video-dummy/v0.3.8/compile
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/compile
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/compile
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.guess b/utils/xorg-deps/xf86-video-dummy/v0.3.8/config.guess
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.guess
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/config.guess
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.h.in b/utils/xorg-deps/xf86-video-dummy/v0.3.8/config.h.in
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.h.in
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/config.h.in
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.sub b/utils/xorg-deps/xf86-video-dummy/v0.3.8/config.sub
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/config.sub
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/config.sub
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/configure b/utils/xorg-deps/xf86-video-dummy/v0.3.8/configure
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/configure
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/configure
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/configure.ac b/utils/xorg-deps/xf86-video-dummy/v0.3.8/configure.ac
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/configure.ac
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/configure.ac
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/depcomp b/utils/xorg-deps/xf86-video-dummy/v0.3.8/depcomp
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/depcomp
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/depcomp
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/install-sh b/utils/xorg-deps/xf86-video-dummy/v0.3.8/install-sh
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/install-sh
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/install-sh
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/ltmain.sh b/utils/xorg-deps/xf86-video-dummy/v0.3.8/ltmain.sh
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/ltmain.sh
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/ltmain.sh
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/missing b/utils/xorg-deps/xf86-video-dummy/v0.3.8/missing
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/missing
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/missing
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.am b/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.am
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.am
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.am
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.in b/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.in
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.in
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/src/Makefile.in
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/compat-api.h b/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/compat-api.h
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/compat-api.h
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/src/compat-api.h
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy.h b/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy.h
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy.h
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy.h
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_cursor.c b/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_cursor.c
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_cursor.c
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_cursor.c
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_dga.c b/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_dga.c
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_dga.c
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_dga.c
diff --git a/runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_driver.c b/utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_driver.c
similarity index 100%
rename from runtime/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_driver.c
rename to utils/xorg-deps/xf86-video-dummy/v0.3.8/src/dummy_driver.c
diff --git a/webpage/docs/app-customization/chromium-browsers.md b/webpage/docs/app-customization/chromium-browsers.md
deleted file mode 100644
index e89e1928..00000000
--- a/webpage/docs/app-customization/chromium-browsers.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 1
----
-
-# Chromium-based Browsers
-
-Custom settings, bookmarks, and persistence across sessions.
diff --git a/webpage/docs/app-customization/customizing-ui.md b/webpage/docs/app-customization/customizing-ui.md
deleted file mode 100644
index cccd3aa7..00000000
--- a/webpage/docs/app-customization/customizing-ui.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 4
----
-
-# Customizing the UI
-
-Modifying Nekoβs interface, branding, and user experience.
diff --git a/webpage/docs/app-customization/firefox-customization.md b/webpage/docs/app-customization/firefox-customization.md
deleted file mode 100644
index 3b61efc6..00000000
--- a/webpage/docs/app-customization/firefox-customization.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 2
----
-
-# Firefox Customization
-
-Specific tweaks for running and configuring Firefox in Neko.
diff --git a/webpage/docs/app-customization/persistent-data-storage.md b/webpage/docs/app-customization/persistent-data-storage.md
deleted file mode 100644
index 3bd40ea4..00000000
--- a/webpage/docs/app-customization/persistent-data-storage.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 3
----
-
-# Persistent Data Storage
-
-How to persist browser data (history, bookmarks, extensions) across sessions.
diff --git a/webpage/docs/configuration/README.md b/webpage/docs/configuration/README.md
index a813bf0a..f5127104 100644
--- a/webpage/docs/configuration/README.md
+++ b/webpage/docs/configuration/README.md
@@ -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,24 +293,23 @@ 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
-```
+
- whether private mode is enabled, users do not receive the room video or audio.
- whether logins are locked for users, admins can still login.
- whether controls are locked for users, admins can still control.
- users can gain control only if at least one admin is in the room.
-- allows switching control implicitly without the need for explicit control request before
-- whether to show inactive cursors server-wide (only for users that have it enabled in their profile)
+- automatically grants control to a user when they click on the screen, unless an admin has locked the controls.
+- whether to show inactive cursors server-wide (only for users that have it enabled in their profile).
- whether to allow reconnecting to the websocket even if the previous connection was not closed. This means that a new login can kick out the previous one.
- interval in seconds for sending a heartbeat message to the server. This is used to keep the connection alive and to detect when the connection is lost.
@@ -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"
-```
+
- 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.
- and 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:
- json: true
- level: "info"
- nocolor: true
- time: "unix"
-```
+
- directory to store logs. If empty, logs are written to stdout. This is useful when running neko in a container.
- 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';
-
-
+
## Next Steps {#next}
diff --git a/webpage/docs/configuration/authentication.md b/webpage/docs/configuration/authentication.md
index 3fd1f758..8bf73322 100644
--- a/webpage/docs/configuration/authentication.md
+++ b/webpage/docs/configuration/authentication.md
@@ -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
@@ -78,6 +80,10 @@ import TabItem from '@theme/TabItem';
Member providers are responsible for deciding whether given credentials are valid or not. This validation can either be done against a local database or an external system.
+:::info
+Currently, Neko supports configuring only one authentication provider at a time. This means you must choose a single provider that best fits your deployment needs.
+:::
+
### Multi-User Provider {#member.multiuser}
This is the **default provider** that works exactly like the authentication used to work in v2 of neko.
@@ -86,21 +92,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:
- ...
-```
+See example configuration
@@ -152,8 +162,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 +171,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
-```
+
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 +251,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:
- ...
-```
+See example configuration
@@ -296,10 +303,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
-```
+
:::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 +317,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
-```
+
:::info
In the future, we plan to add more session providers, such as Redis, PostgreSQL, etc. So the Configuration Options may change.
@@ -324,10 +329,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"
-```
+',
+}} 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 +351,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: ""
-```
+
- - Whether the cookies are enabled or not.
- - Name of the cookie used to store the session.
diff --git a/webpage/docs/configuration/capture.md b/webpage/docs/configuration/capture.md
index 2778c0d5..be998e71 100644
--- a/webpage/docs/configuration/capture.md
+++ b/webpage/docs/configuration/capture.md
@@ -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 "
- codec: "vp8" # default video codec
- ids: [ , , ... ]
- pipelines:
- :
- :
- ...
-```
+
- 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.
- 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.
- is a list of pipeline ids that are defined in the section. The first pipeline in the list will be the default pipeline.
-- 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).
+- is a shorthand for defining [Gstreamer pipeline description](#video.gst_pipeline) for a single pipeline. This is option is ignored if is defined.
+- 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';
-### 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 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 ! ! appsink name=appsink"
+ximagesrc display-name={display} show-pointer=true use-damage=false ! ! 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.
@@ -189,8 +188,9 @@ See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentatio
hq:
gst_pipeline: |
ximagesrc display-name={display} show-pointer=true use-damage=false
- ! videoconvert
+ ! videoconvert ! queue
! vp8enc
+ name=encoder
target-bitrate=3072000
cpu-used=4
end-usage=cbr
@@ -207,8 +207,9 @@ See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentatio
lq:
gst_pipeline: |
ximagesrc display-name={display} show-pointer=true use-damage=false
- ! videoconvert
+ ! videoconvert ! queue
! vp8enc
+ name=encoder
target-bitrate=1024000
cpu-used=4
end-usage=cbr
@@ -236,8 +237,9 @@ See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentatio
main:
gst_pipeline: |
ximagesrc display-name={display} show-pointer=true use-damage=false
- ! videoconvert
+ ! videoconvert ! queue
! x264enc
+ name=encoder
threads=4
bitrate=4096
key-int-max=15
@@ -248,23 +250,62 @@ See documentation for [ximagesrc](https://gstreamer.freedesktop.org/documentatio
! appsink name=appsink
```
+
+
+ ```yaml title="config.yaml"
+ capture:
+ video:
+ codec: h264
+ ids: [ main ]
+ pipelines:
+ main:
+ gst_pipeline: |
+ ximagesrc display-name={display} show-pointer=true use-damage=false
+ ! videoconvert ! queue
+ ! video/x-raw,format=NV12
+ ! nvh264enc
+ name=encoder
+ preset=2
+ gop-size=25
+ spatial-aq=true
+ temporal-aq=true
+ bitrate=4096
+ vbv-buffer-size=4096
+ rc-mode=6
+ ! h264parse config-interval=-1
+ ! video/x-h264,stream-format=byte-stream
+ ! appsink name=appsink
+ ```
+
+ This configuration requires [Nvidia GPU](https://developer.nvidia.com/cuda-gpus) with [NVENC](https://developer.nvidia.com/nvidia-video-codec-sdk) support.
+
+
+Overview of available encoders for each codec is shown in the table below. The encoder name is used in the parameter. The parameters for each encoder are different and you can find the documentation for each encoder in the links below.
+
+| codec | encoder | vaapi encoder | nvenc encoder |
+| ----- | ------- | ------------- | ------------- |
+| VP8 | [vp8enc](https://gstreamer.freedesktop.org/documentation/vpx/vp8enc.html?gi-language=c) | [vaapivp8enc](https://github.com/GStreamer/gstreamer-vaapi/blob/master/gst/vaapi/gstvaapiencode_vp8.c) | ? |
+| VP9 | [vp9enc](https://gstreamer.freedesktop.org/documentation/vpx/vp9enc.html?gi-language=c) | [vaapivp9enc](https://github.com/GStreamer/gstreamer-vaapi/blob/master/gst/vaapi/gstvaapiencode_vp9.c) | ? |
+| AV1 | [av1enc](https://gstreamer.freedesktop.org/documentation/aom/av1enc.html?gi-language=c) | ? | [nvav1enc](https://gstreamer.freedesktop.org/documentation/nvcodec/nvav1enc.html?gi-language=c) |
+| H264 | [x264enc](https://gstreamer.freedesktop.org/documentation/x264/index.html?gi-language=c) | [vaapih264enc](https://gstreamer.freedesktop.org/documentation/vaapi/vaapih264enc.html?gi-language=c) | [nvh264enc](https://gstreamer.freedesktop.org/documentation/nvcodec/nvh264enc.html?gi-language=c) |
+| H265 | [x265enc](https://gstreamer.freedesktop.org/documentation/x265/index.html?gi-language=c) | [vaapih265enc](https://gstreamer.freedesktop.org/documentation/vaapi/vaapih265enc.html?gi-language=c) | [nvh265enc](https://gstreamer.freedesktop.org/documentation/nvcodec/nvh265enc.html?gi-language=c) |
+
+
## WebRTC Audio {#audio}
Only one audio pipeline can be defined in neko. The audio pipeline is used to capture and encode audio, similar to the video pipeline. The encoded audio is then sent to the client using WebRTC.
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: ""
-```
+
- 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.
- 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 +334,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: ""
- url: "rtmp:////"
- autostart: true
-```
+
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 parameter.
- and are the bitrate settings for the default audio and video encoders expressed in kilobits per second.
- 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).
- 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.
-- 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.
+- is the URL of the RTMP server where the broadcast will be sent e.g. `rtmp:////`. This can be set later using the API if the URL is not known at the time of configuration or is expected to change.
- is a boolean value that determines whether the broadcast should start automatically when neko starts, works only if the URL is set.
@@ -380,14 +419,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: ""
-```
+
- is a boolean value that determines whether the screencast is enabled or not.
- 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 +459,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
-```
+
- is a boolean value that determines whether the webcam capture is enabled or not.
- 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 +498,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"
-```
+
- is a boolean value that determines whether the microphone capture is enabled or not.
- is the name of the [pulseaudio device](https://wiki.archlinux.org/title/PulseAudio/Examples) that will be used as a virtual microphone.
diff --git a/webpage/docs/configuration/desktop.md b/webpage/docs/configuration/desktop.md
index d1a3f47f..b492e9ce 100644
--- a/webpage/docs/configuration/desktop.md
+++ b/webpage/docs/configuration/desktop.md
@@ -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: ""
- screen: "1280x720@30" # default
-```
+
- 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.
- refers to the screen resolution and refresh rate. The format is `x@`. 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
-```
+
- enables the input device support. If not specified, the default is `false`.
- 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
-```
+
## 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
-```
+
## 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
-```
+
diff --git a/webpage/docs/configuration/help.json b/webpage/docs/configuration/help.json
index e449cf8e..8320f7e9 100644
--- a/webpage/docs/configuration/help.json
+++ b/webpage/docs/configuration/help.json
@@ -177,11 +177,20 @@
"key": [
"capture",
"video",
- "pipelines"
+ "pipeline"
],
"type": "string",
- "defaultValue": "[]",
- "description": "pipelines config in JSON used for video streaming"
+ "description": "shortcut for configuring only a single gstreamer pipeline, ignored if pipelines is set"
+ },
+ {
+ "key": [
+ "capture",
+ "video",
+ "pipelines"
+ ],
+ "type": "object",
+ "defaultValue": {},
+ "description": "pipelines config used for video streaming"
},
{
"key": [
@@ -295,7 +304,7 @@
],
"type": "boolean",
"defaultValue": "true",
- "description": "member file provider: whether to hash passwords using sha256 (recommended)"
+ "description": "member file provider: whether the passwords are hashed using sha256 or not (recommended)"
},
{
"key": [
@@ -304,7 +313,7 @@
"path"
],
"type": "string",
- "description": "member file provider: storage path"
+ "description": "member file provider: path to the file containing the users and their passwords"
},
{
"key": [
@@ -314,7 +323,7 @@
],
"type": "string",
"defaultValue": "admin",
- "description": "member multiuser provider: admin password"
+ "description": "member multiuser provider: password for admin users"
},
{
"key": [
@@ -322,9 +331,9 @@
"multiuser",
"admin_profile"
],
- "type": "string",
- "defaultValue": "{}",
- "description": "member multiuser provider: admin profile in JSON format"
+ "type": "object",
+ "defaultValue": {},
+ "description": "member multiuser provider: profile template for admin users"
},
{
"key": [
@@ -334,7 +343,7 @@
],
"type": "string",
"defaultValue": "neko",
- "description": "member multiuser provider: user password"
+ "description": "member multiuser provider: password for regular users"
},
{
"key": [
@@ -342,9 +351,9 @@
"multiuser",
"user_profile"
],
- "type": "string",
- "defaultValue": "{}",
- "description": "member multiuser provider: user profile in JSON format"
+ "type": "object",
+ "defaultValue": {},
+ "description": "member multiuser provider: profile template for regular users"
},
{
"key": [
@@ -352,9 +361,9 @@
"object",
"users"
],
- "type": "string",
- "defaultValue": "[]",
- "description": "member object provider: users in JSON format"
+ "type": "array",
+ "defaultValue": [],
+ "description": "member object provider: list of users with their passwords and profiles"
},
{
"key": [
@@ -363,7 +372,7 @@
],
"type": "string",
"defaultValue": "multiuser",
- "description": "choose member provider"
+ "description": "selected member provider"
},
{
"key": [
@@ -758,9 +767,9 @@
"iceservers",
"backend"
],
- "type": "urls",
- "defaultValue": "[]",
- "description": "Backend only STUN and TURN servers in JSON format with urls, `username` and `credential` keys"
+ "type": "array",
+ "defaultValue": [],
+ "description": "STUN and TURN servers used by the backend"
},
{
"key": [
@@ -768,9 +777,9 @@
"iceservers",
"frontend"
],
- "type": "urls",
- "defaultValue": "[]",
- "description": "Frontend only STUN and TURN servers in JSON format with urls, `username` and `credential` keys"
+ "type": "array",
+ "defaultValue": [],
+ "description": "STUN and TURN servers used by the frontend"
},
{
"key": [
diff --git a/webpage/docs/configuration/help.txt b/webpage/docs/configuration/help.txt
index 9ca36858..89ef9120 100644
--- a/webpage/docs/configuration/help.txt
+++ b/webpage/docs/configuration/help.txt
@@ -16,7 +16,8 @@
--capture.video.codec string video codec to be used (default "vp8")
--capture.video.display string X display to capture
--capture.video.ids strings ordered list of video ids
- --capture.video.pipelines string pipelines config in JSON used for video streaming (default "[]")
+ --capture.video.pipeline string shortcut for configuring only a single gstreamer pipeline, ignored if pipelines is set
+ --capture.video.pipelines string pipelines config used for video streaming (default "{}")
--capture.webcam.device string v4l2sink device used for webcam (default "/dev/video0")
--capture.webcam.enabled enable webcam stream
--capture.webcam.height int webcam stream height (default 720)
@@ -28,14 +29,14 @@
--desktop.screen string default screen size and framerate (default "1280x720@30")
--desktop.unminimize automatically unminimize window when it is minimized (default true)
--desktop.upload_drop whether drop upload is enabled (default true)
- --member.file.hash member file provider: whether to hash passwords using sha256 (recommended) (default true)
- --member.file.path string member file provider: storage path
- --member.multiuser.admin_password string member multiuser provider: admin password (default "admin")
- --member.multiuser.admin_profile string member multiuser provider: admin profile in JSON format (default "{}")
- --member.multiuser.user_password string member multiuser provider: user password (default "neko")
- --member.multiuser.user_profile string member multiuser provider: user profile in JSON format (default "{}")
- --member.object.users string member object provider: users in JSON format (default "[]")
- --member.provider string choose member provider (default "multiuser")
+ --member.file.hash member file provider: whether the passwords are hashed using sha256 or not (recommended) (default true)
+ --member.file.path string member file provider: path to the file containing the users and their passwords
+ --member.multiuser.admin_password string member multiuser provider: password for admin users (default "admin")
+ --member.multiuser.admin_profile string member multiuser provider: profile template for admin users (default "{}")
+ --member.multiuser.user_password string member multiuser provider: password for regular users (default "neko")
+ --member.multiuser.user_profile string member multiuser provider: profile template for regular users (default "{}")
+ --member.object.users string member object provider: list of users with their passwords and profiles (default "[]")
+ --member.provider string selected member provider (default "multiuser")
--plugins.dir string path to neko plugins to load (default "./bin/plugins")
--plugins.enabled load plugins in runtime
--plugins.required if true, neko will exit if there is an error when loading a plugin
@@ -78,8 +79,8 @@
--webrtc.estimator.unstable_duration duration how long to wait for stalled connection (neutral trend with low bandwidth) before downgrading (default 6s)
--webrtc.estimator.upgrade_backoff duration how long to wait before upgrading again after previous upgrade (default 5s)
--webrtc.icelite configures whether or not the ICE agent should be a lite agent
- --webrtc.iceservers.backend urls Backend only STUN and TURN servers in JSON format with urls, `username` and `credential` keys (default "[]")
- --webrtc.iceservers.frontend urls Frontend only STUN and TURN servers in JSON format with urls, `username` and `credential` keys (default "[]")
+ --webrtc.iceservers.backend string STUN and TURN servers used by the backend (default "[]")
+ --webrtc.iceservers.frontend string STUN and TURN servers used by the frontend (default "[]")
--webrtc.icetrickle configures whether cadidates should be sent asynchronously using Trickle ICE (default true)
--webrtc.ip_retrieval_url string URL address used for retrieval of the external IP address (default "https://checkip.amazonaws.com")
--webrtc.nat1to1 strings sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used
diff --git a/webpage/docs/configuration/plugins.md b/webpage/docs/configuration/plugins.md
index 9d6fc85a..5f6d5e38 100644
--- a/webpage/docs/configuration/plugins.md
+++ b/webpage/docs/configuration/plugins.md
@@ -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"
-```
+
- 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.
@@ -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
-```
+
- 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
-```
+
+
- enables the file transfer support. If set to `false`, the file transfer is disabled.
- 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.
-
diff --git a/webpage/docs/configuration/webrtc.md b/webpage/docs/configuration/webrtc.md
index 47c5face..6606480f 100644
--- a/webpage/docs/configuration/webrtc.md
+++ b/webpage/docs/configuration/webrtc.md
@@ -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
-```
+
### 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
-```
+
:::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"
-```
+
- - ICE servers that are sent to the client and used to establish a connection between the client and the server.
- - 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"
-```
+
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
-```
+
- - The port used for UDP connections.
- - 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}
+
-```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"
-```
+
+
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
-```
+
diff --git a/webpage/docs/customization/README.md b/webpage/docs/customization/README.md
new file mode 100644
index 00000000..1adf6cb0
--- /dev/null
+++ b/webpage/docs/customization/README.md
@@ -0,0 +1,62 @@
+# Customization
+
+When you want to customize the Neko virtual environment beyond the server configuration, you can do so by mounting your own files to the container that replace or extend the default files. This allows you to customize the desktop environment, browser settings, and more.
+
+## Supervisord Configuration {#supervisord}
+
+The Neko container uses [supervisord](https://supervisord.org/) to manage the processes inside the container. The search path for the configuration files is `/etc/neko/supervisord/.conf`. You can mount your own `app.conf` file to the directory `/etc/neko/supervisord/` to add a new process to the container.
+
+```config title="supervisord.conf"
+[program:app]
+environment=HOME="/home/%(ENV_USER)s",USER="%(ENV_USER)s",DISPLAY="%(ENV_DISPLAY)s"
+command=/opt/path/to/my-app
+stopsignal=INT
+autorestart=true
+priority=800
+user=%(ENV_USER)s
+stdout_logfile=/var/log/neko/app.log
+stdout_logfile_maxbytes=100MB
+stdout_logfile_backups=10
+redirect_stderr=true
+```
+
+For example, with firefox, you can mount your own `firefox.conf` file to the directory `/etc/neko/supervisord/` to overwrite the default configuration file and modify the command that starts Firefox. Make sure to copy the default configuration file from the container to your local machine first:
+
+```bash
+# Create a container without starting it
+docker create --name neko ghcr.io/m1k1o/neko/firefox:latest
+# Copy the default configuration file to your local machine
+docker cp neko:/etc/neko/supervisord/firefox.conf ./firefox.conf
+# Remove the container
+docker rm -f neko
+```
+
+Then, you can modify the configuration file to your liking and mount your new version to the container:
+
+```yaml title="docker-compose.yaml"
+services:
+ neko:
+ image: "ghcr.io/m1k1o/neko/firefox:latest"
+ restart: "unless-stopped"
+ shm_size: "2gb"
+ ports:
+ - "8080:8080"
+ - "52000-52100:52000-52100/udp"
+ # highlight-start
+ volumes:
+ - "./firefox.conf:/etc/neko/supervisord/firefox.conf"
+ # highlight-end
+ environment:
+ NEKO_DESKTOP_SCREEN: 1920x1080@30
+ NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko
+ NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin
+ NEKO_WEBRTC_EPR: 52000-52100
+ NEKO_WEBRTC_ICELITE: 1
+```
+
+## Next Steps
+
+import DocCardList from '@theme/DocCardList';
+import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
+
+
diff --git a/webpage/docs/customization/browsers.json b/webpage/docs/customization/browsers.json
new file mode 100644
index 00000000..c0393d4a
--- /dev/null
+++ b/webpage/docs/customization/browsers.json
@@ -0,0 +1,61 @@
+[
+ {
+ "tag": "firefox",
+ "flavor": "firefox-based",
+ "profileDir": "/home/neko/.mozilla/firefox/profile.default",
+ "policiesFile": "/usr/lib/firefox/distribution/policies.json"
+ },
+ {
+ "tag": "tor-browser",
+ "flavor": "firefox-based",
+ "profileDir": "/opt/tor-browser_en-US/Browser/TorBrowser/Data/Browser/profile.default",
+ "policiesFile": "/opt/tor-browser_en-US/Browser/distribution/policies.json"
+ },
+ {
+ "tag": "waterfox",
+ "flavor": "firefox-based",
+ "profileDir": "/home/neko/.waterfox/profile.default",
+ "policiesFile": "/usr/lib/waterfox/distribution/policies.json"
+ },
+ {
+ "tag": "chromium",
+ "flavor": "chromium-based",
+ "profileDir": "/home/neko/.config/chromium",
+ "policiesFile": "/etc/chromium/policies/managed/policies.json"
+ },
+ {
+ "tag": "google-chrome",
+ "flavor": "chromium-based",
+ "profileDir": "/home/neko/.config/google-chrome",
+ "policiesFile": "/etc/opt/chrome/policies/managed/policies.json"
+ },
+ {
+ "tag": "ungoogled-chromium",
+ "flavor": "chromium-based",
+ "profileDir": "/home/neko/.config/chromium",
+ "policiesFile": "/etc/chromium/policies/managed/policies.json"
+ },
+ {
+ "tag": "microsoft-edge",
+ "flavor": "chromium-based",
+ "profileDir": "/home/neko/.config/microsoft-edge",
+ "policiesFile": "/etc/opt/edge/policies/managed/policies.json"
+ },
+ {
+ "tag": "brave",
+ "flavor": "chromium-based",
+ "profileDir": "/home/neko/.config/brave",
+ "policiesFile": "/etc/brave/policies/managed/policies.json"
+ },
+ {
+ "tag": "vivaldi",
+ "flavor": "chromium-based",
+ "profileDir": "/home/neko/.config/vivaldi",
+ "policiesFile": "/etc/opt/vivaldi/policies/managed/policies.json"
+ },
+ {
+ "tag": "opera",
+ "flavor": "chromium-based",
+ "profileDir": "/home/neko/.config/opera"
+ }
+]
\ No newline at end of file
diff --git a/webpage/docs/customization/browsers.md b/webpage/docs/customization/browsers.md
new file mode 100644
index 00000000..6b3c5c0c
--- /dev/null
+++ b/webpage/docs/customization/browsers.md
@@ -0,0 +1,232 @@
+---
+sidebar_label: "Browsers"
+description: "Customize your browser settings and configurations in Neko."
+---
+
+import { AppIcon } from '@site/src/components/AppIcon';
+import { ProfileDirectoryPaths, PolicyFilePaths } from './browsers'
+
+# Browsers Customization
+
+Browsers use policies to manage settings and configurations programmatically. This is useful for Neko containers that can be set up with a specific configuration every time a fresh container is created. It also prevents users from changing certain settings, which is useful for security and privacy. For example, not allowing users to install extensions can prevent them from installing malicious extensions that could compromise their privacy and security.
+
+However, as a user of Neko, you may want to customize your browser settings (install your own extensions) or wish to have your bookmarks and settings persist across sessions.
+
+## Persistent Browser Profile {#persistent-profile}
+
+When you run a browser in a container, the browser runs in a fresh environment every time you create (or upgrade) the container. This means that any changes you make to the browser settings or your browsing history will be lost when you stop the container. This is because the container is ephemeral and does not persist data across sessions.
+
+If you want to persist your browser settings, bookmarks, extensions, and browsing history across sessions, you need to mount a volume to the browser's profile directory. This allows you to store your browser data outside of the container so it can be accessed even after the container is stopped or removed.
+
+```yaml title="docker-compose.yaml"
+services:
+ neko:
+ image: "ghcr.io/m1k1o/neko/firefox:latest"
+ restart: "unless-stopped"
+ shm_size: "2gb"
+ ports:
+ - "8080:8080"
+ - "52000-52100:52000-52100/udp"
+ # highlight-start
+ volumes:
+ - "./profile:/home/neko/.mozilla/firefox/profile.default"
+ # highlight-end
+ environment:
+ NEKO_DESKTOP_SCREEN: 1920x1080@30
+ NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko
+ NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin
+ NEKO_WEBRTC_EPR: 52000-52100
+ NEKO_WEBRTC_ICELITE: 1
+```
+
+Replace `./profile` with the path to the directory where you want to store your browser data. This directory will be created if it does not exist.
+
+Make sure to set the correct permissions for the directory so that the container can access it. The Neko user inside the container has a UID of `1000`, so you need to set the ownership of the directory to `1000:1000`. You can do this by running the following command:
+
+```bash
+sudo chown -R 1000:1000 ./profile
+```
+
+The path inside the container will be `/home/neko/.mozilla/firefox/profile.default`, which is the default profile directory for Firefox. You can find the profile directory for other browsers in the table below:
+
+
+
+## Browser Policy Files {#policy-files}
+
+Browser policy files are JSON files that contain settings and configurations for the browser. These files are used to manage the browser settings programmatically and can be used to enforce certain policies, such as disabling extensions, setting the homepage, and more.
+
+:::note
+In the example below, we are using the Firefox browser. Make sure you replace `/usr/lib/firefox/distribution/policies.json` with the correct path for the browser you are using according to the tables below.
+:::
+
+If you want to customize the policy file, you can mount your own policy file to the container. This allows you to customize the browser settings and configurations to your liking.
+
+```yaml title="docker-compose.yaml"
+services:
+ neko:
+ image: "ghcr.io/m1k1o/neko/firefox:latest"
+ restart: "unless-stopped"
+ shm_size: "2gb"
+ ports:
+ - "8080:8080"
+ - "52000-52100:52000-52100/udp"
+ # highlight-start
+ volumes:
+ - "./policy.json:/usr/lib/firefox/distribution/policies.json"
+ # highlight-end
+ environment:
+ NEKO_DESKTOP_SCREEN: 1920x1080@30
+ NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko
+ NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin
+ NEKO_WEBRTC_EPR: 52000-52100
+ NEKO_WEBRTC_ICELITE: 1
+```
+
+If you just want to modify the default policy file, you can copy the current policy file from the container to your local machine:
+
+```bash
+# Create a container without starting it
+docker create --name neko ghcr.io/m1k1o/neko/firefox:latest
+# Copy the policy file from the container to your local machine
+docker cp neko:/usr/lib/firefox/distribution/policies.json ./policy.json
+# Remove the container
+docker rm neko
+```
+
+Or you can download the default policy file from the repository directly:
+
+```bash
+# Replace firefox with the browser you are using
+curl -o ./policy.json https://raw.githubusercontent.com/m1k1o/neko/refs/heads/main/apps/firefox/policies.json
+```
+
+If you wish to disable the policies altogether, you can just create an empty JSON file. This will disable all policies and allow you to customize the browser settings as you wish.
+
+```json title="policy.json"
+{}
+```
+
+### Firefox-based Browsers {#firefox-based}
+
+The full configuration options for the Firefox-based policy JSON file can be found in the [Mozilla Policy Templates](https://mozilla.github.io/policy-templates/) documentation.
+
+The policy files are located in the following paths:
+
+
+
+**Allow persistent data in policies**
+
+By default, the browsers in Neko are set up to forget all cookies and browsing history when they are closed. If you want to allow persistent data, you can set the following policies in the JSON file:
+
+```json title="policy.json"
+{
+ "policies": {
+ "SanitizeOnShutdown": false,
+ "Homepage": {
+ "StartPage": "previous-session"
+ }
+ }
+}
+```
+
+**Manage extensions**
+
+By default, the browsers in Neko do not allow installing extensions except for the ones that are pre-installed.
+
+- `installation_mode`: The installation mode for the extension. It can be one of the following:
+ - `allowed`: The extension can be installed by the user.
+ - `blocked`: The extension cannot be installed by the user.
+ - `force_installed`: The extension is installed automatically and cannot be removed by the user.
+
+```json title="policy.json"
+{
+ "policies": {
+ "ExtensionSettings": {
+ "*": {
+ "installation_mode": "blocked"
+ },
+ "sponsorBlocker@ajay.app": {
+ "install_url": "https://addons.mozilla.org/firefox/downloads/latest/sponsorblock/latest.xpi",
+ "installation_mode": "force_installed"
+ },
+ "uBlock0@raymondhill.net": {
+ "install_url": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi",
+ "installation_mode": "force_installed"
+ }
+ }
+ }
+}
+```
+
+
+ How to find the extension ID?
+
+ Extension IDs for Firefox are not available in the URL like in Chrome. You can find the extension ID by navigating to the `about:debugging#/runtime/this-firefox` page and clicking on the extension you want to install. The extension ID will be displayed in the URL.
+
+ Another way is to find the extension on the [Official Add-ons Webpage](https://addons.mozilla.org/en-US/firefox/), then open DevTools (F12) and go to the `Console` tab. Enter the following command:
+
+ ```javascript
+ Object.keys(JSON.parse(document.getElementById('redux-store-state').textContent).addons.byGUID)[0]
+ ```
+
+ This will return the ID of the first extension on the page.
+
+
+### Chromium-based Browsers {#chromium-based}
+
+The full configuration options for the Chromium-based policy JSON file can be found in the [Chrome Enterprise](https://chromeenterprise.google/policies) documentation.
+
+The policy files are located in the following paths:
+
+
+
+**Allow file uploading & downloading**
+
+By default, the browsers in Neko do not allow local file access. If you want to allow file uploading and downloading, you can set the following policies in the JSON file:
+
+```json title="policy.json"
+{
+ "DownloadRestrictions": 0,
+ "AllowFileSelectionDialogs": true,
+ "URLAllowlist": [
+ "file:///home/neko/Downloads"
+ ]
+}
+```
+
+**Allow persistent data in policies**
+
+By default, the browsers in Neko are set up to forget all cookies and browsing history when they are closed. If you want to allow persistent data, you can set the following policies in the JSON file:
+
+```json title="policy.json"
+{
+ "DefaultCookiesSetting": 1,
+ "RestoreOnStartup": 1
+}
+```
+
+**Manage extensions**
+
+By default, the browsers in Neko do not allow installing extensions except for the ones that are pre-installed.
+
+- `ExtensionInstallForcelist`: These extensions will be installed automatically and cannot be removed by the user.
+- `ExtensionInstallAllowlist`: These extensions can be installed by the user when needed; they are not pre-installed.
+- `ExtensionInstallBlocklist`: These extensions cannot be installed by the user, which is `*` (all extensions) by default.
+
+The ID of the extension can be found in the URL of the extension in the Chrome Web Store. For example, the ID of the [uBlock Origin](https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm) extension is `cjpalhdlnbpafiamejdnhcphjbkeiagm`.
+
+```json title="policy.json"
+{
+ "ExtensionInstallForcelist": [
+ "cjpalhdlnbpafiamejdnhcphjbkeiagm;https://clients2.google.com/service/update2/crx",
+ "mnjggcdmjocbbbhaepdhchncahnbgone;https://clients2.google.com/service/update2/crx"
+ ],
+ "ExtensionInstallAllowlist": [
+ "cjpalhdlnbpafiamejdnhcphjbkeiagm",
+ "mnjggcdmjocbbbhaepdhchncahnbgone"
+ ],
+ "ExtensionInstallBlocklist": [
+ "*"
+ ]
+}
+```
diff --git a/webpage/docs/customization/browsers.tsx b/webpage/docs/customization/browsers.tsx
new file mode 100644
index 00000000..091f00ca
--- /dev/null
+++ b/webpage/docs/customization/browsers.tsx
@@ -0,0 +1,55 @@
+import React from 'react';
+import browsers from './browsers.json';
+import Link from '@docusaurus/Link';
+
+export function ProfileDirectoryPaths({flavors, ...props}: { flavors: string[] }) {
+ if (!flavors) {
+ flavors = [];
+ }
+ return (
+
+ {policiesFile ? {policiesFile} : Does not support policies.}
+
+
+ ))}
+
+
+ );
+}
diff --git a/webpage/docs/customization/ui.md b/webpage/docs/customization/ui.md
new file mode 100644
index 00000000..8e372ee0
--- /dev/null
+++ b/webpage/docs/customization/ui.md
@@ -0,0 +1,61 @@
+---
+sidebar_label: "User Interface"
+description: "Customize the Neko user interface with your own UI files."
+---
+
+# Customizing the UI
+
+Currently there is no configuration for customizing the UI of Neko. You need to modify the source code to change the UI.
+
+```bash
+# Clone the repository
+git clone https://github.com/m1k1o/neko
+# Change to the client directory
+cd neko/client
+# Install the dependencies
+npm install
+# Build the project
+npm run build
+```
+
+You can mount your newly created UI files to the container to `/var/www` to overwrite the default files. The Neko web server will automatically reload the new files when they are changed. You can use the following command to mount your new UI files to the container:
+
+```yaml title="docker-compose.yaml"
+services:
+ neko:
+ image: "ghcr.io/m1k1o/neko/firefox:latest"
+ restart: "unless-stopped"
+ shm_size: "2gb"
+ ports:
+ - "8080:8080"
+ - "52000-52100:52000-52100/udp"
+ # highlight-start
+ volumes:
+ - "./client/dist:/var/www"
+ # highlight-end
+ environment:
+ NEKO_DESKTOP_SCREEN: 1920x1080@30
+ NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko
+ NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin
+ NEKO_WEBRTC_EPR: 52000-52100
+ NEKO_WEBRTC_ICELITE: 1
+```
+
+## Query parameters {#query-parameters}
+
+You can use query parameters to customize the Neko web interface. These parameters can be added to the URL when accessing the Neko web interface. The following table lists the available query parameters:
+
+| Query Parameter | Description |
+|--------------------|------------------------------------------------------------|
+| `?usr=` | Prefills the username field. |
+| `?pwd=` | Prefills the password field. |
+| `?cast=1` | Hides all controls and shows only the video. |
+| `?embed=1` | Hides most additional components and shows only the video. |
+| `?volume=<0-1>` | Sets the volume to the given value (between 0 and 1). |
+| `?lang=` | Sets the language to the given value. |
+| `?show_side=1` | Shows the sidebar on startup. |
+| `?mute_chat=1` | Mutes the chat on startup. |
+
+You can combine multiple query parameters in the URL. For example, to set the username to `guest`, the password to `neko`, and enable casting mode, you can use the following URL:
+
+Example: `http(s):///?pwd=neko&usr=guest&cast=1`
\ No newline at end of file
diff --git a/webpage/docs/developer-guide/building-from-source.md b/webpage/docs/developer-guide/README.md
similarity index 52%
rename from webpage/docs/developer-guide/building-from-source.md
rename to webpage/docs/developer-guide/README.md
index 2e709039..8296199c 100644
--- a/webpage/docs/developer-guide/building-from-source.md
+++ b/webpage/docs/developer-guide/README.md
@@ -1,14 +1,10 @@
----
-sidebar_position: 2
----
+# Developer Guide
-# Building from Source
+:::info
+This guide is Work in Progress. It is not complete and will be updated over time.
+:::
-This guide walks you through the process of setting up Neko on your local machine or server.
-
-## Prerequisites {#prerequisites}
-
-Before proceeding, ensure that you have the following installed on your system:
+## Dependencies
- [node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/) (for building the frontend).
- [go](https://golang.org/) (for building the server).
@@ -32,38 +28,9 @@ Before proceeding, ensure that you have the following installed on your system:
sudo apt-get install xdotool xclip libgtk-3-0 libgtk-3-dev libopus0 libvpx6;
```
-## Step 1: Clone the Repository {#step-1}
+## Next Steps
-Start by cloning the Neko Git repository to your machine:
+import DocCardList from '@theme/DocCardList';
+import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
-```bash
-git clone https://github.com/m1k1o/neko.git
-cd neko
-```
-
-## Step 2: Build the Frontend {#step-2}
-
-Navigate to the `client` directory and install the dependencies:
-
-```shell
-cd client;
-npm install;
-npm run build;
-```
-
-## Step 3: Build the Server {#step-3}
-
-Navigate to the `server` directory and build the server:
-
-```shell
-cd server;
-go build;
-```
-
-## Step 4: Run the Server {#step-4}
-
-Finally, run the server:
-
-```shell
-./server/server;
-```
+
diff --git a/webpage/docs/developer-guide/api-reference.md b/webpage/docs/developer-guide/api-reference.md
deleted file mode 100644
index ea634205..00000000
--- a/webpage/docs/developer-guide/api-reference.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 4
----
-
-# API Reference
-
-Documentation of available APIs for integrating with Neko (WebSocket, REST, etc.).
diff --git a/webpage/docs/developer-guide/build.md b/webpage/docs/developer-guide/build.md
new file mode 100644
index 00000000..90a1df52
--- /dev/null
+++ b/webpage/docs/developer-guide/build.md
@@ -0,0 +1,46 @@
+---
+description: Building Neko from source
+---
+
+# Building From Source
+
+This guide walks you through the process of setting up Neko on your local machine or server.
+
+Start by cloning the Neko Git repository to your machine:
+
+```bash
+git clone https://github.com/m1k1o/neko.git
+cd neko
+```
+
+## Building the Frontend {#frontend}
+
+Prerequisites for building the frontend:
+- [node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/)
+
+Navigate to the `client` directory and install the dependencies:
+
+```bash
+cd client;
+npm install;
+npm run build;
+```
+
+The `npm run build` command will create a production build of the frontend in the `client/build` directory.
+
+## Building the Server {#server}
+
+Prerequisites for building the server:
+- [go](https://golang.org/) (version 1.18 or higher)
+- Dependencies for building the server:
+ ```bash
+ sudo apt-get install -y --no-install-recommends libx11-dev libxrandr-dev libxtst-dev libgtk-3-dev libxcvt-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
+ ```
+Navigate to the `server` directory and build the server:
+
+```bash
+cd server;
+./build;
+```
+
+This will create a binary file named `neko` in the `bin` directory along with `plugins` that were built with the server.
diff --git a/webpage/docs/developer-guide/code-structure.md b/webpage/docs/developer-guide/code-structure.md
deleted file mode 100644
index 57ac0b26..00000000
--- a/webpage/docs/developer-guide/code-structure.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 1
----
-
-# Code Structure
-
-Explanation of the projectβs architecture and key components.
diff --git a/webpage/docs/developer-guide/contributing-guide.md b/webpage/docs/developer-guide/contributing-guide.md
deleted file mode 100644
index 8fa0e43c..00000000
--- a/webpage/docs/developer-guide/contributing-guide.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 6
----
-
-# Contributing Guide
-
-How to contribute to the project, submit patches, and report issues.
diff --git a/webpage/docs/developer-guide/development-mode.md b/webpage/docs/developer-guide/development-mode.md
deleted file mode 100644
index ffe525a7..00000000
--- a/webpage/docs/developer-guide/development-mode.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 3
----
-
-# Running in Development Mode
-
-Setting up a development environment for contributing.
diff --git a/webpage/docs/developer-guide/repository-structure.md b/webpage/docs/developer-guide/repository-structure.md
new file mode 100644
index 00000000..b8b6db78
--- /dev/null
+++ b/webpage/docs/developer-guide/repository-structure.md
@@ -0,0 +1,42 @@
+---
+description: "Project structure of the neko repository."
+---
+
+# Repository Structure
+
+This project uses a monorepo structure with the following directories:
+
+- `server/`: Backend code, written in [Go](https://go.dev/).
+ - `server/cmd/`: Subcommands for the neko server, such as `serve` and `plugins`.
+ - `server/dev/`: Development scripts to run/build the neko server locally in Docker.
+ - `server/internal/`: Internal packages used by the neko server.
+ - `server/pkg/`: Packages used by the neko server that can be used by other projects.
+ - `server/plugins/`: Directory for plugins that will be built and used by the neko server.
+
+- `client/`: Frontend code, written in [TypeScript](https://www.typescriptlang.org/) and [Vue.js](https://vuejs.org/).
+ - `client/dev/`: Development scripts to run/build the neko client locally in Docker.
+ - `client/public/`: Public files for the neko client, such as icons and images.
+ - `client/src/`: Source code for the neko client, including components and pages.
+ - `client/tools/`: Tools used for generating code for the neko client, such as emojis.
+
+- `runtime/`: Runtime environment, with [Xorg](https://www.x.org/wiki/) and [Pulseaudio](https://www.freedesktop.org/wiki/Software/PulseAudio/) servers and the necessary dependencies for the applications to run.
+ - `runtime/fontconfig/`: Font configuration files copied to `/etc/fonts/conf.d/` in the neko runtime.
+ - `runtime/fonts/`: Empty directory placeholder to include custom fonts in the neko runtime.
+ - `runtime/icon-theme/`: Empty directory placeholder to include a custom icon theme in the neko runtime.
+ - `runtime/intel/` or `runtime/nvidia/`: Directories with files for flavor-specific releases of the neko runtime.
+
+- `apps/`: Contains the applications that run in the neko, such as Firefox, Chrome, etc.
+ - `apps//Dockerfile`: Dockerfile extending `BASE_IMAGE` for the application used by the build script.
+ - `apps//Dockerfile.`: Dockerfile for the application with flavor-specific optimizations.
+ - Supervisord configuration is expected at `/etc/neko/supervisord/.conf` in the application image.
+
+- `utils/`: Utility scripts, tools, and dependencies.
+ - `utils/docker/`: Dockerfile generator that builds the base image by concatenating multiple Dockerfiles.
+ - `utils/xorg-deps/`: Custom-made or patched Xorg dependencies, such as drivers.
+
+- `webpage/`: Webpage code for neko.m1k1o.net, written in [TypeScript](https://www.typescriptlang.org/) and [Docusaurus](https://docusaurus.io/), deployed on [GitHub Pages](https://pages.github.com/).
+ - `webpage/docs/`: Documentation for the neko project, including this README file.
+ - `webpage/scripts/`: Helper scripts for generating configuration and OpenAPI docs.
+ - `webpage/src/`: Source code, components, and styles for the webpage.
+ - `webpage/static/`: Static files for the webpage, such as images and icons.
+ - `webpage/versioned_*`: Versioned documentation files for the neko project, generated by [Docusaurus](https://docusaurus.io/).
\ No newline at end of file
diff --git a/webpage/docs/developer-guide/style-guide.md b/webpage/docs/developer-guide/style-guide.md
deleted file mode 100644
index 1198e0be..00000000
--- a/webpage/docs/developer-guide/style-guide.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 7
----
-
-# Style Guide
-
-Code formatting, linting, and other development conventions.
diff --git a/webpage/docs/developer-guide/testing-debugging.md b/webpage/docs/developer-guide/testing-debugging.md
deleted file mode 100644
index 71778ae1..00000000
--- a/webpage/docs/developer-guide/testing-debugging.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-sidebar_position: 5
----
-
-# Testing & Debugging
-
-Debugging tools, test cases, and best practices for troubleshooting.
diff --git a/webpage/docs/installation/docker-images.md b/webpage/docs/installation/docker-images.md
index baf03587..44b4d903 100644
--- a/webpage/docs/installation/docker-images.md
+++ b/webpage/docs/installation/docker-images.md
@@ -2,49 +2,86 @@
description: List of available Neko Docker images and their flavors.
---
+import { AppIcon } from '@site/src/components/AppIcon';
+
# Docker Images
Neko as a standalone streaming server is available as a Docker image. But that is rarely interesting for general use. The real power of Neko is in its ability to accommodate custom applications in the virtual desktop environment. This is where the various flavors of Neko Docker images come in.
-The base image is available at [`ghcr.io/m1k1o/neko/base`](https://ghcr.io/m1k1o/neko/base).
+The base image is available as multi-arch image at [`ghcr.io/m1k1o/neko/base`](https://ghcr.io/m1k1o/neko/base). See [Supported Architectures](#arch) for more information.
## Naming Convention {#naming}
-Neko Docker images are available on [GitHub Container Registry (GHCR)](https://github.com/m1k1o?tab=packages&repo_name=neko). The naming convention for Neko Docker images is as follows:
+Neko images are available on two public registries. The [GitHub Container Registry (GHCR)](#ghcr.io) hosts stable releases with all flavors and architectures. The latest development version of the Neko image for the AMD64 architecture is available on [Docker Hub](#docker.io).
+
+:::info
+You should always prefer the GHCR registry, as it supports flavors and specific versions, unless you want to test the latest development version.
+:::
+
+### GitHub Container Registry (GHCR) {#ghcr.io}
+
+Neko Docker images are available on the [GitHub Container Registry (GHCR)](https://github.com/m1k1o?tab=packages&repo_name=neko). The naming convention for Neko Docker images is as follows:
```
ghcr.io/m1k1o/neko/[-]:
```
-- `` is the optional flavor of the image, see [Available Flavors](#flavors) for more information.
-- `` is the application name or base image, see [Available Applications](#apps) for more information.
-- `` is the [semantic version](https://semver.org/) of the image from the [GitHub tags](https://github.com/m1k1o/neko/tags). There is always a `latest` tag available.
+- `` is the optional flavor of the image. See [Available Flavors](#flavors) for more information.
+- `` is the application name or base image. See [Available Applications](#apps) for more information.
+- `` is the version of the image. See [Versioning](#ghcr.io-versioning) for more information.
-An alternative registry is also available on [Docker Hub](https://hub.docker.com/r/m1k1o/neko), however, only images without flavor and with the latest version are available there.
+#### Versioning scheme {#ghcr.io-versioning}
+
+The versioning scheme follows the [Semantic Versioning 2.0.0](https://semver.org/) specification. The following tags are available for each image:
+
+- `latest` - Points to the most recent stable release.
+- `MAJOR` - Tracks the latest release within the specified major version.
+- `MAJOR.MINOR` - Tracks the latest release within the specified major and minor version.
+- `MAJOR.MINOR.PATCH` - Refers to a specific release.
+
+For example:
+- `ghcr.io/m1k1o/neko/firefox:latest` - Latest stable version.
+- `ghcr.io/m1k1o/neko/firefox:3` - Latest release in the 3.x.x series.
+- `ghcr.io/m1k1o/neko/firefox:3.0` - Latest release in the 3.0.x series.
+- `ghcr.io/m1k1o/neko/firefox:3.0.0` - Specific version 3.0.0.
+
+A full list of published versions can be found in the [GitHub tags](https://github.com/m1k1o/neko/tags).
+
+### Docker Hub {#docker.io}
+
+An alternative registry is available on [Docker Hub](https://hub.docker.com/r/m1k1o/neko). This registry hosts images built from the latest code in the [master branch](https://github.com/m1k1o/neko/tree/master). However, it only includes images without flavors and supports the AMD64 architecture. The naming convention for these images is as follows:
```
m1k1o/neko:
```
+- `` is the application name or base image. See [Available Applications](#apps) for more information.
+
:::info
-You should always prefer the GHCR registry with the ability to use flavors and specific versions.
+`m1k1o/neko:latest` is an alias for `m1k1o/neko:firefox` due to historical reasons. It is recommended to use the `ghcr.io/m1k1o/neko/firefox:latest` image instead.
:::
## Available Applications {#apps}
The following applications are available as Neko Docker images:
-### Firefox-based browsers {#firefox}
+### Firefox-based browsers {#firefox-based-browsers}
In comparison to Chromium-based browsers, Firefox-based browsers do not require additional capabilities or a bigger shared memory size to not crash.
| Icon | Name | Docker Image |
| ---- | ---- | ------------ |
-| | [Firefox](https://www.mozilla.org/firefox/) The open-source browser from Mozilla. | [`ghcr.io/m1k1o/neko/firefox`](https://ghcr.io/m1k1o/neko/firefox) |
-| | [Tor Browser](https://www.torproject.org/) A browser designed to access the Tor network for enhanced privacy. | [`ghcr.io/m1k1o/neko/tor-browser`](https://ghcr.io/m1k1o/neko/tor-browser) |
-| | [Waterfox](https://www.waterfox.net/) A privacy-focused browser based on Firefox. | [`ghcr.io/m1k1o/neko/waterfox`](https://ghcr.io/m1k1o/neko/waterfox) |
+| | [Firefox](https://www.mozilla.org/firefox/) The open-source browser from Mozilla. | [`ghcr.io/m1k1o/neko/firefox`](https://ghcr.io/m1k1o/neko/firefox) |
+| | [Tor Browser](https://www.torproject.org/) A browser designed to access the Tor network for enhanced privacy. | [`ghcr.io/m1k1o/neko/tor-browser`](https://ghcr.io/m1k1o/neko/tor-browser) |
+| | [Waterfox](https://www.waterfox.net/) A privacy-focused browser based on Firefox. | [`ghcr.io/m1k1o/neko/waterfox`](https://ghcr.io/m1k1o/neko/waterfox) |
-### Chromium-based browsers {#chromium}
+:::warning
+**Waterfox** is currently not built automatically, because Cloudflare blocks the download and therefore github actions are failing. You can build it manually to get the latest version.
+:::
+
+Check the [Firefox-based browsers customization guide](/docs/v3/customization/browsers#firefox-based) for more information on how to customize Firefox-based browsers (configuring profile, installing extensions, etc.).
+
+### Chromium-based browsers {#chromium-based-browsers}
There are multiple flavors of Chromium-based browsers available as Neko Docker images.
@@ -78,24 +115,26 @@ import TabItem from '@theme/TabItem';
| Icon | Name | Docker Image |
| ---- | ---- | ------------ |
-| | [Chromium](https://www.chromium.org/chromium-projects/) The open-source project behind Google Chrome. | [`ghcr.io/m1k1o/neko/chromium`](https://ghcr.io/m1k1o/neko/chromium) |
-| | [Google Chrome](https://www.google.com/chrome/) The most popular browser in the world. | [`ghcr.io/m1k1o/neko/google-chrome`](https://ghcr.io/m1k1o/neko/google-chrome) |
-| | [Ungoogled Chromium](https://ungoogled-software.github.io/) A fork of Chromium without Google integration. | [`ghcr.io/m1k1o/neko/ungoogled-chromium`](https://ghcr.io/m1k1o/neko/ungoogled-chromium) |
-| | [Microsoft Edge](https://www.microsoft.com/edge) The new Microsoft Edge is based on Chromium. | [`ghcr.io/m1k1o/neko/microsoft-edge`](https://ghcr.io/m1k1o/neko/microsoft-edge) |
-| | [Brave](https://brave.com/) A privacy-focused browser. | [`ghcr.io/m1k1o/neko/brave`](https://ghcr.io/m1k1o/neko/brave) |
-| | [Vivaldi](https://vivaldi.com/) A highly customizable browser. | [`ghcr.io/m1k1o/neko/vivaldi`](https://ghcr.io/m1k1o/neko/vivaldi) |
-| | [Opera](https://www.opera.com/)* A fast and secure browser. | [`ghcr.io/m1k1o/neko/opera`](https://ghcr.io/m1k1o/neko/opera) |
+| | [Chromium](https://www.chromium.org/chromium-projects/) The open-source project behind Google Chrome. | [`ghcr.io/m1k1o/neko/chromium`](https://ghcr.io/m1k1o/neko/chromium) |
+| | [Google Chrome](https://www.google.com/chrome/) The most popular browser in the world. | [`ghcr.io/m1k1o/neko/google-chrome`](https://ghcr.io/m1k1o/neko/google-chrome) |
+| | [Ungoogled Chromium](https://ungoogled-software.github.io/) A fork of Chromium without Google integration. | [`ghcr.io/m1k1o/neko/ungoogled-chromium`](https://ghcr.io/m1k1o/neko/ungoogled-chromium) |
+| | [Microsoft Edge](https://www.microsoft.com/edge) The new Microsoft Edge is based on Chromium. | [`ghcr.io/m1k1o/neko/microsoft-edge`](https://ghcr.io/m1k1o/neko/microsoft-edge) |
+| | [Brave](https://brave.com/) A privacy-focused browser. | [`ghcr.io/m1k1o/neko/brave`](https://ghcr.io/m1k1o/neko/brave) |
+| | [Vivaldi](https://vivaldi.com/) A highly customizable browser. | [`ghcr.io/m1k1o/neko/vivaldi`](https://ghcr.io/m1k1o/neko/vivaldi) |
+| | [Opera](https://www.opera.com/)* A fast and secure browser. | [`ghcr.io/m1k1o/neko/opera`](https://ghcr.io/m1k1o/neko/opera) |
\* requires extra steps to enable DRM, see instructions [here](https://www.reddit.com/r/operabrowser/wiki/opera/linux_widevine_config/). `libffmpeg` is already configured.
+Check the [Chromium-based browsers customization guide](/docs/v3/customization/browsers#chromium-based) for more information on how to customize Chromium-based browsers (configuring profile, installing extensions, etc.).
+
### Desktop Environments {#desktop}
These images feature a full desktop environment where you can install and run multiple applications, use window management, and more. This is useful for people who want to run multiple applications in a single container.
| Icon | Name | Docker Image |
| ---- | ---- | ------------ |
-| | [Xfce](https://xfce.org/) A lightweight desktop environment. | [`ghcr.io/m1k1o/neko/xfce`](https://ghcr.io/m1k1o/neko/xfce) |
-| | [KDE Plasma](https://kde.org/plasma-desktop) A feature-rich desktop environment. | [`ghcr.io/m1k1o/neko/kde`](https://ghcr.io/m1k1o/neko/kde) |
+| | [Xfce](https://xfce.org/) A lightweight desktop environment. | [`ghcr.io/m1k1o/neko/xfce`](https://ghcr.io/m1k1o/neko/xfce) |
+| | [KDE Plasma](https://kde.org/plasma-desktop) A feature-rich desktop environment. | [`ghcr.io/m1k1o/neko/kde`](https://ghcr.io/m1k1o/neko/kde) |
### Other Applications {#other}
@@ -103,8 +142,8 @@ As it would be impossible to include all possible applications in the repository
| Icon | Name | Docker Image |
| ---- | ---- | ------------ |
-| | [Remmina](https://remmina.org/) A remote desktop client. | [`ghcr.io/m1k1o/neko/remmina`](https://ghcr.io/m1k1o/neko/remmina) |
-| | [VLC](https://www.videolan.org/vlc/) A media player. | [`ghcr.io/m1k1o/neko/vlc`](https://ghcr.io/m1k1o/neko/vlc) |
+| | [Remmina](https://remmina.org/) A remote desktop client. | [`ghcr.io/m1k1o/neko/remmina`](https://ghcr.io/m1k1o/neko/remmina) |
+| | [VLC](https://www.videolan.org/vlc/) A media player. | [`ghcr.io/m1k1o/neko/vlc`](https://ghcr.io/m1k1o/neko/vlc) |
#### Remmina Configuration {#remmina}
@@ -159,37 +198,22 @@ See [neko-apps](https://github.com/m1k1o/neko-apps) repository for more applicat
## Available Flavors {#flavors}
:::danger Keep in Mind
-Currently the focus is on AMD64 & CPU image (wihout any flavor). So the flavor images might not work as expected.
+Currently the focus is on CPU images (wihout any flavor). So the GPU support might not work as expected.
:::
-
The following flavors are available for Neko Docker images:
-- `arm` - ARM64 and ARMv7 architecture support.
- `nvidia` - NVIDIA GPU support.
- `intel` - Intel GPU support.
-:::note
-Not all flavors are available for all applications. Since not all applications support ARM architecture or GPU acceleration, the flavors are only available where they make sense.
-:::
+### Intel (VAAPI GPU hardware acceleration) {#intel}
-### ARM {#arm}
-
-For ARM-based images (like Raspberry Pi - with GPU hardware acceleration, [Oracle Cloud ARM free tier](https://www.oracle.com/cloud/free/)). Currently, not all images are available for ARM, because not all applications are available for ARM. Please use the images below:
-
-- [`ghcr.io/m1k1o/neko/arm-firefox`](https://ghcr.io/m1k1o/neko/arm-firefox)
-- [`ghcr.io/m1k1o/neko/arm-chromium`](https://ghcr.io/m1k1o/neko/arm-chromium)
-- [`ghcr.io/m1k1o/neko/arm-ungoogled-chromium`](https://ghcr.io/m1k1o/neko/arm-ungoogled-chromium)
-- [`ghcr.io/m1k1o/neko/arm-vlc`](https://ghcr.io/m1k1o/neko/arm-vlc)
-- [`ghcr.io/m1k1o/neko/arm-xfce`](https://ghcr.io/m1k1o/neko/arm-xfce)
-
-The base image is available at [`ghcr.io/m1k1o/neko/arm-base`](https://ghcr.io/m1k1o/neko/arm-base).
-
-### Intel {#intel}
+Only for architecture `linux/amd64`.
For images with VAAPI GPU hardware acceleration using Intel drivers use:
- [`ghcr.io/m1k1o/neko/intel-firefox`](https://ghcr.io/m1k1o/neko/intel-firefox)
+- [`ghcr.io/m1k1o/neko/intel-waterfox`](https://ghcr.io/m1k1o/neko/intel-waterfox)
- [`ghcr.io/m1k1o/neko/intel-chromium`](https://ghcr.io/m1k1o/neko/intel-chromium)
- [`ghcr.io/m1k1o/neko/intel-google-chrome`](https://ghcr.io/m1k1o/neko/intel-google-chrome)
- [`ghcr.io/m1k1o/neko/intel-ungoogled-chromium`](https://ghcr.io/m1k1o/neko/intel-ungoogled-chromium)
@@ -205,7 +229,9 @@ For images with VAAPI GPU hardware acceleration using Intel drivers use:
The base image is available at [`ghcr.io/m1k1o/neko/intel-base`](https://ghcr.io/m1k1o/neko/intel-base).
-### Nvidia {#nvidia}
+### Nvidia (CUDA GPU hardware acceleration) {#nvidia}
+
+Only for architecture `linux/amd64`.
For images with Nvidia GPU hardware acceleration using EGL use:
@@ -217,7 +243,37 @@ For images with Nvidia GPU hardware acceleration using EGL use:
The base image is available at [`ghcr.io/m1k1o/neko/nvidia-base`](https://ghcr.io/m1k1o/neko/nvidia-base).
-:::danger
-There is a known issue with EGL and Chromium-based browsers, see [m1k1o/neko #279](https://github.com/m1k1o/neko/issues/279).
-:::
+## Supported Architectures {#arch}
+Neko Docker images are built with docker buildx and are available for multiple architectures. The following architectures are supported by the base image:
+
+- `linux/amd64` - 64-bit Intel/AMD architecture (most common).
+- `linux/arm64` - 64-bit ARM architecture (e.g., Raspberry Pi 4, Apple M1/M2).
+- `linux/arm/v7` - 32-bit ARM architecture (e.g., Raspberry Pi 3, Raspberry Pi Zero).
+
+### Availability Matrix {#availability}
+
+The availability of applications for ARM architecture is limited due to the lack of support for some applications. The following table shows the availability of each application for each architecture. The `β ` symbol indicates that the application is available for that architecture, while the `β` symbol indicates that it is not available.
+
+| Application | AMD64 | ARM64 | ARMv7 | Reference |
+| ----------------------------------------- | ----- | ----- | ----- | --------- |
+| [Firefox](#firefox) | β | β \* | β \* | - |
+| [Tor Browser](#tor-browser) | β | β | β | [Forum Post](https://forum.torproject.org/t/tor-browser-for-arm-linux/5240) |
+| [Waterfox](#waterfox) | β | β | β | [Github Issue](https://github.com/BrowserWorks/Waterfox/issues/1506), [Reddit](https://www.reddit.com/r/waterfox/comments/jpqsds/are_there_any_builds_for_arm64/) |
+| [Chromium](#chromium) | β | β \* | β \* | - |
+| [Google Chrome](#google-chrome) | β | β | β | [Community Post](https://askubuntu.com/a/1383791) |
+| [Ungoogled Chromium](#ungoogled-chromium) | β | β | β | [Downloads Page](https://ungoogled-software.github.io/ungoogled-chromium-binaries/) |
+| [Microsoft Edge](#microsoft-edge) | β | β | β | [Community Post](https://techcommunity.microsoft.com/discussions/edgeinsiderdiscussions/edge-for-linuxarm64/1532272) |
+| [Brave](#brave) | β | β \* | β | [Requirements Page](https://support.brave.com/hc/en-us/articles/360021357112-What-are-the-system-requirements-to-install-Brave) |
+| [Vivaldi](#vivaldi) | β | β \* | β \* | - |
+| [Opera](#opera) | β | β | β | [Forum Post](https://forums.opera.com/topic/52811/opera-do-not-support-arm64-on-linux) |
+| [Xfce](#xfce) | β | β | β | - |
+| [KDE](#kde) | β | β | β | - |
+| [Remmina](#remmina) | β | β | β | - |
+| [VLC](#vlc) | β | β | β | - |
+
+\* No DRM support.
+
+:::tip
+[Oracle Cloud ARM free tier](https://www.oracle.com/cloud/free/) is a great way to test Neko on ARM architecture for free. You can use the `ghcr.io/m1k1o/neko/xfce` image to run a full desktop environment with Xfce and test the applications.
+:::
diff --git a/webpage/docs/installation/examples.md b/webpage/docs/installation/examples.md
index a74615f7..ee229cae 100644
--- a/webpage/docs/installation/examples.md
+++ b/webpage/docs/installation/examples.md
@@ -76,31 +76,12 @@ services:
NEKO_WEBRTC_NAT1TO1:
```
-## Raspberry Pi {#raspberry-pi}
-
-```yaml title="config.yaml"
-capture:
- video:
- codec: h264
- ids: [ main ]
- pipelines:
- main:
- gst_pipeline: |
- ximagesrc display-name=%s use-damage=0 show-pointer=true use-damage=false
- ! video/x-raw,framerate=30/1
- ! videoconvert
- ! queue
- ! video/x-raw,framerate=30/1,format=NV12
- ! v4l2h264enc extra-controls="controls,h264_profile=1,video_bitrate=1250000;"
- ! h264parse config-interval=3
- ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline
-```
+## Raspberry Pi GPU Acceleration {#raspberry-pi}
```yaml title="docker-compose.yaml"
services:
neko:
- # see docs for more variants
- image: "ghcr.io/m1k1o/neko/arm-chromium:latest"
+ image: "ghcr.io/m1k1o/neko/chromium:latest"
restart: "unless-stopped"
# increase on rpi's with more then 1gb ram.
shm_size: "520mb"
@@ -110,12 +91,121 @@ services:
# note: this is important since we need a GPU for hardware acceleration alternatively
# mount the devices into the docker.
privileged: true
- volumes:
- - "./config.yaml:/etc/neko/neko.yaml"
environment:
+ NEKO_CAPTURE_VIDEO_PIPELINE: |
+ ximagesrc display-name={display} show-pointer=true use-damage=false
+ ! video/x-raw,framerate=25/1
+ ! videoconvert ! queue
+ ! video/x-raw,format=NV12
+ ! v4l2h264enc
+ name=encoder
+ extra-controls="controls,h264_profile=1,video_bitrate=1250000;"
+ ! h264parse config-interval=-1
+ ! video/x-h264,stream-format=byte-stream
+ ! appsink name=appsink
+ NEKO_CAPTURE_VIDEO_CODEC: "h264"
NEKO_DESKTOP_SCREEN: '1280x720@30'
- NEKO_MEMBER_MULTIUSER_USER_PASSWORD: 'neko'
- NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: 'admin'
+ NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko
+ NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin
NEKO_WEBRTC_EPR: 52000-52100
NEKO_WEBRTC_ICELITE: 1
```
+
+## Nvidia GPU Acceleration {#nvidia}
+
+Neko supports hardware acceleration using Nvidia GPUs. To use this feature, you need to have the Nvidia Container Toolkit installed on your system. You can find the installation instructions [here](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html).
+
+This example shows how to accelerate video encoding and as well the browser rendering using the GPU. You can test if the GPU is used by running `nvidia-smi`, which should show the GPU usage of both the browser and neko. In the browser, you can run the [WebGL Aquarium Demo](https://webglsamples.org/aquarium/aquarium.html) to test the GPU usage.
+
+```yaml title="docker-compose.yaml"
+services:
+ neko:
+ image: "ghcr.io/m1k1o/neko/nvidia-firefox:latest"
+ restart: "unless-stopped"
+ shm_size: "2gb"
+ ports:
+ - "8080:8080"
+ - "52000-52100:52000-52100/udp"
+ environment:
+ NEKO_CAPTURE_VIDEO_PIPELINE: |
+ ximagesrc display-name={display} show-pointer=true use-damage=false
+ ! video/x-raw,framerate=25/1
+ ! videoconvert ! queue
+ ! video/x-raw,format=NV12
+ ! nvh264enc
+ name=encoder
+ preset=2
+ gop-size=25
+ spatial-aq=true
+ temporal-aq=true
+ bitrate=4096
+ vbv-buffer-size=4096
+ rc-mode=6
+ ! h264parse config-interval=-1
+ ! video/x-h264,stream-format=byte-stream
+ ! appsink name=appsink
+ NEKO_CAPTURE_VIDEO_CODEC: "h264"
+ NEKO_DESKTOP_SCREEN: 1920x1080@30
+ NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko
+ NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin
+ NEKO_WEBRTC_EPR: 52000-52100
+ NEKO_WEBRTC_ICELITE: 1
+ deploy:
+ resources:
+ reservations:
+ devices:
+ - driver: nvidia
+ count: 1
+ capabilities: [gpu]
+```
+
+See available [Nvidia Docker Images](/docs/v3/installation/docker-images#nvidia).
+
+If you only want to accelerate the encoding, **not the browser rendering**, you can use the default image with additional environment variables:
+
+```yaml title="docker-compose.yaml"
+services:
+ neko:
+ # highlight-next-line
+ image: "ghcr.io/m1k1o/neko/firefox:latest"
+ restart: "unless-stopped"
+ shm_size: "2gb"
+ ports:
+ - "8080:8080"
+ - "52000-52100:52000-52100/udp"
+ environment:
+ # highlight-start
+ NVIDIA_VISIBLE_DEVICES: all
+ NVIDIA_DRIVER_CAPABILITIES: all
+ # highlight-end
+ NEKO_CAPTURE_VIDEO_PIPELINE: |
+ ximagesrc display-name={display} show-pointer=true use-damage=false
+ ! video/x-raw,framerate=25/1
+ ! videoconvert ! queue
+ ! video/x-raw,format=NV12
+ ! nvh264enc
+ name=encoder
+ preset=2
+ gop-size=25
+ spatial-aq=true
+ temporal-aq=true
+ bitrate=4096
+ vbv-buffer-size=4096
+ rc-mode=6
+ ! h264parse config-interval=-1
+ ! video/x-h264,stream-format=byte-stream
+ ! appsink name=appsink
+ NEKO_CAPTURE_VIDEO_CODEC: "h264"
+ NEKO_DESKTOP_SCREEN: 1920x1080@30
+ NEKO_MEMBER_MULTIUSER_USER_PASSWORD: neko
+ NEKO_MEMBER_MULTIUSER_ADMIN_PASSWORD: admin
+ NEKO_WEBRTC_EPR: 52000-52100
+ NEKO_WEBRTC_ICELITE: 1
+ deploy:
+ resources:
+ reservations:
+ devices:
+ - driver: nvidia
+ count: 1
+ capabilities: [gpu]
+```
diff --git a/webpage/docs/migration-from-v2/README.md b/webpage/docs/migration-from-v2/README.md
index 61ccbade..b8f9bded 100644
--- a/webpage/docs/migration-from-v2/README.md
+++ b/webpage/docs/migration-from-v2/README.md
@@ -2,15 +2,21 @@
Currently, Neko is in compatibility mode, meaning that as soon as a single V2 configuration option is set, the legacy mode is enabled. This approach allows for a smooth transition from V2 to V3, where it does not expose the V2 API for new users but still allows existing users who use the old configuration to continue using it as before.
-The legacy mode can be explicitly enabled or disabled by setting the `NEKO_LEGACY` environment variable to `true` or `false`.
+The legacy mode includes a compatibility layer that allows V2 clients to connect to V3. Currently, the **client is not compatible with V3**, so the legacy mode is enabled by default. It can be explicitly enabled or disabled by setting the `NEKO_LEGACY` environment variable to `true` or `false`.
-:::tip
-You can migrate to a new configuration even if you are using a V2 client. Just make sure to set the `NEKO_LEGACY` environment variable to `true`.
+:::warning
+The legacy mode is **still used by the client**. It is recommended to migrate to the new configuration options, but do not disable the legacy mode unless you are using a new client that is compatible with V3 (e.g., [demodesk/neko-client](https://github.com/demodesk/neko-client)). Once the new client is released, the legacy mode will be automatically removed from the server.
:::
-:::info Built-in Client
-When using Neko in a container with a built-in client, the client will always be compatible with the server regardless of what configuration is used.
-:::
+If you set both V3 and V2 configuration options, the V2 configuration options will take precedence over the V3 configuration options. This is to ensure that the legacy mode works as expected and does not break existing configurations.
+
+## Docker Images {#docker}
+
+Previously, neko was available primarily on Dockerhub as `m1k1o/neko`. While it stays as an option there, now the primary location is `ghcr.io/m1k1o/neko`.
+
+ARM images were previously available as an flavor e.g. `m1k1o/neko:arm-firefox` or `ghcr.io/m1k1o/neko/arm-firefox`. Now, the ARM images are available as multi-arch images under the same tags as the amd64 images., e.g. `ghcr.io/m1k1o/neko/firefox`.
+
+All applications available in the V2 images are also available in the V3 images. See the [Docker Images](/docs/v3/installation/docker-images) documentation for more details.
## Configuration {#config}
@@ -34,8 +40,10 @@ In order to migrate from V2 to V3, you need to update the configuration to the n
| `NEKO_IMPLICIT_CONTROL` | `NEKO_SESSION_IMPLICIT_HOSTING` |
| `NEKO_CONTROL_PROTECTION` | `NEKO_SESSION_CONTROL_PROTECTION` |
| `NEKO_HEARTBEAT_INTERVAL` | `NEKO_SESSION_HEARTBEAT_INTERVAL` |
+| `NEKO_FILE_TRANSFER_ENABLED` | `NEKO_FILETRANSFER_ENABLED` |
+| `NEKO_FILE_TRANSFER_PATH` | `NEKO_FILETRANSFER_DIR` |
-See the V3 [configuration options](/docs/v3/configuration).
+See the V3 [configuration options](/docs/v3/configuration). For file transfer, see the [File Transfer Plugin](/docs/v3/configuration/plugins#filetransfer).
### WebRTC Video {#config.video}
@@ -50,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 instead |
-| `NEKO_HWENC` | **removed**, use custom pipeline instead |
-| `NEKO_MAX_FPS` | **removed**, use custom 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
@@ -125,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';
-
+
See the full [V3 configuration reference](/docs/v3/configuration/#full) for more details.
@@ -152,6 +160,8 @@ Only the [`multiuser`](/docs/v3/configuration/authentication#member.multiuser) p
Since WebSocket messages are not user-facing API, there exists no migration guide for them. When the legacy API is enabled, the user connects to the `/ws` endpoint and is handled by the compatibility layer V2 API. The V3 API is available at the `/api/ws` endpoint.
+V2 used to send WebSocket ping messages every 60 seconds, whereas V3 sends them every 10 seconds and additionally uses a heartbeat mechanism to verify if the connection is still active.
+
### WebRTC API {#api.webrtc}
Since the WebRTC API is not user-facing API, there exists no migration guide for it. It has been changed to Big Endian format (previously Little Endian) to allow easier manipulation on the client side.
diff --git a/webpage/docs/migration-from-v2/help.json b/webpage/docs/migration-from-v2/help.json
index a0c4aa84..0110d40f 100644
--- a/webpage/docs/migration-from-v2/help.json
+++ b/webpage/docs/migration-from-v2/help.json
@@ -15,6 +15,102 @@
"defaultValue": "false",
"description": "save logs to file"
},
+ {
+ "key": [
+ "cert"
+ ],
+ "type": "string",
+ "description": "path to the SSL cert used to secure the neko server"
+ },
+ {
+ "key": [
+ "key"
+ ],
+ "type": "string",
+ "description": "path to the SSL key used to secure the neko server"
+ },
+ {
+ "key": [
+ "bind"
+ ],
+ "type": "string",
+ "description": "address/port/socket to serve neko"
+ },
+ {
+ "key": [
+ "proxy"
+ ],
+ "type": "boolean",
+ "defaultValue": "false",
+ "description": "enable reverse proxy mode"
+ },
+ {
+ "key": [
+ "static"
+ ],
+ "type": "string",
+ "description": "path to neko client files to serve"
+ },
+ {
+ "key": [
+ "path_prefix"
+ ],
+ "type": "string",
+ "description": "path prefix for HTTP requests"
+ },
+ {
+ "key": [
+ "cors"
+ ],
+ "type": "strings",
+ "description": "list of allowed origins for CORS"
+ },
+ {
+ "key": [
+ "locks"
+ ],
+ "type": "strings",
+ "description": "resources, that will be locked when starting (control, login)"
+ },
+ {
+ "key": [
+ "implicit_control"
+ ],
+ "type": "boolean",
+ "defaultValue": "false",
+ "description": "if enabled members can gain control implicitly"
+ },
+ {
+ "key": [
+ "control_protection"
+ ],
+ "type": "boolean",
+ "defaultValue": "false",
+ "description": "control protection means, users can gain control only if at least one admin is in the room"
+ },
+ {
+ "key": [
+ "heartbeat_interval"
+ ],
+ "type": "int",
+ "defaultValue": "120",
+ "description": "heartbeat interval in seconds"
+ },
+ {
+ "key": [
+ "file_transfer_enabled"
+ ],
+ "type": "boolean",
+ "defaultValue": "false",
+ "description": "enable file transfer feature"
+ },
+ {
+ "key": [
+ "file_transfer_path"
+ ],
+ "type": "string",
+ "description": "path to use for file transfer"
+ },
{
"key": [
"display"
@@ -192,93 +288,12 @@
"type": "string",
"description": "admin password for connecting to stream"
},
- {
- "key": [
- "cert"
- ],
- "type": "string",
- "description": "path to the SSL cert used to secure the neko server"
- },
- {
- "key": [
- "key"
- ],
- "type": "string",
- "description": "path to the SSL key used to secure the neko server"
- },
- {
- "key": [
- "bind"
- ],
- "type": "string",
- "description": "address/port/socket to serve neko"
- },
- {
- "key": [
- "proxy"
- ],
- "type": "boolean",
- "defaultValue": "false",
- "description": "enable reverse proxy mode"
- },
- {
- "key": [
- "static"
- ],
- "type": "string",
- "description": "path to neko client files to serve"
- },
- {
- "key": [
- "path_prefix"
- ],
- "type": "string",
- "description": "path prefix for HTTP requests"
- },
- {
- "key": [
- "cors"
- ],
- "type": "strings",
- "description": "list of allowed origins for CORS"
- },
- {
- "key": [
- "locks"
- ],
- "type": "strings",
- "description": "resources, that will be locked when starting (control, login)"
- },
- {
- "key": [
- "implicit_control"
- ],
- "type": "boolean",
- "defaultValue": "false",
- "description": "if enabled members can gain control implicitly"
- },
- {
- "key": [
- "control_protection"
- ],
- "type": "boolean",
- "defaultValue": "false",
- "description": "control protection means, users can gain control only if at least one admin is in the room"
- },
- {
- "key": [
- "heartbeat_interval"
- ],
- "type": "int",
- "defaultValue": "120",
- "description": "heartbeat interval in seconds"
- },
{
"key": [
"nat1to1"
],
"type": "strings",
- "description": "sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP "
+ "description": "sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used"
},
{
"key": [
@@ -307,14 +322,14 @@
"iceserver"
],
"type": "strings",
- "description": "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection "
+ "description": "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer"
},
{
"key": [
"iceservers"
],
"type": "string",
- "description": "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection "
+ "description": "describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer"
},
{
"key": [
diff --git a/webpage/docs/migration-from-v2/help.txt b/webpage/docs/migration-from-v2/help.txt
index 84d0db36..097f114f 100644
--- a/webpage/docs/migration-from-v2/help.txt
+++ b/webpage/docs/migration-from-v2/help.txt
@@ -1,7 +1,22 @@
--legacy enable legacy mode (default true)
+
--logs save logs to file
+ --cert string path to the SSL cert used to secure the neko server
+ --key string path to the SSL key used to secure the neko server
+ --bind string address/port/socket to serve neko
+ --proxy enable reverse proxy mode
+ --static string path to neko client files to serve
+ --path_prefix string path prefix for HTTP requests
+ --cors strings list of allowed origins for CORS
+ --locks strings resources, that will be locked when starting (control, login)
+ --implicit_control if enabled members can gain control implicitly
+ --control_protection control protection means, users can gain control only if at least one admin is in the room
+ --heartbeat_interval int heartbeat interval in seconds (default 120)
+ --file_transfer_enabled enable file transfer feature
+ --file_transfer_path string path to use for file transfer
+
--display string XDisplay to capture
--video_codec string video codec to be used
--av1 DEPRECATED: use video_codec
@@ -12,6 +27,7 @@
--video_bitrate int video bitrate in kbit/s
--hwenc string use hardware accelerated encoding
--max_fps int maximum fps delivered via WebRTC, 0 is for no maximum
+
--device string audio device to capture
--audio_codec string audio codec to be used
--g722 DEPRECATED: use audio_codec
@@ -20,6 +36,7 @@
--pcmu DEPRECATED: use audio_codec
--audio string audio codec parameters to use for streaming
--audio_bitrate int audio bitrate in kbit/s
+
--broadcast_pipeline string custom gst pipeline used for broadcasting, strings {url} {device} {display} will be replaced
--broadcast_url string a default default URL for broadcast streams, can be disabled/changed later by admins in the GUI
--broadcast_autostart automatically start broadcasting when neko starts and broadcast_url is set
@@ -29,24 +46,11 @@
--password string password for connecting to stream
--password_admin string admin password for connecting to stream
- --cert string path to the SSL cert used to secure the neko server
- --key string path to the SSL key used to secure the neko server
- --bind string address/port/socket to serve neko
- --proxy enable reverse proxy mode
- --static string path to neko client files to serve
- --path_prefix string path prefix for HTTP requests
- --cors strings list of allowed origins for CORS
-
- --locks strings resources, that will be locked when starting (control, login)
- --implicit_control if enabled members can gain control implicitly
- --control_protection control protection means, users can gain control only if at least one admin is in the room
- --heartbeat_interval int heartbeat interval in seconds (default 120)
-
- --nat1to1 strings sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP
+ --nat1to1 strings sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used
--tcpmux int single TCP mux port for all peers
--udpmux int single UDP mux port for all peers
--icelite configures whether or not the ice agent should be a lite agent
- --iceserver strings describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection
- --iceservers string describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection
+ --iceserver strings describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer
+ --iceservers string describes a single STUN and TURN server that can be used by the ICEAgent to establish a connection with a peer
--ipfetch string automatically fetch IP address from given URL when nat1to1 is not present
--epr string limits the pool of ephemeral ports that ICE UDP connections can allocate from
diff --git a/webpage/docs/quick-start.md b/webpage/docs/quick-start.md
index 2b4a15f7..3216c0f3 100644
--- a/webpage/docs/quick-start.md
+++ b/webpage/docs/quick-start.md
@@ -46,7 +46,7 @@ Neko is easy to use and requires no technical expertise to get started. All you
```
:::note
- If you want to run Neko on your local network, you have to add `NEKO_NAT1TO1=` to the `docker-compose.yaml` file.
+ If you want to run Neko on your local network, you have to add `NEKO_NAT1TO1: ` to the `docker-compose.yaml` file.
:::
6. Visit the server's IP address in your browser and log in, the default password is `neko`.
diff --git a/webpage/docs/release-notes.md b/webpage/docs/release-notes.md
index 02587a20..9b3f29f9 100644
--- a/webpage/docs/release-notes.md
+++ b/webpage/docs/release-notes.md
@@ -1,5 +1,73 @@
# Release Notes
+## master {#master}
+
+### New Features {#master-feats}
+- Scroll to chat on mobile ([#496](https://github.com/m1k1o/neko/pull/496))
+- Added mobile keyboard icon to open the keyboard on mobile devices ([#497](https://github.com/m1k1o/neko/pull/497))
+
+### Fixes {#master-fixes}
+- Fixed various bugs related to the legacy client and migration.
+- Fixed long standing issue [#279](https://github.com/m1k1o/neko/pull/279) where Google Chrome GPU acceleration did not work with Nvidia GPUs, thanks to [@TobyColeman](https://github.com/TobyColeman), [@alexbakerdev](https://github.com/alexbakerdev) and [@samstefan](https://github.com/samstefan) from [@wearewildcards](https://github.com/wearewildcards).
+
+### Misc {#master-misc}
+- Added an https condition to the healthcheck ([#503](https://github.com/m1k1o/neko/pull/503), by @Garrulousbrevity).
+
+## [n.eko v3.0.0](https://github.com/m1k1o/neko/releases/tag/v3.0.0) {#v3.0.0}
+
+### Repository Changes {#v3.0.0-repo}
+- The default registry is now `ghcr.io/m1k1o/neko` instead of `docker.io/m1k1o/neko`.
+- Multiarch builds for `linux/amd64`, `linux/arm64`, and `linux/arm/v7` are now available instead of `arm-`based images.
+- App folders from `.docker/` have been moved to `apps/`.
+- Dev scripts from `.docker/` are now available in `client/dev/` and `server/dev/`.
+- The `docs/` folder is now available at `webpage/docs/` with a completely new structure.
+- The base `Dockerfile` has been split into `client/Dockerfile`, `server/Dockerfile`, and `runtime/Dockerfile`.
+- The build process has been moved from `.docker/build` to `build/`, supporting more options. See `--help` for more information.
+- Brave, Vivaldi, Remmina, and KDE are now also available as ARM-based images.
+- Waterfox is now available as a new browser.
+
+### Server Changes {#v3.0.0-server}
+- A REST API with OpenAPI 3.0 documentation is now available.
+- Prometheus metrics are now available.
+- The server name is now `github.com/m1k1o/neko/server` and can be used as a library.
+- Reusable components and types are available in the `server/pkg/` folder, such as `gst`, `xevent`, and `xorg`.
+- A new authentication system with support for multiple authentication methods has been added.
+- A new user management system with support for granular feature access control has been implemented.
+- The structure for configuration options has been updated, with options now separated into modules.
+- Compatibility with V2 configuration options is still available but deprecated.
+- **Capture**: Added a screencast feature as a fallback for WebRTC.
+- **Capture**: Added experimental webcam and microphone passthrough support.
+- **Capture**: Added video simulcast support and a stream selector.
+- **Capture**: When joining a pipeline, a keyframe is requested on demand and sent to the client.
+- **Desktop**: Clipboard now uses `xclip` instead of `libclipboard`, allowing multiple targets.
+- **Desktop**: Added drag-and-drop file upload support.
+- **Desktop**: Added a file chooser dialog to prompt users for file uploads (experimental).
+- **Desktop**: Added an unminimize feature to ensure that the window is not minimized when the user is trying to control it.
+- **Desktop**: Created a custom input X11 driver to support touchscreen devices.
+- **Desktop**: Added support for `xrandr` to set the virtual monitor resolution to any resolution, not just predefined ones.
+- **Desktop**: Added a function to send events when the cursor changes, along with the cursor image.
+- **HTTP**: Added batch mode to allow multiple requests in a single connection.
+- **HTTP**: Added `pprof` support to enable server profiling.
+- **HTTP**: Created a legacy driver to support the current neko client.
+- **HTTP**: Refactored HTTP logging.
+- **Plugins**: Added support for Go plugins to enable custom features to be added to the server.
+- **Plugins**: Chat has been implemented as a plugin that can be disabled globally or per user (mute feature).
+- **Plugins**: File upload has been implemented as a plugin that can be disabled globally or per user.
+- **Session**: Added support to save session tokens as cookies to allow persistent login.
+- **Session**: Added the ability to serialize and deserialize sessions to a file to survive restarts.
+- **Session**: Added support for dynamic permissions with granular feature access control.
+- **WebRTC**: Forwarded desktop cursor changes to the client.
+- **WebRTC**: Forwarded cursor position to other clients that have enabled the inactive cursors option.
+- **WebRTC**: Switched from LittleEndian to BigEndian for the video stream to improve browser compatibility.
+- **WebRTC**: Created a legacy driver to support the current neko client.
+- **WebRTC**: Added WebRTC ping to check if the connection is still alive and to determine latency.
+- **WebRTC**: Added the ability to switch video pipelines on the fly.
+- **WebRTC**: Implemented bandwidth estimation and adaptive quality (experimental).
+- **WebSocket**: Added support for controlling the desktop using WebSockets as a fallback for WebRTC.
+- **WebSocket**: Added support for sending unicast and broadcast messages to all clients.
+
+Please note that in this version, only the server has been updated. The client is still in the old version; therefore, new features may not yet be available in the client.
+
## [n.eko v2.9.0](https://github.com/m1k1o/neko/releases/tag/v2.9.0) {#v2.9.0}
### New Features {#v2.9.0-feats}
diff --git a/webpage/docs/reverse-proxy-setup.md b/webpage/docs/reverse-proxy-setup.md
index e2a5bf95..95bb13fb 100644
--- a/webpage/docs/reverse-proxy-setup.md
+++ b/webpage/docs/reverse-proxy-setup.md
@@ -6,6 +6,8 @@ If you want to run Neko behind a reverse proxy, you can use the following exampl
Do not forget to enable [`server.proxy=true`](/docs/v3/configuration#server.proxy) in your `config.yml` file to allow the server to trust the proxy headers.
:::
+Neko pings websocket client every 10 seconds, and client is scheduled to send [heartbeat](/docs/v3/configuration#session.heartbeat_interval) to the server every 120 seconds. Make sure, that your timeout settings in the reverse proxy are set accordingly.
+
## Traefik v2 {#traefik-v2}
See the example below for a `docker-compose.yml` file.
diff --git a/webpage/docs/troubleshooting.md b/webpage/docs/troubleshooting.md
index f381b79f..14b978a6 100644
--- a/webpage/docs/troubleshooting.md
+++ b/webpage/docs/troubleshooting.md
@@ -187,6 +187,85 @@ services:
NEKO_WEBRTC_EPR: 52000-52100
```
+### No internet in the remote browser {#no-internet}
+
+Try visiting `https://1.1.1.1` in the browser. If it works, then your internet is functioning, but DNS is not resolving.
+
+You can specify a custom DNS server in the Docker file using the `--dns` flag or in your `docker-compose.yaml` file.
+
+```yaml title="docker-compose.yaml"
+services:
+ neko:
+ image: "ghcr.io/m1k1o/neko/chromium:latest"
+ # highlight-start
+ dns:
+ - 1.1.1.1
+ - 8.8.8.8
+ # highlight-end
+ cap_add:
+ - SYS_ADMIN
+ restart: "unless-stopped"
+ shm_size: "2gb"
+ # ...
+```
+
+If it still doesn't work, the issue is likely in the Docker/networking configuration. Check if your Docker network is not conflicting with your host network.
+
+List all Docker networks:
+
+```bash
+$ for n in `docker network ls --format '{{ .ID }}'`; do docker network inspect --format '{{ .IPAM.Config }} {{ .Name }}' $n; done
+[{172.16.0.0/24 172.16.0.1 map[]}] bridge
+[] host
+[{172.17.0.0/24 172.17.0.1 map[]}] neko1-net
+[] none
+[{172.18.0.0/24 172.18.0.1 map[]}] neko2-net
+```
+
+You can check your host network using:
+
+```bash
+$ ip route | grep default
+default via 172.18.0.1 dev eth0 proto dhcp src 172.18.0.2 metric 100
+```
+
+In this case, the host subnet is the same as `neko2-net`, meaning that the internet stops working as soon as a second Docker network is created.
+
+To fix this, you can either remove the conflicting Docker network or change the subnet of the Docker network by modifying the `daemon.json` file:
+
+```json title="/etc/docker/daemon.json"
+{
+ "default-address-pools": [
+ {
+ "base" : "10.10.0.0/16",
+ "size" : 24
+ }
+ ]
+}
+```
+
+### Browser is not starting with persistent profile {#browser-profile-not-starting}
+
+If you are using a persistent profile like `google-chrome` shown below, and the browser is not starting (you see a black screen), it may be because the profile is corrupted or not mounted correctly.
+
+```yaml title="docker-compose.yaml"
+volumes:
+# For google-chrome
+- /data:/home/neko/.config/google-chrome
+```
+
+Possible reasons are:
+- The profile is corrupted, which can happen if the container is not stopped properly. Browsers should be able to recover from this, but it may not work in some cases.
+- The profile is not mounted to the correct path. Make sure that you are mounting the profile to the correct path in your `docker-compose.yaml` file.
+- The profile is not owned by the correct user. Make sure that the profile is owned by the `neko` user in the container. You can check this by running the following command:
+
+```bash
+# Check the owner of the profile
+docker exec -it ls -la /home/neko/.config/google-chrome
+# To change the owner
+docker exec -it chown -R neko:neko /home/neko/.config/google-chrome
+```
+
### Common server errors {#common-server-errors}
```
diff --git a/webpage/package.json b/webpage/package.json
index 2fd52f5b..1eddf2a6 100644
--- a/webpage/package.json
+++ b/webpage/package.json
@@ -10,8 +10,8 @@
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
- "gen-help": "node ./src/components/Configuration/generate.js ./docs/configuration/help.txt ./docs/configuration/help.json",
- "gen-help:v2": "node ./src/components/Configuration/generate.js ./docs/v2-migration/help.txt ./docs/v2-migration/help.json",
+ "gen-config": "node ./src/components/Configuration/generate.js ./docs/configuration/help.txt ./docs/configuration/help.json",
+ "gen-config:v2": "node ./src/components/Configuration/generate.js ./docs/migration-from-v2/help.txt ./docs/migration-from-v2/help.json",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"gen-api-docs": "./gen-api-docs.sh",
diff --git a/webpage/gen-api-docs.sh b/webpage/scripts/gen-api-docs.sh
similarity index 95%
rename from webpage/gen-api-docs.sh
rename to webpage/scripts/gen-api-docs.sh
index 443d9737..55fb7d23 100755
--- a/webpage/gen-api-docs.sh
+++ b/webpage/scripts/gen-api-docs.sh
@@ -1,4 +1,5 @@
#!/bin/bash
+cd "$(dirname "$0")/.."
# Clean the API docs
docusaurus clean-api-docs all
diff --git a/webpage/scripts/gen-config.sh b/webpage/scripts/gen-config.sh
new file mode 100755
index 00000000..3212d208
--- /dev/null
+++ b/webpage/scripts/gen-config.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+cd "$(dirname "$0")/.."
+
+HELP_FILE="$(realpath -m docs/configuration/help.txt)"
+
+pushd ../server
+go run cmd/neko/main.go serve --help > $HELP_FILE
+popd
+
+# remove all lines with " V2: "
+sed -i '/ V2: /d' $HELP_FILE
+# remove all lines with " V2 DEPRECATED: "
+sed -i '/ V2 DEPRECATED: /d' $HELP_FILE
+#Β remove --legacy
+sed -i '/--legacy/d' $HELP_FILE
+
+# remove evething until first "Flags:"
+sed -i '1,/Flags:/d' $HELP_FILE
+#Β remove --help
+sed -i '/--help/d' $HELP_FILE
+
+npm run gen-config
diff --git a/webpage/sidebars.ts b/webpage/sidebars.ts
index 2ced8912..77c3566f 100644
--- a/webpage/sidebars.ts
+++ b/webpage/sidebars.ts
@@ -54,13 +54,34 @@ const sidebars: SidebarsConfig = {
'configuration/plugins',
]
},
- 'migration-from-v2/README',
- 'reverse-proxy-setup',
{
- type: 'link',
- label: 'API Reference',
- href: '/docs/v3/api',
- }
+ type: 'category',
+ label: 'Customization',
+ collapsed: false,
+ link: { type: "doc", id: "customization/README" },
+ items: [
+ 'customization/browsers',
+ 'customization/ui',
+ ]
+ },
+ {
+ type: 'category',
+ label: 'Developer Guide',
+ collapsed: true,
+ link: { type: "doc", id: "developer-guide/README" },
+ items: [
+ 'developer-guide/repository-structure',
+ 'developer-guide/build',
+ {
+ type: 'link',
+ label: 'API Reference',
+ description: 'OpenAPI documentation for the Neko API',
+ href: '/docs/v3/api',
+ },
+ ]
+ },
+ 'migration-from-v2/README',
+ 'reverse-proxy-setup'
]
},
//{
@@ -68,16 +89,6 @@ const sidebars: SidebarsConfig = {
// label: 'Hardware Acceleration',
// items: [{ type: "autogenerated", dirName: "hardware-acceleration" }]
//},
- //{
- // type: 'category',
- // label: 'Application Customization',
- // items: [{ type: "autogenerated", dirName: "app-customization" }]
- //},
- //{
- // type: 'category',
- // label: 'Developer Guide',
- // items: [{ type: "autogenerated", dirName: "developer-guide" }]
- //},
{
type: 'category',
label: 'Help & Support',
diff --git a/webpage/src/components/Anchor.tsx b/webpage/src/components/Anchor.tsx
index d9305ee1..838770fc 100644
--- a/webpage/src/components/Anchor.tsx
+++ b/webpage/src/components/Anchor.tsx
@@ -3,7 +3,7 @@ import React from 'react';
export function Anchor(props: {id: string}) {
useBrokenLinks().collectAnchor(props.id);
- return ;
+ return ;
}
// -> http_only
@@ -14,7 +14,7 @@ export function Def({id, code, ...props}: {id: string, code?: string}) {
}
useBrokenLinks().collectAnchor(id);
// get current heading id
- return {code};
+ return {code};
}
// -> http_only
@@ -24,5 +24,5 @@ export function Opt({id, code, ...props}: {id: string, code?: string}) {
code = id.split('.').pop();
}
// get current heading id
- return {code};
+ return {code};
}
diff --git a/webpage/src/components/AppIcon.tsx b/webpage/src/components/AppIcon.tsx
new file mode 100644
index 00000000..e616ef7b
--- /dev/null
+++ b/webpage/src/components/AppIcon.tsx
@@ -0,0 +1,8 @@
+import useBrokenLinks from '@docusaurus/useBrokenLinks';
+
+export function AppIcon({id, ...props}: {id: string}) {
+ useBrokenLinks().collectAnchor(id);
+ return
+
+
+}
diff --git a/webpage/src/components/Configuration/generate.js b/webpage/src/components/Configuration/generate.js
index 1695c2e7..ecc02341 100644
--- a/webpage/src/components/Configuration/generate.js
+++ b/webpage/src/components/Configuration/generate.js
@@ -17,6 +17,16 @@ const parseConfigOptions = (text) => {
defaultValue = 'true';
}
}
+ // this is an opaque object
+ if (type === 'string' && defaultValue === '{}') {
+ type = 'object';
+ defaultValue = {};
+ }
+ // this is an opaque array
+ if (type === 'string' && defaultValue === '[]') {
+ type = 'array';
+ defaultValue = [];
+ }
return { key: key.split('.'), type, defaultValue: defaultValue || undefined, description };
}
return null;
diff --git a/webpage/src/components/Configuration/index.tsx b/webpage/src/components/Configuration/index.tsx
index 1497cd87..79f2422c 100644
--- a/webpage/src/components/Configuration/index.tsx
+++ b/webpage/src/components/Configuration/index.tsx
@@ -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 = ({ 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 (
-
- {code}
-
- );
+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): 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}"` : ''}`;
+ case 'strings':
+ return '';
+ case 'object':
+ return '';
+ case 'array':
+ return '';
+ 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 (
-
- {code}
-
- );
+ 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 (
+
+ {code}
+
+ );
+}
+
+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 (
+
+ {code}
+
+ );
+}
+
+export function YamlFileContent({ options, comments, ...props }: { options: ConfigOption[], comments?: boolean }) {
+ if (typeof comments === 'undefined') {
+ comments = true;
+ }
+
+ const final = Symbol('final');
+
+ const buildYaml = (obj: Record, 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}" ]` : '[ ]';
+ } 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(', ') : ''} ]`;
- break;
- case 'duration':
- case 'string':
- val = `${value.defaultValue ? `"${value.defaultValue}"` : ''}`;
- 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 (
-
- {yamlCode}
-
- );
+ return (
+
+ {yamlCode}
+
+ );
+}
+
+type ConfigurationTabProps = {
+ options?: ConfigOption[] | Record;
+ heading?: boolean;
+ comments?: boolean;
+ filter?: string | string[] | Record;
+};
+
+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 (
-
-
-
+
+
+ {heading && (
You can set the following environment variables in your docker-compose.yaml file or in your shell environment.
You can create a /etc/neko/neko.yaml file with the following configuration options.
- {yamlFile()}
-
-
-
+ )}
+ {YamlFileContent({ options: configOptions, comments })}
+
+
);
-};
-
-export default ConfigurationTab;
+}
diff --git a/webpage/src/css/custom.css b/webpage/src/css/custom.css
index 38a6b564..8ce333f1 100644
--- a/webpage/src/css/custom.css
+++ b/webpage/src/css/custom.css
@@ -29,6 +29,10 @@
--docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3);
}
+.anchorOffset {
+ scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem)
+}
+
/*
* Custom CSS for OpenAPI documentation
*/
diff --git a/webpage/src/pages/contact.md b/webpage/src/pages/contact.md
index fec9db6a..42ea917f 100644
--- a/webpage/src/pages/contact.md
+++ b/webpage/src/pages/contact.md
@@ -9,3 +9,11 @@ We are here to assist you with any issues you may face while setting up or using
- For installation or usage questions, [join our Discord](https://discord.gg/3U6hWpC) and post in the [#community-help](https://discord.com/channels/665851821906067466/696222582114091088) channel.
- To report bugs or request features, [open a new issue on GitHub](https://github.com/m1k1o/neko/issues).
- If you find an issue with this documentation, click the `Edit this page` button at the bottom of the page and then the `edit` button on the GitHub page that opens to make edits directly from your browser. (See a [step-by-step guide here](https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files)).
+
+**Enterprise Support**
+
+For enterprises or organizations requiring **dedicated, paid support**, custom solutions, or priority assistance, please contact us at neko@m1k1o.net.
+
+**Security**
+
+If you discover a security vulnerability in Neko, please report it to us directly at security@m1k1o.net. We take security seriously and will work with you to address the issue promptly. Please do not disclose the vulnerability publicly until we have had a chance to address it. We appreciate your help in keeping Neko secure.
diff --git a/webpage/src/pages/contributing.md b/webpage/src/pages/contributing.md
index 15f55d16..0ceed0a2 100644
--- a/webpage/src/pages/contributing.md
+++ b/webpage/src/pages/contributing.md
@@ -2,9 +2,9 @@
Neko is an open-source project, and we welcome contributions from the community. Whether you're a developer, designer, or user, you can help improve Neko by reporting bugs, suggesting new features, or submitting code changes.
-## Reporting issues
+## Reporting Issues {#issues}
-If you encounter a bug or have a feature request, please open a new issue on the [GitHub repository](https://github.com/m1k1o/neko/issues). Before opening an issue, please check if a similar issue has already been reported.
+If you encounter a bug or have a feature request, please open a new issue on the [GitHub repository](https://github.com/m1k1o/neko/issues). Before opening an issue, please check if a similar issue has already been reported and ensure you read the [Troubleshooting Guide](/docs/v3/troubleshooting) and the [FAQ](/docs/v3/faq) to see if your issue has already been addressed.
When reporting an issue, please provide as much information as possible, including:
@@ -15,34 +15,14 @@ When reporting an issue, please provide as much information as possible, includi
- Screenshots or error messages (if applicable)
- Your operating system and browser version
-## Contributing code
+## Contributing Code {#pull-requests}
If you're a developer and want to contribute code to Neko, follow these steps:
1. **Fork the [project](https://github.com/m1k1o/neko)**: Create a personal copy of the repository by [forking it](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) to your GitHub account.
-2. **Navigate to [.docker/README.md](https://github.com/m1k1o/neko/tree/master/.docker)**: Follow the instructions in the `.docker/README.md` file for setting up the Docker environment required for development.
+2. **Edit files in your branch**: Make your changes in a new branch created from the `master` branch. Ensure your changes are well-documented and tested.
-3. **Edit files in your branch**: Make your changes in a new branch created from the `master` branch. Ensure your changes are as well documented and tested.
+3. **Submit a [pull request](https://github.com/m1k1o/neko/pulls)**: Once your changes are ready, submit a pull request with a detailed explanation of the improvements and any relevant information for the reviewers.
-4. **Submit a [pull request](https://github.com/m1k1o/neko/pulls)**: Once your changes are ready, submit a pull request with a detailed explanation of the improvements and any relevant information for the reviewers.
-
-## Server build dependencies
-
-To compile the Golang code locally, you need to install the following dependencies:
-
-```shell
-apt-get install -y --no-install-recommends libx11-dev libxrandr-dev libxtst-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
-```
-
-### Retrieving Libclipboard files
-
-Libclipboard files can be retrieved from the `neko_dev_server` Docker container. Run the following commands to copy the necessary files:
-
-```shell
-mkdir -p /usr/local/lib/pkgconfig/ /usr/local/include/
-docker cp neko_dev_server:/usr/local/lib/libclipboard.a /usr/local/lib/
-docker cp neko_dev_server:/usr/local/lib/pkgconfig/libclipboard.pc /usr/local/lib/pkgconfig/
-docker cp neko_dev_server:/usr/local/include/libclipboard-config.h /usr/local/include/
-docker cp neko_dev_server:/usr/local/include/libclipboard.h /usr/local/include/
-```
+Make sure you read the [Developer Guide](/docs/v3/developer-guide/) for more information on the code structure and development process.
diff --git a/webpage/static/.well-known/security.txt b/webpage/static/.well-known/security.txt
new file mode 100644
index 00000000..15c7ba55
--- /dev/null
+++ b/webpage/static/.well-known/security.txt
@@ -0,0 +1,3 @@
+Contact: mailto:security@m1k1o.net
+Policy: https://github.com/m1k1o/neko/security
+Expires: 2030-04-01T00:00:00z