Learn Hermes AI Agent - Security & Access Control
Episode 11 of 23

Learn Hermes AI Agent - Security & Access Control

Securing the agent from within: protecting tool access and API keys, restricting capabilities to safe operations, and building audit trails and policy enforcement to track and control every agent action.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 10 you gave the agent great power: chained workflows, planning, and automatic retries. The more power a system has, the more serious the duty to secure it. Episode 11 flips the perspective — we are not adding features, but closing gaps and putting up fences.

Here is the roadmap for this episode: securing tool access and API keys, restricting agent capabilities to safe operations, and then building audit trails and policy enforcement.

Mapping the Threats Before Securing

Security does not start with tools, but with mapping assets and threats. For an agent, the three most important assets are: secrets (API keys), execution capabilities (tools with side effects), and data (memory and tool results). The key question for each asset: who may see it, who may trigger it, and what happens if it is misused.

threat-model.md
Aset        | Pelaku           | Risiko
------------+------------------+-----------------------------
API keys    | user, penyerang  | Pencurian, pemakaian di luar
Tool exec   | prompt injection | Eksekusi perintah berbahaya
Data memory | user lain        | Kebocoran data antar sesi

An important note: the biggest actor is not an attacker on the internet, but unexpected input — the content of web pages, documents, or user messages that smuggles malicious instructions into the prompt (prompt injection, already mentioned in episodes 5 and 6). Every fence in this episode is designed with that threat in mind.

Securing Tool Access and API Keys

Start with the most valuable thing: credentials. The first rule has been repeated since episode 3 — the actual secret values never enter code or committed config. In production, secrets are stored in an external store and injected at runtime (full details in episode 12). Locally, use a dot-env file that is in .gitignore.

Besides storing them safely, also limit the scope of secrets:

  • Scoped API keys — a key with permission for only one resource or one quota. An agent that only needs to read does not get a write-permitted key.
  • Per-tool credentials, not per-agent — the database uses a dedicated read-only user, external APIs use a token with minimal scope.
  • Never log or display secret values in tool output — mask them if they need to be referenced.
secret-helper.ts
import { loadSecret } from "@hermes/security";
 
const dbCred = await loadSecret("DATABASE_READONLY_URL");
const registry = await loadSecret("REGISTRY_TOKEN", { scopedTo: "registry.example.com" });

The loadSecret helper fetches a secret from the configured store, not from code. Make it a habit: every time a new credential is added, ask two things — is its scope as minimal as possible, and would its value leak if the agent log were read.

Restricting Capabilities to Safe Operations

In episode 4 you learned to set permissions per tool. In episode 11, apply that discipline as a comprehensive policy, not a one-by-one decision. The principle: default deny. Everything not explicitly allowed is automatically forbidden.

agents/ops.yml - deny by default policy
security:
  default_deny: true
  allowed_operations:
    - db.query (read_only)
    - web.search
    - web.fetch (domains: ["*.example.com"])
  sandbox:
    backend: docker
    network: none
  secrets:
    env_allowlist: ["DATABASE_READONLY_URL"]

The sandbox block above answers the code execution threat from episode 4: the agent runs in a docker backend with no network access, so an executed script cannot reach the host or the internet. If the code_execution capability is active, always run it in an isolated sandbox — not on the main host. The default_deny value is the master switch that turns on safe mode overall.

Danger

Default deny is not a suggestion, it is a foundation. Once you start with an allow-list, every new tool automatically requires an explicit decision before it can be executed — that is exactly what you want.

Audit Trail

An audit trail is a record of who did what, when, and with what result. Without this record, you cannot confirm a security incident, comply with regulations, or evaluate whether a policy works. In Hermes, every tool call is recorded as an event with complete metadata.

audit-event.json
{
  "event": "tool.call",
  "agent": "ops-agent",
  "session": "ses-ops-8821",
  "user": "arman@example.com",
  "tool": "db.query",
  "args_hash": "a1b2c3...",
  "result": "success",
  "timestamp": "2026-08-03T10:15:30Z",
  "duration_ms": 320
}

Notice args_hash — a hash of the call arguments, not their raw values. This keeps sensitive values out of the log while still allowing verification. For high-risk operations (payments, deleting data, sending email), store a full argument snapshot in a separate protected store.

An audit trail is useless if it is never read. Make review routine — weekly or monthly — to look for anomalies: tools called outside working hours, denied permissions being probed frequently, or suspicious arguments. This pattern will grow into automatic alerting in episode 20.

Policy Enforcement

Policy enforcement is the layer that enforces rules at the runtime level, not just a hope in the system prompt. Prompts can be persuaded; policies cannot. Hermes loads policies from config and enforces them in the controller before every action is executed.

policies/access.yaml
policy: access-control
rules:
  - when: { tool: "http.post", target: "billing-api" }
    require: "user.confirmed_payment"
    else: deny
  - when: { tool: "db.query" }
    require: "tool.read_only == true"
    else: deny
  - when: { tool: "fs.write" }
    require: "user.role in ['admin']"
    else: deny

Every action passes through these rules in the controller before being executed. If the condition is not met, the action is denied and recorded in the audit trail — the agent never even gets to run its tool. This is the crucial difference from static permissions: policies can consider context such as user role or confirmation status, not just the tool name.

Info

Link policies with the audit trail: every denied action is recorded as a separate event. A frequently appearing violation pattern — for example, the agent trying fs.write repeatedly — is a signal to tighten the prompt or add more sandboxing.

Conclusion

Episode 11 closed the gaps on the power you built in episode 10: secrets are secured and scoped as minimally as possible, capabilities are restricted with default deny, the sandbox isolates dangerous execution, the audit trail records every action, and policy enforcement enforces rules at the runtime level that cannot be persuaded through prompts.

Key takeaways:

  • An agent's key assets are secrets, execution capabilities, and data — map the threats before securing.
  • Secrets are stored externally and scoped per resource; per-tool credentials are safer than per-agent ones.
  • Default deny and sandboxing are the mandatory pair for dangerous capabilities like code execution.
  • The audit trail records who, what, when, and what result — with arguments hashed so they do not leak.
  • Policy enforcement works at runtime and can consider context, unlike static permissions.

In episode 12 your agent moves into the real world: Secure Agent Deployments — cloud or edge deployment, secrets management and network policies, and HTTPS and secure webhook endpoints in production. See you there!

Learn Hermes AI Agent - Security & Access Control | Learn Hermes AI Agent