From adbfd66056a257d40b4936db687860b4872df1be Mon Sep 17 00:00:00 2001 From: Stephen Chin Date: Sat, 2 May 2026 06:25:28 -0700 Subject: [PATCH] feat: add Hermes Agent LXC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds container scripts for Hermes Agent (Nous Research), a self-improving AI agent with LLM provider integration, terminal execution, web browsing, and multi-platform messaging support. Files: - ct/hermes-agent.sh - install/hermes-agent-install.sh - json/hermes-agent.json - ct/headers/hermes-agent Deviations from standard patterns (justified): 1. Uses upstream installer (curl-pipe) instead of fetch_and_deploy_gh_release: Hermes is a uv-managed Python application with complex dependency resolution, virtualenv management, and binary placement—not a single binary or tarball from GitHub Releases. 2. Dedicated 'hermes' service user (not running as root): The agent executes arbitrary terminal commands on behalf of the user. Running as root would give the AI unrestricted system access. This follows the protonmail-bridge service-user pattern for isolation. 3. Dashboard (port 9119) bound to localhost only, requiring SSH tunnel: The web UI provides admin access to an AI that can execute commands. SSH tunnel provides an authentication/authorization boundary. 4. /usr/bin/hermes shim script: The hermes CLI validates cwd permissions; running 'hermes' as root from /root fails. The shim cd's to /home/hermes and exec's as the hermes user via runuser. 5. setsid --wait wrapping of upstream installer: The upstream installer probes /dev/tty for interactive prompts even with --skip-setup; setsid detaches the controlling terminal. --- ct/headers/hermes-agent | 6 ++ ct/hermes-agent.sh | 68 ++++++++++++++++++ install/hermes-agent-install.sh | 121 ++++++++++++++++++++++++++++++++ json/hermes-agent.json | 52 ++++++++++++++ 4 files changed, 247 insertions(+) create mode 100644 ct/headers/hermes-agent create mode 100644 ct/hermes-agent.sh create mode 100644 install/hermes-agent-install.sh create mode 100644 json/hermes-agent.json diff --git a/ct/headers/hermes-agent b/ct/headers/hermes-agent new file mode 100644 index 00000000..671ec9d1 --- /dev/null +++ b/ct/headers/hermes-agent @@ -0,0 +1,6 @@ + __ __ ___ __ + / / / /__ _________ ___ ___ _____ / | ____ ____ ____ / /_ + / /_/ / _ \/ ___/ __ `__ \/ _ \/ ___/ / /| |/ __ `/ _ \/ __ \/ __/ + / __ / __/ / / / / / / / __(__ ) / ___ / /_/ / __/ / / / /_ +/_/ /_/\___/_/ /_/ /_/ /_/\___/____/ /_/ |_\__, /\___/_/ /_/\__/ + /____/ diff --git a/ct/hermes-agent.sh b/ct/hermes-agent.sh new file mode 100644 index 00000000..ef76b5e9 --- /dev/null +++ b/ct/hermes-agent.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Stephen Chin (steveonjava) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://hermes-agent.nousresearch.com/ + +APP="Hermes Agent" +var_tags="${var_tags:-ai;automation;agent}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -x /home/hermes/.local/bin/hermes ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Stopping Services" + systemctl stop hermes-dashboard + systemctl stop hermes-gateway + msg_ok "Stopped Services" + + msg_info "Updating ${APP}" + $STD env \ + HOME=/home/hermes \ + HERMES_HOME=/home/hermes/.hermes \ + /home/hermes/.local/bin/hermes update + chown -R hermes:hermes /home/hermes + msg_ok "Updated ${APP}" + + msg_info "Starting Services" + systemctl start hermes-gateway + systemctl start hermes-dashboard + msg_ok "Started Services" + msg_ok "Updated successfully!" + exit +} + +start +build_container +description + +msg_ok "Completed successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Connect via SSH and configure your LLM provider:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}ssh hermes@${IP}${CL}" +echo -e "${TAB}${BGN}hermes setup${CL}" +echo -e "${INFO}${YW} API Server (OpenAI-compatible):${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8642/v1${CL}" +echo -e "${INFO}${YW} API key stored at:${CL}" +echo -e "${TAB}${BGN}/home/hermes/.hermes/.env${CL}" +echo -e "${INFO}${YW} Web Dashboard (via SSH tunnel):${CL}" +echo -e "${TAB}${BGN}ssh -L 9119:localhost:9119 hermes@${IP}${CL}" +echo -e "${TAB}${BGN}Then open: http://localhost:9119${CL}" diff --git a/install/hermes-agent-install.sh b/install/hermes-agent-install.sh new file mode 100644 index 00000000..c1a67bd4 --- /dev/null +++ b/install/hermes-agent-install.sh @@ -0,0 +1,121 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2026 community-scripts ORG +# Author: Stephen Chin (steveonjava) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://hermes-agent.nousresearch.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + git +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs + +msg_info "Creating Hermes User" +useradd -m -s /bin/bash hermes +msg_ok "Created Hermes User" + +msg_info "Installing Hermes Agent" +$STD setsid --wait env \ + HOME=/home/hermes \ + PATH=/home/hermes/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \ + bash <(curl -fsSL https://hermes-agent.nousresearch.com/install.sh) --skip-setup --hermes-home /home/hermes/.hermes --dir /home/hermes/.hermes/hermes-agent + +if [[ ! -x /home/hermes/.local/bin/hermes ]]; then + msg_error "Hermes binary not found after installation" + exit 1 +fi + +chown -R hermes:hermes /home/hermes +git config --system --add safe.directory /home/hermes/.hermes/hermes-agent 2>/dev/null || true +msg_ok "Installed Hermes Agent" + +msg_info "Installing Web Dashboard" +$STD runuser -u hermes -- \ + env HOME=/home/hermes VIRTUAL_ENV=/home/hermes/.hermes/hermes-agent/venv \ + /home/hermes/.local/bin/uv pip install 'hermes-agent[web,pty]' +msg_ok "Installed Web Dashboard" + +msg_info "Configuring API Server" +API_SERVER_KEY=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) +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 +chmod 600 /home/hermes/.hermes/.env +chown hermes:hermes /home/hermes/.hermes/.env +msg_ok "Configured API Server" + +msg_info "Creating Service" +cat </etc/systemd/system/hermes-gateway.service +[Unit] +Description=Hermes Agent Gateway +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=hermes +Group=hermes +WorkingDirectory=/home/hermes +ExecStart=/home/hermes/.local/bin/hermes gateway run --replace +Environment="HERMES_HOME=/home/hermes/.hermes" +Environment="HOME=/home/hermes" +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now hermes-gateway +msg_ok "Created Service" + +msg_info "Creating Dashboard Service" +cat </etc/systemd/system/hermes-dashboard.service +[Unit] +Description=Hermes Agent Web Dashboard +After=network-online.target hermes-gateway.service +Wants=network-online.target + +[Service] +Type=simple +User=hermes +Group=hermes +WorkingDirectory=/home/hermes +ExecStart=/home/hermes/.local/bin/hermes dashboard --host 127.0.0.1 --port 9119 --no-open +Environment="HERMES_HOME=/home/hermes/.hermes" +Environment="HOME=/home/hermes" +Environment="PATH=/home/hermes/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +Environment="NODE_OPTIONS=--max-old-space-size=3072" +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now hermes-dashboard +msg_ok "Created Dashboard Service" + +msg_info "Creating Hermes Shim" +cat <<'EOF' >/usr/bin/hermes +#!/bin/bash +cd /home/hermes +exec runuser -u hermes -- /home/hermes/.local/bin/hermes "$@" +EOF +chmod +x /usr/bin/hermes +msg_ok "Created Hermes Shim" + +motd_ssh +customize +cleanup_lxc diff --git a/json/hermes-agent.json b/json/hermes-agent.json new file mode 100644 index 00000000..b8a7f150 --- /dev/null +++ b/json/hermes-agent.json @@ -0,0 +1,52 @@ +{ + "name": "Hermes Agent", + "slug": "hermes-agent", + "categories": [ + 20 + ], + "date_created": "2026-05-02", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8642, + "documentation": "https://hermes-agent.nousresearch.com/docs", + "website": "https://hermes-agent.nousresearch.com", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/hermes.webp", + "description": "Self-improving AI agent by Nous Research. Connects to 15+ LLM providers, executes terminal commands, browses the web, and learns from experience. Supports 16 messaging platforms (Telegram, Discord, Slack, WhatsApp, Signal, Matrix, and more) with persistent memory and autonomous skill creation.", + "install_methods": [ + { + "type": "default", + "script": "ct/hermes-agent.sh", + "config_path": "/home/hermes/.hermes/", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 20, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": "hermes", + "password": null + }, + "notes": [ + { + "text": "Warning: Hermes can execute terminal commands. The agent runs as a dedicated 'hermes' service user for isolation.", + "type": "warning" + }, + { + "text": "After container startup, SSH in as the 'hermes' user and run 'hermes setup' to configure your LLM provider (OpenRouter, Anthropic, OpenAI, Nous Portal, or custom endpoint).", + "type": "info" + }, + { + "text": "OpenAI-compatible API server available at http://:8642/v1. API key is stored in /home/hermes/.hermes/.env.", + "type": "info" + }, + { + "text": "Access the web dashboard via SSH tunnel: ssh -L 9119:localhost:9119 hermes@, then open http://localhost:9119", + "type": "info" + } + ] +}