Playbooks¶
A playbook is a Markdown file that tells the analyst how to investigate a specific category of threat. The agent loads the playbook based on ThreatEvent.category, prepends it to the system prompt, and follows it.
Where they live¶
p3guardian/ai/playbooks/
├── default.md ← fallback when no specific playbook matches
├── honeypot.md
├── brute_force.md
├── baseline_new_setuid.md
└── security_updates.md
Adding a new playbook is two steps:
- Drop a new
.mdfile in this directory. - Update the routing in
analyst.py::_load_playbook()if your category name doesn't auto-map.
That's it. No restart required for new playbooks (loaded on each investigation), but a service reload is needed if you change the routing logic.
Anatomy of a playbook¶
Every playbook has the same structure:
# <Category> playbook
<one-paragraph description of when this fires>
## Procedura
1. <step 1: what to extract from the event>
2. <step 2: what tools to call, in what order>
3. <step 3: what cross-checks to do>
## Decisione
- **confirmed_threat (confidence X-Y):** <when this verdict applies>
- **false_positive (confidence X-Y):** <when this verdict applies>
- **inconclusive:** <fallback>
## IOC da emettere
- <which IOC types and how to fill them>
## Auto-actions
<what the agent may execute autonomously, or NESSUNA>
The agent doesn't parse the playbook — it just gets it as part of the system prompt. The structure exists for human readability.
Built-in playbooks¶
honeypot.md¶
Fires on category=honeypot_hit. Walks the agent through:
- GeoIP lookup of attacker IP
- Search honeypot.log for additional hits in the last 24h (recidivist?)
- Cross-check auth.log for SSH attempts from same IP
- Decision: confirmed_threat with auto_action=ban_ip if ≥ 2 hits or correlated SSH activity
brute_force.md¶
Fires on category=ssh_brute_force, failed_password_invalid_user, max_auth_exceeded. The agent:
- Counts attempts in last 24h, lists targeted usernames
- Critical check: any
Acceptedentry for that IP? If yes → CONFIRMED COMPROMISE → CRITICAL - Otherwise: bans IP, recommends key rotation if usernames look targeted
baseline_new_setuid.md¶
Fires on category=baseline_new_setuid. The agent:
statandsha256sumthe new binarydpkg -Sto check if it belongs to a known package- Cross-check
dpkg.log±10 min around the file's mtime for a corresponding install - No auto-action — setuid changes need human review (a wrong
chmod -son/usr/bin/sudolocks you out)
security_updates.md¶
Fires on category=security_updates_pending. The agent:
- Lists pending packages, weights by criticality (kernel/openssl/openssh = high concern)
- Extracts CVE IDs from
apt changelogfor top packages - Checks
unattended-upgradesstatus and/var/run/reboot-required - No auto-action — patching is operational
default.md¶
Catch-all. Generic procedure: extract IOC, run geoip, search logs for correlations, pick a verdict. Used when no specific playbook matches the event category.
Writing a new playbook¶
A good playbook is short (200-500 words), opinionated, and tells the agent what to look for rather than just listing tools. The agent already knows the tools — it needs the priorities.
Bad:
- Run
run_commandto look at logs.- Make a decision.
Good:
- Extract the source IP.
- Check
/var/log/auth.logfor prior SSH activity from this IP — especially successful logins.- If ANY successful login exists from this IP in the last 7 days, severity is CRITICAL — possible compromise. Recommend key rotation, do NOT auto-ban yet (the operator may need to investigate the session).
- Otherwise, follow standard ban procedure.
Notice the second one tells the agent why (priority signal: prior auth success), and when not to act (don't auto-ban if there might be a real session in progress).
Internationalization¶
Playbooks are language-agnostic — write them in whatever your team thinks in. Today they're a mix of Italian and English. The LLM handles both fluently. The summary and recommended_actions fields in the final report inherit the language of the playbook.