Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
4a8b8dc
feat(edition): single-harness lock (openclaw|hermes|dual) + premium d…
KrasimirKralev Aug 10, 2026
c329988
feat(hermes): chat model picker + surface Anthropic subscription OAut…
KrasimirKralev Aug 10, 2026
c0afff9
feat(hermes): dashboard desktop icon via auth-gated reverse proxy
KrasimirKralev Aug 10, 2026
370100d
feat(edition): productionize hermes edition in install.sh (skip openc…
KrasimirKralev Aug 10, 2026
8406be7
feat(edition): guard dual with ed25519 license (enforced) + native de…
KrasimirKralev Aug 10, 2026
b124c44
fix(hermes): compact desktop icons (no gap from hidden harness app) +…
KrasimirKralev Aug 10, 2026
dddce8b
fix(hermes): dashboard SSO via gated-mode bind + auth-broker proxy
KrasimirKralev Aug 10, 2026
42b3d3a
fix(hermes): rewrite Origin/Referer to upstream in dashboard proxy
KrasimirKralev Aug 10, 2026
d49d0fb
feat(hermes): configure models/provider/keys from the AI-provider panel
KrasimirKralev Aug 10, 2026
8cb04db
fix(hermes): guard nullable child streams in runHermesCli for TS strict
KrasimirKralev Aug 10, 2026
597a09d
feat(hermes): Hermes-native skills store (edition-gated, registry-only)
KrasimirKralev Aug 10, 2026
8c40ebf
feat(hermes): ClawBox AI through Hermes in the provider panel
KrasimirKralev Aug 10, 2026
a7d4a19
feat(hermes): OpenClaw-style provider cards + skills Browse default l…
KrasimirKralev Aug 10, 2026
63d96b9
fix(hermes): type provider list for special/tier fields (TS strict)
KrasimirKralev Aug 10, 2026
195b988
feat(hermes): native provider OAuth (Anthropic/OpenAI/Nous/Copilot) i…
KrasimirKralev Aug 10, 2026
4f64db9
feat(hermes): rich skill detail (SKILL.md, tags, links) + better stor…
KrasimirKralev Aug 10, 2026
45c388f
feat(hermes): provider stack, skills app, and audit hardening
KrasimirKralev Aug 10, 2026
af40c7a
fix(hermes): show the model picker for ClawBox AI in the chat header
KrasimirKralev Aug 10, 2026
c46a8dd
fix(hermes): thread chat turns into one session + refresh providers a…
KrasimirKralev Aug 10, 2026
9307e6e
test: make the suite hermetic against the device's edition file
KrasimirKralev Aug 10, 2026
eb1eb54
fix(hermes): use 'chat -q' so the chat actually has memory
KrasimirKralev Aug 10, 2026
d01faf1
docs(hermes): correct the chat route header — -z has no session memory
KrasimirKralev Aug 10, 2026
c3ca70b
fix(hermes): read the chat session id from stderr, not stdout
KrasimirKralev Aug 10, 2026
f89db70
fix(hermes): per-provider reasoning levels + fresh session on provide…
KrasimirKralev Aug 10, 2026
dbbab79
feat(hermes): ClawBox AI API-token entry + honest portal button copy
KrasimirKralev Aug 10, 2026
ed16bbb
fix(hermes): preserve the chat when switching provider/model
KrasimirKralev Aug 10, 2026
905ea0a
fix: Chat icon label, harness-aware Browser copy, and a workable chat…
KrasimirKralev Aug 10, 2026
4e7f17d
fix(hermes): hide Mixture of Agents until it is actually configured
KrasimirKralev Aug 10, 2026
da37e43
fix(settings): keep Local AI local, and stop re-fetching the harness …
KrasimirKralev Aug 10, 2026
fb0ad89
fix: add the missing client-harness imports
KrasimirKralev Aug 10, 2026
51ff215
fix(hermes): stop reporting a gateway failure on a device with no gat…
KrasimirKralev Aug 10, 2026
52ede0c
perf(hermes): memoise config reads; fix: show the real chat error
KrasimirKralev Aug 10, 2026
0f5cfca
fix(hermes): register the local model with Hermes, not only OpenClaw
KrasimirKralev Aug 10, 2026
a0930cd
fix(hermes): repair a local model that was enabled before Hermes coul…
KrasimirKralev Aug 10, 2026
fadab6f
fix(hermes): give the local provider a model to offer
KrasimirKralev Aug 10, 2026
bad006c
feat(hermes): drive Telegram through Hermes native commands
KrasimirKralev Aug 10, 2026
aca6ed6
fix(hermes): name the on-device model in the picker
KrasimirKralev Aug 10, 2026
4903175
docs(hermes): correct the measured Hermes CLI latencies in comments
KrasimirKralev Aug 10, 2026
b07ee0f
fix(hermes): drop the thinking dial where it controls nothing
KrasimirKralev Aug 10, 2026
6520724
fix(hermes): key the Telegram status probe by bot token
KrasimirKralev Aug 10, 2026
03c2d04
fix(install): strip the build machine's git identity from flashed dev…
KrasimirKralev Aug 10, 2026
6322008
docs(hermes): clarify why the pairing request id is the approvable token
KrasimirKralev Aug 10, 2026
59770bf
feat: name the token ladders the wizard's surfaces are already painting
KrasimirKralev Aug 10, 2026
a53add9
refactor: compose the setup header instead of stacking it
KrasimirKralev Aug 10, 2026
0779a3a
Revert "fix(install): strip the build machine's git identity from fla…
KrasimirKralev Aug 11, 2026
4e4325f
fix(hermes): read the failure cause from whichever stream carries it
KrasimirKralev Aug 11, 2026
907ebc3
feat: light the hermes skin on a hermes device
KrasimirKralev Aug 11, 2026
c8efbbc
refactor: compose the telegram step around its token field
KrasimirKralev Aug 11, 2026
f5e7c75
fix: outline a success message in the colour it is already using
KrasimirKralev Aug 11, 2026
b6cff63
refactor: put the dashboard on the ladders and stop the reset guessing
KrasimirKralev Aug 11, 2026
d3cbe1b
refactor: put the AI provider step on the shipped ladders
KrasimirKralev Aug 11, 2026
0280e53
refactor: put the security and update steps on the ladders
KrasimirKralev Aug 11, 2026
6d96d3d
fix: pick up new skills without stopping the gateway
KrasimirKralev Aug 11, 2026
80afc1a
fix: validate stored preferences and route the language persona per h…
KrasimirKralev Aug 11, 2026
acda2c4
fix: re-check the stored locale before it reaches the mascot prompt
KrasimirKralev Aug 11, 2026
ea95176
fix: stop the chat header pills truncating their own labels
KrasimirKralev Aug 11, 2026
dd644d2
fix: track the active locale in the html lang attribute
KrasimirKralev Aug 11, 2026
188b758
feat: name the roles five component ports each had to invent
KrasimirKralev Aug 11, 2026
d4177fc
chore: delete the orphaned done step
KrasimirKralev Aug 11, 2026
c3c9a48
fix: point the product at the community server the docs use
KrasimirKralev Aug 11, 2026
3af5726
feat(hermes): open a hermes device on its own wallpaper
KrasimirKralev Aug 11, 2026
c9daefa
feat: put the wizard's primary action back above the fold
KrasimirKralev Aug 11, 2026
4a6bf85
feat: ship the measured gemma 4 qat gguf
KrasimirKralev Aug 11, 2026
a7f1c34
perf(install): accept a prebuilt llama.cpp, and pin the one we build
KrasimirKralev Aug 11, 2026
daf484e
perf(local-ai): choose whether Gemma thinks, instead of inheriting it
KrasimirKralev Aug 11, 2026
8deae1c
fix(hermes): stop the model pill vanishing when the provider changes
KrasimirKralev Aug 11, 2026
66a2a24
fix: tighten html-to-text handling in mcp web tools
KrasimirKralev Aug 11, 2026
adf66c8
chore: release 3.9.0
KrasimirKralev Aug 11, 2026
b081fa1
fix: match end tags carrying attribute text in html-to-text
KrasimirKralev Aug 11, 2026
8dcaf71
fix(e2e): run the suite and fix the three ways it was red
KrasimirKralev Aug 11, 2026
e354704
fix(install): establish where a prebuilt came from, not just that it …
KrasimirKralev Aug 11, 2026
c306674
fix(test): repair a regex broken by an escaping slip
KrasimirKralev Aug 11, 2026
8d27ad0
fix(license): validate the --days argument and the expiry field
KrasimirKralev Aug 11, 2026
106956f
fix(hermes): handle a stdin write failure on a hermes cli call
KrasimirKralev Aug 11, 2026
f591eef
refactor(preferences): check the persona language domain in the writer
KrasimirKralev Aug 11, 2026
0a7b36a
fix(ui): follow the reported harness health in the locked badge
KrasimirKralev Aug 11, 2026
f6876e3
fix(hermes): quote the username in the generated dashboard auth block
KrasimirKralev Aug 11, 2026
e721dc3
test(harness): assert the store is untouched when the edition lock re…
KrasimirKralev Aug 11, 2026
a821e27
fix(ui): keep the harness picker rendering without a harness list
KrasimirKralev Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions config/clawbox-hermes-dashboard-proxy.service
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
Comment on lines +3 to +4

Copy link
Copy Markdown

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= omits clawbox-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 when clawbox-setup.service is not otherwise pulled into the transaction. The proxy reads data/.session-secret and data/config.json, both owned by the main app, so state the dependency explicitly.

♻️ Proposed change
 After=clawbox-hermes-dashboard.service clawbox-setup.service
-Wants=clawbox-hermes-dashboard.service
+Wants=clawbox-hermes-dashboard.service clawbox-setup.service
📝 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.

Suggested change
After=clawbox-hermes-dashboard.service clawbox-setup.service
Wants=clawbox-hermes-dashboard.service
After=clawbox-hermes-dashboard.service clawbox-setup.service
Wants=clawbox-hermes-dashboard.service clawbox-setup.service
🤖 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 `@config/clawbox-hermes-dashboard-proxy.service` around lines 3 - 4, Update the
Wants= declaration in the proxy service unit to include clawbox-setup.service
alongside clawbox-hermes-dashboard.service, preserving the existing After=
ordering so the proxy explicitly pulls in both required services.

Source: Path instructions


[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
Comment thread
coderabbitai[bot] marked this conversation as resolved.

[Install]
WantedBy=multi-user.target
29 changes: 29 additions & 0 deletions config/clawbox-hermes-dashboard.service
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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Add restart rate limiting.

Restart=always with RestartSec=5 and no StartLimitIntervalSec/StartLimitBurst override uses the systemd defaults. On an embedded Jetson a crash loop that builds the web dist on each start consumes CPU continuously. Set an explicit limit, or set StartLimitBurst=0 deliberately if a permanent retry is intended.

🤖 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 `@config/clawbox-hermes-dashboard.service` around lines 1 - 4, Add explicit
systemd restart-rate limiting in the [Unit] section of
clawbox-hermes-dashboard.service using appropriate StartLimitIntervalSec and
StartLimitBurst values, while preserving the existing restart behavior; if
permanent retries are intentionally required, explicitly set StartLimitBurst=0
instead.

Source: 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
78 changes: 78 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Comment thread
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
Expand Down Expand Up @@ -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
Comment thread
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"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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 -50

Repository: 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 -200

Repository: 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 bash.

The Hermes install step fetches https://hermes-agent.nousresearch.com/install.sh from a fixed remote host, pipes it directly into bash, and has no pinned revision, checksum, or signature. Download it to a temporary file, verify a pinned SHA-256 or signed artifact, then execute it. Keep the step non-fatal only after the integrity check passes.

🤖 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 `@install.sh` around lines 800 - 806, Update the Hermes installation block to
download the remote installer into a temporary file, verify it against a pinned
SHA-256 checksum or trusted signature, and execute it only after successful
integrity validation. Keep the command running as CLAWBOX_USER, avoid piping the
response directly into bash, and retain the non-fatal warning only for failures
after verification is enforced.


# 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"
}
Comment thread
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`
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:-}"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
Binary file added public/hermes-agent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading