Table of contents
How it flows
Agents dial out. An enrolled agent holds one connection to the controller and never listens: the controller cannot dial a machine, and a machine behind NAT or a deny-all firewall enrols like any other. On that connection the controller pushes what changed the moment it changes; every thirty seconds the agent also syncs — uploading what it decided, presenting the revision it holds and its cursors — so a connection that cannot be held costs latency, never correctness. Every cursor is the agent's own, persisted after it acted; a lost message is asked for again and nothing is applied twice.
Configuration goes down. The controller assembles each machine's
tree from the defaults and its declaration, at the agent's version, and
sends it when the revision differs. The agent writes it whole under
/var/lib/shieldlist/received/ and applies it in place — no restart;
the rules' windows start over, sanctions and the kernel's sets do not
move. A tree the agent's loader refuses is kept for inspection, the
previous one keeps running, and both sides say so.
Sanctions go up, then around — the pack is the unit of sharing.
Every ban an agent decides is recorded — rule, the threshold that
fired, hits, report state — and, when the machine offers its bans,
handed to the machines that enforce and run a pack holding the ban's
rule. Three nameservers on the bind pack apply each other's DNS bans;
a web server on other packs never sees them. A rule outside every pack
never travels, and share: no on a rule keeps its bans home whatever
the packs say. Each receiver applies a ban after its own checks: never
the machine itself, never an exempted address; an active ban is
extended, not duplicated — and shield why on the receiver names the
origin (fleet:<machine>/<rule>). A ban unbanned or lifted at its
origin takes the distributed copies down with it. A machine may sit
outside all of this (fleet: {offer: no, enforce: no}) and still be
seen and managed.
Lifts come down. A lift is a controller decision — console, API,
shield unban — recorded per machine and applied by the agent within
seconds, like a local shield unban. lift: operator on a machine
means only the operator may lift its bans, whatever the owner model
grants elsewhere.
Who reports. Per machine: the agent (its own AbuseIPDB key), the controller (next release), or nobody. An agent told that someone else reports stands its sender down and lets those sanctions pass: a report has one chance, where the controller said.
Versions both ways. Agent and controller exchange versions on every request; the controller advertises the oldest agent it serves, the agent the oldest controller it works with, and a pair outside either range is refused with a message naming both and which side is behind — on both sides. The controller never sends a key an agent's version would refuse: it flattens levels for an older agent, and refuses by name what cannot be flattened.
When the controller is away. The agent says so once, keeps enforcing on the last configuration it received, keeps deciding locally, queues what it decided, and says once more when the controller is back.