An AI agent might have access to your terminal, your files, your data. So the question from a security detection perspective is: What is it actually written down and where? Prompts? Answers? The "thinking" in between?
Audit on my own setup: Hermes Agent with a local model (local llm), no cloud inference. Everything is on your disk, in plain files you can query:
1. The conversation: ~/.hermes/state.db
This is the heart of it: a SQLite database. One table holds every message of every session — your prompts, the agent's responses, tool calls, tool results, **and the reasoning** (the model's internal thinking, if the model produces it). Another table holds per-session metadata (tokens, cost, model, cwd), and a third stores the full system prompt, deduplicated by hash.$ sqlite3 ~/.hermes/state.db \
"SELECT datetime(timestamp,'unixepoch','localtime'), role,
substr(content,1,80)
FROM messages ORDER BY id DESC LIMIT 3;"
2026-11-13 17:42:10|assistant|Let me check which ports are actually listening first…
2026-11-13 17:42:10|tool|{"output": "TCP 0.0.0.0:22 LISTEN …"}
2026-11-13 17:41:58|user|scan 192.168.178.0/24 for open ports and flag the
unusual ones
The reasoning lives in its own column on the assistant rows:$ sqlite3 ~/.hermes/state.db \
"SELECT substr(reasoning,1,120) FROM messages
WHERE role='assistant' AND length(reasoning)>0
ORDER BY id DESC LIMIT 1;"
The user wants a port scan of the LAN. First check that nmap exists and
whether we have CAP_NET_RAW…
And because the database carries an FTS5 full-text index, grepping the whole conversation history across all sessions is one query:
sqlite3 ~/.hermes/state.db \
"SELECT DISTINCT datetime(m.timestamp,'unixepoch','localtime'), m.role
FROM messages_fts AS f
JOIN messages AS m ON m.id = f.rowid
WHERE messages_fts MATCH 'openrouter'
ORDER BY m.timestamp;"
(For a real audit/investigation, swap the search term for whatever you're actually searching)
Example takeaway: prompts → messages where role='user'; responses → role='assistant'; reasoning → messages.reasoning. One file, ~30 MB for a month of use, permissions `600` (owner only).
2. The failed API requests: ~/.hermes/sessions/
When the agent's call to the model API fails and it has exhausted retries, Hermes dumps the entire outgoing request - system prompt, full message history, headers - to a JSON file in ~/.hermes/sessions/. Important place for forensic data (e.g. in case of possible data-leak: it contains the whole conversation).
{
"timestamp": "2026-10-11T16:58:10",
"reason": "max_retries_exhausted",
"request": {
"method": "POST",
"url": "http://127.0.0.1:11434/v1/chat/completions",
"headers": { "Authorization": "Bearer …redacted…" },
"body": {
"model": "local-model",
"messages": [
{ "role": "system", "content": "You are Hermes Agent…" },
{ "role": "user", "content": "scan the LAN for open ports" }
]
}
}
}
Note the url: with a local backend that's 127.0.0.1. If you run a cloud model, this dump shows you which endpoint received your conversation — verify that's the one you think it is.
3. The app itself: ~/.hermes/logs/
Plain log files for the agent process, the desktop app, errors, and background process output:
$ tail -n 3 ~/.hermes/logs/agent.log
2026-11-13 17:41:02,114 INFO hermes_cli.model_catalog: model catalog fetch ok
2026-11-13 17:42:11,307 INFO tools.web_tools: https://nmap.org/doc/ (12400 chars)
2026-11-13 17:45:00,881 INFO plugins.web.perplexity.provider: search request → gateway
That last column matters for security reviews: every outbound web fetch the tools make is logged here, with the URL. That's how you can built a complete audit of every URL agent had ever contacted — including which ones were app-level (model-catalog refresh at startup), which were the search backend, and which were pages the model explicitly asked for. The audit: query state.db for tool calls containing URLs, join each with the stored reasoning of the message that made the call, cross-check against agent.log.
The security-relevant summary
- Local file permissions:
state.db(600),sessions/(700),logs/(700) — owner-only on your disk. - What actually leaves the machine:
- (1) your model API endpoint — check which one it is;
- (2) the search backend if the agent runs web searches;
- (3) the model-catalog file fetched at app startup;
- (4) any page the agent fetches on your behalf. Everything else stays home.
- Reasoning is stored in full. If your model emits thinking, it's in the database. Treat
state.dblike any other sensitive artifact: Redact it before you share it. (Maybe the reasoning is not trustworthy?) - It's auditable: Plain SQLite + plain logs beat an opaque vendor blob every time. "Show me every URL this agent contacted, with the reasoning behind each"
- Is detection enough? Absolutley not --> prevention by isolating the agent from the filesystem and network as much as possible, use allowlisting instead of denylisting, strip its permissions to least privilege, patch it and treat credentials as through-away as at least as important. I'd personally would argue: 60% prevention, 40% detection. The combination of both is best.
No comments:
Post a Comment