Running Hermes Agent on KruxOS¶
Hermes Agent is an open-source, self-improving AI agent framework from Nous Research with built-in MCP client support. Connecting Hermes to KruxOS gives every file, process, network, and git operation that Hermes performs the same governance the rest of your agents get: sandboxing, policy enforcement, approval flows, and audit logging.
This guide walks through the one-time setup — about five minutes if KruxOS is already running.
Full Hermes integration ships in v0.0.3
v0.0.1 supports Hermes through the same generic mcp-bridge path documented
below — any client that speaks MCP stdio can use it. Full Hermes
integration (a dedicated kruxos cli-config adapter for Hermes,
on-appliance pip installation, ACP gating for web/browser/messaging tools)
is tracked separately and lands in v0.0.3. Until then, follow the
generic MCP setup below.
Why connect Hermes to KruxOS?¶
Hermes ships with 40+ built-in tools covering the same surface area KruxOS
governs: terminal, read_file, write_file, list_directory, shell, git,
web. Those built-in tools run directly on the host with no sandbox and no
audit trail. Routing them through KruxOS turns every Hermes OS call into a
policy-checked, audit-logged capability invocation — without changing the way
you write prompts or deploy Hermes.
Architecture:
Hermes Agent (python)
│ stdio (newline-delimited JSON-RPC, MCP 2024-11-05)
▼
kruxos.connectors.claude_bridge (launched by Hermes as a subprocess)
│ WebSocket
▼
KruxOS Gateway ──► Policy Engine ──► Sandbox ──► Capabilities
│
└──► Audit Log ──► Dashboard Approvals
The bridge is the same kruxos.connectors.claude_bridge stdio-to-WebSocket
bridge that Claude Desktop and Claude Code use — KruxOS does not ship a
Hermes-specific adapter because MCP stdio is a standard transport.
Prerequisites¶
- KruxOS running and reachable at
ws://HOST:7700(Docker viaaltvale/kruxos:latest, the VM image, or a native build all work). - A KruxOS agent token. Create one with: Write down the 64-char hex token it prints. You will pass this to Hermes as an environment variable.
- The bundled
mcp-bridgeat/opt/kruxos/bin/mcp-bridgeon the appliance, or a host-side copy of the in-appliance Python SDK at/opt/kruxos/sdk/python/(until the PyPI distribution lands in v0.0.3). - Hermes Agent v0.7.0 or newer installed via the project's install script (see the Hermes README for current instructions).
Step 1 — Add KruxOS as an MCP server in Hermes¶
Edit ~/.hermes/config.yaml and add an entry under mcp_servers:
mcp_servers:
kruxos:
command: /opt/kruxos/bin/mcp-bridge
args: []
env:
KRUXOS_ENDPOINT: wss://localhost:7700
KRUXOS_AGENT_NAME: hermes
KRUXOS_AGENT_TOKEN: 7f3a8c1d2e9b5a4f8e6c1d3b7a9f2e5c8d1b4a7f3c9e6d8b1a4c7f2e5d9b8a3c
If KruxOS is running on a different host (for example, in a Docker container
on your dev machine while Hermes runs locally), replace localhost with the
gateway host. Hermes launches the bridge as a child process on startup, and
the bridge proxies MCP messages over the WebSocket connection.
Config location
Hermes reads ~/.hermes/config.yaml by default. If you run Hermes with
a custom config (hermes --config path/to/config.yaml), add the entry
to that file instead.
Step 2 — Disable Hermes's built-in OS tools¶
Hermes's native terminal, read_file, write_file, list_directory, and
similar tools run directly on the host and bypass KruxOS governance. Once you
connect KruxOS, you want Hermes to use KruxOS equivalents
(filesystem.read, filesystem.write, filesystem.list, process.run, …)
so every call is sandboxed and audited.
Hermes manages tool enablement through the hermes tools CLI, not through
config.yaml keys. Disable the overlapping native tools in one shot:
You can also run hermes tools with no arguments to get the interactive
picker and toggle tools on and off visually.
Leave the non-OS tools enabled — for example Hermes's web search, markdown renderer, and planner are fine alongside KruxOS. List what's currently enabled with:
The goal is a clean split: KruxOS owns anything that touches the host OS; Hermes owns reasoning, planning, and in-process tools.
Step 3 — Verify the connection¶
Start Hermes and ask the agent to list its tools:
Hermes should report the KruxOS capabilities it discovered. You should see
names such as filesystem.read, filesystem.write, process.run,
network.http_request, and so on — roughly 80+ tools depending on which
packs you have loaded.
If you see tools like filesystem_read with underscores in the Hermes
output, that is expected: MCP tool names must match ^[a-zA-Z0-9_-]{1,128}$,
so the bridge translates dots to underscores on the way out and back again
on the way in. You can invoke either form — Hermes will call the mangled
name, and the bridge will restore the original dotted name before forwarding
to the gateway.
Try a concrete tool call:
Hermes will dispatch a filesystem_read call through the bridge. On the
KruxOS side you will see the call appear in the gateway audit log and — if
the capability is sensitive — in the dashboard approval queue.
Approval flow¶
KruxOS and Hermes both have approval flows, and it is worth understanding the distinction:
- KruxOS approvals happen at the OS level. When Hermes attempts a
capability whose policy is
require_approval, the gateway holds the tool call and queues a request in the dashboard. The bridge polls for the decision and returns the real result (approved) or a STOP error (rejected) to Hermes. Hermes does not need to know anything about this — the tool call simply takes longer to return. - Hermes approvals (v0.7.0) happen at the agent level via Slack or Telegram buttons. They gate whether a command becomes a tool call at all.
Running both approval systems in parallel causes double-approval friction: the user has to confirm once in Slack/Telegram before Hermes fires the tool call, then again in the KruxOS dashboard before the call executes.
Command approval is a global Hermes setting — it is not configurable per MCP server. You have two reasonable choices:
- Rely on KruxOS alone — disable Hermes command approval globally via
hermes config set(seehermes config --helpfor the exact key in your installed version). KruxOS will hold every sensitive tool call in the dashboard queue, and the bridge will block until the decision lands. This is the simplest story for teams who want a single source of truth for governance. - Keep both for defense-in-depth — leave Hermes command approval on. You will see Slack/Telegram prompts before the tool call is dispatched, and then again in the dashboard before the capability executes. More friction, but two independent stop-buttons.
For v0.0.1 we recommend the first option: one approval path, one audit log, one place to change your mind.
Tool filtering: KruxOS policy vs Hermes include/exclude¶
Hermes supports per-server tools.include / tools.exclude lists. This is a
client-side filter that controls which tools Hermes sees. KruxOS policy is
a server-side filter that controls which tools an agent is allowed to call.
The two filters compose:
| KruxOS policy | Hermes include/exclude | Result |
|---|---|---|
| Allowed | Included | Hermes can see and call the tool |
| Allowed | Excluded | Tool hidden from Hermes, but a direct MCP call would still succeed (Hermes won't make one) |
| Blocked | Included | Tool hidden by the gateway — Hermes never sees it |
| Blocked | Excluded | Tool hidden by both — same result |
Recommendation: let KruxOS handle filtering via policies and leave Hermes
unfiltered. That way the governance decision lives in one place (policies/),
and changing a policy updates every client automatically. Only use Hermes's
filter when you want to hide a KruxOS-allowed tool from a specific Hermes
profile for UX reasons.
Production mode: running Hermes inside the KruxOS container¶
The setup above is a good starting point but assumes Hermes runs on the host with its own process space. For full governance, run Hermes inside a KruxOS agent container so that even Hermes's reasoning-time file access (working directory writes, temp files, cache) is inside the sandbox:
- Build a Docker image that layers Hermes on top of
altvale/kruxos:latest. - Point Hermes's MCP server entry at
ws://gateway:7700(the compose-network hostname) instead oflocalhost. - Mount the agent's workspace volume at
/workspace— that becomes Hermes's working directory and KruxOS's sandbox root simultaneously.
Production mode is the same story as Codex in a KruxOS container — see the Codex guide for a full compose example. The Hermes-specific bit is just the image build.
Troubleshooting¶
Hermes reports "MCP server kruxos failed to start"¶
The bridge is failing before it can accept messages on stdin. Run the bridge manually to see the error:
KRUXOS_ENDPOINT=wss://localhost:7700 \
KRUXOS_AGENT_NAME=hermes \
KRUXOS_AGENT_TOKEN=7f3a8c1d2e9b5a4f8e6c1d3b7a9f2e5c8d1b4a7f3c9e6d8b1a4c7f2e5d9b8a3c \
/opt/kruxos/bin/mcp-bridge
Structured exit codes (10 = auth, 11 = network) point at the cause.
Common causes:
mcp-bridgebinary missing.ls -l /opt/kruxos/bin/mcp-bridge— on the appliance it's always present; on a host machine, copy it off the VM image or run it from inside adocker exec.- Environment variables missing. The bridge exits immediately if
KRUXOS_AGENT_NAMEorKRUXOS_AGENT_TOKENis unset. - Gateway unreachable. Test with
curl -sk -o /dev/null -w '%{http_code}\n' https://localhost:7800. If the dashboard is not responding either, the gateway is down — checkdocker compose psorsystemctl status kruxos-gateway.
"Authentication failed" in the bridge logs¶
The token the bridge is using does not match a registered agent. Re-create
the agent (kruxos agent create --name hermes) and copy the token exactly —
agent tokens are 64-char hex strings in v0.0.1. Set the KRUXOS_BRIDGE_LOG_LEVEL=DEBUG
env var for verbose output.
Hermes sees tools but every call errors with "Session not initialized"¶
Hermes is skipping the MCP initialize handshake. Upgrade to the current
Hermes release — older builds (< 0.6) had a bug where they sent tools/list
before initialize. If you cannot upgrade, raise a Hermes issue; the bridge
faithfully forwards whatever Hermes sends and the gateway requires
initialize first per the MCP spec.
Long-running tool calls time out¶
The bridge holds tool calls for up to _EXECUTION_TIMEOUT_SECS (120 seconds
by default) when an approval is pending. If you frequently approve requests
that take longer to run after approval, raise the timeout by forking the
bridge module or open an issue.
Approval requests never appear in the dashboard¶
Check that the gateway sees the tool call at all:
If nothing shows up, Hermes is not dispatching the call (check Hermes logs).
If the call is logged but no approval queues, the matching policy is not set
to require_approval — inspect policies/ for the agent's effective
policy group.
Next steps¶
- Approval Workflow — how dashboard approvals work
- Policies — write the policy that governs what Hermes can do
- Managing Agents — rotate the Hermes API key, set rate limits, assign policy groups
- Running Codex on KruxOS — same pattern for the OpenAI Codex CLI