Docker-sandbox artifacts fail to open in file browser (container path not mapped to host sandbox mirror)
#7,097 opened on Aug 16, 2026
Repository metrics
- Stars
- (17,426 stars)
- PR merge metrics
- (Avg merge 14h 31m) (314 merged PRs in 30d)
Description
Summary
When the Hermes terminal backend is docker with filesystem persistence enabled (the default, container_persistent: true), agent-created artifacts written to container paths under /root/… cannot be opened from the WebUI file browser — clicking them fails with "Could not open file" (HTTP 404) even though the file exists on the host.
Environment
- hermes-webui on
master - Hermes Agent terminal backend:
docker, persistent container (container_persistent: true) - Reproducible independent of host OS
Root cause
With docker persistence, Hermes bind-mounts the container's home and workspace from the host sandbox dir:
- container
/root←<sandbox_dir>/docker/<task_id>/home/ - container
/workspace←<sandbox_dir>/docker/<task_id>/workspace/
where <sandbox_dir> defaults to ~/.hermes/sandboxes (overridable via TERMINAL_SANDBOX_DIR). For the default shared persistent container, <task_id> is default.
So a file the agent creates at container path /root/subdir/file.py physically lives on the host at:
<sandbox_dir>/docker/default/home/subdir/file.py
However, the artifact records the container path (/root/subdir/file.py). The file-browser handlers — _handle_list_dir (/api/list) and the /api/file read handler — resolve the requested path relative to the session's host workspace (e.g. a plain host directory) via list_dir() / safe_resolve_ws(), which enforce containment under that workspace. The container path is neither under the session workspace nor present on the host at that literal location, so resolution 404s. The leading slash is stripped client-side, producing a request like:
GET /api/list?session_id=<id>&path=root/subdir -> 404
Steps to reproduce
- Run Hermes with
terminal.backend: docker(defaultcontainer_persistent: true). - Open a WebUI session whose workspace is an ordinary host directory.
- Have the agent create a file at a container path outside that workspace, e.g.
/root/subdir/file.py. - Click the resulting artifact in the WebUI.
- Observed:
/api/list?...&path=root/subdirreturns 404; the UI shows "Could not open file". The file is present on the host at<sandbox_dir>/docker/default/home/subdir/file.py.
Expected
The artifact opens. For a docker-sandbox session, when a requested path isn't resolvable under the session workspace, the browser should map container paths to their host bind-mount mirror:
/root/X→<sandbox_dir>/docker/<task_id>/home/X/workspace/X→<sandbox_dir>/docker/<task_id>/workspace/X
Notes / suggested fix
- The mapping is deterministic (see above);
<sandbox_dir>comes fromTERMINAL_SANDBOX_DIR(default~/.hermes/sandboxes), and the shared persistent container uses task iddefault. - Containment/security must be preserved: apply the existing
safe_resolve_ws/O_NOFOLLOWanchored-walk containment against the sandbox mirror root so symlink escapes remain blocked. - Both the directory-listing (
/api/list) and file-read (/api/file,/api/file/raw) paths need the mapping.