The Problem

Hermes WebUI provides a browser interface for Hermes Agent, but the codebase shows signs of technical debt that increase risk and maintenance cost. The repository lacks dependency pinning, has circular imports, oversized files, and no lockfile for reproducible builds. These issues affect stability and make changes higher-risk than necessary.

What This Does

Hermes WebUI is a Python/vanilla JavaScript web interface for Hermes Agent (199 code files analyzed). Execution starts at server.py:103 (main) and server.py:66/84 (do_GET/do_POST). The internal call graph shows 2877 resolved call edges; post is called from 204 places, get from 153. Requests flow through api/routes.py (66 functions, called from 7 files) and api/config.py (36 functions, called from 31 files). do_POST -> handle_post -> filesystem writes via p.unlink. do_GET -> handle_get -> reads _INDEX_HTML_PATH.read_text. Traced paths leave the process via main -> install_hermes_agent (subprocess) and do_POST -> handle_post (file deletion). Key files owning effects: api/config.py (config, network calls, file I/O), api/routes.py (routing, database, CSRF), api/streaming.py (AI inference, file I/O), and api/profiles.py (profile management). The module graph has 4 modules in circular dependencies (api/config, api/profiles, api/auth), and api/config is a hub with 44 importers — changes here have blast radius.

How It Is Wired

Entry point server.py:103 (main) reaches 31 functions. do_POST at server.py:84 reaches 196 functions; do_GET at server.py:66 reaches 136. The call graph shows handle_post -> bad x63, handle_post -> j x35, handle_post -> get_session x9, and handle_post -> compact x8. api/config.py defines _discover_agent_dir, _get_config_path, get_config, reload_config — it reads/writes files and makes an outbound network call without a timeout. api/routes.py defines route handlers, CSRF checks, and origin validation. api/streaming.py defines _get_ai_agent, _strip_thinking_markup, and calls a model for inference. api/profiles.py manages profile selection. Circular imports exist between api/config, api/profiles, and api/auth. The test suite has 166 test files; tests/conftest.py sets up test infrastructure with _discover_agent_dir and _discover_python.

How To Use It

Setup: No package manager lockfile present — requirements.txt is the only dependency file. Docker is configured with python:3.12-slim (mutable tag). Three compose files exist: docker-compose.yml, docker-compose.two-container.yml, docker-compose.three-container.yml.

Configuration: Environment variables likely loaded from .env.example. No secrets detected in the scan, but CI workflows use GITHUB_TOKEN.

Running it: Start the server with python server.py or via docker-compose up. Access through an SSH tunnel as described in HERMES.md. No build step or bundler required.

Real-World Use

A developer runs docker-compose up -d to start the Hermes WebUI container. They access it locally, then SSH tunnel to a remote server running Hermes Agent. From the UI, they send a message; handle_post processes it, potentially calling api/config.py for configuration, which may make an outbound network call to check for updates or resolve provider aliases. The agent retains context across sessions, so the next session picks up where the last left off. If a cron job is configured, it fires offline and delivers results to the chosen messaging platform.

Code Health & Issues

Static analysis of 199 code files found 135 issues across 7 kinds:

  • [HIGH] Pin third-party GitHub Actions to commit SHA — .github/workflows uses @vN tags on softprops/action-gh-release@v2, docker/setup-qemu-action@v3, docker/setup-buildx-action@v3, docker/login-action@v3. Tags can move, so CI runs with whatever the owner last pushed.
  • [HIGH/soundness] Import cycle member — api/config.py, api/profiles.py, api/auth.py participate in circular import dependencies.
  • [HIGH/cognitive_load] Oversized file — api/config.py, api/streaming.py, api/routes.py each exceed 1500 lines; max indentation depth 9 in api/config.py, tests/_pytest_port.py, api/streaming.py.
  • [HIGH/clarity] Hub module — api/config.py is depended on by 44 modules; tests/_pytest_port.py and api/profiles.py also qualify. Churn here is high-blast-radius.
  • [MEDIUM/resilience] Broad exception handling — api/config.py, api/profiles.py, api/streaming.py have bare except clauses that swallow errors indiscriminately.
  • [MEDIUM/resource_safety] File opened without context manager — api/providers.py, bootstrap.py use open(...) without with, risking handle leaks.
  • [MEDIUM] No dependency lockfile — requirements.txt has no requirements.lock or similar; builds are non-reproducible.
  • [MEDIUM] Outbound request has no timeout — api/config.py makes outbound calls via requests without a timeout parameter; a slow peer can hold workers indefinitely.
  • [LOW] Workflow jobs lack timeout-minutes — .github/workflows/release.yml declares no job timeout; a wedged step can run to the six-hour platform default.

The Bottom Line

The codebase functions but carries significant technical debt. Circular imports and a 1500-line config file make changes risky; the missing lockfile and untagged Docker base image compromise reproducibility; and unpinned GitHub Actions versions introduce supply-chain risk. Teams that need stable, auditable deployments will want to lock dependencies, fix the import cycles, and add timeouts before production use. It's suitable for self-hosted enthusiasts who can manage these risks, but not yet production-ready for risk-averse organizations.