Skip to content

feat(cli): hermes web dashboard tunnel support#3295

Merged
louisgv merged 3 commits intoOpenRouterTeam:mainfrom
AhmedTMM:feat/hermes-dashboard-tunnel
Apr 14, 2026
Merged

feat(cli): hermes web dashboard tunnel support#3295
louisgv merged 3 commits intoOpenRouterTeam:mainfrom
AhmedTMM:feat/hermes-dashboard-tunnel

Conversation

@AhmedTMM
Copy link
Copy Markdown
Collaborator

Summary

Closes #3293 — wires the Hermes Agent v0.9.0 local web dashboard (hermes dashboard, default 127.0.0.1:9119) into spawn's existing SSH-tunnel infrastructure. `spawn run hermes` now auto-exposes the dashboard to the user's local browser, same flow as OpenClaw's WhatsApp-QR dashboard on port 18789.

Changes

  • `agent-setup.ts`
    • New `startHermesDashboard()` helper — session-scoped background launch via `setsid`/`nohup` with a 60s port-ready wait loop. Mirrors `startGateway()` but deliberately skips systemd — the dashboard only needs to live for the duration of the spawn session, so a supervised unit + cron heartbeat is unnecessary complexity. Falls back gracefully (warn + continue) if `hermes` isn't on PATH or the dashboard fails to come up; the TUI still works.
    • Wires `preLaunch`, `preLaunchMsg`, and `tunnel: { remotePort: 9119, browserUrl }` into the hermes `AgentConfig`. The existing generic tunnel plumbing at `orchestrate.ts:628` picks it up — no changes needed in `orchestrate.ts` or `connect.ts`.
  • `manifest.json` — update hermes `notes` to mention the dashboard and its port.
  • `hermes-dashboard.test.ts` (new, 7 tests) — verifies the deploy script:
    • calls `hermes dashboard --port 9119 --host 127.0.0.1 --no-open`
    • checks all three port-probe fallbacks (`ss` / `/dev/tcp` / `nc`) for Debian/Ubuntu compatibility
    • uses `setsid`/`nohup` to detach from the TTY
    • no-ops if the dashboard is already running
    • sources `~/.spawnrc` and exports the hermes venv PATH
    • waits for the port with a bounded timeout
    • does not install a systemd unit (explicit contrast with `startGateway`)
  • `package.json` — bump cli version 1.0.6 → 1.0.7 per `.claude/rules/cli-version.md`.

Discovery notes

Source of truth for the dashboard port and subcommand is `hermes_cli/main.py` in NousResearch/hermes-agent:

```python
dashboard_parser.add_argument("--port", type=int, default=9119, ...)
dashboard_parser.add_argument("--host", default="127.0.0.1", ...)
dashboard_parser.add_argument("--no-open", action="store_true", ...)
```

The dashboard self-authenticates via a session token injected into the SPA HTML (`hermes_cli/web_server.py`), so no token needs to be appended to the tunnel URL — `http://localhost:/` just works.

Test plan

  • `bunx biome check src/` — 0 errors on 185 files
  • `bun test` — 2050/2050 pass (up from 2043; the 7 new hermes tests landed cleanly)
  • Manual: `spawn run hermes` on a Sprite or DO VM — verify the browser opens automatically to the Hermes dashboard
  • Manual: `spawn connect` to an existing Hermes VM — verify the tunnel reattaches and the dashboard is reachable
  • Manual: kill the dashboard process on the VM — verify the TUI still works and the cycle can recover

Hermes Agent v0.9.0 ships a local web dashboard (hermes dashboard, default
127.0.0.1:9119) for config / session / skill / gateway management. This wires
Hermes into spawn's existing SSH-tunnel infrastructure so `spawn run hermes`
auto-exposes the dashboard to the user's local browser.

- agent-setup.ts: new startHermesDashboard() helper — session-scoped
  background launch via setsid/nohup with a port-ready wait loop. No systemd
  (unlike OpenClaw's gateway) because the dashboard only needs to live for
  the duration of the spawn session. Falls back gracefully if hermes isn't
  in PATH or the dashboard fails to come up.
- Wire preLaunch, preLaunchMsg, and tunnel { remotePort: 9119 } into the
  hermes AgentConfig. Mirrors the OpenClaw tunnel pattern at
  orchestrate.ts:628 — startSshTunnel + openBrowser happen automatically.
- manifest.json: update hermes notes to mention the dashboard.
- hermes-dashboard.test.ts: 7 new unit tests verifying the deploy script
  calls `hermes dashboard --port 9119 --host 127.0.0.1 --no-open`, checks
  all three port-probe fallbacks (ss / /dev/tcp / nc), uses setsid+nohup,
  waits for the port, and does NOT install a systemd unit.
- Bump cli version 1.0.6 -> 1.0.7.

Closes OpenRouterTeam#3293
louisgv
louisgv previously approved these changes Apr 14, 2026
Copy link
Copy Markdown
Member

@louisgv louisgv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Review

Verdict: APPROVED
Commit: 8a10953

Findings

No security issues found.

Security Analysis

  • Shell script generation: All strings are static constants or shell-internal variables ($HOME, $elapsed). No user input injection vectors.
  • Port binding: Correctly binds to 127.0.0.1:9119 (loopback only), not exposed to external networks.
  • Process backgrounding: Proper use of setsid/nohup with I/O redirection, TTY-independent.
  • Timeout handling: Bounded wait loop (60s max) prevents infinite hangs.
  • Tunnel configuration: Static port (9119), safe URL construction with typed localPort parameter.
  • Test coverage: Comprehensive mocked tests with no real subprocess spawning.

Tests

  • bun test: PASS (2050 tests, all passing)
  • biome check: PASS (0 errors)
  • bash compatibility: OK (uses arithmetic expansion $((elapsed + 1)), no bash 4+ features)

Compliance

  • CLI version bumped: 1.0.6 → 1.0.7 ✓
  • Loopback-only binding (127.0.0.1) ✓
  • No credential leaks ✓
  • Proper shell quoting ✓
  • Session-scoped (not persistent) ✓

-- security/pr-reviewer

@louisgv louisgv added the security-approved Security review approved label Apr 14, 2026
la14-1
la14-1 previously approved these changes Apr 14, 2026
Copy link
Copy Markdown
Member

@la14-1 la14-1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed: all 2050 tests pass, biome lint clean (185 files), CI checks all green. The hermes dashboard tunnel wiring follows the OpenClaw pattern correctly — startHermesDashboard() is non-fatal (TUI still works if dashboard doesn't start), port-probe uses the correct 3-fallback chain for Debian/Ubuntu compat, session-scoped via setsid/nohup (no unnecessary systemd unit), and the tunnel config at remotePort: 9119 wires into the existing generic tunnel plumbing without needing changes to orchestrate.ts or connect.ts. Good implementation.\n\n-- refactor/issue-reviewer

@AhmedTMM AhmedTMM dismissed stale reviews from louisgv and la14-1 via 0efcd5b April 14, 2026 00:57
Copy link
Copy Markdown
Member

@louisgv louisgv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Review

Verdict: APPROVED ✅

Commit: 0efcd5b

Security Analysis

Port and URL Construction

  • localPort is always a number (validated via tcpCheck in SSH tunnel setup)
  • Template literal http://localhost:${localPort}/ is safe (no string interpolation risk)
  • Port properly validated before use

Shell Script Generation (startHermesDashboard)

  • No command injection risks - all variables are hardcoded literals or properly quoted
  • _hermes_bin=$(command -v hermes) uses safe command discovery
  • All command invocations use proper quoting: "$_hermes_bin", "$HOME"
  • Port check uses hardcoded port 9119 (no dynamic values)

macOS bash 3.x Compatibility

  • Uses $((elapsed + 1)) instead of ((elapsed++))
  • No set -u flag
  • No echo -e usage

Process Isolation

  • Uses setsid/nohup to properly detach from TTY
  • Binds to localhost only (127.0.0.1:9119)
  • Proper stdin/stdout/stderr redirection

Test Coverage

  • 7 new tests, all passing
  • Validates security-critical aspects (port binding, process isolation, PATH setup)
  • Pure unit tests with mocked runners (no subprocess spawning)

Test Results

  • bash -n: N/A (no .sh files modified)
  • bun test: 2111 pass, 0 fail (full test suite)

Summary

This PR adds Hermes web dashboard tunnel support with proper security controls:

  • Session-scoped background process (not persistent systemd service)
  • Localhost-only binding with SSH tunnel access
  • No command injection vectors
  • Comprehensive test coverage

-- security/pr-reviewer

@louisgv louisgv merged commit c6287b9 into OpenRouterTeam:main Apr 14, 2026
5 checks passed
@AhmedTMM AhmedTMM deleted the feat/hermes-dashboard-tunnel branch April 14, 2026 23:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

security-approved Security review approved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[CLI]: Add dashboard tunnel piping for Hermes Agent

3 participants