Skip to content
Draft, pre-release documentation

Enable the runtime on a workspace

This guide switches Kasm Session Runtime on for a Kasm workspace, so an outside agent can drive its sessions over MCP and a person can watch in the sidebar.

The runtime does nothing until a session has KASM_RUNTIME_SESSION_TOKEN set. The variable is both the on switch and the credential. When it is set, the runtime starts its MCP endpoint on port 9434 and every caller must send the same value in the X-Kasm-Runtime-Session-Token header. When it is unset the session behaves like any other.

None of this needs a vision model. An outside agent brings its own. See When you need a model for what a model adds inside the runtime.

Image What you do
Kasm nix images, for example chrome:nix The runtime is already in the image. Register the port map and set the environment (part A)
Stock kasmweb/* images Build a thin layer on top of the published image, then do the same as for nix (part B)
  • Kasm administrator access, to edit a workspace’s Docker run configuration.

  • A token for the session: a long random value. For example:

    Terminal window
    python3 -c 'import secrets; print(secrets.token_hex(16))'
  • For stock images only: Docker, and a registry your Kasm deployment can pull from.

Kasm reaches a service inside a session through a custom_port_map entry on the workspace. The runtime needs one named mcp that points at container port 9434. The sidebar uses the same entry, so register it even if only people will look at the session.

Open the workspace in the Kasm admin UI and add this to Docker Run Config Override (JSON) :

{
"custom_port_map": {
"mcp": { "container_port": "9434/tcp/http/none" }
}
}

The value is one string, <port>/tcp/<scheme>/<auth>. none means Kasm’s proxy does not add Basic authentication of its own. Kasm’s session cookie still gates the path in every mode.

Kasm applies the change only to sessions requested after it. Sessions already running do not get the port map.

Optional: the SDK does the same from the command line with an API key that has IMAGES_VIEW, IMAGES_MODIFY and IMAGES_MODIFY_RESOURCES. Run it from the SDK’s examples directory with settings.env filled in:

Terminal window
uv run lib/kasm_provision.py ensure-port-map --ref <image> --port 9434 --service mcp

Add an environment map to the same Docker Run Config Override (JSON) :

{
"custom_port_map": {
"mcp": { "container_port": "9434/tcp/http/none" }
},
"environment": {
"KASM_RUNTIME_SESSION_TOKEN": "<your token>",
"KASM_RUNTIME_SIDEBAR": "default"
}
}

KASM_RUNTIME_SIDEBAR=default shows the runtime’s own sidebar in the viewer. Leave it out for the stock viewer.

A value set on the workspace is the same for every session of that workspace. To give each session its own token, leave it off the workspace and set it when the session is requested (see One token per session).

Variable Effect
KASM_ENABLE_CDP=true Also exposes a raw Chrome DevTools Protocol proxy at /cdp/ on the same port, gated by the same token. Ignored, with a log line, when no session token is set
ACCESSIBILITY_ENABLE=1 Turns on the desktop’s accessibility switch before the app starts, for toolkits that publish nothing over AT-SPI otherwise
VNC_FIXED_RESOLUTION=1 Refuses client-driven resizes, so an attached viewer cannot change the screen size during a run. Needs a recent core image; older images ignore it
KASM_RUNTIME_VISION_ENDPOINT and the other vision settings Gives the runtime a model for the screen.* tools. See Serve a vision model

For the dom.* tools, Chrome has to start with a debugging port. Set APP_ARGS, which replaces the image’s default arguments rather than adding to them. Optional, in the same environment map:

{
"environment": {
"APP_ARGS": "--start-maximized --test-type --ignore-certificate-errors --remote-debugging-port=9222 --remote-debugging-address=127.0.0.1 --remote-allow-origins=* --user-data-dir=/home/kasm-user/.config/chrome-cdp-profile"
}
}

Keep --user-data-dir. Chrome 136 and later ignores --remote-debugging-port without it and gives no error.

A vision-grounded click and a baseline screenshot are only valid at the screen size they were produced at, so a model-driven run needs a fixed size. On nix images Kasm sets VNC_RESOLUTION from the session request’s x_res and y_res, overwriting any value in the environment map, so fix the size in the request. The SDK’s kasm_provision.py up sends x_res and y_res when you pass --env VNC_RESOLUTION=1024x768.

The SDK’s stock_image_mods directory adds the runtime to any kasmweb/* image. Every addition stays off until an environment variable turns it on, and the built image behaves like the stock one by default.

Dockerfile.chrome and Dockerfile.libre-office are ready to build. They differ only in BASE_IMAGE. From the stock_image_mods directory:

Terminal window
docker build -t kasmweb/chrome:stock-mods -f Dockerfile.chrome .

The build downloads the released runtime for the image’s architecture (amd64 or arm64), checks its SHA-256 and installs it with the bundled sidebar. No registry login is needed. The image then starts the runtime with the session when KASM_RUNTIME_SESSION_TOKEN is set, honours KASM_RUNTIME_SIDEBAR, ACCESSIBILITY_ENABLE and the KVNC_* screen settings, and otherwise behaves as the stock image. The Dockerfile pins the same release as the nix images; the build arguments at its top select another.

For another kasmweb/* image, copy Dockerfile.chrome and change BASE_IMAGE.

Push the image to your registry and add it as a workspace . Then register the port map and set the environment exactly as in part A, steps 1 and 2.

Stock images ignore VNC_RESOLUTION, and the screen stays at 1024x768 whatever Kasm sets. Use the KVNC_* variables in the environment map:

Variable Effect Unset
KVNC_DESKTOP_RESOLUTION_WIDTH, KVNC_DESKTOP_RESOLUTION_HEIGHT Screen size 1024x768
KVNC_DESKTOP_ALLOW_RESIZE=false An attached viewer can no longer resize the desktop. Set it for agent runs Resize allowed
ACCESSIBILITY_ENABLE=1 Sets the accessibility switch before the app starts. LibreOffice on the stock image publishes its full accessibility tree without it Switch stays off

To give every session its own token, set KASM_RUNTIME_SESSION_TOKEN in the environment map of the Kasm API’s request_kasm call instead of on the workspace. Kasm keeps that map only when the call is authenticated with an API key. With a username and password the variables are dropped and no error is returned.

The SDK’s provisioning helper sends the map with an API key. From the SDK’s examples directory:

Terminal window
TOKEN=$(python3 -c 'import secrets; print(secrets.token_hex(16))')
eval "$(uv run lib/kasm_provision.py up --ref <image> --env KASM_RUNTIME_SESSION_TOKEN="$TOKEN")"

Drive a session from Claude Code uses this.

Start a session of the workspace.

The endpoint is https://<kasm host>/desktop/<kasm_id>/mcp/mcp. The first mcp is the port map’s name and the second is the runtime’s own path. A request carries Kasm’s session cookie and the token header. The SDK prints both, with lines to paste into Claude Code and Codex:

Terminal window
uv run lib/kasm_provision.py mcp-connect --kasm-id <kasm_id> --token <your token>

Then send an MCP initialize request:

Terminal window
curl -s -X POST "https://<kasm host>/desktop/<kasm_id>/mcp/mcp" \
-H "X-Kasm-Runtime-Session-Token: <your token>" \
-H "Cookie: session_token=<cookie>; username=<user>" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}'

The reply names serverInfo.name as kasm-session-runtime. A wrong token gets HTTP 401.

From a terminal in the session, send the same request to http://127.0.0.1:9434/mcp with only the token header. On a stock image, the runtime’s log is /tmp/kasm-session-runtime-mcp.log, and a line containing serving N methods over HTTP on 0.0.0.0:9434 means it started.

With KASM_RUNTIME_SIDEBAR=default, the viewer shows a panel to the right of the desktop with three tabs: Home, Timeline and Inbox. Home shows the session’s mode and its CDP and vision configuration.

Symptom Cause Fix
HTTP 401 The X-Kasm-Runtime-Session-Token header is missing or does not match the session’s token Send the value the session was started with, in that header. Kasm’s proxy overwrites Authorization with the literal string None before the request reaches the runtime
HTTP 502 The runtime is not up yet, or the workspace has no mcp port map Wait a few seconds and retry. If it persists, check the port map, and that the session was started after you added it
The token variables never reach the session The session was requested through the API with a username and password Request it with an API key
No sidebar KASM_RUNTIME_SIDEBAR is unset, malformed, or names a sidebar the image does not have Set it to default. An unknown name is logged with the installed names and gives the stock viewer
Stock image stays 1024x768 Stock images ignore VNC_RESOLUTION Use KVNC_DESKTOP_RESOLUTION_WIDTH and _HEIGHT on an image built with the layer
dom.* tools cannot reach the browser Chrome was started without a debugging port, or without --user-data-dir Set APP_ARGS as above. Flags apply only at Chrome’s first launch