Skip to content

selkies

Version 2026.184.2020
Repo superproject

Browser-accessible desktop streaming over HTTPS via pixelflux Wayland capture and a Traefik reverse proxy selkies installs a Python streaming server (the launch wrapper, the capture-bridge server, and a static web-UI fileserver), a Traefik reverse proxy with a self-signed cert for the WebCodecs secure context, and the pre-built web UI bundle. pixelflux creates the nested wayland-1 compositor and streams H.264 to the browser; Traefik terminates TLS on :3000 and proxies the web UI plus the /websockets backend. Each step below asserts a real installed file or a live HTTPS endpoint, so every scenario fails if the candy installed nothing.

  • traefik
  • selkies-fileserver
  • selkies

This candy’s plan: — the runnable spec charly check executes against a live deployment. check: steps are idempotent probes; run: steps change state.

Intent Step
run command=SRCH=“$(getent passwd 1000 | cut -d: -f6)” mkdir -p /usr/local/share/selkies/web cp -r “$SRCH”/.local/share/selkies-build/web/* /usr/local/share/selkies/web/ rm -rf “$SRCH”/.local/share/selkies-build
run download=https://github.com/traefik/traefik/releases/download/${TRAEFIK_VERSION}/traefik_${TRAEFIK_VERSION}_linux_${ARCH}.tar.gz
run mkdir=/usr/local/share/selkies/certs
run command=openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout /usr/local/share/selkies/certs/selkies.key \ -out /usr/local/share/selkies/certs/selkies.crt \ -days 3650 -nodes \ -subj “/CN=selkies” \ -addext “subjectAltName=DNS:localhost,DNS:selkies.localhost,DNS:*.localhost,DNS:charly-selkies-desktop,IP:127.0.0.1” chmod 644 /usr/local/share/selkies/certs/selkies.crt /usr/local/share/selkies/certs/selkies.key
run copy=traefik.yml
run copy=traefik-dynamic.yml
run mkdir=${HOME}/.local/bin
run mkdir=${HOME}/.config/selkies
run mkdir=${HOME}/.local/share/selkies
run copy=selkies-wrapper
run copy=selkies-capture-server
run copy=selkies-fileserver
check the selkies launch wrapper is installed and executable
check the capture-bridge server script is installed
check the static web-UI fileserver script is installed
check the Traefik reverse-proxy binary is installed
check the self-signed TLS certificate for the WebCodecs secure context exists
check the matching private key for the certificate exists
check the pre-built Selkies web UI is staged into the system share directory
check the streaming endpoint serves the web UI over HTTPS on the published port
agent-check a connected browser shows the live desktop rendering, not a black or frozen frame
check command=ldd ${HOME}/.pixi/envs/default/lib/python3./site-packages/pixelflux/pixelflux_wayland.so
check service=selkies
check command=d=“$XDG_RUNTIME_DIR”; [ -n “$d” ] || d=/tmp test -e “$d/wayland-1”
check service=traefik
check addr=127.0.0.1:${HOST_PORT:3000}
check command=node=“$DRINODE” [ -n “$node” ] || node=$(ls -1 /dev/dri/renderD* 2>/dev/null | head -1) [ -n “$node” ] || node=/dev/dri/renderD128 vendor=$(cat /sys/class/drm/$(basename “$node”)/device/vendor 2>/dev/null) if vainfo –display drm –device “$node” 2>/dev/null | grep -q “VAProfileH264.*VAEntrypointEncSlice”; then echo “VAAPI-ENCODE-CAPABLE ($node vendor=$vendor)” elif [ “$vendor” = “0x1002” ] || [ “$vendor” = “0x8086” ]; then echo “FAIL: AMD/Intel GPU ($vendor) at $node but no VAAPI H264 encode — DRINODE misconfig / broken libva (CPU x264 fallback)”; exit 1 else echo “VAAPI-N/A (no VAAPI-encode GPU at $node vendor=${vendor:-none} — NVENC/x264 path)” fi
check command=rm -f /tmp/charly-frame.png pixelflux-screenshot > /tmp/charly-frame.png 2>/dev/null [ -s /tmp/charly-frame.png ] || { echo “no PNG captured yet”; exit 1; } out=$(ffmpeg -hide_banner -i /tmp/charly-frame.png -vf “signalstats,metadata=print” -f null - 2>&1) ymin=$(printf “%s” “$out” | grep -oE “YMIN=[0-9]+” | grep -oE “[0-9]+” | head -1); ymin=${ymin:-0} ymax=$(printf “%s” “$out” | grep -oE “YMAX=[0-9]+” | grep -oE “[0-9]+” | head -1); ymax=${ymax:-0} sp=$((ymax - ymin)) [ “$sp” -gt 24 ] && { echo “FRAME-NOT-BLACK (luma spread=$sp ymin=$ymin ymax=$ymax)”; exit 0; } echo “frame still uniform (spread=$sp ymin=$ymin ymax=$ymax)”; exit 1
check command=home=“$HOME” [ -n “$home” ] || home=$(getent passwd “$(id -u)” 2>/dev/null | cut -d: -f6) log=“$home/.local/share/selkies/selkies.log” [ -f “$log” ] || { echo “selkies log not present yet”; exit 1; } node=“$DRINODE” [ -n “$node” ] || node=$(ls -1 /dev/dri/renderD* 2>/dev/null | head -1) # Only trust a render node actually present in THIS container: a # GPU-less (CPU) pod can still carry an injected DRINODE for a host # device that was never mapped in — selkies itself then falls to # software too, so treat it as no-GPU (the clean CPU x264 path). [ -n “$node” ] && [ ! -e “$node” ] && node=“” vendor=“” [ -n “$node” ] && vendor=$(cat /sys/class/drm/$(basename “$node”)/device/vendor 2>/dev/null) if grep -qiE “Failed to init NVENC|Falling back to CPU|Using CPU Software Encoding|Mode: H264 (CPU)” “$log”; then cpu_fallback=1 else cpu_fallback=0 fi case “$vendor” in 0x10de) [ “$cpu_fallback” = “1” ] && { echo “FAIL: NVIDIA GPU ($node) but pixelflux fell back to CPU — NVENC not active. log tail:”; grep -iE “NVENC|CUDA|CPU|encoder” “$log” | tail -8; exit 1; } grep -qiE “NVENC” “$log” || { echo “NVENC not initialized yet”; exit 1; } echo “ENCODER-OK: NVENC active on NVIDIA $node”; exit 0 ;; 0x1002|0x8086) [ “$cpu_fallback” = “1” ] && { echo “FAIL: AMD/Intel GPU ($node) but pixelflux fell back to CPU — hardware encode not selected. log tail:”; grep -iE “VAAPI|VA-API|CPU|encoder” “$log” | tail -8; exit 1; } echo “ENCODER-OK: hardware encode selected on $node (VAAPI capability asserted by selkies-vaapi-encode)”; exit 0 ;; *) echo “ENCODER-OK: CPU x264 (no GPU render node — vendor=${vendor:-none})”; exit 0 ;; esac