Skip to content
Draft, pre-release documentation

The sidebar is the panel to the right of the desktop in a Kasm session. It shows what the run is doing and has an inbox for sending messages to it. Kasm Session Runtime ships one, named default. This guide builds your own and installs it beside that one.

A sidebar is a bundle: a directory with sidebar.js and sidebar.css. You install it into the image under a name of your own, and each session picks one with KASM_RUNTIME_SIDEBAR=<name>. An image can carry several, and installing one overwrites nothing. A sidebar needs no vision model.

A bundle depends on two contracts: how the viewer page hosts it (this guide) and how it talks to the runtime (the sidebar protocol). The bundled sidebar’s internal structure is not part of either contract, and it has no slot or plugin API.

  • A stock kasmweb/* image built with the SDK’s stock_image_mods layer. It carries the kasm-sidebar-install installer. See Enable the runtime on a workspace, part B.
  • The workspace’s mcp port map. The sidebar reaches the runtime through it.
  • A copy of the 09-custom-sidebar example from the SDK: a working sidebar in under two hundred lines of plain JavaScript, with no build step. Start from it.

sidebar.js is an ES module that the viewer page loads with <script type="module">. It mounts itself when it runs:

  • Append one element to document.body. Do not replace or move the viewer’s #noVNC_container.
  • Link the stylesheet from the module. The installer puts the CSS next to the script under the same hashed name, so derive its URL from import.meta.url by swapping .js for .css.
  • Scope every CSS selector under your root element, so nothing leaks into the viewer.
  • Take width by making body a flex row, so #noVNC_container shrinks beside the sidebar. After any change of width, dispatch a resize event on window; KasmVNC re-fits the desktop on that event and no other.
  • Keep everything in the bundle. The viewer runs under Cross-Origin-Embedder-Policy: require-corp, so web fonts, icon CDNs and scripts from another origin will not load. Other files in the bundle directory are copied beside the script; refer to them relative to the module URL.

Typing in the sidebar must not reach the session, and the session must get focus back when the person is done.

  • Give every control that is not a text input tabindex="-1", and cancel mousedown on it with preventDefault.
  • Stop keydown, keypress and keyup from propagating out of your root element.
  • A text input takes focus while the person types. Escape in any input, and submitting a form, blur it and return focus to the viewer’s canvas.
  • Never call focus() on the sidebar except when the person clicks into an input.

The viewer page is /desktop/<kasm_id>/vnc/vnc.html, and the runtime is its sibling at /desktop/<kasm_id>/mcp/. Derive that base from the page’s own location.

  • Read GET <base>events?after=<seq>&run=<run_id> as a stream with fetch and a stream reader. The runtime may not be up when the page loads, and may restart for another run while it is open: probe, reconnect, and reset when a different run_id arrives.
  • Screenshots are at GET <base>artifacts/<file>.
  • Writes to the inbox (POST <base>messages, <base>attachments, <base>actions) carry the token in the X-Kasm-Runtime-Session-Token header. The event stream cannot carry a header, so it takes the token as ?token=.
  • Name the run on every write, and generate each write’s idempotency key before the first attempt, reusing it on retry.
  • Keep nothing you need after a reload in the page. The inbox and the trace live in the runtime.

The event types, request shapes and inbox rules are in the sidebar protocol reference. The 09-custom-sidebar example does all of the above; keep those parts when you change it.

On top of an image built with stock_image_mods:

FROM kasmweb/chrome:stock-mods
USER root
COPY my-bundle /tmp/my-bundle
RUN kasm-sidebar-install --name acme /usr/share/kasmvnc/www /tmp/my-bundle && rm -rf /tmp/my-bundle
USER 1000

With --name acme, kasm-sidebar-install copies the stock KasmVNC web root to /usr/share/kasmvnc-sidebars/acme/www and puts your files in its assets/ as sidebar-<hash>.js and sidebar-<hash>.css. The stock web root and every other sidebar are left alone. Running it again replaces the earlier build of that name.

Names match [a-z0-9][a-z0-9_-]*. KASM_SIDEBAR_ROOT changes the root directory; the default is /usr/share/kasmvnc-sidebars.

Kasm’s proxy caches everything under /desktop/ except vnc.html for four hours. A new build gets a new hashed name, so users never see a stale script.

Set the variable in the workspace’s environment, or in the session request:

{
"environment": {
"KASM_RUNTIME_SIDEBAR": "acme"
}
}
KASM_RUNTIME_SIDEBAR Viewer served
unset The stock viewer, no sidebar
an installed name That sidebar
malformed, or not installed The stock viewer; the reason and the installed names are logged

A sidebar is chosen per session and cannot change while the session runs.

The compose box and forms need the runtime’s inbox. Set a messaging token on the run, and give the sidebar the same value on the viewer URL:

Terminal window
KASM_RUNTIME_INBOX_TOKEN=change-me KASM_RUNTIME_OBSERVE=1 kasm-session-runtime run scenario.yaml
# open the viewer as .../vnc/vnc.html?kasm_runtime_token=change-me

KASM_RUNTIME_OBSERVE=1 makes a run serve the event stream and artifacts for the sidebar; under mcp they are served whenever HTTP is on. Without an inbox token the inbox routes are absent and the sidebar can show only the timeline. See People in the loop for messages, ask_human and stop.

Start a session of the workspace with KASM_RUNTIME_SIDEBAR=acme set. Your panel appears to the right of the desktop, the desktop re-fits beside it, and typing in the panel does not reach the session.

To confirm which bundle the viewer loaded, open your browser’s developer tools on the session page and look in the Network tab for assets/sidebar-<hash>.js. The hash changes with each build of the bundle. With KASM_RUNTIME_SIDEBAR unset, no such file loads.

The session’s log has a line sidebar-select: sidebar 'acme' (KASM_VNC_PATH=/usr/share/kasmvnc-sidebars/acme) when the selector found the bundle.

Symptom Cause Fix
Stock viewer, and sidebar-select: no sidebar 'acme' under /usr/share/kasmvnc-sidebars (installed: ...) in the log The name is not installed in this image Check the name in the Dockerfile matches the variable, and that the session uses the rebuilt image
Stock viewer, and ... is not a valid name ([a-z0-9][a-z0-9_-]*) -- stock viewer Uppercase letters or other characters in the name Use lowercase letters, digits, - and _, starting with a letter or digit
Build fails with has no sidebar.js/sidebar.css The bundle directory is missing a required file Put both files at the top of the directory
Build fails with does not look like a KasmVNC web root (no vnc.html) The second argument is not the stock web root Pass /usr/share/kasmvnc/www
A font, icon or script does not load It comes from another origin, which the viewer’s embedder policy blocks Copy it into the bundle
Keys typed in the sidebar also appear in the app Key events propagate out of the sidebar Stop propagation on your root element, as in step 2
The desktop does not shrink beside the panel No resize event after the width changed Dispatch resize on window
The timeline stays empty The runtime is not serving events, or the mcp port map is missing Check the port map; under run, set KASM_RUNTIME_OBSERVE=1