Files
smallwebwaf/docs/state-files.md
T
sneak 7b523720e8
check / check (push) Waiting to run
Caches of outside APIs live in /var/cache/sww (closes #314)
The answers of GeoJS, the lists fetched by URL, the DNS blocklists,
AbuseIPDB and routewatch each go in a cache file in SWW_CACHE_DIR
(default /var/cache/sww): lookups.json, lists.json, dnsbl.json,
abuseipdb-scores.json and routewatch.json. reputation.json is gone. The
AbuseIPDB checks spent today are a count, not a cache, and stay in
SWW_STATE_DIR as abuseipdb-checks-spent.json.

A cache file never stops the start: one that does not parse is set
aside as an edit is, one that cannot be read is written over, and its
cache starts empty. An unwritable SWW_CACHE_DIR stops the start, as an
unwritable SWW_STATE_DIR does. Each directory is watched on its own.
The run script makes the cache directory and gives it to sww.

Model: opus-5-5
2026-11-11 07:56:58 +00:00

5.4 KiB

State and cache files

The JSON files waf keeps, and what each holds. "Persistent state" in SPEC.md says how they are written, read and edited. Each has a top-level version of 1.

  • The state files are in SWW_STATE_DIR, /var/lib/sww by default. They hold what cannot be fetched again, so back them up:
    • bans.json;
    • clients.json;
    • abuseipdb-checks-spent.json;
    • alerts.json.
  • The cache files are in SWW_CACHE_DIR, /var/cache/sww by default. They hold answers from outside services, which are asked again when a file is lost, so the directory can be left out of backups:
    • lookups.json;
    • lists.json;
    • dnsbl.json;
    • abuseipdb-scores.json;
    • routewatch.json.

bans.json

Active and past bans: every ban an admin made, and up to SWW_MAX_BANS of those waf made. Per entry:

  • netblock, start and expires (null for a permanent ban);
  • cause: attack, limit, admin or crowdsec;
  • reason: a short reason;
  • lifted: when an admin lifted it, if one did;
  • notes: the notes below.

An entry an admin adds needs netblock, start and expires; reason, notes and cause may be left out, and the cause is then admin:

{
    "version": 1,
    "bans": [
        {
            "netblock": "203.0.113.0/24",
            "start": "2026-10-06T12:00:00Z",
            "expires": null,
            "reason": "probes for logins"
        }
    ]
}

"Bans" in SPEC.md says how an edit lifts, keeps or forgets a ban. The notes:

  • asn, as_name and country: the client's, added when the lookup answers (empty when lookups are off);
  • for a broken limit, kind (requests, bytes or refusals), limit, window and count: what the limit was on, the limit, its window and the count reached;
  • limit_percent and limit_percent_setting: the client's percentage of that limit and the setting that gave it, when a setting lowered it;
  • for a clear sign of attack, rule_id and target, or trap_path;
  • reputation: the sources the client was on, each with its source and, for AbuseIPDB, its score;
  • request: the request that caused the ban, each text cut to 256 bytes:
    • time, method and host;
    • path, with its query string;
    • status and user_agent;
  • requests and refused: the netblock's requests since it was first seen, and how many this ban has refused so far, kept up to date while the ban lasts;
  • earlier_bans: how many earlier bans the netblock had, by cause.

clients.json

Per client, one per line: its minute, hour and day counters, and its history since it was first seen:

  • the first and last time seen;
  • the AS number, AS name and country last looked up, and when;
  • total requests, and how many were forwarded and how many refused;
  • body bytes of its requests, and of the responses it was sent;
  • responses by status class;
  • offences by kind: limit, attack, blocked_by_block_rule and blocked_by_core_rule_set. A file may also hold token_refused, which is kept, and which nothing adds to.

abuseipdb-checks-spent.json

The AbuseIPDB checks spent today, so that a restart does not spend the daily budget again:

  • day: the day, in UTC;
  • spent: the checks spent on it.

alerts.json

  • the anomaly counters;
  • the alert cooldowns: for each event and netblock, source, file or anomaly scope, when it was last sent and the count of repeats held back since;
  • the alerts sent and held back in the current hour;
  • the alerts waiting for each destination.

lookups.json

A cache file. GeoJS answers, one per line:

  • client;
  • asn, as_name and country;
  • answered: when GeoJS answered;
  • used: when the answer was last used.

lists.json

A cache file. The last good copy of each list fetched from a URL, with when it was last tried:

  • url;
  • tried: when it was last fetched or tried;
  • fetched and lines: when the last good copy was fetched, and its lines, comment lines included.

dnsbl.json

A cache file. Each DNS blocklist verdict still in use, one per line:

  • zone and client;
  • listed: whether the zone lists the client;
  • fetched: when the zone answered.

abuseipdb-scores.json

A cache file. AbuseIPDB's scores still in use, one per line:

  • client;
  • score: from 0 to 100;
  • fetched: when AbuseIPDB answered.

routewatch.json

A cache file. routewatch's answers from the last hour, one per line:

  • client;
  • netblock: the netblock the client's address is announced in, empty for an address without a live route;
  • asn: the number of the AS that originates the netblock, 0 for none;
  • answered: when routewatch answered.

Sizes

  • clients.json: about 1 KiB per client, about 20 MiB at the default SWW_MAX_TRACKED_CLIENTS. Written every 15 minutes, that is under 2 GiB of disk writes a day.
  • bans.json: about 1.2 KiB per ban, at most about 2.5 KiB, so about 6 MiB at the default SWW_MAX_BANS. Written every 15 minutes, a full file comes to about 600 MiB of writes a day, and a hundred new bans a day add about 600 MiB more.
  • lookups.json: about 150 bytes per answer, under 1.5 GiB of writes a day when full.
  • routewatch.json: about 120 bytes per answer, at most about 12 MiB.
  • abuseipdb-checks-spent.json: a few bytes.
  • lists.json, dnsbl.json, abuseipdb-scores.json and alerts.json: usually a few MiB or less.