KASM Void
Overview
Section titled “Overview”kasm-void is the production workspace image used by the kasm-vpn
Deployment in apps/kube/kasm/. It extends kasmweb/discord with
CloakBrowser so the KASM session
exposes both Discord and a browser side-by-side. The browser is the canonical
screen-share target inside Discord — the operator opens any URL in the bundled
Chromium build and streams the window through Discord’s video feature.
Why a custom image
Section titled “Why a custom image”- The stock
kasmweb/discordimage ships only Discord; closing it leaves an empty desktop with no way to recover. - We need a known, reproducible browser binary inside the workspace so the Discord stream picks up consistent fonts/codecs across sessions.
- Resource targets are higher than the stock image’s defaults — bundling lets
us own the
requests/limitsagainst a single artifact instead of patching a vendor tag.
Crash recovery
Section titled “Crash recovery”/dockerstartup/custom_startup.sh spawns three background supervisors:
| Loop | Process matched (pgrep -f) | Action |
|---|---|---|
cloak_loop | cloakbrowser | Relaunches /opt/cloakbrowser/cloakbrowser $CLOAK_APP_ARGS $START_URL |
discord_loop | `discord | electron` |
nav_shim_loop | nav_shim.py | Relaunches python3 /dockerstartup/nav_shim.py |
Each loop polls every 3–5 seconds. Closing the Discord window or the browser window from the desktop triggers a respawn — no manual KASM session restart needed.
The original kasmweb/discord startup script is preserved as
/dockerstartup/discord_startup.sh so its arg-handling, profile setup, and
maximize logic continue to work unchanged.
Runtime env
Section titled “Runtime env”| Variable | Default | Notes |
|---|---|---|
START_URL | https://kbve.com | Launch URL for the cloakbrowser instance |
CLOAK_APP_ARGS | (chromium defaults) | Override the full cloakbrowser arg list |
LAUNCH_DISCORD | 1 | Set to 0 to disable the Discord supervisor |
LAUNCH_CLOAK | 1 | Set to 0 to disable the browser supervisor |
LAUNCH_NAV_SHIM | 1 | Set to 0 to disable the URL launcher shim |
NAV_SHIM_PORT | 9998 | Shim listens on this port (0.0.0.0) |
CDP_PORT | 9222 | Cloakbrowser CDP port (bound to 127.0.0.1) |
URL_LAUNCHER_TOKEN | (k8s secret) | Reuses kasm-vnc-pw value as bearer token |
VNC_PW | (k8s secret) | Provided by the kasm Deployment |
APP_ARGS | (inherited) | Discord-side Electron args, consumed upstream |
Desktop shortcuts + helper scripts (v0.0.5)
Section titled “Desktop shortcuts + helper scripts (v0.0.5)”The Desktop ships with launcher icons so the operator does not have to
remember paths. Each .desktop entry is built into the image at
/home/kasm-user/Desktop/ and the underlying scripts live in
/home/kasm-user/scripts/.
| Icon | Action |
|---|---|
| Terminal | Opens xfce4-terminal (the package is now installed) |
| Discord | Direct launcher for /usr/share/discord/Discord (host-update skipped) |
| CloakBrowser | Direct launcher for the bundled Chromium fork |
| Open URL in Browser | zenity prompt → posts to the local nav_shim (falls back to a fresh cloakbrowser --new-window if the shim is down) |
| Reload CloakBrowser | Kills cloakbrowser, clears the Singleton* locks; the supervisor respawns it in ~3s |
| Reset Discord | Stops Discord, clears Cache//Code Cache//GPUCache//Shared Dictionary/, re-seeds SKIP_HOST_UPDATE |
Discord host-update gate
Section titled “Discord host-update gate”scripts/seed-discord-config.sh writes SKIP_HOST_UPDATE: true to
~/.config/discord/settings.json before every Discord launch. The
upstream Discord-on-Linux build otherwise attempts a self-update on each
boot, which fails inside the gluetun-tunneled namespace and leaves the
desktop without a Discord window.
CloakBrowser stability
Section titled “CloakBrowser stability”cloak_loop now passes
--user-data-dir=/home/kasm-user/.config/cloakbrowser --disable-software-rasterizer and clears stale SingletonLock/SingletonSocket
files before each respawn. The loop also tracks an exponential backoff
when the binary exits in under 2 seconds and tails the last few lines of
/tmp/cloakbrowser.log so the failure is visible from kubectl logs.
URL launcher (nav_shim)
Section titled “URL launcher (nav_shim)”The bundled browser opens whatever START_URL was at session start. To
swap it without restarting the pod, the dashboard POSTs a JSON body to
/dashboard/kasm/launch-url/{workspace} (axum-kbve). axum forwards to the
in-pod nav_shim.py over an internal kasm-vpn-service:9998 route.
The shim is a thin HTTP front for CDP
Page.navigate and only exposes that one method. Defense in depth:
| Layer | Mitigation |
|---|---|
| Cloakbrowser CDP | Bound to 127.0.0.1 inside the workspace container only |
| Shim API surface | Only POST /open {url} and GET /healthz are routed; everything else 404s |
| Bearer auth | Every request must carry the URL_LAUNCHER_TOKEN (= rotated kasm-vnc-pw) |
| Cilium NetworkPolicy | Port 9998 is only reachable from the kbve namespace |
| URL policy | Scheme allowlist (http/https), no embedded creds, no loopback/private/link-local hosts, no .cluster.local/.svc |
| Audit | Each launch request and shim response is captured via the existing Vector → ClickHouse project_logs pipeline |
Resources
Section titled “Resources”The workspace container in apps/kube/kasm/manifest/deployment.yaml is
sized for Discord + a browser tab + a screenshare encoder running in parallel:
| CPU | Memory | |
|---|---|---|
requests | 2 | 4Gi |
limits | 4 | 8Gi |
The Gluetun sidecar keeps its own modest profile (100m/128Mi request,
500m/384Mi limit) — bumping it does not buy noticeable latency since
the VPN tunnel is bandwidth-, not CPU-bound.
npx nx run kasm-void:containernpx nx run kasm-void:testPublished tags land at ghcr.io/kbve/kasm-void:<version>; the post-publish
chore workflow syncs the Deployment image pin to match version.toml.