The whole-mesh dry-run found fail2ban unassignable on every node: it declared
`capabilities: ["intrusion-prevention"]`, which mesh-host has no detector for
(its detectors are container-runtime, package-manager, service-manager,
firewall, overlay, graphical-session, seat, privileged). intrusion-prevention
is what fail2ban PROVIDES, not a host capability it needs. It bans via
iptables/ufw, so it needs `firewall` — the same capability the firewall module
declares. The `the-intrusion-prevention` claim (node-exclusive) is unchanged.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
- bookshelf: Servarr v1 fork on the radarr template (4 tools).
- unifi: portainer-shaped tooled app (7 tools, 9 ports), settings-merged config.
- fail2ban: host-level security module mirroring firewall (service + restart-on,
no container); ban actions preserved as source ufw/iptables and FLAGGED to be
rewritten nftables-native before it actually bans.
- marrytts: manifest-only plain container (no tools), like resolv-conf.
All typecheck against the built @novox/mesh-sdk; service images digest-pinned.
Held from merge pending the hq initialization reconciliation.
Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF