From 94a3374039367bccac83ce15620e3c95e5df0817 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Sun, 25 Oct 2026 19:51:57 +0100 Subject: [PATCH] Hermes Agent: install, update and run everything as the hermes user (#18203) hermes update refuses a checkout its caller does not own, so the root-run update stopped working for every install. The installer and hermes update now run as hermes, and the dashboard becomes a user unit next to the gateways, so Hermes Desktop and the dashboard can update and restart it themselves. libatomic1 is installed up front, the one package Hermes would otherwise ask sudo for. update moves existing containers over once: the system unit becomes a user unit, leftover root processes are stopped and /home/hermes is handed back to hermes. --- ct/hermesagent.sh | 64 +++++++++++++++++++++++----------- install/hermesagent-install.sh | 40 +++++++++------------ 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/ct/hermesagent.sh b/ct/hermesagent.sh index 59047cac4..b7802f10d 100644 --- a/ct/hermesagent.sh +++ b/ct/hermesagent.sh @@ -33,36 +33,60 @@ function update_script() { confirm_external_installer "https://hermes-agent.nousresearch.com/" "hermes update (https://hermes-agent.nousresearch.com/)" - msg_info "Stopping Services" - systemctl stop hermes-dashboard - msg_ok "Stopped Services" - - msg_info "Updating Hermes Agent" - $STD setsid --wait bash -c ' - set -a; source /etc/default/hermes; set +a - /home/hermes/.local/bin/hermes update --yes - ' - mapfile -t root_gateway_pids < <(ps -eo user=,pid=,args= | awk '$1 == "root" && /\/home\/hermes\/\.hermes\// && /hermes_cli[.]main/ && / gateway run --replace$/ { print $2 }') - if ((${#root_gateway_pids[@]})); then - kill -TERM "${root_gateway_pids[@]}" - for pid in "${root_gateway_pids[@]}"; do - while kill -0 "$pid" 2>/dev/null; do - sleep 0.1 + if [[ -f /etc/systemd/system/hermes-dashboard.service ]]; then + msg_info "Moving Hermes to its own user" + systemctl disable -q --now hermes-dashboard + rm -f /etc/systemd/system/hermes-dashboard.service + systemctl daemon-reload + mapfile -t root_hermes_pids < <(ps -eo user=,pid=,args= | awk '$1 == "root" && /\/home\/hermes\/\.hermes\// && /hermes_cli[.]main/ { print $2 }') + if ((${#root_hermes_pids[@]})); then + kill -TERM "${root_hermes_pids[@]}" 2>/dev/null || true + for pid in "${root_hermes_pids[@]}"; do + while kill -0 "$pid" 2>/dev/null; do + sleep 0.1 + done done - done - fi - if ps -eo user=,args= | awk '$1 == "root" && /\/home\/hermes\/\.hermes\// { found = 1 } END { exit !found }'; then - msg_warn "A root-owned Hermes process is still running; it may keep writing root-owned files" + fi + ensure_dependencies libatomic1 + sed -i '/^HOME=/d' /etc/default/hermes + grep -q '^GATEWAY_HEALTH_URL=' /etc/default/hermes || echo "GATEWAY_HEALTH_URL=http://127.0.0.1:8642/health" >>/etc/default/hermes + mkdir -p /home/hermes/.config/systemd/user + cat </home/hermes/.config/systemd/user/hermes-dashboard.service +[Unit] +Description=Hermes Agent Web Dashboard +After=network-online.target + +[Service] +Type=simple +UMask=0077 +WorkingDirectory=%h +EnvironmentFile=/etc/default/hermes +ExecStart=%h/.local/bin/hermes dashboard --host 127.0.0.1 --port 9119 --no-open +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=default.target +EOF + msg_ok "Moved Hermes to its own user" fi + # A writer racing chown makes files vanish between readdir and chown; one retry covers it. if ! chown -R hermes:hermes /home/hermes 2>/dev/null; then sleep 1 chown -R hermes:hermes /home/hermes || msg_warn "Could not take ownership of everything under /home/hermes" fi + $STD runuser -l hermes -c 'systemctl --user daemon-reload && systemctl --user enable hermes-dashboard' + + msg_info "Updating Hermes Agent" + $STD setsid --wait runuser -l hermes -c ' + set -a; source /etc/default/hermes; set +a + hermes update --yes + ' msg_ok "Updated Hermes Agent" msg_info "Starting Services" - systemctl start hermes-dashboard + $STD runuser -l hermes -c 'systemctl --user start hermes-dashboard' msg_ok "Started Services" msg_ok "Updated successfully!" exit diff --git a/install/hermesagent-install.sh b/install/hermesagent-install.sh index 25c868d5a..aa659b95e 100644 --- a/install/hermesagent-install.sh +++ b/install/hermesagent-install.sh @@ -14,7 +14,9 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y git +$STD apt install -y \ + git \ + libatomic1 msg_ok "Installed Dependencies" NODE_VERSION="22" setup_nodejs @@ -27,9 +29,9 @@ msg_ok "Created Hermes User" msg_info "Configuring Service Environment" cat </etc/default/hermes -HOME=/home/hermes PATH=/home/hermes/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin NODE_OPTIONS=${NODE_OPTIONS} +GATEWAY_HEALTH_URL=http://127.0.0.1:8642/health EOF msg_ok "Configured Service Environment" @@ -38,68 +40,60 @@ confirm_external_installer "https://hermes-agent.nousresearch.com/" "https://her msg_info "Installing Hermes Agent" curl_with_retry "https://hermes-agent.nousresearch.com/install.sh" /tmp/hermes-install.sh || curl_with_retry "https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh" /tmp/hermes-install.sh -$STD setsid --wait bash -c ' +chmod 644 /tmp/hermes-install.sh +$STD setsid --wait runuser -l hermes -c ' set -a; source /etc/default/hermes; set +a export npm_config_yes=true - bash /tmp/hermes-install.sh --skip-setup --hermes-home /home/hermes/.hermes --dir /home/hermes/.hermes/hermes-agent + bash /tmp/hermes-install.sh --skip-setup --hermes-home ~/.hermes --dir ~/.hermes/hermes-agent ' rm -f /tmp/hermes-install.sh -chown -R hermes:hermes /home/hermes chmod 750 /home/hermes chmod 700 /home/hermes/.hermes -git config --system --add safe.directory /home/hermes/.hermes/hermes-agent 2>/dev/null || true msg_ok "Installed Hermes Agent" msg_info "Configuring API Server" API_SERVER_KEY=$(random_password 32) -mkdir -p /home/hermes/.hermes cat </home/hermes/.hermes/.env API_SERVER_ENABLED=true API_SERVER_HOST=0.0.0.0 API_SERVER_PORT=8642 API_SERVER_KEY=${API_SERVER_KEY} EOF +chown hermes:hermes /home/hermes/.hermes/.env chmod 600 /home/hermes/.hermes/.env msg_ok "Configured API Server" msg_info "Creating Dashboard Service" -cat </etc/systemd/system/hermes-dashboard.service +mkdir -p /home/hermes/.config/systemd/user +cat </home/hermes/.config/systemd/user/hermes-dashboard.service [Unit] Description=Hermes Agent Web Dashboard After=network-online.target -Wants=network-online.target [Service] Type=simple -User=hermes -Group=hermes UMask=0077 -WorkingDirectory=/home/hermes -ExecStart=/home/hermes/.local/bin/hermes dashboard --host 127.0.0.1 --port 9119 --no-open +WorkingDirectory=%h EnvironmentFile=/etc/default/hermes +ExecStart=%h/.local/bin/hermes dashboard --host 127.0.0.1 --port 9119 --no-open Restart=on-failure RestartSec=5 -ProtectProc=invisible -ProcSubset=pid [Install] -WantedBy=multi-user.target +WantedBy=default.target EOF -systemctl enable -q --now hermes-dashboard +chown -R hermes:hermes /home/hermes/.config +$STD runuser -l hermes -c 'systemctl --user daemon-reload && systemctl --user enable --now hermes-dashboard' msg_ok "Created Dashboard Service" msg_info "Creating Setup Helper" cat <<'SETUP' >/usr/bin/hermes-setup #!/usr/bin/env bash -set -a; source /etc/default/hermes; set +a -/home/hermes/.local/bin/hermes setup -chown -R hermes:hermes /home/hermes -chmod 750 /home/hermes -chmod 700 /home/hermes/.hermes +su - hermes -c 'set -a; source /etc/default/hermes; set +a; hermes setup' if [[ -f /home/hermes/.config/systemd/user/hermes-gateway.service ]]; then su - hermes -c 'systemctl --user enable --now hermes-gateway' fi -echo "Hermes setup complete. File permissions restored." +echo "Hermes setup complete." SETUP chmod +x /usr/bin/hermes-setup msg_ok "Created Setup Helper"