hermes-onboarding · diff
v1.2.0 to v1.3.0
137 added, 36 removed. Audit B to B.
---
name: hermes-onboarding
description: "Use when onboarding a new customer — configure gateway, dashboard, memory, services for production."
license: MIT
metadata:
- version: 1.2.0
+ version: 1.3.0
author: moonlight-lupin
platforms: [linux]
tags: [onboarding, setup, configuration, deployment, customer]
---
# Hermes Onboarding
Configure a fresh Hermes Agent from a working base chat to a production-ready deployment.
**Precondition:** Hermes is installed and the main model + provider are configured. Verify with `hermes doctor`. If the agent cannot complete a normal chat, stop here and fix the provider first.
**Leading word:** *onboard* — configure every layer before declaring the setup complete.
## When to Use
- A customer has a fresh Hermes install with a working base chat and needs full configuration
- An operator is setting up Hermes on a new VPS or machine for production use
- A customer wants to go from "it works" to "it is secured, memory-enabled, always-on, and maintained"
Do not use if the main model is not yet configured. Fix the provider first.
## Step 0 — Load references
- Load `references/setup-details.md`. It holds config snippets, systemd templates, SearXNG engine settings, skill guardrail principles, and verbosity examples.
+ Load `references/setup-details.md`. It holds config snippets, systemd templates, DonSeTch MCP config, skill guardrail principles, and verbosity examples.
**Done:** references file loaded into context.
## Step 1 — Detect environment
Run the detection block from `references/setup-details.md` § Detection. Collect:
| Item | How |
|------|-----|
| OS | `uname -a` |
| systemd | `systemctl --version` |
| Docker | `docker --version` |
| root vs user | `whoami` |
| container vs bare metal | `head -5 /proc/1/cgroup` |
| Hermes path | `which hermes` |
| Hermes version | `hermes --version` |
| Main model vision | Check model capabilities via provider docs or test with `vision_analyze` |
**Done:** all 8 items detected and recorded. No user input required.
## Step 2 — Ask up-front questions
Ask the customer 3 questions in one batch:
1. **Customer name** — used for profile name and soul.md identity
2. **Timezone** — IANA timezone (e.g. Asia/Singapore, America/New_York)
3. **Gateway platform(s)** — Telegram, Discord, WhatsApp, Slack, Signal. Collect bot tokens or pairing info. Tokens are stored in `~/.hermes/.env` (chmod 600). Do not paste tokens into chat transcripts.
If Step 1 detected the main model lacks vision, add a 4th question:
4. **Vision-capable aux model** — which model + provider for vision tasks? Default: same provider as main.
**Done:** all up-front questions answered. Conditional vision question asked only if needed.
## Step 3 — Alternative providers and aux models
| Task | Command | Default |
|------|---------|---------|
| Aux vision model | `hermes config set agent.aux_models.vision <model>` | Same provider as main |
| Embedding model | Set `NVIDIA_API_KEY` in `~/.hermes/.env` | NVIDIA NIM (nemotron-3-embed-1b) |
| Delegation | Leave as default (follows main model) | No action |
| Fallback provider | `hermes config set fallback_providers '[...]'` | Skip unless customer asks |
If no `NVIDIA_API_KEY`: help customer get a free key at https://build.nvidia.com/nvidia/nemotron-3-embed-1b. Fallback: OpenRouter bge-m3 using existing `OPENROUTER_API_KEY`.
**Done:** aux vision model configured (if needed). `NVIDIA_API_KEY` set. Delegation confirmed as following main model.
## Step 4 — Profile and soul.md
1. Create single profile: `hermes profile create <customer-name>`
2. Ask customer for soul.md input:
- Agent name (default: "Hermes")
- Preferred language (default: English)
- Personality (default: business)
- Domain-specific instructions (optional)
3. Write `~/.hermes/SOUL.md` from customer input. See `references/setup-details.md` § Soul.md template.
Multi-profile: only if customer specifically mentions needing separate profiles.
**Done:** profile created. soul.md written with customer's input.
## Step 5 — Gateway and services
1. Configure gateway: `hermes gateway setup` — select customer's chosen platform(s), enter tokens
2. Deploy gateway as systemd service. Use template from `references/setup-details.md` § Gateway systemd unit:
- Root mode: `/etc/systemd/system/hermes-gateway.service`
- User mode: `~/.config/systemd/user/hermes-gateway.service` + `loginctl enable-linger $USER`
- Set `TimeoutStopSec=240`
3. Deploy dashboard as systemd service. Use template from `references/setup-details.md` § Dashboard systemd unit:
- Set `HERMES_DASHBOARD_TUI=1`
- Set `HERMES_PYTHON` to venv python path
- **Binding choice — ask the customer:**
- **Loopback (default):** bind to 127.0.0.1. Access via SSH tunnel: `ssh -L 9119:127.0.0.1:9119 user@host`. Most secure. No firewall change needed.
- **LAN (0.0.0.0):** bind to all interfaces with `--host 0.0.0.0`. Direct access from any device on the same network at `http://<host-ip>:9119`. Suitable for internal WiFi/LAN where all devices are trusted. Set up basic auth (Step 5b) so the dashboard is not unprotected.
4. Enable and start both services
5. Set timezone: `hermes config set timezone '<customer-timezone>'` + `timedatectl set-timezone '<tz>'`
**Done:** gateway and dashboard running as systemd services. Test message sent through gateway. Dashboard accessible via SSH tunnel or LAN with basic auth.
## Step 5b — Dashboard basic auth (required for LAN mode, recommended for all)
Hermes has a built-in basic auth provider. Set a username and password so the dashboard is not unprotected.
1. Hash the password:
```bash
python3 -c "from plugins.dashboard_auth.basic import hash_password; print(hash_password('customer-password'))"
```
2. Set in config.yaml (edit directly — `hermes config set` stringifies nested values):
```yaml
dashboard:
basic_auth:
username: admin
password_hash: <hash-from-step-1>
```
3. Restart dashboard: `systemctl restart hermes-dashboard`
4. Verify: open dashboard URL in browser — should show login page
For loopback-only deployments this is optional (SSH tunnel already gates access). For LAN deployments this is required.
**Done:** basic auth configured. Dashboard shows login page when accessed.
## Step 6 — Approvals and terminal backend
| Setting | Value | Command |
|---------|-------|---------|
| Approvals | smart | `hermes config set approvals.mode smart` |
| Terminal backend | docker (if detected) or local | `hermes config set terminal.backend docker` |
**Done:** approvals set to smart. Terminal backend set based on Docker detection.
## Step 7 — Compression
Set compression based on model context length:
| Context length | Threshold | Target ratio |
|----------------|-----------|--------------|
| 64K–128K | 0.35 | 0.20 |
| 200K+ | 0.50 | 0.20 |
```bash
hermes config set compression.enabled true
hermes config set compression.threshold <value>
hermes config set compression.target_ratio 0.20
```
**Done:** compression enabled with threshold matched to model context length.
- ## Step 8 — Search backend
+ ## Step 8 — Search and fetch backend (DonSeTch + fallback)
- If Docker detected:
+ Primary: DonSeTch MCP server (`mcp_donsetch_web_search` / `web_fetch` / `web_crawl`). It handles research-grade queries, bot-walled pages, and JS-rendered content. Install and wire it, then set a lightweight fallback for single-fact lookups.
- 1. Deploy SearXNG container. See `references/setup-details.md` § SearXNG deployment.
- 2. Configure engines. See `references/setup-details.md` § SearXNG engine settings.
- 3. Set `web.search_backend: searxng` in config
+ ### 8a — Install DonSeTch
- If no Docker:
+ ```bash
+ npm install -g donsetch
+ ```
- 1. Install ddgs: `pip install ddgs`
- 2. Set `web.search_backend: ddgs` in config
+ ### 8b — Register as MCP server
- If customer has Nous Portal: Tool Gateway search is already active. Still set a search backend as fallback.
+ Add to `mcp:` in `~/.hermes/config.yaml` (edit directly — `hermes config set` stringifies nested values):
- **Done:** search backend configured and verified with a test query.
+ ```yaml
+ donsetch:
+ command: /usr/local/lib/node_modules/donsetch/binaries/donsetch
+ args:
+ - mcp
+ - --supervised
+ env:
+ DONGHOST_CHROME: /usr/bin/google-chrome-stable
+ DONGHOST_NO_SANDBOX: '1'
+ enabled: true
+ ```
+ Verify: `hermes plugins list` or session info shows `donsetch` with 3 tools connected. Test with a query through `mcp_donsetch_web_search`.
+
+ ### 8c — Fallback backend
+
+ If Docker detected: deploy SearXNG. See `references/setup-details.md` § SearXNG deployment. Set `web.search_backend: searxng`.
+
+ If no Docker: `pip install ddgs`, set `web.search_backend: ddgs`.
+
+ The fallback serves single-fact quick lookups (one URL, one version, one price) and covers the case where DonSeTch is down.
+
+ ### 8d — Web-routing rules in SOUL.md
+
+ Write the web-routing section into the customer's SOUL.md at Step 4 (soul.md write), not later. Use the template in `references/setup-details.md` § Web tool routing (SOUL.md template). Core rules:
+
+ - Research or precision search, comparisons, multi-source verification → `mcp_donsetch_web_search` FIRST
+ - Built-in `web_search` = single-fact quick lookup ONLY
+ - Bot-walled, captcha-adjacent, or JS-rendered URL → `mcp_donsetch_web_fetch`; built-in `web_extract` = plain pages only
+ - Site-wide inventory → `mcp_donsetch_web_crawl`
+ - Authenticated sites, file uploads, interactive flows → `browser_exec`
+ - Scanned-PDF/OCR → pdftoppm + tesseract, not donsetch
+ - LAN/loopback URLs → built-ins (donsetch SSRF-blocks them)
+
+ ### 8e — DonSeTch weekly update cron
+
+ Install the release-binary self-updater (no source builds — lighter dependency footprint):
+
+ ```bash
+ # Script ships with the onboarding skill
+ cp ~/.hermes/skills/agent-ops/hermes-onboarding/scripts/donsetch_update.sh ~/.hermes/scripts/
+ chmod +x ~/.hermes/scripts/donsetch_update.sh
+ hermes cron create --name "DonSeTch weekly update" --schedule "0 9 * * 1" --mode no_agent ~/.hermes/scripts/donsetch_update.sh
+ ```
+
+ The script gates on GitHub `releases/latest` (published releases with assets), NOT tags — a tag without published assets makes `donsetch update` 404. Silent (empty stdout) when already latest: the no_agent cron only alerts on failure or update.
+
+ If customer has Nous Portal: Tool Gateway search is already active. Still install DonSeTch + fallback — Tool Gateway search does not fetch bot-walled pages.
+
+ **Done:** DonSeTch installed and connected as MCP server (3 tools). Fallback backend set. Web-routing rules written into SOUL.md. Update cron armed. Search verified with a test query.
+
## Step 9 — Extraction
Verify keyless extraction works. Step 1 recorded the Hermes version — confirm it is 0.20.5+ for the keyless MCP ring (exa, parallel, tavily, firecrawl, keenable):
```bash
hermes chat -q "Extract the content from https://example.com"
```
If the extraction succeeds, no action needed. If customer wants a pinned backend, set `web.extract_backend` and the corresponding API key.
**Done:** keyless extraction verified working.
## Step 10 — Memory (Mnemosyne)
1. Enable Mnemosyne: `hermes memory setup` → select Mnemosyne
2. Confirm `NVIDIA_API_KEY` is set (from Step 3) — Mnemosyne uses it for embeddings
3. Verify memory works: `mnemosyne_recall({"query": "test", "limit": 1})`
**Done:** Mnemosyne enabled. Embedding key confirmed. Memory recall verified.
## Step 11 — Skill writing guardrails
Apply the 7 Matt Pocock principles to self-generated skills. See `references/setup-details.md` § Skill guardrails. The agent should review any skill it creates against these principles.
For full skill authoring validation, load the bundled skill: `skill_view(name='hermes-agent-skill-authoring')`. This is a Hermes-bundled skill — it ships with every install under the `software-development` category.
**Done:** guardrail principles loaded. Agent knows where to find full skill authoring validation.
## Step 12 — Skill retrieval plugin (BM25)
- Install the skill-retrieval plugin from the agent-skills repo. The plugin replaces the full skill list in the system prompt with a compact names-only index and injects top-K relevant descriptions per turn via BM25 retrieval. Install disabled. Activate only when skill count or token overhead warrants it.
+ Install the skill-retrieval plugin from the agent-skills repo. The plugin replaces the full skill list in the system prompt with a compact names-only index and injects top-K relevant descriptions per turn via a `pre_llm_call` hook. It honors named profiles, `skills.external_dirs`, disabled lists, and platform/condition gates — the corpus matches what Hermes itself resolves. Install disabled. Activate only when skill count or token overhead warrants it.
### 12a — Install (disabled)
```bash
hermes plugins install moonlight-lupin/agent-skills/plugins/skill-retrieval --no-enable
```
### 12b — Assess activation need
Run the assessment to measure skill count and overhead ratio:
```bash
python3 -c "
- import yaml, pathlib, glob, os, re
+ import yaml, pathlib, glob, os, re, sys
- files = glob.glob(os.path.expanduser('~/.hermes/skills/**/SKILL.md'), recursive=True)
+ # Count skills the way Hermes resolves them: discovery helpers when importable
+ # (honors named profiles, external_dirs, plugin-bundled skills, symlinks),
+ # glob fallback for standalone contexts.
count = 0; total_chars = 0
- for f in files:
- try:
- text = pathlib.Path(f).read_text()
- m = re.match(r'^---\n(.*?)\n---\n', text, re.DOTALL)
- if not m: continue
- fm = yaml.safe_load(m.group(1))
- if not fm: continue
- desc = fm.get('description', '')
- if desc:
- count += 1
- total_chars += min(len(desc), 200)
- except: pass
+ try:
+ from hermes_constants import get_skills_dir
+ from agent.skill_utils import get_all_skills_dirs, get_project_skills_dirs, iter_skill_index_files
+ from agent.prompt_builder import _parse_skill_file, _skill_should_show, extract_skill_conditions, _current_session_platform_hint
+ hint = _current_session_platform_hint() or None
+ seen = set()
+ for root in list(get_project_skills_dirs()) + list(get_all_skills_dirs()):
+ for f in iter_skill_index_files(root, 'SKILL.md'):
+ try:
+ ok, fm, desc = _parse_skill_file(f)
+ if not ok or not desc: continue
+ name = fm.get('name', '')
+ if name in seen: continue
+ if not _skill_should_show(extract_skill_conditions(fm), None, None, hint): continue
+ seen.add(name); count += 1
+ total_chars += min(len(desc), 200)
+ except Exception: pass
+ # Plugin-bundled skills also occupy the system prompt (keyed prefix:name)
+ from hermes_constants import get_hermes_home
+ plugins_root = get_hermes_home() / 'plugins'
+ if plugins_root.is_dir():
+ for pdir in sorted(plugins_root.iterdir()):
+ pskills = pdir / 'skills'
+ if not pdir.is_dir() or pdir.name.startswith('.') or not pskills.is_dir(): continue
+ for f in iter_skill_index_files(pskills, 'SKILL.md'):
+ try:
+ ok, fm, desc = _parse_skill_file(f)
+ if not ok or not desc: continue
+ name = fm.get('name', '')
+ if name in seen: continue
+ if not _skill_should_show(extract_skill_conditions(fm), None, None, hint): continue
+ seen.add(name); count += 1
+ total_chars += min(len(desc), 200)
+ except Exception: pass
+ except ImportError:
+ for f in glob.glob(os.path.expanduser('~/.hermes/skills/**/SKILL.md'), recursive=True):
+ try:
+ text = pathlib.Path(f).read_text()
+ m = re.match(r'^---\n(.*?)\n---\n', text, re.DOTALL)
+ if not m: continue
+ fm = yaml.safe_load(m.group(1))
+ if not fm: continue
+ desc = fm.get('description', '')
+ if desc:
+ count += 1
+ total_chars += min(len(desc), 200)
+ except Exception: pass
desc_tokens = total_chars // 4
- ctx = 128000
+
+ # Context window: provider-aware resolution when Hermes is importable
+ # (covers OpenRouter probing and cached per-model metadata), else config,
+ # else 128K default.
+ ctx = 0
try:
- with open(os.path.expanduser('~/.hermes/config.yaml')) as fh:
- cfg = yaml.safe_load(fh) or {}
- ctx = cfg.get('model', {}).get('context_length', 128000)
- except: pass
+ from model_tools import _resolve_active_context_length
+ ctx = int(_resolve_active_context_length() or 0)
+ except Exception:
+ pass
+ if not ctx:
+ try:
+ with open(os.path.expanduser('~/.hermes/config.yaml')) as fh:
+ cfg = yaml.safe_load(fh) or {}
+ ctx = int(cfg.get('model', {}).get('context_length', 0))
+ except Exception:
+ pass
+ if not ctx:
+ ctx = 128000
skill_ratio = desc_tokens / ctx if ctx else 0
SKILL_COUNT_THRESHOLD = 50
SKILL_RATIO_THRESHOLD = 0.05 # 5% of context from skill descriptions alone
print(f'Skills: {count}')
print(f'Skill description tokens: ~{desc_tokens} ({skill_ratio:.1%} of {ctx} context)')
print(f'Thresholds: skill count > {SKILL_COUNT_THRESHOLD} OR skill ratio > {SKILL_RATIO_THRESHOLD:.0%}')
if count > SKILL_COUNT_THRESHOLD or skill_ratio > SKILL_RATIO_THRESHOLD:
print('RECOMMEND: enable skill-retrieval plugin')
else:
print('RECOMMEND: keep disabled — overhead within healthy range')
"
```
Two thresholds trigger the recommendation:
- **Skill count > 50** — the full skill list in the system prompt exceeds ~2.5K tokens. The plugin's compact index (~2.3K) saves more than it costs.
- **Skill description overhead > 5%** of context window — skill descriptions alone at 5% push total overhead above 15% when combined with fixed costs (tool schemas, system prompt, behavioral rules). The 15% threshold is the upper bound of the healthy range from the `input-token-overheads` skill. Load that skill for the full overhead audit if the customer wants a deeper analysis.
### 12c — Activate if recommended
```bash
hermes plugins enable skill-retrieval
```
- Restart the session for the plugin to take effect. The plugin patches the system prompt at load time.
+ Restart the session for the plugin to take effect. Per-turn injection runs from the `pre_llm_call` hook; the names-only compaction applies at prompt build.
If not recommended, the plugin stays installed but disabled. Re-run this assessment after adding skills — the customer can enable it later.
### 12d — Record in memory if not enabled
If the plugin was not enabled, store a Mnemosyne memory so the agent remembers the upgrade path exists:
```python
mnemosyne_remember(
content="Skill-retrieval plugin (BM25) is installed but disabled. Enable with `hermes plugins enable skill-retrieval` if skill count exceeds 50 or input token overhead from skill descriptions exceeds 5% of context window. Re-run the Step 12b assessment after adding skills.",
importance=0.6,
scope="global",
source="onboarding",
veracity="stated"
)
```
**Done:** skill-retrieval plugin installed (disabled). Activation recommended only if skill count > 50 or skill overhead > 5% of context window. If disabled, Mnemosyne memory records the upgrade path.
## Step 13 — Light RAG (library-rag)
1. Install library-rag skill from the agent-skills repo
2. Follow library-rag's onboarding workflow (directories, NVIDIA API key, first index)
3. Register MCP server in config.yaml if customer wants auto-available search tools
Note: The index grows ~8KB per chunk. Start with a small corpus. A 50-book library is ~100MB. A full research library can exceed 1GB.
**Done:** library-rag installed. First document indexed. MCP server registered (if customer opted in).
## Step 14 — Browser automation (CDP)
1. Check for Chromium: `which chromium-browser || which chromium || which google-chrome`
2. If missing, install: `apt install -y chromium-browser` (or platform equivalent)
3. Set browser backend: `hermes config set browser.cdp_url http://127.0.0.1:9222`
4. Install the chrome-cdp systemd service with memory guardrails and the idle-tab drain from `references/setup-details.md` § "CDP browser systemd service". Headless Chrome leaks renderer memory over weeks — the cap + 6-hourly drain + weekly restart keep it from starving the host.
5. Verify: agent can open a browser tab and navigate
6. Verify the drain: `journalctl -t chrome-cdp-drain -n 1` shows activity (defer is fine); `systemctl list-timers 'chrome-cdp*'` shows both timers armed
Browserbase and Firecrawl are documented as upgrades for anti-detection or cloud browser needs.
**Done:** CDP browser configured with systemd service, memory cap, drain + restart timers. Chromium available. Browser test passed. Drain timers armed.
## Step 15 — Toolset audit
1. Run `hermes tools list`
2. Present toolsets grouped:
| Category | Toolsets |
|----------|---------|
| Essential (keep) | terminal, file, web, search, browser, code_execution, memory, session_search, todo, skills, cronjob, clarify |
| Optional (ask) | vision, image_gen, tts, delegation, messaging, kanban |
| Advanced (default off) | spotify, homeassistant, discord, discord_admin, feishu_doc, feishu_drive, yuanbao, rl, debugging, x_search, video |
3. Ask customer which optional toolsets to keep
4. Disable unneeded: `hermes tools disable <name>`
Note: `messaging` = cross-platform message sending (only for multi-platform setups). `rl` = reinforcement learning tools. `debugging` = extra introspection for Hermes development.
**Done:** toolsets audited. Unneeded toolsets disabled. Customer confirmed optional selections.
## Step 16 — Verbosity confirmation
Present the 4 tool_progress modes with examples from `references/setup-details.md` § Verbosity. Ask customer to confirm.
| Mode | What you see |
|------|-------------|
| off | Final response only, no tool output |
| new | One line per tool, skips consecutive repeats |
| all | One line per tool call with duration (default) |
| verbose | Same as all plus full tool arguments |
Recommend `show_cost: true` to track spending.
```bash
hermes config set display.tool_progress all
hermes config set display.show_cost true
```
**Done:** verbosity confirmed. show_cost enabled.
## Step 17 — Cron fleet default model
Set cron fleet default to prevent drift guard failures on provider switches:
```bash
hermes config set cron.model_provider <main-provider>
hermes config set cron.model <main-model>
```
**Done:** cron fleet default set. Future unpinned cron jobs will not fail on provider switches.
## Step 18 — Maintenance crons
Create 4 scheduled crons + document 1 triggered procedure:
| Cron | Schedule | Mode | What |
|------|----------|------|------|
| Memory consolidation | Every 4 days, 02:00 | no_agent | `mnemosyne_sleep.sh` — working memory → episodic. Silent when done. |
| Backup + update + health | Weekly (Sun, 03:00) | Agent | `hermes backup` (max 2 copies), then `hermes update`, then post-update health check (re-apply LAN patches if needed). Alert on failure. |
| Weekly health check | Weekly (Sun, 06:00) | no_agent | `weekly_health_check.sh` — consolidated report: host status, disk usage, log anomalies, input token overhead. Silent when healthy. Alerts with breakdown when any check finds issues. |
| Input token audit | Every 30 days, 09:30 | Agent | Run the `input-token-analysis` skill (`scripts/audit.py --days 30`): rank consumers, pre-check levers, report deltas vs the prior baseline, propose changes with verified config values. Delivers a full report each run — the deep audit the weekly check only samples. Requires the `input-token-analysis` skill installed (see Step 20). |
+ | DonSeTch weekly update | Weekly (Mon, 09:00) | no_agent | `donsetch_update.sh` — release-binary self-updater. Gates on GitHub `releases/latest`, not tags. Silent when already latest. See Step 8e. |
The backup+update cron runs in agent mode (not no_agent) because the post-update health check may need to re-apply dashboard patches that `hermes update` overwrites. A no_agent script cannot re-apply patches or run `skill_view`.
The weekly health check script (`~/.hermes/scripts/weekly_health_check.sh`) chains four checks into one report:
1. **Host status** — detects host type (Raspberry Pi, VM, bare metal, Mac, Windows/WSL) via `systemd-detect-virt` and `/proc/device-tree/model`. Reports uptime and load average.
2. **Disk usage** — `df -h` across all mounts. Alerts when any mount exceeds 80%.
3. **Log anomalies** — runs `log-analyzer` scan (`analyze_logs.py --since 7d --quiet`) + `state_failures.py --quiet --days 7`. Reports error clusters, rate limits, timeouts, tool failures, crashes, and session failures.
4. **Input token overhead** — counts skills and measures skill-description token overhead against context window. Alerts when skill count > 50 or skill overhead > 5% of context. Recommends enabling skill-retrieval plugin.
The script always outputs a report (even when healthy) and includes a **Suggested Actions** section with specific remediation steps when alerts are found. The cron delivers the report to the customer's home channel.
Suggested actions are conditionally generated per check:
| Alert | Suggested action |
|-------|-----------------|
| High load (> 80% CPU capacity) | Check for runaway processes with `top` or `htop` |
| Disk above 80% | Run the disk-cleanup skill (`skill_view(name='disk-cleanup')`) |
| Error clusters in logs | Review the most frequent error; schedule a fix if infrastructure issue |
| Rate limit hits | Check provider quotas or rotate API keys in `~/.hermes/.env` |
| Timeout clusters | Check network connectivity to affected endpoints |
| Tools below 95% success rate | Review failing tool calls and check configurations |
| Skill overhead exceeds threshold | Enable skill-retrieval plugin (`hermes plugins enable skill-retrieval`) |
When all checks pass, the Suggested Actions section reads: "No actions needed. All checks passed."
Install the script during onboarding:
```bash
# Script ships with the onboarding skill at scripts/weekly_health_check.sh
cp ~/.hermes/skills/agent-ops/hermes-onboarding/scripts/weekly_health_check.sh ~/.hermes/scripts/
chmod +x ~/.hermes/scripts/weekly_health_check.sh
```
Triggered (not scheduled): post-update health check — run `hermes doctor`, verify gateway + dashboard status, re-apply patches if needed. See `references/setup-details.md` § Post-update health check. Also triggered manually after any `hermes update` outside the weekly cron.
Create the input-token audit cron (agent mode — it needs `skill_view` and reasoning):
```
Prompt skeleton (adapt to the customer):
"You are the input-token auditor. Load the `input-token-analysis` skill and run
its scripts/audit.py for the last 30 days. Compare against the previous audit's
baseline (read it from the prior cron output if available, else establish this
run as the first baseline). Pre-check every lever before proposing changes:
read current values with `hermes config get`, confirm mechanisms against
config_defaults.py or the Hermes docs, mark unverifiable claims UNVERIFIED.
Report under 600 words: (1) total vs baseline with % change, (2) breakdown by
task, (3) verdict on any previously applied changes with proving numbers,
(4) top 3 remaining consumers, (5) one recommended next tweak citing the
pre-checked current value."
Schedule: every 30 days. Delivery: customer's home channel.
```
**Done:** 4 crons created. Weekly health check script installed. Post-update procedure documented.
## Step 19 — Verification summary
1. Run `hermes doctor`
2. Verify each item:
| Item | Check |
|------|-------|
| Gateway | `systemctl status hermes-gateway` + test message |
| Dashboard | `systemctl status hermes-dashboard` + URL accessible |
| Dashboard auth | Browser shows login page (LAN mode) or SSH tunnel works (loopback) |
| Memory | `mnemosyne_recall` returns results |
- | Search | `web_search` test query returns results |
+ | Search (primary) | `mcp_donsetch_web_search` test query returns results; session info shows donsetch connected with 3 tools |
+ | Search (fallback) | `web_search` test query returns results |
+ | Web routing | SOUL.md contains the web-routing section (donsetch first, built-in for single-fact lookups) |
| Extraction | `web_extract` test URL returns content |
| Browser | CDP browser opens and navigates |
- | Crons | `hermes cron list` shows 4 jobs |
+ | Crons | `hermes cron list` shows 5 jobs (incl. DonSeTch weekly update) |
| Weekly health check | `~/.hermes/scripts/weekly_health_check.sh` exists and is executable |
| Input token audit | `hermes cron list` shows a 30-day audit job; `input-token-analysis` skill installed and `scripts/audit.py` runs |
| Timezone | `hermes config get timezone` matches customer input |
| Approvals | `hermes config get approvals.mode` returns `smart` |
| Terminal backend | `hermes config get terminal.backend` matches detection |
| Compression | `hermes config get compression.threshold` matches model context |
| Cron fleet default | `hermes config get cron.model` is set |
| Toolsets | `hermes tools list` shows only needed toolsets enabled |
| Verbosity | `hermes config get display.tool_progress` matches customer choice |
| show_cost | `hermes config get display.show_cost` returns `true` |
| Profile | `hermes profile list` shows customer profile |
| Soul.md | `~/.hermes/SOUL.md` exists and contains customer input |
| Skill guardrails | 7 principles loaded from references |
| Skill-retrieval plugin | `hermes plugins list` shows skill-retrieval installed (enabled or disabled per assessment) |
| Library-rag | `hermes skills list` shows library-rag (if opted in) |
| Use case | Customer answered, skills recommended |
3. Present config snapshot: provider, model, aux model, gateway platform, memory backend, search backend, browser backend, compression settings, cron jobs, soul.md path, profile name
4. Present pass/fail for each item
**Done:** all verification items checked. Config snapshot presented. Failures flagged with troubleshooting steps.
## Step 20 — Use case and skill recommendations
Ask: "What will you use Hermes for?"
Based on the answer, recommend 3-5 skills from the Skills Hub:
```bash
hermes skills search <use-case-keyword>
```
Install customer's chosen skills: `hermes skills install <id>`
Also install the `input-token-analysis` skill from this repo (`agent-ops/input-token-analysis`) — the 30-day audit cron from Step 18 depends on it.
**Done:** use case recorded. Relevant skills recommended and installed. Input-token-analysis skill installed for the Step 18 audit cron.
## Common Pitfalls
- **Gateway crash loop with --replace:** Never use `--replace` in systemd unit files for multiple profiles. It SIGTERMs other gateway processes.
- **TimeoutStopSec too short:** Always set 240s. Default 90s causes SIGKILL mid-drain on WhatsApp/Telegram bridges.
- **Cron drift guard:** Unpinned cron jobs fail closed when global model changes. Set cron fleet default early (Step 17).
- - **SearXNG IP reputation:** Google/Brave may block datacenter IPs. No config fix — use residential proxy or accept DDG fallback.
+ - **SearXNG IP reputation:** Google/Brave may block datacenter IPs. No config fix — use residential proxy or accept DDG fallback. This affects the fallback backend only; DonSeTch is the primary search path.
- **Double-indexing in library-rag:** Keep raw files outside LIBRARY_ROOT. Only structured markdown goes under LIBRARY_ROOT.
- **plugins.enabled stringification:** `hermes config set plugins.enabled '["a"]'` stores a JSON string, not a YAML list. Edit config.yaml directly for plugin lists.
- **Dashboard TUI on LAN:** Requires HERMES_PYTHON env var and CORS/loopback patches. See hermes-service-deployment skill references.
## Verification
Step 19's verification table is the single source of truth for setup completeness. Do not duplicate it here.
All items must pass before declaring onboarding complete. Failed items get troubleshooting steps from the Common Pitfalls section.