Repository navigation
feat: Hermes single-harness edition + premium dual, with dashboard SSO #361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
4a8b8dc
c329988
c0afff9
370100d
8406be7
b124c44
dddce8b
42b3d3a
d49d0fb
8cb04db
597a09d
8c40ebf
a7d4a19
63d96b9
195b988
4f64db9
45c388f
af40c7a
c46a8dd
9307e6e
eb1eb54
d01faf1
c3ca70b
f89db70
dbbab79
ed16bbb
905ea0a
4e7f17d
da37e43
fb0ad89
51ff215
52ede0c
0f5cfca
a0930cd
fadab6f
bad006c
aca6ed6
4903175
b07ee0f
6520724
03c2d04
6322008
59770bf
a53add9
0779a3a
4e4325f
907ebc3
c8efbbc
f5e7c75
b6cff63
d3cbe1b
0280e53
6d96d3d
80afc1a
acda2c4
ea95176
dd644d2
188b758
d4177fc
c3c9a48
3af5726
c9daefa
4a6bf85
a7f1c34
daf484e
8deae1c
66a2a24
adf66c8
b081fa1
8dcaf71
e354704
c306674
8d27ad0
106956f
f591eef
0a7b36a
f6876e3
e721dc3
a821e27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| [Unit] | ||
| Description=ClawBox Hermes Dashboard Auth Proxy (LAN :8090, clawbox_session-gated) | ||
| After=clawbox-hermes-dashboard.service clawbox-setup.service | ||
| Wants=clawbox-hermes-dashboard.service | ||
|
|
||
| [Service] | ||
| Type=simple | ||
| User=clawbox | ||
| WorkingDirectory=/home/clawbox/clawbox | ||
| Environment=HOME=/home/clawbox | ||
| Environment=CLAWBOX_ROOT=/home/clawbox/clawbox | ||
| Environment=HERMES_DASH_PROXY_PORT=8090 | ||
| ExecStart=/usr/bin/node /home/clawbox/clawbox/scripts/hermes-dashboard-proxy.js | ||
| Restart=always | ||
| RestartSec=3 | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| [Install] | ||
| WantedBy=multi-user.target | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| [Unit] | ||
| Description=ClawBox Hermes Dashboard (host-local web UI, gated auth) | ||
| After=network-online.target | ||
| Wants=network-online.target | ||
|
Comment on lines
+1
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value Add restart rate limiting.
🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| [Service] | ||
| Type=simple | ||
| User=clawbox | ||
| WorkingDirectory=/home/clawbox | ||
| Environment=HOME=/home/clawbox | ||
| Environment=PATH=/home/clawbox/.local/bin:/usr/local/bin:/usr/bin:/bin | ||
| # Stop any stray hermes server first so this instance can bind :9119 cleanly | ||
| # (avoids an "address already in use" crash loop if a serve/dashboard from a | ||
| # CLI chat or a prior run is still holding the port). Best-effort (`-`). | ||
| ExecStartPre=-/home/clawbox/.local/bin/hermes dashboard --stop | ||
| # Bind 127.0.0.2, NOT 127.0.0.1: Hermes only engages its cookie-session auth | ||
| # gate on a non-loopback bind (plain loopback uses a legacy shared-token mode | ||
| # that ignores session cookies, which a reverse proxy can't satisfy). 127.0.0.2 | ||
| # is still inside 127.0.0.0/8 — host-local, never LAN-reachable — so the | ||
| # clawbox_session-gated proxy stays the only way in. | ||
| # `hermes dashboard` builds the web dist on first run, so allow a generous | ||
| # start window before the health check gives up. | ||
| ExecStart=/home/clawbox/.local/bin/hermes dashboard --host 127.0.0.2 --port 9119 --no-open | ||
| Restart=always | ||
| RestartSec=5 | ||
| TimeoutStartSec=300 | ||
|
|
||
| [Install] | ||
| WantedBy=multi-user.target | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,6 +61,19 @@ PROJECT_DIR="/home/clawbox/clawbox" | |
| CLAWBOX_USER="clawbox" | ||
| CLAWBOX_HOME="/home/clawbox" | ||
|
|
||
| # ── Edition (single-harness lock) ──────────────────────────────────────────── | ||
| # openclaw | hermes | dual. Baked at flash time via env CLAWBOX_EDITION or | ||
| # config/edition.txt. Empty/unknown → "openclaw" (the native product: single, | ||
| # locked). "dual" is premium (both harnesses + switcher) and requires a signed | ||
| # license; "hermes" is its own SKU. On "hermes", install.sh skips the OpenClaw | ||
| # gateway steps and stands up Hermes + the dashboard proxy instead. | ||
| CLAWBOX_EDITION="${CLAWBOX_EDITION:-}" | ||
| if [ -z "$CLAWBOX_EDITION" ] && [ -f "$PROJECT_DIR/config/edition.txt" ]; then | ||
| CLAWBOX_EDITION="$(tr -d '[:space:]' < "$PROJECT_DIR/config/edition.txt" 2>/dev/null || true)" | ||
| fi | ||
| case "$CLAWBOX_EDITION" in openclaw|hermes|dual) ;; *) CLAWBOX_EDITION="openclaw" ;; esac | ||
| is_hermes_edition() { [ "$CLAWBOX_EDITION" = "hermes" ]; } | ||
|
|
||
| # CLAWBOX_TEST_MODE=1 skips hardware-only steps (Jetson power modes, CUDA | ||
| # llama.cpp build, snap Chromium, WiFi AP, VNC, cloudflared, jtop) so the | ||
| # installer can run inside a CI container. See e2e-install/README.md. | ||
|
|
@@ -114,6 +127,20 @@ EXPECTED_INSTALLED_SERVICES=( | |
| clawbox-codex-auth-sync.service | ||
| ) | ||
|
|
||
| # Hermes edition runs no OpenClaw gateway — drop it from the active set and add | ||
| # the Hermes dashboard + auth-proxy so the generic service loop installs them. | ||
| if is_hermes_edition; then | ||
| _active_svcs=() | ||
| for _s in "${EXPECTED_ACTIVE_SERVICES[@]}"; do | ||
| [ "$_s" = "clawbox-gateway.service" ] || _active_svcs+=("$_s") | ||
| done | ||
| EXPECTED_ACTIVE_SERVICES=( | ||
| "${_active_svcs[@]}" | ||
| clawbox-hermes-dashboard.service | ||
| clawbox-hermes-dashboard-proxy.service | ||
| ) | ||
| fi | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Load persisted WiFi interface if available | ||
| IFACE_ENV="$PROJECT_DIR/data/network.env" | ||
| if [ -f "$IFACE_ENV" ]; then | ||
|
|
@@ -756,12 +783,44 @@ step_build() { | |
| } | ||
|
|
||
| step_openclaw_setup() { | ||
| is_hermes_edition && { log " [hermes edition] skipping OpenClaw install"; return 0; } | ||
| step_openclaw_install | ||
| step_openclaw_patch | ||
| step_openclaw_config | ||
| } | ||
|
|
||
| # Install the Hermes agent (git-based install into ~/.hermes). Only on the | ||
| # hermes edition, and only if it isn't already present. | ||
| step_hermes_install() { | ||
| is_hermes_edition || return 0 | ||
| if [ -x "$CLAWBOX_HOME/.local/bin/hermes" ]; then | ||
| log " Hermes already installed ($("$CLAWBOX_HOME/.local/bin/hermes" --version 2>/dev/null | head -1))" | ||
| return 0 | ||
| fi | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| log " Installing Hermes agent (NousResearch)..." | ||
| # Official installer clones NousResearch/hermes-agent + builds a venv. Runs as | ||
| # the clawbox user so it lands in ~/.hermes and ~/.local/bin. | ||
| runuser -u "$CLAWBOX_USER" -- bash -c \ | ||
| 'curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash' \ | ||
| || echo " Warning: Hermes install failed (non-fatal) — install it manually then re-run install.sh" | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check whether the repo pins any Hermes artifact version or checksum.
set -euo pipefail
rg -n -i 'hermes' --glob 'config/*' --glob '*.txt' --glob '*.json' | head -50
rg -n -i 'sha256sum|shasum|gpg --verify' install.sh scripts | head -50Repository: ID-Robots/clawbox Length of output: 155 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== install.sh around Hermes installer =="
sed -n '780,815p' install.sh | cat -n
echo
echo "== nearby installer references in install.sh =="
rg -n -i 'hermes-agent|hermes|curl.*bash|sha256sum|shasum|gpg --verify|openclaw-target' install.sh | head -120
echo
echo "== repo-wide targeted hermes/config integrity checks =="
rg -n -i 'hermes-agent|hermes|sha256sum|shasum|gpg --verify|openclaw-target|version|checksum' --glob '!node_modules' . | head -200Repository: ID-Robots/clawbox Length of output: 24508 Security Misconfiguration (CWE-494): Download of Code Without Integrity Check Reachability: Internal Do not pipe the remote Hermes installer into The Hermes install step fetches 🤖 Prompt for AI Agents |
||
|
|
||
| # Bake the single-harness edition lock into a root-owned systemd drop-in so a | ||
| # customer can't flip the harness by editing config.json. No-op on "dual". | ||
| step_edition_lock() { | ||
| case "$CLAWBOX_EDITION" in | ||
| openclaw|hermes) ;; | ||
| *) return 0 ;; | ||
| esac | ||
| mkdir -p /etc/systemd/system/clawbox-setup.service.d | ||
| printf '[Service]\nEnvironment=CLAWBOX_EDITION=%s\n' "$CLAWBOX_EDITION" \ | ||
| > /etc/systemd/system/clawbox-setup.service.d/edition.conf | ||
| systemctl daemon-reload 2>/dev/null || true | ||
| log " Baked edition lock: CLAWBOX_EDITION=$CLAWBOX_EDITION" | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| step_openclaw_install() { | ||
| is_hermes_edition && { log " [hermes edition] skipping OpenClaw npm install"; return 0; } | ||
| # Always re-assert the .bashrc PATH stanza before any early-return. The | ||
| # function is idempotent (greps before appending), and skipping it here | ||
| # was the root cause of the recurring `bash: openclaw: command not found` | ||
|
|
@@ -963,6 +1022,7 @@ step_clawkeep_install() { | |
| } | ||
|
|
||
| step_openclaw_patch() { | ||
| is_hermes_edition && return 0 | ||
| # Patcher restricts file searches to .js (runtime bundles) — newer openclaw | ||
| # releases ship .d.ts declaration files alongside bundled JS, and literal | ||
| # type strings would otherwise match files we cannot patch. | ||
|
|
@@ -1060,6 +1120,7 @@ oc_config_set() { | |
| } | ||
|
|
||
| step_openclaw_config() { | ||
| is_hermes_edition && return 0 | ||
| local CLAWBOX_CONFIG="$PROJECT_DIR/data/config.json" | ||
| local CLAWBOX_AI_ENV="$PROJECT_DIR/.env" | ||
| local CLAWBOX_AI_KEY="${CLAWBOX_AI_API_KEY:-}" | ||
|
|
@@ -1783,6 +1844,7 @@ step_recover() { | |
| } | ||
|
|
||
| step_gateway_setup() { | ||
| is_hermes_edition && { log " [hermes edition] skipping OpenClaw gateway setup"; return 0; } | ||
| cp "$PROJECT_DIR/config/clawbox-gateway.service" /etc/systemd/system/ | ||
|
|
||
| # Mask any leftover user-level openclaw-gateway.service. Standalone | ||
|
|
@@ -2362,6 +2424,9 @@ step_build | |
| log "Installing and configuring OpenClaw..." | ||
| step_openclaw_setup | ||
|
|
||
| log "Installing Hermes (if this is the Hermes edition)..." | ||
| step_hermes_install | ||
|
|
||
| log "Installing ClawKeep CLI..." | ||
| step_clawkeep_install | ||
|
|
||
|
|
@@ -2406,9 +2471,22 @@ step_desktop_theme | |
| log "Ensuring clawbox user PATH (openclaw, claude, codex, gemini, hf, clawkeep)..." | ||
| ensure_clawbox_bashrc_path | ||
|
|
||
| # Bake the single-harness edition lock before services start so the first | ||
| # clawbox-setup start already sees it (no-op on dual). | ||
| step_edition_lock | ||
|
|
||
| log "Starting services..." | ||
| step_start_services | ||
|
|
||
| # Hermes edition: seed shared identity, bake the edition lock, (re)start the | ||
| # dashboard + auth-proxy. Runs after services so the lock's clawbox-setup | ||
| # restart lands last. No-op on openclaw/dual editions. | ||
| if is_hermes_edition; then | ||
| log "Applying Hermes edition (lock + dashboard + shared identity)..." | ||
| bash "$PROJECT_DIR/scripts/setup-hermes-edition.sh" \ | ||
| || echo " Warning: hermes-edition setup failed (non-fatal)" | ||
| fi | ||
|
|
||
| log "Validating services..." | ||
| step_validate_services | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🔵 Trivial | 💤 Low value
Wants=omitsclawbox-setup.service.Line 3 orders the proxy after
clawbox-setup.service, but Line 4 requests only the dashboard. Ordering without a dependency has no effect whenclawbox-setup.serviceis not otherwise pulled into the transaction. The proxy readsdata/.session-secretanddata/config.json, both owned by the main app, so state the dependency explicitly.♻️ Proposed change
📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Path instructions