From 297a7d3fe03f551ea9c0df72b6147b7f98e78351 Mon Sep 17 00:00:00 2001 From: domfelipe Date: Tue, 4 Aug 2026 18:45:03 -0300 Subject: [PATCH] Fix Telegram allowlist .env pitfall; default one-liner to Hermes Document and force TELEGRAM_ALLOWED_USERS into ~/.hermes/.env (gateway source of truth). Client creates bot via BotFather. install.sh defaults conductor to hermes for a true single-command demo start. --- README.md | 11 +++-- install.sh | 27 ++++++----- skill/hermes-client-onboarding/SKILL.md | 46 ++++++++++++++----- .../references/troubleshooting.md | 43 ++++++++++++----- .../scripts/apply-core-config.sh | 37 ++++++++++++++- 5 files changed, 122 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 40af4d1..77ad02f 100644 --- a/README.md +++ b/README.md @@ -7,21 +7,22 @@ One-liner + skill conversacional para deixar o **Hermes Agent** pronto no client - Personalidade em `SOUL.md` - Setup guiado por LLM (Codex ou Hermes) -## One-liner (produção) +## One-liner (produção) — comando único ```bash curl -fsSL https://setup.domhubs.com.br/hermes | bash ``` +Instala/atualiza Hermes + skill + launcher e **abre o onboarding** (condutor Hermes por padrão). + Variantes: ```bash -# só instalar skill + Hermes, sem abrir agente +# só instalar, sem abrir agente curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --no-launch -# forçar condutor -curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --conductor hermes -curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --conductor codex +# perguntar Codex vs Hermes +curl -fsSL https://setup.domhubs.com.br/hermes | bash -s -- --ask-conductor ``` Espelho GitHub (fallback): diff --git a/install.sh b/install.sh index a402778..52decf2 100755 --- a/install.sh +++ b/install.sh @@ -9,7 +9,7 @@ DEFAULT_BASE="${HERMES_ONBOARD_BASE:-https://setup.domhubs.com.br/hermes}" HERMES_INSTALL_URL="${HERMES_INSTALL_URL:-https://hermes-agent.nousresearch.com/install.sh}" KICKOFF_MSG="${HERMES_ONBOARD_KICKOFF:-Inicie o onboarding agora. Skill hermes-client-onboarding. Pre-flight silencioso e Phase 1 (voce fala primeiro).}" -CONDUCTOR="${HERMES_ONBOARD_CONDUCTOR:-}" +CONDUCTOR="${HERMES_ONBOARD_CONDUCTOR:-hermes}" NO_LAUNCH=0 NONINTERACTIVE=0 @@ -21,10 +21,11 @@ usage() { cat <<'EOF' Usage: install.sh [options] - --conductor codex|hermes|skip Who runs the guided onboarding (default: prompt) + --conductor codex|hermes|skip Who runs onboarding (default: hermes; use skip for install-only) --no-launch Install only; do not start the conductor --base URL Asset base for skill files (or HERMES_ONBOARD_BASE) - --non-interactive No prompts; default conductor=hermes if unset + --non-interactive No prompts + --ask-conductor Prompt for conductor even when default is hermes -h, --help Show help Env: @@ -33,12 +34,14 @@ Env: EOF } +ASK_CONDUCTOR=0 while [[ $# -gt 0 ]]; do case "$1" in --conductor) CONDUCTOR="${2:-}"; shift 2 ;; --no-launch) NO_LAUNCH=1; shift ;; --base) DEFAULT_BASE="${2:-}"; shift 2 ;; --non-interactive) NONINTERACTIVE=1; shift ;; + --ask-conductor) ASK_CONDUCTOR=1; shift ;; -h|--help) usage; exit 0 ;; *) die "unknown arg: $1" ;; esac @@ -176,18 +179,18 @@ install_skill() { # Conductor selection + launch # --------------------------------------------------------------------------- pick_conductor() { - if [[ -n "$CONDUCTOR" ]]; then + # Default path for one-liner demos: hermes (set above). Only prompt if asked. + if [[ "$ASK_CONDUCTOR" -eq 0 && -n "$CONDUCTOR" ]]; then echo "$CONDUCTOR" return fi if [[ "$NONINTERACTIVE" -eq 1 ]]; then - if need_cmd codex; then echo codex; else echo hermes; fi + echo "${CONDUCTOR:-hermes}" return fi - # curl|bash: stdin is the script pipe — read prompts from the real TTY when possible local tty_in="/dev/tty" if [[ ! -r "$tty_in" ]]; then - if need_cmd codex; then echo codex; else echo hermes; fi + echo "${CONDUCTOR:-hermes}" return fi @@ -197,15 +200,15 @@ pick_conductor() { echo "" >&2 echo "Quem deve conduzir o onboarding conversacional?" >&2 if [[ "$has_codex" -eq 1 ]]; then - echo " 1) Codex (recomendado se disponível)" >&2 - echo " 2) Hermes (modelos baratos / já instalado)" >&2 + echo " 1) Hermes (default — DeepSeek barato)" >&2 + echo " 2) Codex" >&2 echo " 3) Só instalar skill — não abrir agente" >&2 printf "Escolha [1]: " >&2 read -r ans <"$tty_in" || ans=1 case "${ans:-1}" in - 2|hermes|h) echo hermes ;; - 3|skip|s) echo skip ;; - *) echo codex ;; + 2|codex|c) echo codex ;; + 3|skip|s) echo skip ;; + *) echo hermes ;; esac else echo " 1) Hermes" >&2 diff --git a/skill/hermes-client-onboarding/SKILL.md b/skill/hermes-client-onboarding/SKILL.md index 69c271b..e6b93d5 100644 --- a/skill/hermes-client-onboarding/SKILL.md +++ b/skill/hermes-client-onboarding/SKILL.md @@ -1,7 +1,7 @@ --- name: hermes-client-onboarding description: Use when setting up Hermes for a client, install Hermes + Telegram + DeepSeek, run a demo setup, or launch client onboarding. Conducts guided conversational onboarding on a clean Linux VM (deepseek-v4-flash, Telegram gateway, systemd, SOUL.md). -version: 1.1.0 +version: 1.2.0 author: DomHubs license: MIT platforms: [linux, macos] @@ -122,24 +122,44 @@ Optional: Offer fallback model `deepseek-v4-pro` if the user wants a stronger mo **Done when:** provider=deepseek, model=deepseek-v4-flash, key set without printing it. -### Phase 3 — Telegram Bot +### Phase 3 — Telegram Bot (client creates their agent) -1. Guide the user (or do it yourself if they give you the token) to create a bot with @BotFather if they do not have one yet. +The client owns the bot — guide **them** to create it (interactive, no BotFather quota on DomHubs). Narrative: “você está criando o seu agente de IA”. + +1. Ask them to open Telegram (company account preferred) and talk to @BotFather: + - `/newbot` → choose display name + username ending in `bot` + - Copy the **HTTP API token** 2. Collect: - `TELEGRAM_BOT_TOKEN` - - At least one numeric User ID (from @userinfobot or @get_id_bot). Multiple IDs can be comma-separated. -3. Apply: + - At least one **numeric** User ID (from @userinfobot or @get_id_bot). Multiple IDs comma-separated. +3. Apply **and force the values into `~/.hermes/.env`** (source of truth for the gateway): ```bash hermes config set TELEGRAM_BOT_TOKEN "TOKEN" hermes config set TELEGRAM_ALLOWED_USERS "ID1,ID2" ``` -4. Optional advanced settings (only if requested): - - Home channel for proactive messages - - Group chat IDs +**Critical pitfall (do not skip):** the messaging gateway reads allowlist / token from **`~/.hermes/.env`**, not from a free-form key dumped only into `config.yaml`. An **old** `TELEGRAM_ALLOWED_USERS=` line in `.env` silently wins → bot ignores the client and logs: -**Done when:** token set, at least one allowed user ID set, values repeated back (IDs only, never full token). +```text +Blocked unauthorized user +``` + +After every `config set` for Telegram secrets: + +```bash +# Verify .env actually has the NEW ids (do not print full token) +grep -E '^TELEGRAM_ALLOWED_USERS=' ~/.hermes/.env +# If stale or missing, write explicitly: +# printf 'TELEGRAM_ALLOWED_USERS=%s\n' 'ID1,ID2' >> ~/.hermes/.env # or edit in place +# Prefer: hermes config set again, then re-grep +``` + +If `.env` still shows the wrong ID after `config set`, rewrite the line yourself (sed/python) so only the new IDs remain, `chmod 600 ~/.hermes/.env`, then restart gateway (Phase 5). + +4. Optional advanced settings (only if requested): home channel, group chat IDs. + +**Done when:** token set, allowed user IDs confirmed in **`.env`**, values repeated back (IDs only, never full token). ### Phase 4 — Agent Personality (SOUL.md) @@ -218,7 +238,7 @@ hermes update ## Error Handling Guidelines - If `hermes config set` fails, check file permissions on `~/.hermes/.env` and `~/.hermes/config.yaml`. -- If Telegram does not respond: verify token with a direct `getMe` call, confirm Allowed Users, restart gateway, check logs for connection errors. +- If Telegram does not respond: verify token with `getMe`, confirm **`TELEGRAM_ALLOWED_USERS` inside `~/.hermes/.env`** (not only yaml), restart gateway. Log signature of wrong allowlist: `Blocked unauthorized user`. - If DeepSeek returns auth errors: re-validate `DEEPSEEK_API_KEY` and model name (`deepseek-v4-flash`). See `references/troubleshooting.md`. - Prefer fixing issues yourself when possible, then explain what was wrong in plain language. - Never leave the system in a half-configured state. Either finish a phase or clearly roll back. @@ -257,12 +277,14 @@ hermes config set model.default deepseek-v4-flash hermes config set model.base_url "https://api.deepseek.com/v1" hermes config set TELEGRAM_BOT_TOKEN "..." hermes config set TELEGRAM_ALLOWED_USERS "123456789" +grep -E '^TELEGRAM_ALLOWED_USERS=' ~/.hermes/.env # must match IDs above # Gateway hermes gateway install -hermes gateway start +hermes gateway restart hermes gateway status -hermes gateway logs +# Linux: journalctl --user -u hermes-gateway -n 50 +# macOS: tail -f ~/.hermes/logs/gateway.log # Validation hermes doctor diff --git a/skill/hermes-client-onboarding/references/troubleshooting.md b/skill/hermes-client-onboarding/references/troubleshooting.md index 35d7330..bc48209 100644 --- a/skill/hermes-client-onboarding/references/troubleshooting.md +++ b/skill/hermes-client-onboarding/references/troubleshooting.md @@ -49,26 +49,47 @@ hermes config set model.base_url "https://api.deepseek.com/v1" ## Telegram bot does not reply +### Log signature: `Blocked unauthorized user` + +The gateway **ignores** the sender because `TELEGRAM_ALLOWED_USERS` in **`~/.hermes/.env`** does not include their numeric ID. + +**Root cause seen in production:** `hermes config set TELEGRAM_ALLOWED_USERS "..."` may leave a **stale** line in `.env` (or write somewhere the gateway does not use). Gateway loads allowlist from **`.env`**, not from a yaml-only mirror. + +**Fix:** + +```bash +# See what gateway will actually load (do not paste tokens into chat) +grep -E '^TELEGRAM_(BOT_TOKEN|ALLOWED_USERS)=' ~/.hermes/.env + +# Set again, then re-grep until ALLOWED_USERS is exactly the new IDs +hermes config set TELEGRAM_ALLOWED_USERS "ID1,ID2" +grep -E '^TELEGRAM_ALLOWED_USERS=' ~/.hermes/.env + +# If still stale, rewrite the line (example): +# sed -i.bak '/^TELEGRAM_ALLOWED_USERS=/d' ~/.hermes/.env +# echo 'TELEGRAM_ALLOWED_USERS=ID1,ID2' >> ~/.hermes/.env +chmod 600 ~/.hermes/.env + +hermes gateway restart +# Linux (user service): journalctl --user -u hermes-gateway -n 50 --no-pager +# macOS: tail -n 50 ~/.hermes/logs/gateway.log +``` + +### Other checks + 1. Token validity: ```bash -# TOKEN from env; do not log it source ~/.hermes/.env 2>/dev/null || true curl -s "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getMe" | head -c 200 ``` -2. Allowed users: numeric IDs only (from @userinfobot). Restart after change: - -```bash -hermes config set TELEGRAM_ALLOWED_USERS "ID1,ID2" -hermes gateway restart -hermes gateway status -hermes gateway logs -``` +2. Allowed users must be **numeric** IDs only (@userinfobot / @get_id_bot), comma-separated. 3. Common mistakes: - - User ID is username string instead of numeric ID - - Gateway not running + - Username string instead of numeric ID + - Stale ALLOWED_USERS in `.env` after a previous client on the same machine + - Gateway not running / not restarted after env change - Bot blocked by user / wrong bot ## Gateway service won't start diff --git a/skill/hermes-client-onboarding/scripts/apply-core-config.sh b/skill/hermes-client-onboarding/scripts/apply-core-config.sh index 31a9b7f..c4f503d 100755 --- a/skill/hermes-client-onboarding/scripts/apply-core-config.sh +++ b/skill/hermes-client-onboarding/scripts/apply-core-config.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash # Apply DeepSeek native + model + Telegram core config for Hermes client onboarding. +# Forces Telegram secrets into ~/.hermes/.env (gateway source of truth). # Does not print secrets. Requires: hermes on PATH. set -euo pipefail @@ -26,7 +27,7 @@ TG_USERS="${TELEGRAM_ALLOWED_USERS:-}" while [[ $# -gt 0 ]]; do case "$1" in - --deepseek-key|--openrouter-key) DS_KEY="${2:-}"; shift 2 ;; # --openrouter-key kept as alias + --deepseek-key|--openrouter-key) DS_KEY="${2:-}"; shift 2 ;; --telegram-token) TG_TOKEN="${2:-}"; shift 2 ;; --allowed-users) TG_USERS="${2:-}"; shift 2 ;; --model) MODEL="${2:-}"; shift 2 ;; @@ -57,6 +58,26 @@ if [[ "$PROVIDER" == "openrouter" ]]; then BASE_URL="https://openrouter.ai/api/v1" fi +HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}" +ENV_FILE="${HERMES_HOME}/.env" +mkdir -p "$HERMES_HOME" +touch "$ENV_FILE" +chmod 600 "$ENV_FILE" + +# Upsert KEY=VALUE in .env (gateway reads this file) +env_upsert() { + local key="$1" val="$2" tmp + tmp="$(mktemp)" + if [[ -f "$ENV_FILE" ]]; then + grep -v -E "^${key}=" "$ENV_FILE" >"$tmp" || true + else + : >"$tmp" + fi + printf '%s=%s\n' "$key" "$val" >>"$tmp" + mv "$tmp" "$ENV_FILE" + chmod 600 "$ENV_FILE" +} + hermes config set DEEPSEEK_API_KEY "$DS_KEY" hermes config set model.provider "$PROVIDER" hermes config set model.default "$MODEL" @@ -64,8 +85,20 @@ hermes config set model.base_url "$BASE_URL" hermes config set TELEGRAM_BOT_TOKEN "$TG_TOKEN" hermes config set TELEGRAM_ALLOWED_USERS "$TG_USERS" +# Force .env — prevents stale ALLOWED_USERS silently blocking the client +env_upsert "DEEPSEEK_API_KEY" "$DS_KEY" +env_upsert "TELEGRAM_BOT_TOKEN" "$TG_TOKEN" +env_upsert "TELEGRAM_ALLOWED_USERS" "$TG_USERS" + +# Verify allowlist line matches what we just wrote +got="$(grep -E '^TELEGRAM_ALLOWED_USERS=' "$ENV_FILE" | tail -1 | cut -d= -f2- || true)" +if [[ "$got" != "$TG_USERS" ]]; then + echo "error: TELEGRAM_ALLOWED_USERS in $ENV_FILE is '$got', expected '$TG_USERS'" >&2 + exit 1 +fi + echo "ok: provider=$(hermes config get model.provider 2>/dev/null || echo "$PROVIDER")" echo "ok: model=$(hermes config get model.default 2>/dev/null || echo "$MODEL")" echo "ok: base_url=$(hermes config get model.base_url 2>/dev/null || echo "$BASE_URL")" echo "ok: allowed_users=$TG_USERS" -echo "ok: secrets written (not displayed)" +echo "ok: secrets written to hermes config + $ENV_FILE (not displayed)"