
The story of Security — from ai-jail to the watchdog that hunts leaks on its own
The day the secrets almost leaked
At the end of July, Hermes ran with full shell access. A well-crafted prompt injection and the agent’s tokens would be gone — along with .env, SSH keys, Cloudflare credentials. Security was a hope, not a layer.
Two months later, the story is different: the agent runs sandboxed (ai-jail with bwrap + landlock), all 4 projects go through automatic agentic scanning (ZAP, gitleaks, bandit, opengrep), and a watchdog monitors secret leaks without anyone looking. And the ecosystem has 2 security posts — which tell parts, but not the whole story. Until now.
Context — the problem of a 7-project ecosystem
Security in a personal ecosystem is treacherous: no CISO, no audit, no “security team”. There are 7 projects (Arachne, Dogwalk, Capivara, Portifólio, TatuEngine, LifeLog, Ajudante), each with its own database, deploy and secrets — and a single agent with shell power.
Three problem fronts:
- The agent itself — full shell access = one prompt injection away from token exfiltration.
- The code — unlocked database, route delivering too much data, exposed key in the repo, unvalidated input.
- Surveillance — what nobody looks at, nobody fixes. A vulnerability found and not documented is a vulnerability that comes back.
The struggle — three hardening phases
Phase 1: the ai-jail (07/28) — the agent’s sandbox
The first response was isolating the agent. ai-jail uses bwrap (bubblewrap) + landlock to run commands in a sandbox that masks secrets and hides sensitive directories:
# ~/.ai-jail — the most important part:
mask = [".env", "credentials.json", "*.pem", "id_ed25519*", "id_rsa*"]
hide_dotdirs = [".hermes"] # Hermes tokens protected
deny_paths = ["secrets/", "*.key", ".gnupg/"]
The fine detail: hide_dotdirs = [".hermes"] — hiding Hermes’ entire directory inside the sandbox. Neither the agent nor a prompt injection can read the tokens. Then came phase 2 in practice (08/10): the sandbox learned from our mistakes — new masks, deny_paths, and TatuEngine took the concept to code with a Hybrid Sandbox with 51/51 tests.
Phase 2: active hunting (08/11) — agentic scan in every project
A sandbox protects the agent, but not the code. The second phase was actively hunting vulnerabilities in every project, with agentic scanning:
- ZAP — web security scanner (OWASP ZAP) against the apps
- gitleaks — hunts leaked secrets in git history
- bandit — static security analysis for Python
- opengrep — insecure pattern analysis across multiple languages
The hunt found what nobody was looking for: unlocked database, browser permission too broad, route delivering data without checks, exposed key, unvalidated input. Every finding became a fix — and a standard: every change goes through a scan before deploy.
Phase 3: the watchdog (always on) — surveillance that never sleeps
The third phase was the most important: turning surveillance into a continuous process, not a campaign. The security-watchdog.py monitors the points nobody looks at and alerts the Notifications channel when something changes:
- secrets appearing where they shouldn’t
- health checks failing without explanation
- processes that should be running but aren’t
- unusual access patterns
The golden rule: crons only notify when there’s a problem — a silent system is a healthy system. The watchdog doesn’t spam the group; it waits for a problem to happen to scream.
Resolution — the rules born from the struggle
- Agent security is a layer, not a feature — sandbox, mask and hide_dotdirs protect against the worst case (prompt injection), not against the user. Design for the adversary.
- Scan before deploy, always — ZAP + gitleaks + bandit + opengrep run in CI before any delivery. Check failed = no delivery.
- Surveillance is a process, not a campaign — a 24/7 watchdog beats a weekend audit.
- Fixes apply to ALL accounts — a security fix found in my project applies to Douglas, Juliana, any ecosystem user.
Metrics
| Phase | Tool | Result |
|---|---|---|
| Sandbox | ai-jail (bwrap + landlock) | Agent isolated, tokens masked |
| Sandbox in code | TatuEngine Hybrid Sandbox | 51/51 tests |
| Static scan | gitleaks + bandit + opengrep | Secrets hunted in history, audited code |
| Web scan | ZAP | Apps scanned against OWASP |
| Surveillance | security-watchdog.py | 24/7 monitoring with Notifications alert |
Learnings
- Security is not a feature, it’s a layer — you can’t “add it later”; it has to be in the foundation of the agent and the deploy.
- The agent is the target — whoever has shell power needs the worst case in mind: prompt injection, not user error.
- What nobody looks at breaks — active hunting found real vulnerabilities no functional test would catch.
- Documenting the threat is half the defense — every documented finding became a rule protecting all projects.