Defense in Depth, Not a Single Layer
Lesson 19 shrank what’s possible by curating toolsets and skills down to what a deployment actually needs. This lesson is about the actions you do choose to enable: sandboxing where they run, and gating the riskiest ones behind explicit approval, so a capability being available isn’t the same as it executing unsupervised.
flowchart TB
REQ["Model requests\na tool call"] --> CHECK1{"Is this toolset\nenabled at all?\n(Lesson 19)"}
CHECK1 -->|no| BLOCKED["Blocked: never\nreaches execution"]
CHECK1 -->|yes| CHECK2{"Does this action\nrequire approval?"}
CHECK2 -->|yes| GATE["Human confirmation\nrequired"]
CHECK2 -->|no| SANDBOX["Executes inside the\nconfigured terminal\nbackend, Module 5"]
GATE -->|approved| SANDBOX
GATE -->|denied| BLOCKED
style REQ fill:#EEF0F7,stroke:#6366F1,color:#0F172A
style CHECK1 fill:#EEF0F7,stroke:#6366F1,color:#0F172A
style CHECK2 fill:#EEF0F7,stroke:#6366F1,color:#0F172A
style BLOCKED fill:#fee2e2,stroke:#ef4444,color:#0F172A
style GATE fill:#fff7ed,stroke:#f59e0b,color:#0F172A
style SANDBOX fill:#f0fdf9,stroke:#0D9488,color:#0F172A
Layer 1: Sandboxing (Where It Runs)
You already configured this in Module 5. Revisit it here as a safety decision, not just a deployment one: the Docker or serverless (Daytona/Modal) terminal backends contain the blast radius of a shell command gone wrong to an isolated environment, rather than your host machine. For any deployment reachable by more than just you (Module 6), running local as the terminal backend means a mistake or a successfully-manipulated agent has your full local user permissions to work with.
terminal:
backend: "docker" # isolates execution from your host, per Module 5
Layer 2: Approval Gates (What Requires Confirmation)
For actions that are hard to undo, configure explicit human confirmation before execution:
# ~/.hermes/config.yaml
agent:
require_approval:
- tool: "send_email"
- tool: "execute_shell"
match: "rm -rf*" # pattern-match specific dangerous invocations
- tool: "*"
when: "platform == 'discord-public'" # gate everything on a low-trust surface
In an interactive session, this surfaces as a confirmation prompt before the action runs:
Hermes wants to run:
rm -rf ./build
Approve? [y/N]
Layer 3: Human-in-the-Loop for Unattended Contexts
Here’s the tension worth naming directly: approval gates assume someone is present to respond. A cron job (Lesson 17) running every 15 minutes with nobody watching cannot meaningfully wait on a y/N prompt. For unattended contexts, the real guardrail has to live upstream, in Lesson 19’s curation, not in a gate that will just hang or auto-deny.
cron:
- name: "error-rate-alert"
schedule: "*/15 * * * *"
prompt: "Check the error dashboard; report if error rate exceeds 2%"
# This job's toolset is scoped to read-only monitoring tools.
# It structurally cannot reach an approval-gated action,
# because it was never given the tools that would trigger one.
platform_toolsets:
cli: ["web_search"]
The design principle: for interactive, human-present surfaces, use approval gates for risky actions. For unattended surfaces, use tight toolset curation so the job never needs to ask.
Which Actions Almost Always Deserve a Gate
A practical heuristic, regardless of how trusted the surface otherwise is:
| Category | Example | Why it deserves a gate |
|---|---|---|
| Externally visible | Sending an email, posting a message, making an API call a third party sees | A mistaken send is attributed to you and often can’t be un-sent |
| Financial | Making a payment, changing a subscription | Direct, sometimes irreversible cost |
| Destructive | Deleting files, dropping a database, force-pushing | Often unrecoverable without a backup |
| Read-only lookups | Searching the web, reading a dashboard | Low stakes: a bad read is cheap to notice and correct |
The asymmetry is the point: a wrong search result costs you a moment of confusion. A wrongly sent email or deleted file may cost you something you cannot get back. Gate the second category even on surfaces you otherwise trust.
Exercise: for your current deployment, list every enabled tool from
hermes toolsand classify each using the table above. Add arequire_approvalentry for at least one destructive or externally-visible action, then trigger it deliberately and confirm the approval prompt actually appears before it executes.