DEV Community

Nawi
Nawi

Posted on

Running Hermes Agent in the Cloud Safely: A Reader's Guide to Their Trust Model

You can run Hermes Agent on a $5 VPS. You can run it on a GPU cluster. You can run it serverless on Daytona or Modal so it hibernates when idle and costs almost nothing between sessions. You can talk to it from Telegram while it works on a cloud VM. That flexibility is the headline feature - and it's also the security question this post is about.

NousResearch publishes a detailed security policy for Hermes Agent. It is unusually clear about what the project treats as load-bearing and what it does not. If you operate Hermes in the cloud, read it first; this post is the operator-friendly companion, not a replacement.

What Hermes already gives you

Three things in the box are worth knowing about up front, because they shape the rest of the deployment story.

1. A trust model that names the boundary. Hermes Agent's policy says, in so many words: the only security boundary against an adversarial LLM is the operating system. Not the approval gate. Not output redaction. Not the Skills Guard. Those are useful - they catch the cooperative-mode mistakes that account for most real-world incidents - but they are heuristics operating on an attacker-influenced string, and the project does not pretend they are containment.

That's an honest framing, and it determines what "safely" means: you are responsible for choosing an OS-level isolation posture that matches the trust you've extended to the content flowing through the agent.

2. Seven terminal backends. The terminal() tool - the one through which shell commands run - is pluggable. The supported backends are local, docker, ssh, singularity, modal, daytona, and vercel_sandbox. Switching from the default local backend to a containerized or remote one moves the agent's shell out of your host. Pick deliberately; the default does not isolate anything.

3. A non-trivial gateway. Hermes can be talked to from Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, SMS, and a dozen other platforms. Each adapter is a network-exposed surface. Each one needs an allowlist. The project's policy treats every adapter without an allowlist as a code bug, but operator configuration is required - adapters do not magically lock themselves to your account.

With those three points in mind, the rest of this guide is the practical follow-through.

Step 1 - Pick your isolation posture

Hermes Agent's policy describes two postures. They protect against different things, and operators should choose one explicitly rather than fall into the default.

Terminal-backend isolation swaps the local backend for a sandboxed one - Docker, an SSH'd container, Modal, Daytona, Vercel Sandbox. Shell and file tools (terminal, read_file, write_file, patch) execute inside the sandbox. What this confines: anything the agent does through the shell contract. What it does not confine: the code-execution tool (which spawns a host subprocess), MCP server subprocesses, plugins, hooks, and skill loading. Those are all in the agent's own Python process.

Whole-process wrapping runs the entire agent process tree - shell, code-execution, MCP, plugins, hooks, skills - inside a single sandbox. Hermes supports this two ways:

  • The project's own Docker image and Compose setup. Lighter weight; standard container with operator-configured mounts and network policy.
  • NVIDIA OpenShell. Per-session sandboxes with declarative policy across filesystem, network L7 egress, process/syscall layers, and inference routing. Credentials live in a Provider store and never touch the sandbox filesystem.

The decision rule from the policy: if the content flowing into the agent comes from surfaces you do not control - the open web, inbound email, multi-user Slack channels, untrusted MCP servers - you want whole-process wrapping. If the operator is trusted and the concern is just LLM-emitted destructive shell, terminal-backend isolation is the supported posture.

In practice: most cloud deployments fall into the first category. If your Hermes can be messaged from Telegram, you are accepting input from a surface you don't fully control. Plan accordingly.

Step 2 - Lock down the gateway

If you have any gateway adapter enabled, this is your largest remote attack surface. Three rules apply uniformly across all of them.

Allowlist. Every adapter must refuse to dispatch agent work, resolve approvals, or relay output until a caller allowlist is configured. The policy treats fail-open behavior as a bug - but the operator still has to set the list. For Telegram, that means the chat IDs (or user IDs) that are authorized to talk to your bot. For Slack, the workspace and user IDs. For email, the From addresses. Until you set this, anyone who guesses your bot token or scrapes a public Slack channel has the same access you do.

Session IDs are routing handles, not authorization. Knowing another caller's session ID grants no access. Authorization is re-checked against the allowlist on every call. You should not treat session URLs as secrets, and you should not embed authorization checks that rely on them.

Treat token leakage as account compromise. Telegram bot tokens, Slack tokens, Discord webhook URLs - if any of these leaks, an attacker with the token has whatever the bot can do. Rotate proactively, store in a secrets manager (not in the repo, not in your shell history), and audit access logs.

For network-exposed HTTP surfaces (the dashboard plugin, the API server, the kanban plugin), the defaults bind to loopback. Switching them to --host 0.0.0.0 is a break-glass operator decision. If you make it, you own the public-internet hardening - TLS, auth, rate limiting - none of which Hermes provides for you on that path.

Step 3 - Match the cloud environment to the posture

The cloud-deployment specifics that Hermes' own docs leave to the operator:

On AWS or any VPC-based cloud:

  • Run Hermes in a private subnet. The agent does not need to be reachable from the public internet for any of the messaging gateways to work - those poll outbound or accept webhooks via a separate ingress that you control.
  • Outbound security group: deny by default. Allowlist (a) your secrets manager endpoint, (b) the inference provider's endpoint (whichever LLM you've selected via hermes model), (c) the messaging platforms in use, (d) your logging endpoint. Nothing else.
  • Inbound security group: deny by default. If a gateway uses inbound webhooks (Slack events API, Telegram webhook mode), put them behind an ALB with WAF and explicit path-based routing.
  • IAM role attached to the EC2 / Fargate task: scoped to specific resources. The agent does not need *:* on anything. If it does file work, scope to specific S3 buckets. If it queries databases, scope to specific Secrets Manager secrets and specific RDS hosts.

On Modal or Daytona (the serverless paths):

  • The "hibernates when idle" feature is genuinely great for cost. It is also a different security model from a persistent VM: cold-start state and warm-start state may both be reachable. Keep credentials in the Modal/Daytona secret store, not in the image.
  • Default network policy on Modal lets containers reach the internet broadly. If your workload doesn't need that, restrict it.

On a $5 VPS (the path most casual users will take):

  • Disable password auth on SSH. Use keys. This is table stakes, but it is also the most common compromise vector for a small VPS.
  • Run Hermes as a dedicated non-root user (the Docker image already does this - UID 10000). If you're running outside Docker, create the user explicitly.
  • Put ufw (or equivalent) in front of everything. Allow SSH from your IP only. Deny everything else inbound.
  • If you need the dashboard or API surfaces externally, front them with Tailscale or WireGuard rather than exposing them to the public internet. A free-tier Tailscale account covers a small operator deployment indefinitely.

Step 4 - Skills and plugins are code you're installing

Hermes' Skills system is one of the things that makes it powerful: the agent creates skills from experience, improves them during use, and can install community-contributed skills from external repositories. Plugins extend the architecture further - they load into the agent's Python process and run with full agent privileges.

This is - and the project says so explicitly - operator review surface. Skills Guard exists as a review aid that scans installable content for injection patterns. It is not a boundary. The supported workflow for third-party skills is:

  1. Read the actual Python and shell scripts in the skill, not just SKILL.md. Skills execute arbitrary Python at import time.
  2. Treat plugin installs the same way you treat installing any package from PyPI on a production system. Pin versions. Review the source. Treat the install audit log as evidence of what you've actually run.
  3. If you wouldn't install the underlying code on the system without review, don't install the wrapping skill or plugin either.

For credential handling, Hermes filters the environment passed to shell, MCP, and code-execution subprocesses (provider API keys and gateway tokens are stripped by default), but anything running inside the agent process - every skill, every plugin, every hook handler - can read whatever the agent itself can read. The mitigation is review-before-install. There is no in-process containment of plugin code, and the policy is explicit about that.

Step 5 - Add an in-process gate, knowing what it is

OS-level isolation is the boundary. In-process heuristics catch most of the day-to-day mistakes - the agent generating a rm -rf because of a confusing prompt, the agent reaching for git push --force because the LLM concluded that was the simplest path. These mistakes are common, they are usually not adversarial, and a sharper in-process gate prevents them from reaching the boundary at all.

Hermes' built-in approval gate does some of this. It detects common destructive shell patterns and asks the operator before execution. The policy is upfront that it's a denylist over shell strings and structurally incomplete - "the gate catches cooperative-mode mistakes, not adversarial output."

If you want the in-process gate to be sharper, you can layer one on. This is where Node9 fits in a Hermes deployment: an AST-based policy engine that parses shell commands the way the OS does (not the way regex does), so obfuscated payloads (echo "Y3VybCAuLi4="| base64 -d | bash) collapse into their actual execution graph before the approval decision is made. It also runs a per-call inspection layer that catches credentials in outbound arguments, anomalously large payloads, and force-push patterns that simple denylists miss. The AST-parsing approach is covered in detail in Why Regex Is Not Enough.

The honest framing - and the one Hermes' policy would approve of - is that this is belt and suspenders on top of OS isolation, not a replacement for it. It reduces the rate at which the boundary has to do the catching, which makes the whole system more livable. It does not change the load-bearing layer.

A 30-minute audit checklist

If you have Hermes running in the cloud right now, walk through this before you close your laptop:

  1. Isolation posture. Have you explicitly chosen one - terminal-backend or whole-process - and is the configuration consistent with the choice? "I'm not sure" is a finding.
  2. Gateway allowlists. Does every enabled adapter (Telegram, Slack, Discord, email, etc.) have an explicit allowlist? Send a message from a non-allowlisted account - does it actually refuse, or does it accept and let the LLM-level gate sort it out?
  3. Inbound security group / firewall. From an external IP, what's reachable? The answer should be either nothing or only your ALB / webhook endpoint, never the agent process directly.
  4. Outbound security group. From inside the agent container, curl https://example.com. It should fail. If it succeeds, you don't have egress control.
  5. Secret storage. Are your gateway tokens, LLM provider keys, and SSH keys in a secrets manager / OpenShell Provider store, or in a .env file on disk?
  6. Skills installed. List every skill installed. For each: did someone read the Python before running it? Or did it just get added via a one-line command?
  7. Approval gate behavior. Trigger a destructive shell command (rm -rf /tmp/test) through a chat. Does the gate intercept it? Now try the obfuscated form (r''m -rf /tmp/test, \rm -rf /tmp/test, echo cm0gLXJmIC90bXAvdGVzdA== | base64 -d | sh). Does the gate still intercept it? The honest answer is usually "not all of them" - that's why OS isolation is the boundary and the gate is a heuristic.

If any of these come back as "I'm not sure", that's the next thing to fix.

Closing

Hermes Agent is one of the more interesting things to land in the open-source agent space - model-agnostic, multi-platform, runs on infrastructure ranging from a Raspberry Pi to a GPU cluster. The flexibility is genuine. It also means the operator has more setup work than a typical desktop agent: gateway allowlists, isolation posture, terminal backend choice, skill review, credential scoping. None of it is exotic; all of it has to be done.

If you want to see whether the agents you're already running have hit any of the failure patterns above - without installing anything - run:

npx node9-ai scan
Enter fullscreen mode Exit fullscreen mode

It reads existing Claude Code, Codex, Gemini, and Cursor session files locally and produces a report of the risky tool calls already in your history. Local-only, no telemetry. Apache 2.0. (Hermes Agent session support is on the roadmap - if you're a Hermes operator who'd like to test the integration early, drop a note in the issues.)

If you've deployed Hermes Agent in the cloud and have war stories worth sharing - failure modes, hardening tricks, edge cases that surprised you - drop them in the comments. The boundary work is the part nobody writes about.

Top comments (0)