• v0.0.3 8c441d5a51

    v0.0.3 Pre-release

    Robin released this 2026-08-15 16:07:48 +02:00 | 121 commits to main since this release

    Shieldlist agent v0.0.3 — the reporting release. The agent can now tell the world about the offenders it bans, watch itself do it, and post its decisions to your own endpoints, while several sharp edges found by running it armed on a production host were filed off.

    Abuse reporting (AbuseIPDB)

    • The agent submits the offenders it bans to AbuseIPDB under your API key. Off unless you arm it: reporting.provider in agent.yaml is the only switch, the key and your daily_limit live in secrets.yaml (0600), and what each report says is a preset file you own — reports/*.yaml, referenced by a rule's report_as:. An agent in detect-only never reports anybody.
    • Redaction is default-deny: a preset's include: list is the only way a parsed field can leave the machine, the comment is composed the moment the ban is decided and stored with it (shield why shows the exact text), and the validator refuses a preset naming a field its rule never captures. A comment describes the offender's behaviour — one sample per included field with a distinct count — and never your thresholds, your server's configuration, or anyone else's usernames and mailboxes.
    • A report gets one chance, at the moment of the detection, and it leaves on the ban's heels: the decision loop nudges the sender as soon as a round's bans are durable. Whatever stops a report — allowance spent, provider down, key refused — the reason is one log line and the sanction stays on record, still owed. Nothing is retried on a cadence and no backlog drains by itself.
    • shield report -last 6h sends the past on purpose: it counts what is owed in the window you name, shows the estimate, asks, then submits freshest first at full speed, stopping cleanly when the provider says the allowance is spent. The allowance is watched from both ends — your daily_limit binds absolutely, the provider's rate-limit headers are read on every response, the reset is midnight UTC, and a bounded probe notices an upgraded plan or another machine freeing room.
    • Addresses that are not publicly routable are never submitted, and the machine's own addresses are never offenders in the first place (see below).

    Outbound hooks

    • hooks: in agent.yaml POSTs one JSON document per enforced ban to any URL you name — a chat relay, a SIEM, your own dashboard — carrying the ban's facts and the same default-deny comment a report would, never a raw log line. Hooks are local configuration by invariant: a controller can never push one. One delivery attempt each, right behind the ban, outcome logged, nothing in detect-only.

    Watching it: shield tail, and a store that counts returns

    • shield tail follows decisions live, each with the log lines that convicted it, plus again lines when a sanctioned offender keeps coming back, reported lines when the report leaves (with the provider's abuse score), and the provider's own words when it refuses. One terminal shows the whole story.
    • Sanctions now carry hits and last seen (schema 11, migrated automatically): every absorbed return is counted, evidenced (bounded per sanction), and said — an address that keeps coming back is visible coming back. shield bans grows HITS, LAST and REPORTED columns; shield why tells each sanction's full story including its report's fate; last seen is the log's clock, not the verdict's.
    • shield status answers more without costing more: reporting counters (owed, sent, duplicate, refused) and the exemption count in the default output, published by the agent's own aggregates pass — plus every loaded rule listed, the zero-ban ones included, because a rule that never fires should be visibly at zero, not absent. Reports piling past ten thousand owed is one edge-triggered WARN.

    The machine itself is never an offender

    • Loopback and the machine's own interface addresses cannot be sanctioned by any rule, built into the core rather than left to an exemption file — no configuration of this program can firewall the machine it runs on. Loopback events are dropped at evaluation, before any state exists: a local service authenticating against another (a webmail against its IMAP server) is plumbing, not an attack, and the real client is in the web application's own log.

    Faster list parsers

    • prefilter_any: — a parser may now gate on a list of tokens instead of a single literal, answered by a multi-literal automaton in under a microsecond. This is what makes big list parsers affordable: a 683-entry User-Agent list that cost ~920µs per line ungated now costs ~1µs on the lines it ignores, a measured ~3300× improvement. Exactly one gate per parser, and the gate only ever admits lines the pattern then judges.

    Also

    • Log lines about reports read like the decision lines they follow — rule, action, bare copyable address — and every egress outcome is one line with the provider's exact words where they matter.
    • The systemd-less quick start, examples and wiki are updated; docs/CONFIG.md gains the full reporting, hooks and prefilter_any reference.

    Upgrading from v0.0.2: replace the binaries and restart; the store migrates itself (schema 9 → 11). The reporting block, presets and hooks are opt-in — nothing changes until you write them.

    Downloads