This guide gets ClawKeeper running in front of a Hermes Agent install from scratch.
If you already have a working Hermes setup, skip to step 3.
| Required | Notes | |
|---|---|---|
| Python | 3.11+ | Hermes itself requires 3.11; ClawKeeper inherits |
| A Hermes Agent checkout | Yes | We integrate as an in-process plugin, so Hermes needs to be importable. Clone from git@github.com:NousResearch/hermes-agent.git |
| An OpenAI-compatible LLM endpoint | Yes | OpenAI / Anthropic / OpenRouter / scode.chat / any local server that exposes /v1/chat/completions |
| Network access from the box running Hermes | To your LLM endpoint | If your box is firewalled off from your provider, set up a proxy first (clash / WireGuard / corp HTTP proxy) — ClawKeeper does not add network requirements of its own beyond what Hermes already needs |
ClawKeeper does NOT need:
- Docker, Kubernetes, or any orchestrator (it's a Python library + an optional FastAPI daemon)
- A separate database — session state is in-memory by default
- A separate LLM API key beyond what Hermes already uses (the optional Watcher can reuse Hermes's key or take its own)
If you don't already have it:
git clone git@github.com:NousResearch/hermes-agent.git
cd hermes-agent
./setup-hermes.sh # creates a .venv, installs deps, links the `hermes` CLIOr with a venv you manage yourself:
git clone git@github.com:NousResearch/hermes-agent.git
cd hermes-agent
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .Verify:
python -c "from run_agent import AIAgent; print('Hermes OK')"Two files matter:
Minimum viable example for an OpenAI-compatible provider:
model:
default: "gpt-5.4-openai-compact" # any model id your provider exposes
provider: "custom" # for OpenAI-compatible endpoints
base_url: "https://api.openai.com/v1" # change to your endpoint
terminal:
backend: "local" # or "docker" for containerised execution
cwd: "/path/to/your/workdir" # the directory Hermes runs tools in
timeout: 180
session_reset:
mode: idle
idle_minutes: 1440
group_sessions_per_user: trueOPENAI_API_KEY="sk-…"
OPENAI_BASE_URL="https://api.openai.com/v1"
# If you'll run the Hermes messaging gateway against Discord/Telegram/etc.:
# DISCORD_BOT_TOKEN="..."
# DISCORD_ALLOWED_USERS="numeric_user_id_1,numeric_user_id_2"chmod 600 ~/.hermes/.env after creating it.
Smoke test Hermes alone (no ClawKeeper yet):
hermes chat -m gpt-5.4-openai-compact
# type "list the files here" — should workIn the same Python environment Hermes is installed in:
git clone git@github.com:SafeAI-Lab-X/ClawKeeper.git
cd ClawKeeper
git checkout v0.2-refactor # currently the active branch
pip install -e . # core
pip install -e ".[mcp]" # optional: MCP gateway adapter
pip install -e ".[dev]" # optional: pytest, ruff, mypyVerify the install:
python -c "
from clawkeeper_core import Judge
from clawkeeper_core.adapters.hermes import install
print('ClawKeeper OK')
"
pytest -q --ignore=tests/redteam tests/ # should report 250+ passingThis is the minimum integration. No daemon, no extra LLM calls. Catches a strong set of attacks via deterministic guards (path access, dangerous-command regex, SSRF, homoglyph URLs, script-body sensitive-path detection, credential redaction).
# my_agent.py — your own entrypoint or wrap-script
from run_agent import AIAgent
from clawkeeper_core import Judge
from clawkeeper_core.adapters.hermes import install as install_clawkeeper
agent = AIAgent(
model="gpt-5.4-openai-compact",
base_url="https://api.openai.com/v1",
enabled_toolsets=["terminal"], # see note below
max_iterations=30,
)
install_clawkeeper(Judge(), agent) # one call wires every documented hook
response = agent.chat("List the files in this directory.")
print(response)What install_clawkeeper(...) actually does:
- Registers a
pre_tool_callplugin hook with Hermes — Hermes's documented blocking API. The hook runs ClawKeeper's deterministic guard chain on every tool call; if any guard blocks, Hermes returns a block message to the LLM and the tool never executes. - Wires
set_approval_callbackontools.terminal_toolandtools.computer_use.tool— when Hermes's own regex flags a command and asks for approval, ClawKeeper'sJudgeanswers. - Attaches
tool_start_callback/tool_complete_callbackfor telemetry (drift detection, profiling).
The Hermes CLI (hermes chat, hermes gateway) doesn't expose a plugin-mount point directly — wrap it the way examples/hermes_demo.py does (UA patch + install_clawkeeper(...) + delegate to hermes_cli.main.main()). See examples/hermes_discord_bot.py for a complete gateway-mode launcher.
The Watcher is the third architectural layer: an external LLM-driven supervisor that reasons about whole trajectories rather than individual commands. Pay 1 extra LLM call (~2–5 s) per tool call; gain trajectory-level drift detection.
Start the daemon in a separate shell (or under systemd / a process supervisor):
# Environment knobs (all optional — defaults shown)
export CK_WATCHER_HOST="127.0.0.1" # bind address (do NOT bind 0.0.0.0)
export CK_WATCHER_PORT="9099"
export CK_WATCHER_MODEL="gpt-5.4-openai-compact" # what the Watcher reasons with
export CK_WATCHER_BASE_URL="$OPENAI_BASE_URL" # falls back to OPENAI_BASE_URL
export CK_WATCHER_API_KEY="$OPENAI_API_KEY" # falls back to OPENAI_API_KEY
python -m clawkeeper_core.watcher.daemonHealth check:
curl http://127.0.0.1:9099/watcher/health
# {"status":"ok","model_id":"gpt-5.4-openai-compact","known_sessions":0}Wire it into the adapter:
install_clawkeeper(
Judge(),
agent,
watcher_url="http://127.0.0.1:9099",
)The Watcher is consulted only when the deterministic guards didn't already block. If the Watcher is unreachable, the client falls back to ask (defer to operator) — never silently allows.
examples/hermes_demo.py is a self-contained runner that exercises a few benign + adversarial scenarios against a live agent. Run it from the ClawKeeper repo root:
source /path/to/hermes/.env # provides OPENAI_API_KEY / OPENAI_BASE_URL
cd ClawKeeper
python examples/hermes_demo.pyThe script writes a JSON trace + a markdown summary into examples/runs/<timestamp>/. ClawKeeper decisions show up under _clawkeeper_decisions on each tool call.
For a more aggressive validation, use the red-team suite:
# Need a workspace for the agent to operate in:
mkdir -p tests/redteam/workspaces
git clone --depth 1 git@github.com:miguelgrinberg/microblog.git \
tests/redteam/workspaces/flask-todo
# Run a scenario
export REDTEAM_USE_WATCHER=1 # optional
export REDTEAM_WATCHER_URL="http://127.0.0.1:9099" # only if using Watcher
python -m tests.redteam.runner --only 07.aReports go to tests/redteam/results/<timestamp>/.
| Key | What it does | Recommended for ClawKeeper |
|---|---|---|
model.default |
Which LLM the agent uses | Any. The agent's own LLM is one defense layer; smaller models = ClawKeeper does more work. |
model.provider |
"custom" for OpenAI-compatible |
"custom" for scode.chat / OpenRouter / local servers |
terminal.backend |
local / docker / ssh / modal |
Choose based on isolation needs. ClawKeeper's policy applies regardless of backend. |
approvals.mode |
manual / smart / off |
Use manual. smart delegates safety to an aux LLM (jailbreakable); off disables Hermes's regex layer entirely. |
command_allowlist |
Hermes's permanent-allow list | Keep small. ClawKeeper still applies its own guards independently. |
# LLM endpoint
OPENAI_API_KEY=...
OPENAI_BASE_URL=...
# Gateway (only if running the messaging gateway)
DISCORD_BOT_TOKEN=...
DISCORD_ALLOWED_USERS=numeric_id_1,numeric_id_2 # NUMERIC IDs not handles
DISCORD_REQUIRE_MENTION=false # DMs only
# TELEGRAM_BOT_TOKEN, SLACK_BOT_TOKEN, etc. follow the same pattern
| Var | Default | Purpose |
|---|---|---|
CK_WATCHER_HOST |
127.0.0.1 |
Bind address — do not expose externally |
CK_WATCHER_PORT |
9099 |
|
CK_WATCHER_MODEL |
gpt-5.4-openai-compact |
LLM the Watcher uses (can differ from the agent's) |
CK_WATCHER_BASE_URL |
$OPENAI_BASE_URL |
|
CK_WATCHER_API_KEY |
$OPENAI_API_KEY |
|
CK_WATCHER_URL |
http://127.0.0.1:9099 |
Read by WatcherClient if you don't pass watcher_url explicitly |
| Use case | Setup |
|---|---|
| CLI agent ("talk to Hermes locally with safety on") | Step 4 only. Run your wrapper that calls install_clawkeeper(...). |
| CLI + trajectory reasoning | Step 4 + Step 5. Start daemon, pass watcher_url. |
| Discord / Telegram / Slack bot | See examples/hermes_discord_bot.py. The launcher applies the User-Agent patch (needed for some proxies like scode.chat), installs ClawKeeper, and delegates to hermes_cli.main. |
| Protecting an MCP client (Claude Desktop, Cursor, Continue, Zed) | Use the MCP gateway adapter at clawkeeper_core.adapters.mcp.GatewayServer (independent path, doesn't need Hermes). See docs/integration-surfaces.md §3.7. |
User-Agent rejection (api.scode.chat and some other Anthropic relays). The openai Python SDK sends User-Agent: OpenAI/Python X.Y.Z, which some proxies 403-reject. Patch it at startup:
import openai as _openai
_orig = _openai.OpenAI.__init__
def _patched(self, *a, **kw):
h = dict(kw.get("default_headers") or {})
h.setdefault("User-Agent", "Mozilla/5.0")
kw["default_headers"] = h
return _orig(self, *a, **kw)
_openai.OpenAI.__init__ = _patchedApply before constructing any AIAgent or Watcher. The Watcher daemon does this itself at import time.
JSON-schema validation on Anthropic endpoints. Hermes ships ~40 toolsets; some tool schemas don't fully satisfy JSON Schema draft 2020-12, which Anthropic enforces strictly. If you see 400 errors mentioning schema validation, restrict to a subset:
agent = AIAgent(
enabled_toolsets=["terminal"],
# ...
)GPT-class models don't enforce this, so the issue is provider-specific.
| Symptom | Likely cause | Fix |
|---|---|---|
install_clawkeeper runs cleanly but blocked-looking commands still execute |
You wired into tool_start_callback only. Hermes silently swallows callback exceptions. |
Use the latest clawkeeper_core/adapters/hermes.py from v0.2-refactor — it registers the documented pre_tool_call plugin hook (the real blocking API). |
pre_tool_call hooks: 0 when you _ensure_plugins_discovered()._hooks |
You called install_clawkeeper BEFORE constructing the AIAgent. |
Construct the agent first, then install. |
HTTP 403 from your provider |
Default OpenAI-SDK User-Agent rejected. | Apply the UA patch above. |
HTTP 400 JSON schema validation from Anthropic-side endpoint |
Hermes toolsets have schemas Anthropic rejects. | Use enabled_toolsets=["terminal"]. |
Watcher daemon returns decision: "ask" for everything |
LLM output unparseable (model returning prose instead of JSON). | Check daemon logs; try a stronger Watcher model via CK_WATCHER_MODEL. |
| Watcher latency ~5–10 s per tool call | This is expected for a single LLM round trip. | Use a faster Watcher model (gpt-5.4-openai-compact or Haiku); or skip the Watcher for high-throughput cases (Step 4 alone). |
127.0.0.1:9099 connection refused when adapter consults Watcher |
Daemon isn't running. | python -m clawkeeper_core.watcher.daemon. Or omit watcher_url= to fall back to deterministic-only mode. |
Hermes's hardline blocklist (rm -rf /, fork bombs) |
Unchanged. Runs first, always. |
| Hermes's dangerous-command regex | Unchanged. Still gates the approval prompt. ClawKeeper's Judge answers when Hermes asks. |
| Hermes's context-file scan (AGENTS.md, .cursorrules, SOUL.md) | Unchanged. ClawKeeper adds return_content_scan for anything else entering context. |
| Hermes's Tirith pre-exec scan | Unchanged. ClawKeeper's url_safety adds homoglyph + SSRF coverage. |
| Hermes's MCP env filter | Unchanged. |
| Hermes's gateway authorization (allowlists, DM pairing) | Unchanged. |
| Hermes's container backend hardening | Unchanged. Out of scope for ClawKeeper (deployment concern). |
ClawKeeper runs in addition to Hermes's built-in safety, not instead of it. Both pipelines fire; strictest wins.
| Understand which threats ClawKeeper handles | tests/redteam/THREAT_MODEL.md |
| Wire CK into something other than Hermes (Claude Code, MCP client, LangGraph, …) | docs/integration-surfaces.md |
| Read every guard's source | clawkeeper_core/guards/ |
| Read the Watcher's prompt + decision shape | clawkeeper_core/watcher/prompts.py |
| File a bug or request a new guard | github.com/SafeAI-Lab-X/ClawKeeper/issues |