docs: inventory agent source boundary

This commit is contained in:
Michael Lam committed 2026-05-17 16:11:29 -07:00
1 parent f1d399b437
commit 310d69bed8
6 files changed
+158

No files matched your search

+8
View File
@@ -2,6 +2,14 @@
## [Unreleased]
### Added
- **PR #2482** by @Michaelyklam (refs #2453) — Add a durable source/API boundary inventory for the WebUI's remaining Hermes Agent source dependencies: chat execution, runtime events, profiles, goals, slash/plugin commands, provider/auth/model catalogs, redaction parity, and imported Agent/Gateway sessions. The new RFC tracks replacement API contracts before the source mount can be removed.
### Changed
- **PR #2482** by @Michaelyklam (refs #2453) — Make the multi-container source boundary more explicit: Docker docs and README now link the boundary inventory, and `docker_init.bash` emits a startup warning when the WebUI sees a writable agent-source mount instead of the default read-only `hermes-agent-src` mount.
## [v0.51.85] — 2026-05-17 — Release BI (stage-378 — 3-PR batch — workspace-prefix display leakage fix + release-tag update banner + Slice 3a cancel-control gate RFC)
### Fixed
+2
View File
@@ -208,6 +208,8 @@ docker compose -f docker-compose.three-container.yml up -d
Both compose files use **named Docker volumes** by default, which solves the UID/GID problem by construction. If you need bind mounts to share an existing host directory, see [`docs/docker.md`](docs/docker.md) for the full migration recipe.
> **Known limitation (#681)**: in the two-container setup, tools triggered from the WebUI run in the **WebUI container**, not the agent container. If you need git/node/etc. on the WebUI's filesystem, either use the single-container setup, extend the WebUI Dockerfile, or use the community [all-in-one image](https://github.com/sunnysktsang/hermes-suite).
>
> **Source boundary note (#2453)**: the multi-container setup mounts `hermes-agent-src` read-only into the WebUI by default. This prevents WebUI-side source rewrites but is still an implementation-coupling bridge, not a stable Agent API boundary. See [`docs/rfcs/agent-source-boundary.md`](docs/rfcs/agent-source-boundary.md) for the current source/API decoupling inventory.
### Common failure modes
+9
View File
@@ -391,6 +391,15 @@ else
fi
done
if [ -n "$_agent_src" ]; then
if [ -w "$_agent_src" ]; then
echo ""
echo "!! WARNING: hermes-agent source mount is writable from the WebUI container."
echo "!! Path: $_agent_src"
echo "!! The multi-container compose defaults use a read-only mount for defence-in-depth."
echo "!! If this is not an intentional local development checkout, switch the WebUI"
echo "!! agent source volume/bind mount to read-only. See docs/rfcs/agent-source-boundary.md."
echo ""
fi
uv pip install "$_agent_src[all]" --trusted-host pypi.org --trusted-host files.pythonhosted.org || error_exit "Failed to install hermes-agent's requirements"
else
echo ""
+2
View File
@@ -211,6 +211,8 @@ What multi-container does **not** isolate:
If you need **filesystem isolation** between the chat UI and the agent (e.g. you don't trust the WebUI to read agent state), the multi-container setup is not enough — run the agent on a separate host and connect the WebUI to it via the gateway HTTP API. If you don't need any boundary, the single-container setup is simpler.
The direct source mount is a compatibility bridge, not the long-term API contract. The current source/API boundary inventory and decoupling task list live in [`docs/rfcs/agent-source-boundary.md`](rfcs/agent-source-boundary.md) for [#2453](https://github.com/nesquena/hermes-webui/issues/2453). If you customize the compose files with bind mounts, keep the WebUI-side agent source mount read-only unless you are intentionally doing local development; `docker_init.bash` warns at startup when that path is writable.
## Bind-mount migration (advanced)
If you really need to bind-mount an existing host `~/.hermes` (e.g. you're keeping config in dotfiles, sharing with a non-Docker `hermes` install, etc.):
+70
View File
@@ -0,0 +1,70 @@
# Agent Source Boundary and API Decoupling Inventory
- **Status:** Proposed
- **Created:** 2026-05-17
- **Tracking issue:** [#2453](https://github.com/nesquena/hermes-webui/issues/2453)
## Problem
The WebUI currently depends on Hermes Agent Python source being importable at
runtime. In local installs this usually means a neighboring checkout; in the
multi-container Docker setup it means the WebUI reads the `hermes-agent-src`
volume that the agent container also uses.
That source mount is a compatibility bridge, not the desired long-term contract.
Even when mounted read-only on the WebUI side, it couples WebUI releases to
Hermes Agent internal module layout and makes the multi-container setup look more
isolated than it really is.
## Current safety posture
- The multi-container compose files mount `hermes-agent-src` read-only into the
WebUI service by default.
- `docker_init.bash` prunes the agent source subtree from `chown` so read-only
mounts do not break startup.
- If an operator overrides the compose files with a mutable agent-source mount,
startup now emits a notable warning. The WebUI still starts because local dev
checkouts and custom deployments may intentionally be writable, but the warning
makes the reduced boundary explicit.
## Source-access inventory
These are the current WebUI capabilities that still rely on Agent source or
`hermes_cli`/`agent` modules being importable. Each item should eventually move
behind an explicit, versioned Agent API or a packaged library contract that does
not require mounting the live source checkout.
| WebUI capability | Current dependency | Desired API / contract | Notes |
|---|---|---|---|
| Browser chat execution | `run_agent.AIAgent` imported by `api/streaming.py` | Run lifecycle API: start, observe, status, cancel, approval, clarify, final usage | Covered by the runtime-adapter migration in [#1925](https://github.com/nesquena/hermes-webui/issues/1925), but still source-backed today. |
| Runtime event rendering | WebUI callbacks around Agent token/reasoning/tool events | Stable event envelope for tokens, reasoning, progress, tool lifecycle, approvals, clarify, errors, and final usage | The existing run-adapter RFC describes the browser-facing shape; Agent still needs a durable producer contract. |
| Profile list/create/delete/seed | `hermes_cli.profiles` from `api/profiles.py` | Profile management API with profile metadata, env/runtime context, seed/delete operations, and validation errors | WebUI has fallback filesystem handling for some operations, but feature parity follows Hermes CLI internals. |
| Goal command state | `hermes_cli.goals` from `api/goals.py` | Goal CRUD/control API: get, save, pause/resume/clear, and status | Should preserve current `/goal` WebUI behavior without direct module import. |
| Slash command registry and plugin commands | `hermes_cli.commands` and `hermes_cli.plugins` from `api/commands.py` | Command/plugin capability discovery API scoped by active profile | WebUI should render command help from a stable capability response. |
| Provider/auth/model catalogs | `hermes_cli.models`, `hermes_cli.auth`, and `agent.credential_pool` from `api/config.py` | Provider registry, model catalog, auth status, OAuth/credential-pool status APIs | WebUI has static fallbacks, but exact parity and custom provider state come from Agent internals. |
| Redaction helper parity | `agent.redact.redact_sensitive_text` from `api/helpers.py` | Redaction service/library contract with signature/version compatibility | WebUI keeps a fallback redactor because this import has changed before. |
| CLI/Gateway session bridge | Agent `state.db` schema and gateway metadata read by sidebar/session helpers | Session listing/transcript/metadata API for non-WebUI-originated sessions | Direct SQLite/schema coupling should narrow over time, especially for messaging/email/gateway sessions. |
## Decoupling task list
1. Keep the Docker default safe: WebUI-side `hermes-agent-src` stays read-only in
two- and three-container compose files.
2. Keep documenting the boundary honestly: multi-container isolates process,
network, and resources, not filesystem/source compatibility.
3. Warn loudly when the WebUI container sees a writable agent-source mount in
Docker, because that weakens the defense-in-depth posture.
4. Convert runtime execution first through the #1925 RuntimeAdapter path instead
of adding new direct imports.
5. For each inventory row, file or link a follow-up that defines the Agent API
response shape before replacing the import.
6. Do not claim the source mount can be removed until chat execution, provider
catalogs/auth status, profiles, goals, commands/plugins, redaction, and
imported Agent/Gateway sessions all have stable replacement contracts.
## Non-goals for this slice
- Do not remove `HERMES_WEBUI_AGENT_DIR`.
- Do not break local source-checkout development.
- Do not fail startup solely because the agent source is writable.
- Do not replace the runtime adapter or Hermes Agent API in this document-only
inventory slice.
@@ -0,0 +1,67 @@
"""Regression coverage for issue #2453 agent-source boundary docs/warnings."""
from __future__ import annotations
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
def test_agent_source_boundary_rfc_inventories_import_coupling():
"""The #2453 source-boundary work must keep a concrete import inventory.
The risk in #2453 is not just a Docker mount mode; it is that WebUI behavior
still relies on Hermes Agent internals. Pinning these rows prevents the docs
from degrading into a vague security note without the follow-up task list.
"""
doc = REPO / "docs" / "rfcs" / "agent-source-boundary.md"
assert doc.exists(), "#2453 needs a durable source/API boundary RFC"
src = doc.read_text(encoding="utf-8")
required_terms = [
"run_agent.AIAgent",
"hermes_cli.profiles",
"hermes_cli.goals",
"hermes_cli.commands",
"hermes_cli.plugins",
"hermes_cli.models",
"hermes_cli.auth",
"agent.credential_pool",
"agent.redact.redact_sensitive_text",
"state.db",
]
for term in required_terms:
assert term in src, f"agent source-boundary RFC must inventory {term}"
for api_phrase in (
"Run lifecycle API",
"Profile management API",
"Command/plugin capability discovery API",
"Provider registry, model catalog, auth status",
"Session listing/transcript/metadata API",
):
assert api_phrase in src, f"RFC must name replacement contract: {api_phrase}"
def test_docker_startup_warns_when_agent_source_mount_is_writable():
"""Mutable WebUI-side agent source mounts should be visibly discouraged.
The compose default is read-only, but custom bind mounts can still make the
WebUI's agent source path writable. The entrypoint should warn instead of
silently accepting a weakened boundary.
"""
src = (REPO / "docker_init.bash").read_text(encoding="utf-8")
assert "agent source mount is writable" in src
assert "read-only mount" in src
assert "$_agent_src" in src
assert "-w \"$_agent_src\"" in src
def test_docker_docs_link_source_boundary_inventory():
"""Docker docs should link the #2453 inventory from the boundary section."""
src = (REPO / "docs" / "docker.md").read_text(encoding="utf-8")
assert "agent-source-boundary.md" in src
assert "source/API boundary inventory" in src
assert "#2453" in src