Evidence record / openhands

A dated record of one change, kept so the writing that cites it can be checked. Compiled from the sources listed below by the research run, not written for reading. The judgment lives in the signals and issues that cite it, below.

2026-10-13-openhands-per-conversation-docker-containers-ship-through-oh-conversation-runtime-the-execution-only

Per-conversation Docker containers ship through OH_CONVERSATION_RUNTIME; the execution-only design in v1.21.0’s headline and the docs never shipped. Carry-forward answer: the Docker work reached a tag, but not in the form its headline described. v1.21.0’s “request Docker execution workspaces” (#17177) was built for an SDK design (#4883) that ran only tools in a container and kept LLM calls and credentials outside. That PR closed unmerged, no released agent server advertises the execution_runtime field Canvas checks, and Canvas never sends that workspace kind. What works is the SDK’s per-conversation container mode (#3403, SDK 1.49.0), reachable through the OH_CONVERSATION_RUNTIME=docker forwarding Canvas has had since v1.20.0. The outer agent server starts one container per conversation running a full agent server, so the agent loop, its LLM calls and the secrets its profile allows all run inside. Containers run as the host UID and GID with --cap-drop ALL, no-new-privileges, a loopback-only port and a read-write bind mount of the conversation’s workspace. v1.21.0 lets them reach host services through host.docker.internal (#17518 with SDK #5164). v1.25.0 stops offering host folders on such backends (“This backend uses isolated workspaces”) and documents the mode as README Option 3. The container image defaults to the floating ghcr.io/openhands/agent-server:latest-python, and switching LLM or profile mid-conversation, forking and credential bindings return 501 in this mode (routers.py L339-L345). From 2026-09-06 to 2026-10-04 the docs page told operators to set OH_EXECUTION_RUNTIME, OH_EXECUTION_IMAGE and OH_EXECUTION_PLATFORM, which nothing reads, so a stack started that way ran conversations on the host.

Channel: tagged-release (runtime, host services, isolated-workspace UI); docs-only (docs page named variables nothing reads, 2026-09-06 to 2026-10-04). Half: both. Date: 2026-09-22 (v1.21.0); 2026-10-04 (docs corrected); 2026-10-06 (v1.25.0 UI and README).

Operator consequence: For per-conversation isolation on v1.21.0 or later, set OH_CONVERSATION_RUNTIME=docker, not OH_EXECUTION_*, and pin OH_CONVERSATION_IMAGE to the agent-server version Canvas pins (ghcr.io/openhands/agent-server:1.54.0-python at v1.27.1) so the inner and outer servers match. If you set it up from the docs before 2026-10-04, run docker ps --filter name=agent-server-conversation- during a conversation; no container means it ran on the host, the same failure the profile recorded for tags before v1.20.0. Know the trust model that shipped: allowed credentials live inside the container, so the profile’s secret allow-list is what limits them (the outer alias is not copied wholesale in this mode; live scope enforcement remains untested, section 4). Expect the Canvas port to listen on all interfaces in this mode (section 1).

Receipt

Finding metadata

Run: 2026-10-13-profile-refresh-2026-09-21_2026-10-13-frontier-v0

Finding ID: 2026-10-13-openhands-per-conversation-docker-containers-ship-through-oh-conversation-runtime-the-execution-only

Profile citations

Source links

Primary links, including exact changelog lines when available.

Versioned source: run artifact