Skip to content

feat(hermes): expose built-in dashboard#4811

Merged
cv merged 7 commits into
mainfrom
salvage/pr-4391-hermes-dashboard
Jun 5, 2026
Merged

feat(hermes): expose built-in dashboard#4811
cv merged 7 commits into
mainfrom
salvage/pr-4391-hermes-dashboard

Conversation

@cv
Copy link
Copy Markdown
Collaborator

@cv cv commented Jun 5, 2026

Summary

This PR salvages #4391 by rebasing the Hermes dashboard work onto current main, resolving conflicts narrowly, and keeping the change focused on exposing Hermes' built-in dashboard while preserving the OpenAI-compatible API on port 8642. It also prebuilds Hermes dashboard assets in the sandbox image so hermes dashboard does not run npm as the sandbox user under /opt/hermes.

Related Issue

Salvages #4391. Related: #4764.

Changes

  • Exposes the Hermes built-in dashboard as the browser surface on port 18789 while keeping the OpenAI-compatible API forward on port 8642.
  • Prebuilds Hermes dashboard assets in agents/hermes/Dockerfile.base and the final Hermes image fallback path, then starts hermes dashboard --skip-build at runtime.
  • Updates dashboard URL, deployment verification, and onboarding forward logic for separate dashboard/API health probes and session-authenticated Hermes dashboard URLs.
  • Updates Hermes quickstart/command docs and tests for dashboard behavior, prebuilt assets, forwards, and guardrail-friendly onboarding changes.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Additional validation run:

  • npm run build:cli passes
  • npm run typecheck:cli passes
  • Focused Vitest for changed Hermes/dashboard/onboard/provisioning paths passes
  • npm run source-shape:check passes
  • Local codebase-growth guardrail simulation passes (src/lib/onboard.ts net -1; no newly added JS files)

npm test was attempted, but one existing installer preflight test failed in this local host environment (warns on Podman but still runs onboarding) because the host reports missing NVIDIA CDI specs and sudo is unavailable; this is outside the Hermes dashboard diff.


Signed-off-by: Carlos Villela cvillela@nvidia.com

Summary by CodeRabbit

  • New Features

    • Built-in Hermes web dashboard is now the primary interface (default port 18789) and ships with prebuilt assets for faster startup.
    • CLI dashboard-url now prints a plain browser-facing dashboard URL for Hermes.
  • Bug Fixes / Reliability

    • Improved dashboard/gateway port validation, collision handling, and health-checking for more reliable sandbox startup and forwarding.
  • Documentation

    • Quickstart and reference docs updated to reflect dashboard-first UX and separate API endpoint.

Signed-off-by: Shannon Sands <shannon.sands.1979@gmail.com>
Signed-off-by: Shannon Sands <shannon.sands.1979@gmail.com>
@cv cv self-assigned this Jun 5, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 5, 2026

Ready to act? Review this PR in Change Stack to turn feedback into patch suggestions you can inspect and refine.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7d3b37b4-e904-4d65-a8a9-ec8884933f4d

📥 Commits

Reviewing files that changed from the base of the PR and between 148f595 and 12e5d04.

📒 Files selected for processing (1)
  • test/e2e/test-hermes-e2e.sh

📝 Walkthrough

Walkthrough

The PR makes the Hermes browser dashboard the primary surfaced endpoint (port 18789): it adds manifest dashboard auth/health fields, ensures web assets at image-build time, refactors start.sh for dashboard port/forward/socat lifecycle, splits dashboard vs gateway health probes, updates onboarding/CLI URL/token flows, and aligns tests/docs.

Changes

Hermes dashboard transition

Layer / File(s) Summary
Dashboard manifest and agent contract fields
agents/hermes/manifest.yaml, src/lib/agent/defs.ts, src/lib/agent/*.test.ts
Manifest now declares a UI dashboard with auth and health_path; AgentDashboard gained healthPath and auth; readDashboard normalizes/validates values; tests updated for new contract fields.
Image-time dashboard bundle and defaults
agents/hermes/Dockerfile, agents/hermes/Dockerfile.base, test/sandbox-provisioning.test.ts
Dockerfile conditionally builds hermes_cli/web_dist when missing and sets HERMES_WEB_DIST; Dockerfile.base removes UI node_modules; CHAT_UI_URL build-arg default changed to dashboard port (http://127.0.0.1:18789); provisioning test verifies build steps and cleanup.
start.sh dashboard ports, socat wiring, and orphan cleanup
agents/hermes/start.sh, test/hermes-start.test.ts
start.sh derives/validates dashboard port from CHAT_UI_URL (default 18789), rejects reserved 8642, defines DASHBOARD_PUBLIC_PORT/DASHBOARD_INTERNAL_PORT, refactors socat helper to capture PID, extends orphan cleanup to detect dashboard forwarders, and standardizes PID bookkeeping; tests updated for new env names and validations.
Dashboard URL output and token-auth branching
src/lib/agent/onboard.ts, src/lib/dashboard-url-command.ts, src/commands/sandbox/dashboard-url.ts, src/lib/dashboard-url-command.test.ts, src/lib/agent/*.test.ts
Onboard and dashboard-url command resolve dashboard auth (url_token/session/none): session/none print plain dashboard URLs without token fetch; url_token continues token flow; CLI metadata and tests updated to reflect browser-facing dashboard URL behavior.
Dashboard forward preservation and multi-port agent forwarding
src/lib/onboard/dashboard-forward-control.ts, src/lib/onboard/agent-dashboard-forward.ts, src/lib/onboard/dashboard.ts, test/onboard-dashboard.test.ts, test/onboard.test.ts
Adds options and stopper utilities to manage preserved ports and stop logic; ensureDashboardForward enforces preserved ports and optional no-reallocation; ensureAgentDashboardForward supports forward_ports with best-effort extra forwards and warnings; tests cover retry/cleanup and multi-port behavior.
Separate gateway and dashboard health contract in deployment chain
src/lib/dashboard/contract.ts, src/lib/dashboard/contract.test.ts, src/lib/onboard.ts, src/lib/verify-deployment.ts, src/lib/verify-deployment-agent.test.ts
Delivery chain now includes dashboardHealthEndpoint, gatewayPort, and gatewayHealthEndpoint; buildChain normalizes and returns expanded chain; onboarding passes these hints and verifyDeployment probes gateway and dashboard independently; tests added.
Quickstart and command reference updates
docs/get-started/quickstart-hermes.mdx, docs/reference/commands.mdx, docs/reference/commands-nemohermes.mdx, .github/workflows/nightly-e2e.yaml, test/e2e/test-hermes-e2e.sh
Docs and e2e/workflows updated to present the built-in Hermes dashboard on port 18789 as primary, clarify plain vs tokenized dashboard URLs, update environment variable semantics and e2e checks to the new dashboard port and registry/log assertions.

Sequence Diagram(s)

sequenceDiagram
  participant OnboardFinalization
  participant buildChain
  participant verifyDeployment
  participant SandboxGateway
  participant HostDashboardProbe
  OnboardFinalization->>buildChain: pass dashboardHealthEndpoint, gatewayPort, gatewayHealthEndpoint
  buildChain->>verifyDeployment: return expanded delivery chain
  verifyDeployment->>SandboxGateway: curl gatewayPort + gatewayHealthEndpoint
  verifyDeployment->>HostDashboardProbe: probe dashboard public port + dashboardHealthEndpoint
Loading
sequenceDiagram
  participant User
  participant start_sh
  participant dashboard_proc
  participant socat_forwarder
  User->>start_sh: set CHAT_UI_URL / NEMOCLAW_DASHBOARD_PORT
  start_sh->>start_sh: derive DASHBOARD_PUBLIC_PORT / INTERNAL_PORT
  start_sh->>dashboard_proc: start Hermes dashboard bind INTERNAL_PORT
  start_sh->>socat_forwarder: start_socat_forwarder PUBLIC→INTERNAL (capture PID)
  start_sh->>start_sh: print Dashboard URL and API URL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#4072: Related onboarding/dashboard-forward changes in src/lib/onboard that overlap forwarding/cleanup logic.

Suggested labels

feature, Integration: Hermes, area: onboarding, area: e2e, v0.0.58

Suggested reviewers

  • prekshivyas
  • cjagwani
  • miyoungc

Poem

🐰 I baked the dashboard in the Docker night,

Ports aligned so traffic flows just right.
Gateway hums on eight six four two,
The web UI greets at one eight seven eight nine too.
Logs and probes keep everything in sight. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.58% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(hermes): expose built-in dashboard' directly and clearly describes the main change—exposing Hermes' built-in dashboard as the primary browser endpoint on port 18789, which is the core objective evident across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch salvage/pr-4391-hermes-dashboard

Comment @coderabbitai help to get the list of available commands and usage tips.

@cv cv mentioned this pull request Jun 5, 2026
12 tasks
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Comment thread agents/hermes/start.sh
*socat*"TCP-LISTEN:${dashboard_public}"*"TCP:127.0.0.1:${dashboard_internal}"*)
echo "[gateway] Removing orphaned dashboard socat forwarder for ${dashboard_public}->${dashboard_internal} (pid ${pid})" >&2
*socat*"TCP-LISTEN:${dashboard_public_port}"*"TCP:127.0.0.1:${dashboard_internal_port}"*)
[ -n "$dashboard_public_port" ] && [ -n "$dashboard_internal_port" ] || continue
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

E2E Advisor Recommendation

Required E2E: hermes-dashboard-e2e, hermes-e2e, hermes-root-entrypoint-smoke-e2e, rebuild-hermes-stale-base-e2e, hermes-secret-boundary-e2e, hermes-onboard-security-posture-e2e
Optional E2E: rebuild-hermes-e2e, hermes-inference-switch-e2e, runtime-overrides-e2e

Dispatch hint: hermes-dashboard-e2e,hermes-e2e,hermes-root-entrypoint-smoke-e2e,rebuild-hermes-stale-base-e2e,hermes-secret-boundary-e2e,hermes-onboard-security-posture-e2e

Auto-dispatched E2E: hermes-dashboard-e2e, hermes-e2e, hermes-root-entrypoint-smoke-e2e, rebuild-hermes-stale-base-e2e, hermes-secret-boundary-e2e, hermes-onboard-security-posture-e2e via nightly-e2e.yaml at 12e5d04dd177be6d7f1259cb99831f2c1fd67f7bnightly run

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • hermes-dashboard-e2e (high): Primary coverage for this PR: validates real Hermes install/onboard with the built-in web dashboard, dashboard forward on 18789, API health remaining on 8642, registry metadata, host reachability, and workflow env changes.
  • hermes-e2e (high): Required baseline Hermes user journey because Dockerfiles, manifest, start.sh, onboarding, deployment verification, and dashboard-url behavior changed; proves install, onboard, sandbox health, and live inference still work without the dashboard-specific E2E flag.
  • hermes-root-entrypoint-smoke-e2e (medium): start.sh changed root entrypoint behavior, gateway/dashboard process launch ordering, socat forwarding, PID cleanup, log paths, and permission restoration; this job is designed to catch root-entrypoint and runtime layout regressions.
  • rebuild-hermes-stale-base-e2e (high): Dockerfile adds a fallback for stale published Hermes base images missing prebaked dashboard assets; this existing job directly exercises the stale-base rebuild path impacted by the change.
  • hermes-secret-boundary-e2e (medium): The PR changes Hermes image contents and start.sh runtime environment handling; because dashboard and gateway now run with separate homes/processes, verify raw secret-shaped values still do not enter sandbox env/config.
  • hermes-onboard-security-posture-e2e (high): start.sh and onboarding changes touch non-root/security-sensitive runtime posture, trusted shell env emission, config permissions, and dashboard process ownership; run the full Hermes non-root security-posture onboard flow.

Optional E2E

  • rebuild-hermes-e2e (high): Useful adjacent confidence for normal Hermes rebuilds with the new manifest/dashboard defaults and image build changes, but stale-base rebuild is the more targeted required rebuild coverage.
  • hermes-inference-switch-e2e (high): API routing should remain on port 8642 while dashboard moves to 18789; optional confidence that provider switching and live Hermes API requests still work after dashboard/API separation.
  • runtime-overrides-e2e (medium): Dashboard port derivation and NEMOCLAW_DASHBOARD_PORT behavior changed for Hermes; generic runtime override coverage is useful if CI capacity allows.

New E2E recommendations

  • Hermes dashboard custom port and remote URL onboarding (high): Existing hermes-dashboard-e2e validates the default dashboard port, but this PR changes CHAT_UI_URL/NEMOCLAW_DASHBOARD_PORT port derivation and remote-bind-facing docs. Add coverage for a non-default Hermes dashboard port and/or non-loopback CHAT_UI_URL while ensuring the API stays on 8642.
    • Suggested test: Add a Hermes dashboard port override E2E that onboards with NEMOCLAW_DASHBOARD_PORT or CHAT_UI_URL set to a custom dashboard port, verifies dashboard-url output, registry dashboardPort, OpenShell forward list, dashboard health, and API health on 8642.
  • Hermes dashboard lifecycle after restart (medium): start.sh cleanup and socat orphan handling changed for both API and dashboard forwarders, but the existing survival E2E is not Hermes-dashboard-specific.
    • Suggested test: Add a Hermes dashboard survival E2E that restarts the gateway/sandbox process and verifies dashboard and API forwards recover without stale socat/PID/log issues.

Dispatch hint

  • Workflow: nightly-e2e.yaml
  • jobs input: hermes-dashboard-e2e,hermes-e2e,hermes-root-entrypoint-smoke-e2e,rebuild-hermes-stale-base-e2e,hermes-secret-boundary-e2e,hermes-onboard-security-posture-e2e

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

E2E Scenario Advisor Recommendation

Required scenario E2E: ubuntu-repo-cloud-hermes
Optional scenario E2E: ubuntu-repo-cloud-hermes-discord, ubuntu-repo-cloud-hermes-slack

Dispatch required scenario E2E:

  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes

Workflow run

Full scenario advisor summary

E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required scenario E2E

  • ubuntu-repo-cloud-hermes: Primary Hermes scenario covers repo-current cloud onboarding for the Hermes agent, sandbox startup, health/inference, and Hermes-specific checks. This PR changes Hermes image contracts, manifest dashboard metadata, entrypoint dashboard/API forwarding, dashboard-url handling, and onboarding/verification paths that directly affect this scenario.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes

Optional scenario E2E

  • ubuntu-repo-cloud-hermes-discord: Optional adjacent Hermes scenario with Discord messaging enabled. It exercises the same changed Hermes entrypoint/image/manifest surface with messaging channel setup enabled, but the PR is primarily about dashboard/API forwarding rather than Discord-specific behavior.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes-discord
  • ubuntu-repo-cloud-hermes-slack: Optional adjacent Hermes scenario with Slack messaging enabled. It provides extra coverage for the changed Hermes startup path when messaging configuration is present, but is not the smallest required target for the dashboard/onboarding changes.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes-slack

Relevant changed files

  • agents/hermes/Dockerfile
  • agents/hermes/Dockerfile.base
  • agents/hermes/manifest.yaml
  • agents/hermes/start.sh
  • src/commands/sandbox/dashboard-url.ts
  • src/lib/agent/defs.ts
  • src/lib/agent/onboard.ts
  • src/lib/dashboard-url-command.ts
  • src/lib/dashboard/contract.ts
  • src/lib/onboard.ts
  • src/lib/onboard/agent-dashboard-forward.ts
  • src/lib/onboard/dashboard-forward-control.ts
  • src/lib/onboard/dashboard.ts
  • src/lib/verify-deployment.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

PR Review Advisor

Findings: 1 needs attention, 12 worth checking, 0 nice ideas
Since last review: 0 prior items resolved, 9 still apply, 0 new items found

Review findings

🛠️ Needs attention

  • Avoid growing the agent definition monolith (src/lib/agent/defs.ts:250): This PR grows src/lib/agent/defs.ts by 31 lines while adding dashboard auth and health-path parsing. The file is already a current large-file hotspot, and the deterministic guardrail flags growth of 20 or more lines as needing attention.
    • Recommendation: Extract the new dashboard manifest parsing and validation into a focused helper module, or offset the growth in defs.ts before merge.
    • Evidence: Deterministic drift context reports src/lib/agent/defs.ts grew from 562 to 593 lines (+31), and the diff adds dashboard.auth and dashboard.health_path parsing in readDashboard().

🔎 Worth checking

  • Source-of-truth review needed: agents/hermes/Dockerfile stale-base dashboard asset fallback: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: agents/hermes/Dockerfile runs npm ci --prefix "$hermes_web_dir" and npm run build --prefix "$hermes_web_dir" if /opt/hermes/hermes_cli/web_dist is absent.
  • Source-of-truth review needed: agents/hermes/start.sh dashboard home preparation: The advisor marked localized patch analysis as missing.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: start.sh sets HERMES_DASHBOARD_HOME from the environment and prepare_hermes_dashboard_home applies mkdir, chown, and chmod to it.
  • Source-of-truth review needed: Legacy NEMOCLAW_HERMES_DASHBOARD compatibility forwarding: The advisor marked localized patch analysis as missing.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: src/lib/hermes-dashboard.ts still defaults to 9119 and src/lib/onboard.ts still appends legacy Hermes dashboard env args, while start.sh uses NEMOCLAW_DASHBOARD_PORT/CHAT_UI_URL for the real dashboard public port.
  • Source-of-truth review needed: Hermes session-auth plain dashboard URL: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: manifest.yaml sets dashboard.auth: session and dashboard-url-command.ts builds a plain URL for session/none dashboards.
  • Source-of-truth review needed: agents/hermes/start.sh dashboard config permission restore: The advisor marked localized patch analysis as missing.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: start.sh launches the dashboard with HERMES_HOME="${HERMES_DASHBOARD_HOME}" but still calls restore_hermes_config_permissions_after_dashboard_start.
  • Constrain HERMES_DASHBOARD_HOME before root chown/chmod (agents/hermes/start.sh:189): The root entrypoint still honors an externally supplied HERMES_DASHBOARD_HOME and later creates, chowns, and chmods that path after only symlink and directory checks. If a launch environment or attacker-controlled env can set this before root startup, the entrypoint can change ownership or permissions on an unintended existing directory.
    • Recommendation: Do not allow this path to be externally overridden, or require it to resolve under a fixed safe parent such as /tmp/hermes-dashboard-home with owner/path checks before mkdir, chown, or chmod. Add a negative test for unsafe absolute paths.
    • Evidence: start.sh sets HERMES_DASHBOARD_HOME="${HERMES_DASHBOARD_HOME:-/tmp/hermes-dashboard-home}" and prepare_hermes_dashboard_home runs mkdir -p, chown "$owner", and chmod 700 on that variable.
  • Prove or gate the plain-URL Hermes dashboard auth model for remote binds (agents/hermes/manifest.yaml:40): The PR changes Hermes from an API-oriented dashboard contract to a built-in browser dashboard with auth: session, and dashboard-url intentionally prints a plain URL without an OpenClaw token. That is reasonable for loopback defaults, but remote bind paths via non-loopback CHAT_UI_URL or NEMOCLAW_DASHBOARD_BIND=0.0.0.0 can expose the Hermes dashboard if upstream session auth is missing or weak.
    • Recommendation: Add targeted runtime/auth validation that remote-bound Hermes dashboard management endpoints reject unauthenticated requests, or gate remote bind for Hermes behind an explicit warning/opt-in that documents and checks the upstream auth dependency.
    • Evidence: manifest.yaml declares dashboard.auth: session; dashboard-url-command.ts skips token fetching for session/none dashboards; dashboard/contract.ts binds to 0.0.0.0 for non-loopback URLs or bindOverride=0.0.0.0; docs only warn users not to expose the dashboard publicly.
  • Tighten the final-image npm fallback trust boundary (agents/hermes/Dockerfile:31): The final Hermes image falls back to running npm ci and npm run build from /opt/hermes/web when the selected base lacks hermes_cli/web_dist. This handles stale bases, but the default base image reference is latest and the fallback executes dependency lifecycle/build scripts from the selected base image, which is a sensitive installer/build trust boundary.
    • Recommendation: Prefer rebuilding or pinning the base image that contains HERMES_WEB_DIST. If the fallback must remain, fail closed unless the base source matches the pinned Hermes release and package-lock.json is present, and track the removal condition so this compatibility path is retired once supported bases are rebuilt.
    • Evidence: Dockerfile comments describe a stale-base fallback; the RUN block executes npm ci --prefix "$hermes_web_dir" and npm run build --prefix "$hermes_web_dir" if /opt/hermes/hermes_cli/web_dist is absent, while ARG BASE_IMAGE defaults to ghcr.io/nvidia/nemoclaw/hermes-sandbox-base:latest.
  • Legacy NEMOCLAW_HERMES_DASHBOARD forwarding can drift from the new default dashboard port (src/lib/onboard.ts:3543): The PR makes the Hermes built-in dashboard the default surface on NEMOCLAW_DASHBOARD_PORT/18789, but the onboard compatibility path still reads NEMOCLAW_HERMES_DASHBOARD and can register a separate fixed forward using the old 9119 default. start.sh no longer uses that old public port as the dashboard public port, so legacy env users can get a dead or misleading 9119 forward.
    • Recommendation: Remove the old optional-dashboard forward path, alias it to the new dashboard port with a clear deprecation warning, or add a compatibility test proving NEMOCLAW_HERMES_DASHBOARD=1 cannot create a dead 9119 forward.
    • Evidence: start.sh derives DASHBOARD_PUBLIC_PORT from NEMOCLAW_DASHBOARD_PORT/CHAT_UI_URL, while src/lib/onboard.ts still calls onboardHermesDashboard.appendHermesDashboardEnvArgs and src/lib/hermes-dashboard.ts still defaults the legacy dashboard port to 9119 when NEMOCLAW_HERMES_DASHBOARD is truthy.
  • Add runtime validation for the actual Hermes dashboard process (agents/hermes/start.sh:617): The changed behavior depends on the real container image, prebuilt Hermes dashboard assets, upstream Hermes session behavior, and OpenShell forwards. The current unit and shell-snippet tests are useful, but they do not fully prove the real image serves /api/status with --skip-build or avoids runtime npm/build activity under /opt/hermes.
    • Recommendation: Add or identify targeted runtime/container validation for Hermes dashboard startup on 18789, API health on 8642, /api/status probing, and no npm execution or /opt/hermes writes at runtime.
    • Evidence: Existing tests cover helper behavior and extracted shell snippets; the E2E script probes dashboard/API reachability, but does not assert that npm was not invoked, that /api/status is served by the real dashboard, or that /opt/hermes remains unwritten at runtime.
  • Either prove or remove the dashboard config permission restore workaround (agents/hermes/start.sh:683): The entrypoint still contains restore_hermes_config_permissions_after_dashboard_start, which repeatedly repairs the shared Hermes config root after dashboard startup. The dashboard now runs with HERMES_HOME=/tmp/hermes-dashboard-home, so the invalid state this recovery path addresses is unclear and may be stale localized tolerance.
    • Recommendation: Document the exact invalid state still created by Hermes dashboard startup, add a regression test that reproduces it, or remove this recovery path if the separate dashboard home made it obsolete.
    • Evidence: start.sh launches the dashboard with HERMES_HOME="${HERMES_DASHBOARD_HOME}" but still calls restore_hermes_config_permissions_after_dashboard_start after start_hermes_dashboard_sandbox_user.
  • Update stale release-note guidance for the old optional Hermes dashboard (docs/about/release-notes.mdx:74): A nearby public documentation page still describes the old optional Hermes dashboard contract using NEMOCLAW_HERMES_DASHBOARD=1 and port 9119. That conflicts with this PR's new default built-in dashboard on 18789 and can send users toward the legacy path that now risks a dead forward.
    • Recommendation: Update or remove the stale release-note text so all public docs consistently describe the built-in Hermes dashboard on NEMOCLAW_DASHBOARD_PORT/18789 and only mention legacy env vars as deprecated if they remain supported.
    • Evidence: grep found docs/about/release-notes.mdx still saying users can set NEMOCLAW_HERMES_DASHBOARD=1 to start and forward the dashboard on port 9119, while changed quickstart/reference docs now describe the built-in dashboard on port 18789.

🌱 Nice ideas

  • None.
Consider writing more tests for
  • **Runtime validation** — Hermes sandbox starts with dashboard on 18789 serving /api/status and API on 8642 serving /health after hermes dashboard --skip-build.. The highest-risk behavior crosses the real Docker image, sandbox entrypoint, upstream Hermes dashboard process, auth/session model, and OpenShell forwarding boundary. Unit and shell-snippet tests are useful but insufficient for the runtime security and correctness claims.
  • **Runtime validation** — Hermes dashboard startup does not execute npm and does not write under /opt/hermes as the sandbox user.. The highest-risk behavior crosses the real Docker image, sandbox entrypoint, upstream Hermes dashboard process, auth/session model, and OpenShell forwarding boundary. Unit and shell-snippet tests are useful but insufficient for the runtime security and correctness claims.
  • **Runtime validation** — Root entrypoint rejects or ignores HERMES_DASHBOARD_HOME values outside the fixed safe dashboard-home path before any mkdir, chown, or chmod.. The highest-risk behavior crosses the real Docker image, sandbox entrypoint, upstream Hermes dashboard process, auth/session model, and OpenShell forwarding boundary. Unit and shell-snippet tests are useful but insufficient for the runtime security and correctness claims.
  • **Runtime validation** — Setting legacy NEMOCLAW_HERMES_DASHBOARD=1 does not create a dead 9119 forward and either aliases to NEMOCLAW_DASHBOARD_PORT or emits a deprecation warning.. The highest-risk behavior crosses the real Docker image, sandbox entrypoint, upstream Hermes dashboard process, auth/session model, and OpenShell forwarding boundary. Unit and shell-snippet tests are useful but insufficient for the runtime security and correctness claims.
  • **Runtime validation** — Remote-bound Hermes dashboard through non-loopback CHAT_UI_URL or NEMOCLAW_DASHBOARD_BIND=0.0.0.0 rejects unauthenticated dashboard/API management requests.. The highest-risk behavior crosses the real Docker image, sandbox entrypoint, upstream Hermes dashboard process, auth/session model, and OpenShell forwarding boundary. Unit and shell-snippet tests are useful but insufficient for the runtime security and correctness claims.
  • **Add runtime validation for the actual Hermes dashboard process** — Add or identify targeted runtime/container validation for Hermes dashboard startup on 18789, API health on 8642, /api/status probing, and no npm execution or /opt/hermes writes at runtime.
  • **Acceptance clause:** Salvages feat(hermes): expose built-in dashboard #4391. Related: [Brev][Agent&Skills] hermes whatsapp QR pairing fails with EACCES — npm install cannot write to /opt/hermes/scripts/whatsapp-bridge/node_modules #4764. — add test evidence or identify existing coverage. No linked issue clauses or issue comments were provided in deterministic context, so the exact feat(hermes): expose built-in dashboard #4391/[Brev][Agent&Skills] hermes whatsapp QR pairing fails with EACCES — npm install cannot write to /opt/hermes/scripts/whatsapp-bridge/node_modules #4764 acceptance criteria could not be mapped beyond the PR body.
  • **Acceptance clause:** Exposes the Hermes built-in dashboard as the browser surface on port `18789` while keeping the OpenAI-compatible API forward on port `8642`. — add test evidence or identify existing coverage. manifest.yaml declares dashboard kind ui and forward_ports [18789, 8642], start.sh defaults the dashboard to 18789 and API to 8642, and tests cover forward-list behavior. Legacy NEMOCLAW_HERMES_DASHBOARD still defaults to 9119 and can drift from the new port.
Since last review details

Current findings:

  • Source-of-truth review needed: agents/hermes/Dockerfile stale-base dashboard asset fallback: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: agents/hermes/Dockerfile runs npm ci --prefix "$hermes_web_dir" and npm run build --prefix "$hermes_web_dir" if /opt/hermes/hermes_cli/web_dist is absent.
  • Source-of-truth review needed: agents/hermes/start.sh dashboard home preparation: The advisor marked localized patch analysis as missing.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: start.sh sets HERMES_DASHBOARD_HOME from the environment and prepare_hermes_dashboard_home applies mkdir, chown, and chmod to it.
  • Source-of-truth review needed: Legacy NEMOCLAW_HERMES_DASHBOARD compatibility forwarding: The advisor marked localized patch analysis as missing.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: src/lib/hermes-dashboard.ts still defaults to 9119 and src/lib/onboard.ts still appends legacy Hermes dashboard env args, while start.sh uses NEMOCLAW_DASHBOARD_PORT/CHAT_UI_URL for the real dashboard public port.
  • Source-of-truth review needed: Hermes session-auth plain dashboard URL: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: manifest.yaml sets dashboard.auth: session and dashboard-url-command.ts builds a plain URL for session/none dashboards.
  • Source-of-truth review needed: agents/hermes/start.sh dashboard config permission restore: The advisor marked localized patch analysis as missing.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: start.sh launches the dashboard with HERMES_HOME="${HERMES_DASHBOARD_HOME}" but still calls restore_hermes_config_permissions_after_dashboard_start.
  • Avoid growing the agent definition monolith (src/lib/agent/defs.ts:250): This PR grows src/lib/agent/defs.ts by 31 lines while adding dashboard auth and health-path parsing. The file is already a current large-file hotspot, and the deterministic guardrail flags growth of 20 or more lines as needing attention.
    • Recommendation: Extract the new dashboard manifest parsing and validation into a focused helper module, or offset the growth in defs.ts before merge.
    • Evidence: Deterministic drift context reports src/lib/agent/defs.ts grew from 562 to 593 lines (+31), and the diff adds dashboard.auth and dashboard.health_path parsing in readDashboard().
  • Constrain HERMES_DASHBOARD_HOME before root chown/chmod (agents/hermes/start.sh:189): The root entrypoint still honors an externally supplied HERMES_DASHBOARD_HOME and later creates, chowns, and chmods that path after only symlink and directory checks. If a launch environment or attacker-controlled env can set this before root startup, the entrypoint can change ownership or permissions on an unintended existing directory.
    • Recommendation: Do not allow this path to be externally overridden, or require it to resolve under a fixed safe parent such as /tmp/hermes-dashboard-home with owner/path checks before mkdir, chown, or chmod. Add a negative test for unsafe absolute paths.
    • Evidence: start.sh sets HERMES_DASHBOARD_HOME="${HERMES_DASHBOARD_HOME:-/tmp/hermes-dashboard-home}" and prepare_hermes_dashboard_home runs mkdir -p, chown "$owner", and chmod 700 on that variable.
  • Prove or gate the plain-URL Hermes dashboard auth model for remote binds (agents/hermes/manifest.yaml:40): The PR changes Hermes from an API-oriented dashboard contract to a built-in browser dashboard with auth: session, and dashboard-url intentionally prints a plain URL without an OpenClaw token. That is reasonable for loopback defaults, but remote bind paths via non-loopback CHAT_UI_URL or NEMOCLAW_DASHBOARD_BIND=0.0.0.0 can expose the Hermes dashboard if upstream session auth is missing or weak.
    • Recommendation: Add targeted runtime/auth validation that remote-bound Hermes dashboard management endpoints reject unauthenticated requests, or gate remote bind for Hermes behind an explicit warning/opt-in that documents and checks the upstream auth dependency.
    • Evidence: manifest.yaml declares dashboard.auth: session; dashboard-url-command.ts skips token fetching for session/none dashboards; dashboard/contract.ts binds to 0.0.0.0 for non-loopback URLs or bindOverride=0.0.0.0; docs only warn users not to expose the dashboard publicly.
  • Tighten the final-image npm fallback trust boundary (agents/hermes/Dockerfile:31): The final Hermes image falls back to running npm ci and npm run build from /opt/hermes/web when the selected base lacks hermes_cli/web_dist. This handles stale bases, but the default base image reference is latest and the fallback executes dependency lifecycle/build scripts from the selected base image, which is a sensitive installer/build trust boundary.
    • Recommendation: Prefer rebuilding or pinning the base image that contains HERMES_WEB_DIST. If the fallback must remain, fail closed unless the base source matches the pinned Hermes release and package-lock.json is present, and track the removal condition so this compatibility path is retired once supported bases are rebuilt.
    • Evidence: Dockerfile comments describe a stale-base fallback; the RUN block executes npm ci --prefix "$hermes_web_dir" and npm run build --prefix "$hermes_web_dir" if /opt/hermes/hermes_cli/web_dist is absent, while ARG BASE_IMAGE defaults to ghcr.io/nvidia/nemoclaw/hermes-sandbox-base:latest.
  • Legacy NEMOCLAW_HERMES_DASHBOARD forwarding can drift from the new default dashboard port (src/lib/onboard.ts:3543): The PR makes the Hermes built-in dashboard the default surface on NEMOCLAW_DASHBOARD_PORT/18789, but the onboard compatibility path still reads NEMOCLAW_HERMES_DASHBOARD and can register a separate fixed forward using the old 9119 default. start.sh no longer uses that old public port as the dashboard public port, so legacy env users can get a dead or misleading 9119 forward.
    • Recommendation: Remove the old optional-dashboard forward path, alias it to the new dashboard port with a clear deprecation warning, or add a compatibility test proving NEMOCLAW_HERMES_DASHBOARD=1 cannot create a dead 9119 forward.
    • Evidence: start.sh derives DASHBOARD_PUBLIC_PORT from NEMOCLAW_DASHBOARD_PORT/CHAT_UI_URL, while src/lib/onboard.ts still calls onboardHermesDashboard.appendHermesDashboardEnvArgs and src/lib/hermes-dashboard.ts still defaults the legacy dashboard port to 9119 when NEMOCLAW_HERMES_DASHBOARD is truthy.
  • Add runtime validation for the actual Hermes dashboard process (agents/hermes/start.sh:617): The changed behavior depends on the real container image, prebuilt Hermes dashboard assets, upstream Hermes session behavior, and OpenShell forwards. The current unit and shell-snippet tests are useful, but they do not fully prove the real image serves /api/status with --skip-build or avoids runtime npm/build activity under /opt/hermes.
    • Recommendation: Add or identify targeted runtime/container validation for Hermes dashboard startup on 18789, API health on 8642, /api/status probing, and no npm execution or /opt/hermes writes at runtime.
    • Evidence: Existing tests cover helper behavior and extracted shell snippets; the E2E script probes dashboard/API reachability, but does not assert that npm was not invoked, that /api/status is served by the real dashboard, or that /opt/hermes remains unwritten at runtime.
  • Either prove or remove the dashboard config permission restore workaround (agents/hermes/start.sh:683): The entrypoint still contains restore_hermes_config_permissions_after_dashboard_start, which repeatedly repairs the shared Hermes config root after dashboard startup. The dashboard now runs with HERMES_HOME=/tmp/hermes-dashboard-home, so the invalid state this recovery path addresses is unclear and may be stale localized tolerance.
    • Recommendation: Document the exact invalid state still created by Hermes dashboard startup, add a regression test that reproduces it, or remove this recovery path if the separate dashboard home made it obsolete.
    • Evidence: start.sh launches the dashboard with HERMES_HOME="${HERMES_DASHBOARD_HOME}" but still calls restore_hermes_config_permissions_after_dashboard_start after start_hermes_dashboard_sandbox_user.
  • Update stale release-note guidance for the old optional Hermes dashboard (docs/about/release-notes.mdx:74): A nearby public documentation page still describes the old optional Hermes dashboard contract using NEMOCLAW_HERMES_DASHBOARD=1 and port 9119. That conflicts with this PR's new default built-in dashboard on 18789 and can send users toward the legacy path that now risks a dead forward.
    • Recommendation: Update or remove the stale release-note text so all public docs consistently describe the built-in Hermes dashboard on NEMOCLAW_DASHBOARD_PORT/18789 and only mention legacy env vars as deprecated if they remain supported.
    • Evidence: grep found docs/about/release-notes.mdx still saying users can set NEMOCLAW_HERMES_DASHBOARD=1 to start and forward the dashboard on port 9119, while changed quickstart/reference docs now describe the built-in dashboard on port 18789.

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Selective E2E Results — ❌ Some jobs failed

Run: 27001798205
Target ref: 864a334d8436e4b4280cb88b126148ab246292fc
Workflow ref: main
Requested jobs: hermes-dashboard-e2e,hermes-root-entrypoint-smoke-e2e,hermes-e2e,rebuild-hermes-stale-base-e2e
Summary: 2 passed, 2 failed, 0 skipped

Job Result
hermes-dashboard-e2e ❌ failure
hermes-e2e ✅ success
hermes-root-entrypoint-smoke-e2e ❌ failure
rebuild-hermes-stale-base-e2e ✅ success

Failed jobs: hermes-dashboard-e2e, hermes-root-entrypoint-smoke-e2e. Check run artifacts for logs.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
docs/get-started/quickstart-hermes.mdx (1)

101-105: ⚡ Quick win

Use active voice in the dashboard asset sentence.

Line 104 is passive (“are built into”). Please rewrite it in active voice to match the docs style requirements.

As per coding guidelines, "Active voice required. Flag passive constructions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/get-started/quickstart-hermes.mdx` around lines 101 - 105, Rewrite the
passive sentence about dashboard assets into active voice: change "The Hermes
dashboard assets are built into the sandbox image, so the dashboard starts
without running `npm` as the sandbox user under `/opt/hermes`." to an active
construction such as "NemoClaw builds the Hermes dashboard assets into the
sandbox image, so the dashboard starts without running `npm` as the sandbox user
under `/opt/hermes`." Ensure the subject (e.g., NemoClaw) performs the action to
match the docs' active-voice guideline and keep the rest of the sentence
unchanged.
src/lib/verify-deployment.test.ts (1)

58-59: ⚡ Quick win

Reduce order-coupling in probe assertions.

Line 58 and Line 59 assume probe call order. This can fail on harmless internal reordering. Prefer existence-based checks.

Suggested diff
-    expect(sandboxScripts[0]).toContain("http://127.0.0.1:8642/health");
-    expect(hostProbes[0]).toEqual({ port: 18789, path: "/api/status" });
+    expect(
+      sandboxScripts.some((script) => script.includes("http://127.0.0.1:8642/health")),
+    ).toBe(true);
+    expect(hostProbes).toContainEqual({ port: 18789, path: "/api/status" });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/verify-deployment.test.ts` around lines 58 - 59, The assertions
currently rely on probe call order (sandboxScripts[0] and hostProbes[0]) which
is fragile; change them to existence-based checks by asserting the
sandboxScripts array contains the health URL (e.g., use a
contains/arrayContaining style check against "http://127.0.0.1:8642/health") and
assert hostProbes contains the probe object (use a
contains/containEqual/arrayContaining check for { port: 18789, path:
"/api/status" }) so the test no longer depends on element ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/reference/commands.mdx`:
- Around line 656-659: The docs currently contradict about Hermes and the
dashboard-url: update the Hermes-specific section to match the earlier statement
that agent dashboards managing their own session (e.g., "Hermes Agent") will
print a plain dashboard URL; specifically edit the Hermes section text that
currently says "`dashboard-url` is not applicable to Hermes" to instead state
that `dashboard-url` is applicable and returns the plain dashboard URL for
Hermes Agent (mentioning "Hermes Agent" and the `dashboard-url` command so
readers can locate the paragraph), or alternatively change the earlier general
sentence to say Hermes is not applicable—ensure both occurrences use the same
behavior and wording.

In `@src/lib/dashboard/contract.ts`:
- Around line 69-81: In normalizeEndpointPath change the URL-handling branch so
that when new URL(raw).pathname is obtained you return that pathname even if
it's "/" (i.e. remove the special-case that rejects "/"); specifically update
the condition inside normalizeEndpointPath (the branch that calls new
URL(raw).pathname) to return path when it exists (or is defined) rather than
only when path !== "/", ensuring URL-like inputs such as "http://127.0.0.1/"
produce "/" instead of "/http://127.0.0.1/".

In `@src/lib/onboard/dashboard.ts`:
- Around line 243-246: The call to runCaptureOpenshell is normalizing a null
ownership-failure signal into an empty string, hiding failures; change the
wrapper passed to bestEffortForwardStopForSandbox so that if
deps.runCaptureOpenshell(...) returns null you throw an error (preserving the
failure), otherwise return the string — i.e., in the callback used by
bestEffortForwardStopForSandbox (the one now doing (args, opts) =>
(deps.runCaptureOpenshell(args, opts) ?? "") as string) detect a null result and
throw a descriptive Error instead of converting to "", so ownership lookup
failures still trigger the stop-path behavior.

In `@test/sandbox-provisioning.test.ts`:
- Around line 1001-1003: The npm stub inside the runLoggedDockerShell invocation
uses $hermes_web_dist which is unbound under set -u; change the stub to guard
against unset by using parameter expansion and a non-empty check, e.g. replace
the mkdir line with something like: if [ -n "${hermes_web_dist:-}" ] && [
"${1:-}" = "run" ] && [ "${2:-}" = "build" ]; then mkdir -p "$hermes_web_dist";
fi so the code in the runLoggedDockerShell call (the npm() { ... } stub) never
references an unbound variable.

---

Nitpick comments:
In `@docs/get-started/quickstart-hermes.mdx`:
- Around line 101-105: Rewrite the passive sentence about dashboard assets into
active voice: change "The Hermes dashboard assets are built into the sandbox
image, so the dashboard starts without running `npm` as the sandbox user under
`/opt/hermes`." to an active construction such as "NemoClaw builds the Hermes
dashboard assets into the sandbox image, so the dashboard starts without running
`npm` as the sandbox user under `/opt/hermes`." Ensure the subject (e.g.,
NemoClaw) performs the action to match the docs' active-voice guideline and keep
the rest of the sentence unchanged.

In `@src/lib/verify-deployment.test.ts`:
- Around line 58-59: The assertions currently rely on probe call order
(sandboxScripts[0] and hostProbes[0]) which is fragile; change them to
existence-based checks by asserting the sandboxScripts array contains the health
URL (e.g., use a contains/arrayContaining style check against
"http://127.0.0.1:8642/health") and assert hostProbes contains the probe object
(use a contains/containEqual/arrayContaining check for { port: 18789, path:
"/api/status" }) so the test no longer depends on element ordering.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2f41ecca-f3a6-4460-9622-55cc0c38898b

📥 Commits

Reviewing files that changed from the base of the PR and between 5dac380 and 864a334.

📒 Files selected for processing (25)
  • agents/hermes/Dockerfile
  • agents/hermes/Dockerfile.base
  • agents/hermes/manifest.yaml
  • agents/hermes/start.sh
  • docs/get-started/quickstart-hermes.mdx
  • docs/reference/commands.mdx
  • src/commands/sandbox/dashboard-url.ts
  • src/lib/agent/base-image.test.ts
  • src/lib/agent/defs.test.ts
  • src/lib/agent/defs.ts
  • src/lib/agent/onboard.test.ts
  • src/lib/agent/onboard.ts
  • src/lib/agent/runtime.test.ts
  • src/lib/dashboard-url-command.test.ts
  • src/lib/dashboard-url-command.ts
  • src/lib/dashboard/contract.test.ts
  • src/lib/dashboard/contract.ts
  • src/lib/onboard.ts
  • src/lib/onboard/dashboard.ts
  • src/lib/verify-deployment.test.ts
  • src/lib/verify-deployment.ts
  • test/hermes-start.test.ts
  • test/onboard-dashboard.test.ts
  • test/onboard.test.ts
  • test/sandbox-provisioning.test.ts

Comment thread docs/reference/commands.mdx
Comment thread src/lib/dashboard/contract.ts
Comment thread src/lib/onboard/dashboard.ts Outdated
Comment thread test/sandbox-provisioning.test.ts
@cv
Copy link
Copy Markdown
Collaborator Author

cv commented Jun 5, 2026

Addressed the review-advisor feedback in f01d1dc:

  • Updated Hermes command docs (including generated nemohermes variant) so dashboard-url documents the default 18789 dashboard and separate 8642 API.
  • Made dashboard-url honor the agent dashboard auth contract; session/none auth prints a plain URL, url_token still fetches a token.
  • Kept the Hermes in-browser TUI opt-in by defaulting HERMES_DASHBOARD_TUI to 0 and documented NEMOCLAW_HERMES_DASHBOARD_TUI=1.
  • Added manifest validation for dashboard.auth and dashboard health/path fields.
  • Added focused port-bootstrap tests for CHAT_UI_URL/NEMOCLAW_DASHBOARD_PORT collisions and API preservation.
  • Moved new multi-port dashboard-forward logic out of src/lib/onboard/dashboard.ts and moved agent-specific deployment probe coverage out of src/lib/verify-deployment.test.ts; src/lib/onboard/dashboard.ts is now net-neutral vs main and verify-deployment.test no longer grows.
  • Expanded the Dockerfile fallback comment with the invalid base-image state and removal condition.

Validation after changes:

  • npm run build:cli
  • npm run typecheck:cli
  • focused Vitest for dashboard-url, agent defs, verify-deployment, Hermes start, onboard dashboard, and sandbox provisioning
  • npm run source-shape:check
  • npm run docs:check-agent-variants
  • pre-commit/pre-push hooks passed on commit/push

I also re-ran the one npx prek failure from this environment (test/onboard-selection.test.ts -t "returns to provider selection when Ollama manual entry chooses back"), and it passed in isolation.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Selective E2E Results — ❌ Some jobs failed

Run: 27002940784
Target ref: f01d1dc1fe587c2a445c35a13958ffa6ee475ab3
Workflow ref: main
Requested jobs: hermes-dashboard-e2e,hermes-root-entrypoint-smoke-e2e,rebuild-hermes-stale-base-e2e,hermes-onboard-security-posture-e2e
Summary: 2 passed, 2 failed, 0 skipped

Job Result
hermes-dashboard-e2e ❌ failure
hermes-onboard-security-posture-e2e ✅ success
hermes-root-entrypoint-smoke-e2e ❌ failure
rebuild-hermes-stale-base-e2e ✅ success

Failed jobs: hermes-dashboard-e2e, hermes-root-entrypoint-smoke-e2e. Check run artifacts for logs.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Selective E2E Results — ❌ Some jobs failed

Run: 27041711625
Target ref: salvage/pr-4391-hermes-dashboard
Requested jobs: hermes-dashboard-e2e,hermes-root-entrypoint-smoke-e2e
Summary: 1 passed, 1 failed, 0 skipped

Job Result
hermes-dashboard-e2e ❌ failure
hermes-root-entrypoint-smoke-e2e ✅ success

Failed jobs: hermes-dashboard-e2e. Check run artifacts for logs.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Selective E2E Results — ❌ Some jobs failed

Run: 27041868556
Target ref: 148f59558b7b44d60750181c4721805dbfcadc69
Workflow ref: main
Requested jobs: hermes-dashboard-e2e,hermes-e2e,hermes-root-entrypoint-smoke-e2e,hermes-onboard-security-posture-e2e,rebuild-hermes-stale-base-e2e
Summary: 3 passed, 1 failed, 0 skipped

Job Result
hermes-dashboard-e2e ❌ failure
hermes-e2e ✅ success
hermes-onboard-security-posture-e2e ✅ success
hermes-root-entrypoint-smoke-e2e ✅ success
rebuild-hermes-stale-base-e2e ⚠️ cancelled

Failed jobs: hermes-dashboard-e2e. Check run artifacts for logs.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Selective E2E Results — ✅ All requested jobs passed

Run: 27041994567
Target ref: salvage/pr-4391-hermes-dashboard
Requested jobs: hermes-dashboard-e2e,hermes-root-entrypoint-smoke-e2e
Summary: 2 passed, 0 failed, 0 skipped

Job Result
hermes-dashboard-e2e ✅ success
hermes-root-entrypoint-smoke-e2e ✅ success

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/nightly-e2e.yaml:
- Line 2540: The github-script step uses template-expanded `${{
toJSON(inputs.jobs) }}` inside the JS body causing injection warnings; change to
pass inputs.jobs via the step's env (e.g., set env.REQUESTED_JOBS: ${{
toJSON(inputs.jobs) }}) and update the script to read process.env.REQUESTED_JOBS
when computing requestedJobsRaw (and similarly update the separate "Post E2E
results to PR" github-script step to use
env.REQUESTED_JOBS/process.env.REQUESTED_JOBS instead of `${{
toJSON(inputs.jobs) }}`) so both places (the step creating requestedJobsRaw and
the "Post E2E results to PR" step) avoid template expansion in the script body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 67095f95-947f-4419-9d0b-98dd4826a053

📥 Commits

Reviewing files that changed from the base of the PR and between 864a334 and 148f595.

📒 Files selected for processing (22)
  • .github/workflows/nightly-e2e.yaml
  • agents/hermes/Dockerfile
  • agents/hermes/Dockerfile.base
  • agents/hermes/start.sh
  • docs/get-started/quickstart-hermes.mdx
  • docs/reference/commands-nemohermes.mdx
  • docs/reference/commands.mdx
  • src/lib/agent/defs.test.ts
  • src/lib/agent/defs.ts
  • src/lib/dashboard-url-command.test.ts
  • src/lib/dashboard-url-command.ts
  • src/lib/dashboard/contract.test.ts
  • src/lib/dashboard/contract.ts
  • src/lib/onboard.ts
  • src/lib/onboard/agent-dashboard-forward.ts
  • src/lib/onboard/dashboard-forward-control.ts
  • src/lib/onboard/dashboard.ts
  • src/lib/verify-deployment-agent.test.ts
  • test/e2e/test-hermes-e2e.sh
  • test/hermes-start.test.ts
  • test/onboard.test.ts
  • test/sandbox-provisioning.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/commands.mdx
🚧 Files skipped from review as they are similar to previous changes (7)
  • test/sandbox-provisioning.test.ts
  • agents/hermes/Dockerfile.base
  • test/onboard.test.ts
  • src/lib/onboard.ts
  • docs/get-started/quickstart-hermes.mdx
  • src/lib/agent/defs.ts
  • src/lib/dashboard/contract.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/nightly-e2e.yaml:
- Line 2540: The github-script step uses template-expanded `${{
toJSON(inputs.jobs) }}` inside the JS body causing injection warnings; change to
pass inputs.jobs via the step's env (e.g., set env.REQUESTED_JOBS: ${{
toJSON(inputs.jobs) }}) and update the script to read process.env.REQUESTED_JOBS
when computing requestedJobsRaw (and similarly update the separate "Post E2E
results to PR" github-script step to use
env.REQUESTED_JOBS/process.env.REQUESTED_JOBS instead of `${{
toJSON(inputs.jobs) }}`) so both places (the step creating requestedJobsRaw and
the "Post E2E results to PR" step) avoid template expansion in the script body.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 67095f95-947f-4419-9d0b-98dd4826a053

📥 Commits

Reviewing files that changed from the base of the PR and between 864a334 and 148f595.

📒 Files selected for processing (22)
  • .github/workflows/nightly-e2e.yaml
  • agents/hermes/Dockerfile
  • agents/hermes/Dockerfile.base
  • agents/hermes/start.sh
  • docs/get-started/quickstart-hermes.mdx
  • docs/reference/commands-nemohermes.mdx
  • docs/reference/commands.mdx
  • src/lib/agent/defs.test.ts
  • src/lib/agent/defs.ts
  • src/lib/dashboard-url-command.test.ts
  • src/lib/dashboard-url-command.ts
  • src/lib/dashboard/contract.test.ts
  • src/lib/dashboard/contract.ts
  • src/lib/onboard.ts
  • src/lib/onboard/agent-dashboard-forward.ts
  • src/lib/onboard/dashboard-forward-control.ts
  • src/lib/onboard/dashboard.ts
  • src/lib/verify-deployment-agent.test.ts
  • test/e2e/test-hermes-e2e.sh
  • test/hermes-start.test.ts
  • test/onboard.test.ts
  • test/sandbox-provisioning.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/reference/commands.mdx
🚧 Files skipped from review as they are similar to previous changes (7)
  • test/sandbox-provisioning.test.ts
  • agents/hermes/Dockerfile.base
  • test/onboard.test.ts
  • src/lib/onboard.ts
  • docs/get-started/quickstart-hermes.mdx
  • src/lib/agent/defs.ts
  • src/lib/dashboard/contract.ts
🛑 Comments failed to post (1)
.github/workflows/nightly-e2e.yaml (1)

2540-2540: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
if command -v zizmor >/dev/null 2>&1; then
  zizmor .github/workflows/nightly-e2e.yaml | rg -n "template-injection|2540" -C2 || true
else
  echo "zizmor is not installed in this environment; run your CI static-analysis job to confirm the warning is cleared."
fi

Repository: NVIDIA/NemoClaw

Length of output: 2710


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Show the exact JS snippet around workflow line 2540 to ensure the proposed change targets the right code.
nl -ba .github/workflows/nightly-e2e.yaml | sed -n '2525,2555p'

Repository: NVIDIA/NemoClaw

Length of output: 102


🏁 Script executed:

#!/bin/bash
set -euo pipefail
awk 'NR>=2525 && NR<=2555 {printf "%d:%s\n", NR, $0}' .github/workflows/nightly-e2e.yaml

Repository: NVIDIA/NemoClaw

Length of output: 1807


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "workflow_dispatch|inputs:|jobs:" .github/workflows/nightly-e2e.yaml | head -n 200

Repository: NVIDIA/NemoClaw

Length of output: 9436


🏁 Script executed:

#!/bin/bash
set -euo pipefail
# 1) Inspect workflow_dispatch inputs definition for `jobs`
awk 'NR>=100 && NR<=160 {print NR ":" $0}' .github/workflows/nightly-e2e.yaml

# 2) Inspect the entire `scorecard` github-script step around the flagged line
awk 'NR>=2485 && NR<=2565 {print NR ":" $0}' .github/workflows/nightly-e2e.yaml

Repository: NVIDIA/NemoClaw

Length of output: 7716


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/nightly-e2e.yaml"
TMP="$(mktemp)"
cp "$FILE" "$TMP"

python3 - <<'PY'
import re, pathlib, sys, os
path = os.environ["TMP"]
p = pathlib.Path(path)
s = p.read_text()

# Insert env block for REQUESTED_JOBS between uses and with: in the scorecard step
# Match the specific step header region we saw in the repo.
s2 = s
s2 = s2.replace(
    "        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n",
    "        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        env:\n          REQUESTED_JOBS: ${{ inputs.jobs }}\n        with:\n"
)

# Replace the template expansion inside the JS body
s2 = s2.replace(
    "const requestedJobsRaw = isDispatch ? (${{ toJSON(inputs.jobs) }} || '').trim() : '';\n",
    "const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';\n"
)

if s2 == s:
    raise SystemExit("No changes applied; patterns didn't match.")
p.write_text(s2)
print("temp workflow written:", path)
PY
echo "---- zizmor results (temp patch) ----"
if command -v zizmor >/dev/null 2>&1; then
  zizmor "$TMP" | rg -n "template-injection|2540" -C2 || true
else
  echo "zizmor not installed"
fi

echo "---- sanity check: remaining toJSON(inputs.jobs) ----"
rg -n "toJSON\(inputs\.jobs\)" "$TMP" || true

Repository: NVIDIA/NemoClaw

Length of output: 195


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/nightly-e2e.yaml"
TMP="$(mktemp)"
cp "$FILE" "$TMP"

python3 - <<PY
import pathlib
p = pathlib.Path("$TMP")
s = p.read_text()

old = "        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n"
new = "        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        env:\n          REQUESTED_JOBS: \${{ inputs.jobs }}\n        with:\n"
if old not in s:
    raise SystemExit("Pattern for scorecard step header not found; aborting.")
s = s.replace(old, new)

needle = "const requestedJobsRaw = isDispatch ? (\${{ toJSON(inputs.jobs) }} || '').trim() : '';\n"
repl = "const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';\n"
if needle not in s:
    raise SystemExit("Target JS line not found; aborting.")
s = s.replace(needle, repl)

p.write_text(s)
print("temp workflow written to:", p)
PY

echo "---- zizmor results (temp patch) ----"
if command -v zizmor >/dev/null 2>&1; then
  zizmor "$TMP" | rg -n "template-injection|2540" -C2 || true
else
  echo "zizmor not installed"
fi

echo "---- remaining toJSON(inputs.jobs) ----"
rg -n "toJSON\\(inputs\\.jobs\\)" "$TMP" || true

Repository: NVIDIA/NemoClaw

Length of output: 385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/nightly-e2e.yaml"

TMP="$(mktemp --suffix=.yaml)"
cp "$FILE" "$TMP"

python3 - <<PY
import pathlib
p = pathlib.Path("$TMP")
s = p.read_text()

old = "        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n"
new = "        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        env:\n          REQUESTED_JOBS: \${{ inputs.jobs }}\n        with:\n"
if old not in s:
    raise SystemExit("Pattern for scorecard step header not found; aborting.")
s = s.replace(old, new)

needle = "const requestedJobsRaw = isDispatch ? (\${{ toJSON(inputs.jobs) }} || '').trim() : '';\n"
repl = "const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';\n"
if needle not in s:
    raise SystemExit("Target JS line not found; aborting.")
s = s.replace(needle, repl)

p.write_text(s)
print("temp workflow written to:", p)
PY

echo "---- zizmor results (temp patch) ----"
if command -v zizmor >/dev/null 2>&1; then
  zizmor "$TMP" | rg -n "template-injection" -C2 || true
else
  echo "zizmor not installed"
fi

echo "---- remaining toJSON(inputs.jobs) in patched workflow ----"
rg -n "toJSON\\(inputs\\.jobs\\)" "$TMP" || true

echo "---- scorecard requestedJobsRaw line in patched workflow ----"
rg -n "requestedJobsRaw = isDispatch" "$TMP" || true

Repository: NVIDIA/NemoClaw

Length of output: 2364


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/nightly-e2e.yaml"

TMP="$(mktemp --suffix=.yaml)"
cp "$FILE" "$TMP"

python3 - <<PY
import pathlib
p = pathlib.Path("$TMP")
s = p.read_text()

old = "        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        with:\n"
new = "        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n        env:\n          REQUESTED_JOBS: \${{ inputs.jobs }}\n        with:\n"
if old not in s:
    raise SystemExit("header pattern not found")
s = s.replace(old, new)

needle = "const requestedJobsRaw = isDispatch ? (\${{ toJSON(inputs.jobs) }} || '').trim() : '';\n"
repl = "const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';\n"
if needle not in s:
    raise SystemExit("JS needle not found")
s = s.replace(needle, repl)

p.write_text(s)
print(str(p))
PY

TMP_PATH="$(cat <(python3 - <<'PY'
import pathlib,glob
# read the tmp path from the last line of python? can't
PY
) 2>/dev/null || true)"
# Since we can't reliably capture from the previous python via bash here, just locate the temp file by suffix:
# (In practice, just print lines for the most recent tmp file is enough.)
# We'll instead print from "$TMP" using the variable still in scope.
echo "Using temp: $TMP"
echo "---- lines around the remaining finding near 2540 in patched file ----"
awk 'NR>=2528 && NR<=2555 {printf "%d:%s\n", NR, $0}' "$TMP"
echo "---- remaining toJSON(inputs.jobs) occurrences ----"
rg -n "toJSON\\(inputs\\.jobs\\)" "$TMP" || true

Repository: NVIDIA/NemoClaw

Length of output: 1826


🏁 Script executed:

#!/bin/bash
set -euo pipefail
awk 'NR>=2310 && NR<=2385 {printf "%d:%s\n", NR, $0}' .github/workflows/nightly-e2e.yaml

Repository: NVIDIA/NemoClaw

Length of output: 3618


🏁 Script executed:

#!/bin/bash
set -euo pipefail
awk 'NR>=2310 && NR<=2385 {printf "%d:%s\n", NR, $0}' .github/workflows/nightly-e2e.yaml

Repository: NVIDIA/NemoClaw

Length of output: 3618


Use env passthrough for inputs.jobs in github-script to avoid template-expansion injection warnings

actions/github-script at line 2540 uses ${{ toJSON(inputs.jobs) }} inside the JS body (flagged by static analysis). Switching to env.REQUESTED_JOBS + process.env.REQUESTED_JOBS avoids the template expansion.
Also update the separate actions/github-script step “Post E2E results to PR” (it still uses ${{ toJSON(inputs.jobs) }} around line 2351), otherwise the inputs.jobs warning will remain.

Suggested patch (scorecard step)
   - name: Generate nightly scorecard
     id: scorecard
     uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+    env:
+      REQUESTED_JOBS: ${{ inputs.jobs }}
     with:
       script: |
@@
-            const requestedJobsRaw = isDispatch ? (${{ toJSON(inputs.jobs) }} || '').trim() : '';
+            const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

   - name: Generate nightly scorecard
     id: scorecard
     uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
     env:
       REQUESTED_JOBS: ${{ inputs.jobs }}
     with:
       script: |
             const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';
🧰 Tools
🪛 zizmor (1.25.2)

[error] 2540-2540: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/nightly-e2e.yaml at line 2540, The github-script step uses
template-expanded `${{ toJSON(inputs.jobs) }}` inside the JS body causing
injection warnings; change to pass inputs.jobs via the step's env (e.g., set
env.REQUESTED_JOBS: ${{ toJSON(inputs.jobs) }}) and update the script to read
process.env.REQUESTED_JOBS when computing requestedJobsRaw (and similarly update
the separate "Post E2E results to PR" github-script step to use
env.REQUESTED_JOBS/process.env.REQUESTED_JOBS instead of `${{
toJSON(inputs.jobs) }}`) so both places (the step creating requestedJobsRaw and
the "Post E2E results to PR" step) avoid template expansion in the script body.

@cv cv added the v0.0.60 Release target label Jun 5, 2026
@cv
Copy link
Copy Markdown
Collaborator Author

cv commented Jun 5, 2026

Updated PR after the merge with main and addressed the remaining feedback.

Changes pushed through d3e7c04:

  • Resolved the main-branch conflicts in Hermes Dockerfile/base/startup.
  • Fixed CodeRabbit items: active-voice docs sentence, URL health-path normalization for '/', null forward-list failures no longer get converted to empty output, unbound shell stub guard, and order-independent verify-deployment assertions.
  • Stabilized Hermes dashboard startup by giving the dashboard its own runtime home and waiting for the API gateway before launching the dashboard, preventing the dashboard from tightening /sandbox/.hermes permissions out from under the gateway user.
  • Updated Hermes dashboard E2E to the new always-on 18789 dashboard contract and removed the legacy optional 9119 flag from the selective workflow env.

Validation:

  • npm run build:cli
  • focused Vitest for Hermes start, sandbox provisioning, dashboard contract, verify-deployment agent probes, E2E workflow coverage
  • local prebuilt-image run of test/e2e/test-hermes-root-entrypoint-smoke.sh passed
  • PR CI checks are green
  • Selective E2E run 27041994567 passed: hermes-dashboard-e2e and hermes-root-entrypoint-smoke-e2e

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Selective E2E Results — ❌ Some jobs failed

Run: 27042071176
Target ref: d3e7c0436d858160d9dfc6c0faa75a5374b05322
Workflow ref: main
Requested jobs: hermes-dashboard-e2e,hermes-e2e,hermes-root-entrypoint-smoke-e2e,hermes-secret-boundary-e2e,hermes-onboard-security-posture-e2e,rebuild-hermes-stale-base-e2e
Summary: 5 passed, 1 failed, 0 skipped

Job Result
hermes-dashboard-e2e ❌ failure
hermes-e2e ✅ success
hermes-onboard-security-posture-e2e ✅ success
hermes-root-entrypoint-smoke-e2e ✅ success
hermes-secret-boundary-e2e ✅ success
rebuild-hermes-stale-base-e2e ✅ success

Failed jobs: hermes-dashboard-e2e. Check run artifacts for logs.

@cv
Copy link
Copy Markdown
Collaborator Author

cv commented Jun 5, 2026

Update after latest push:

  • Merged latest origin/main into salvage/pr-4391-hermes-dashboard and resolved the remaining conflicts.
  • Addressed the outstanding review feedback:
    • Reworded Hermes quickstart dashboard-assets sentence in active voice.
    • Normalized URL-shaped dashboard health endpoints so / stays /.
    • Preserved null runCaptureOpenshell() failures during dashboard-forward cleanup instead of converting them to empty output.
    • Guarded the E2E npm stub against unset hermes_web_dist under set -u.
    • Made verify-deployment probe assertions order-independent.
  • Fixed the failing Hermes E2Es:
    • Root entrypoint failure came from hermes dashboard tightening /sandbox/.hermes permissions while the separate gateway user still needed group read/execute access.
    • The dashboard now uses a separate runtime home, waits for gateway readiness before launch, and preserves NemoClaw's shared config-root permissions.
    • Updated Hermes dashboard E2E to the always-on 18789 dashboard contract and removed the legacy optional 9119/NEMOCLAW_HERMES_DASHBOARD=1 workflow path.

Validation:

  • npm run build:cli
  • Focused Vitest for Hermes start, sandbox provisioning, dashboard contract, verify-deployment agent probes, Hermes dashboard helpers, and E2E workflow coverage
  • bash -n agents/hermes/start.sh
  • bash -n test/e2e/test-hermes-e2e.sh
  • Local prebuilt-image run of test/e2e/test-hermes-root-entrypoint-smoke.sh
  • Pre-commit/pre-push hooks passed
  • Selective E2E run passed for hermes-dashboard-e2e and hermes-root-entrypoint-smoke-e2e: https://github.com/NVIDIA/NemoClaw/actions/runs/27041994567

Current status: PR checks are green; PR remains review-required.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Selective E2E Results — ✅ All requested jobs passed

Run: 27042720120
Target ref: salvage/pr-4391-hermes-dashboard
Requested jobs: hermes-dashboard-e2e
Summary: 1 passed, 0 failed, 0 skipped

Job Result
hermes-dashboard-e2e ✅ success

@cv cv merged commit 4107f5b into main Jun 5, 2026
103 checks passed
@cv cv deleted the salvage/pr-4391-hermes-dashboard branch June 5, 2026 22:18
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Selective E2E Results — ✅ All requested jobs passed

Run: 27042799774
Target ref: 12e5d04dd177be6d7f1259cb99831f2c1fd67f7b
Workflow ref: main
Requested jobs: hermes-dashboard-e2e,hermes-e2e,hermes-root-entrypoint-smoke-e2e,rebuild-hermes-stale-base-e2e,hermes-secret-boundary-e2e,hermes-onboard-security-posture-e2e
Summary: 6 passed, 0 failed, 0 skipped

Job Result
hermes-dashboard-e2e ✅ success
hermes-e2e ✅ success
hermes-onboard-security-posture-e2e ✅ success
hermes-root-entrypoint-smoke-e2e ✅ success
hermes-secret-boundary-e2e ✅ success
rebuild-hermes-stale-base-e2e ✅ success

@wscurran wscurran added area: ui Web UI, terminal display, visual layout, or UX behavior feature PR adds or expands user-visible functionality integration: hermes Hermes integration behavior labels Jun 6, 2026
miyoungc added a commit that referenced this pull request Jun 6, 2026
## Summary
- Adds the `v0.0.60` section to `docs/about/release-notes.mdx` using the
dev announcement from discussion #4877.
- Fills the source-doc gaps found during release-prep review across
inference, policy tiers, command behavior, security boundaries, Hermes
dashboard/tooling, runtime context, and troubleshooting.
- Refreshes generated agent skills under `.agents/skills/` from the
current Fern docs output and upgrades Fern from `5.44.3` to `5.45.0`.

## Source summary
- #4037 -> `docs/reference/architecture.mdx`,
`docs/about/how-it-works.mdx`, `docs/about/release-notes.mdx`: Documents
system-only runtime context that stays out of visible chat.
- #4875 -> `docs/reference/architecture.mdx`,
`docs/about/how-it-works.mdx`, `docs/about/release-notes.mdx`: Documents
try-first sandbox network/filesystem guidance and clearer failure
classification.
- #4788 -> `docs/security/best-practices.mdx`,
`docs/about/release-notes.mdx`: Documents shared OpenClaw
device-approval policy for startup and connect.
- #4768 -> `docs/reference/network-policies.mdx`,
`docs/network-policy/integration-policy-examples.mdx`,
`docs/get-started/quickstart.mdx`,
`docs/get-started/quickstart-hermes.mdx`, `docs/reference/commands.mdx`:
Documents `weather`, `public-reference`, and Hermes managed-tool gateway
preset behavior.
- #3788 and #4864 -> `docs/reference/network-policies.mdx`,
`docs/reference/commands.mdx`: Documents non-interactive policy-tier
fail-fast behavior and interactive prompt fallback.
- #4756 and #4866 -> `docs/reference/commands.mdx`: Documents env-aware
default sandbox resolution for `list`, `status`, and `tunnel` commands.
- #4320 -> `docs/reference/commands.mdx`: Documents `$$nemoclaw tunnel
status` behavior.
- #4328 -> `docs/reference/commands.mdx`: Documents line-scoped policy
preset descriptions in `policy-list`.
- #4580 and #4748 -> `docs/reference/architecture.mdx`: Documents
package-managed OpenShell gateway service and Docker-driver
gateway-marker behavior.
- #4598 -> `docs/manage-sandboxes/lifecycle.mdx`: Documents concurrent
gateway/dashboard cleanup isolation by sandbox name and port.
- #4777 -> `docs/reference/troubleshooting.mdx`: Documents Docker GPU
patch rollback behavior.
- #4610 -> `docs/reference/troubleshooting.mdx`,
`docs/reference/commands.mdx`: Keeps mutable OpenClaw config permission
guidance aligned and removes skipped experimental wording.
- #4868 -> `docs/reference/commands.mdx`: Keeps `.dockerignore` handling
for custom `onboard --from <Dockerfile>` contexts in generated skills.
- #4870 -> `docs/reference/commands.mdx`,
`docs/manage-sandboxes/runtime-controls.mdx`: Documents
`NEMOCLAW_MINIMAL_BOOTSTRAP` and generated skill coverage.
- #4641 -> `docs/inference/inference-options.mdx`,
`docs/reference/troubleshooting.mdx`: Documents local NVIDIA NIM
platform-digest pulls and served-model id adoption.
- #4810 and #4867 -> `docs/inference/inference-options.mdx`: Documents
stable NGC managed-vLLM image lineage and DGX Station DeepSeek V4 Flash
coverage.
- #4852 -> `docs/inference/use-local-inference.mdx`,
`docs/reference/troubleshooting.mdx`: Documents Ollama model fit
filtering, 16K context floor, cold-load retry, and failed-model
exclusion.
- #4847 -> `docs/inference/switch-inference-providers.mdx`: Documents
API-family sync, Hermes `api_mode`, and Bedrock Runtime exception.
- #4800 -> `docs/inference/tool-calling-reliability.mdx`: Documents
Nemotron managed-inference native tool-search fallback.
- #4333 -> `docs/inference/switch-inference-providers.mdx`: Documents
interactive multimodal input prompting.
- #4086 -> `docs/reference/troubleshooting.mdx`: Keeps proxy bypass
normalization in generated troubleshooting coverage.
- #4811 and #4855 -> `docs/get-started/quickstart-hermes.mdx`: Documents
prebuilt Hermes dashboard assets and TUI recovery without runtime
rebuilds.
- #4854 -> `docs/inference/switch-inference-providers.mdx`,
`docs/reference/commands.mdx`: Documents Hermes proxy API-key
placeholder preservation during inference switches.
- #4248 -> `docs/manage-sandboxes/messaging-channels.mdx`,
`.agents/skills/`: Keeps messaging enrollment behavior aligned with
manifest-hook implementation.
- #4771 -> `docs/security/best-practices.mdx`,
`docs/security/credential-storage.mdx`: Documents Hermes
placeholder-only secret boundary for sandbox-visible runtime files.
- #4787 -> `docs/security/best-practices.mdx`,
`docs/about/release-notes.mdx`: Documents expanded memory scanner
examples for OpenAI project keys and Slack app-level tokens.
- #4848 -> `docs/reference/commands.mdx`: Documents OpenClaw skill
install mirroring into the agent home directory.
- #4790 -> `docs/about/release-notes.mdx`: Uses the prior release-prep
structure and generated `.agents/skills/` refresh as the template for
this release.

## Verification
- `python3 scripts/docs-to-skills.py docs/ .agents/skills/ --prefix
nemoclaw-user --doc-platform fern-mdx`
- `python3 scripts/docs-to-skills.py docs/ .agents/skills/ skills/
--prefix nemoclaw-user --doc-platform fern-mdx --dry-run`
- `npm run docs`
- `git diff --check`
- skip-term scan across `docs/`, `.agents/skills/`, and `skills/`
- `npm run build:cli`
- `npm run typecheck:cli`
- Commit and pre-push hook suites, including markdownlint, gitleaks,
env-var docs gate, docs-to-skills verification, and skills YAML tests

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* DeepSeek-V4-Flash now available as default inference model for DGX
Station.
* Hermes dashboard improved with dedicated port and OAuth-authenticated
tool gateway selection.
* Added weather and public-reference policy presets for expanded agent
capabilities.
* Enhanced Ollama model selection with GPU memory filtering and
automatic retry for timeouts.

* **Bug Fixes**
  * Improved policy tier validation to prevent invalid configurations.
* Better sandbox cleanup scoping by port to prevent conflicts across
deployments.
  * Added GPU patch failure recovery with automatic rollback.

* **Documentation**
* Expanded troubleshooting guides for inference, security, and sandbox
lifecycle.
  * Added .dockerignore best practices for custom deployments.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ui Web UI, terminal display, visual layout, or UX behavior feature PR adds or expands user-visible functionality integration: hermes Hermes integration behavior v0.0.60 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants