Import lists
Subscribe to a feed of domains — from the purge.cc catalog or from any URL you trust — then let each profile decide what to do with it.
What this step does
A list is a feed of domains. It carries no allow/deny nature of its own beyond a default: base on its [[blocklists]] row, which is deny unless you say otherwise. What actually happens to a list’s domains is decided per profile — profiles.<id>.lists maps a list id to deny, allow or ignore, and that override wins. With no override, the profile inherits base. One function answers the question for a (profile, list) pair: src/config/schema/blocklist.rs → effective_direction().
So a list is not an “allowlist” or a “blocklist”. It is a list, and the profile decides its nature. This page subscribes you to one, then points a profile at it.
Add a list
Browse the catalog
warden lists catalog
warden lists catalog --scope privacyThe four scopes are privacy, security, content and services. Rows already in your config are marked [active]:
purge.cc list catalog (5 lists, offline — using built-in data) privacy (5 lists): privacy/ads ads [active] privacy/devices devices privacy/general general privacy/mobile mobile privacy/tracking tracking [active] usage: warden lists add <list-id>
With network access the catalog is fetched live and each row carries a domain count. Because warden lists add resolves slugs offline, a list newer than your binary is marked [newer than this build] and printed with the URL to use instead.
Add one from the catalog
warden lists add privacy/adsadded: privacy/ads (list "privacy-ads") run `warden blocklist show privacy-ads` to check what it filters for
The id is the slug with its slash turned into a dash, so privacy/ads becomes privacy-ads — the same id warden init writes for the lists it subscribes you to, and the id you will type everywhere else.
Add any URL
warden lists add https://lists.example.org/ads.txtThe id is derived from the host and the file name — https://lists.example.org/ads.txt becomes lists-example-org-ads. Both halves are kept because either alone collides too easily. Any port and userinfo are dropped, anything outside a-z0-9- collapses to a dash, and the result is capped at 64 characters.
Two things can come back instead of a new subscription:
The URL is already subscribed. Not an error — re-running an install script should be quiet:
already subscribed: https://lists.example.org/ads.txt (list "lists-example-org-ads")
The derived id is taken by a different list. Two URLs on one host can genuinely reduce to one id, so this is refused rather than silently reported as success:
"https://lists.example.org/trackers/list.txt" would be named "lists-example-org-list", which is already taken by a different list (https://lists.example.org/ads/list.txt). Add it with a name of your own: warden blocklist add <name> --url https://lists.example.org/trackers/list.txt
Do what it says: run warden blocklist add and choose the id yourself.
Which URLs are refused
warden lists add, warden blocklist add, warden blocklist set url and the TUI form refuse a source that is not safe to fetch: plain http://, a private or loopback address, or a URL with a user name or password in it. Use https:// with a public host. For a file on this machine, use warden blocklist import-local instead; the error message says so.
What is inside a list file
List entries are plain ASCII domains. For an internationalised name, a list entry must be in xn-- form, for example xn--mnchen-3ya.de for münchen.de. An entry written in Unicode is dropped. No [[blocklists]] list can use @@, wildcards, regex or $important, whether it comes from a URL or from import-local: those lines are skipped. Write them in a Custom List.
What happens next
The list starts filtering, and there is no second step. Nothing was written into any profile — warden lists add does not attach the list to your default profile or to any other. It filters because base on the new row defaults to deny, and a profile with no override for that list inherits base.
One older channel behaves differently. If a subscription is recorded in the [lists].sources array — the form this verb used to write — it downloads on schedule and filters nothing, because no profile can reach it. warden lists add says so when it finds one, and warden lists list flags them under inactive entries in [lists].sources. Convert one by removing it and adding it again:
warden lists remove https://lists.example.org/ads.txt
warden lists add https://lists.example.org/ads.txtLet the profile decide
Override base for one profile, ask what is in force, or drop the override again:
warden profile list-policy show kids
warden profile list-policy set kids privacy-ads allow
warden profile list-policy clear kids privacy-adsThe same thing in the config file:
[profiles.kids]
lists = { privacy-ads = "allow" }clear and set … ignore are not the same move:
| You want | Use | What it means later |
|---|---|---|
| This profile never uses this list | set <profile> <list> ignore | A standing declaration. It survives a change to base. |
| This profile follows the list’s own default | clear <profile> <list> | The key is dropped. The pair follows base wherever it goes. |
set and clear both go over the daemon socket, never by direct file edit, and the daemon re-validates the whole config before the write lands. So a typo is refused before anything reaches disk — and a two-list change with one bad id writes neither.
A profile naming a list id that no [[blocklists]] row declares is an error, not a warning. Over the socket it is refused before the write; in a hand-edited file it fails at load:
profile "kids" names blocklist "privacy-ad" in its list policy, but no [[blocklists]] entry with that id is configured. Run `warden blocklist list` to see what is. profile "kids" sets lists.privacy-ad but no [[blocklists]] entry has id "privacy-ad" suggestion: add a [[blocklists]] entry with that id, or remove the entry from this profile's `lists`
Pointing a profile at a list with allow means that feed can unblock domains on your network. If the list is not trust = "local", Warden refuses the allow until you declare on the list that you accept that: accept_unsigned_allow = true on its [[blocklists]] row, or --accept-unsigned-allow on warden blocklist add, set-kind or set-trust. A profile override cannot declare it; the refusal suggests warden blocklist set-trust <list> remote-unsigned --accept-unsigned-allow. Whoever publishes that feed can add a domain to it tomorrow and it will be permitted here.
deny and ignore are not gated: both narrow what a profile permits, so there is nothing to declare.
Import a local file
For a list you wrote yourself, or one you downloaded and want to keep frozen:
warden blocklist import-local ./office-deny.txt --id office-deny --kind denyImported '/etc/purge-warden/lists/office-deny.txt' as blocklist 'office-deny' (kind=deny, 412 entries).
The file is copied into the directory Warden manages for lists — lists/<id>.txt beside your config, so /etc/purge-warden/lists/office-deny.txt on a fresh install (/var/lib/purge-warden/lists/office-deny.txt on a host that still keeps its config there) — and registered with trust = "local". The format is detected from the content. --kind accepts deny, allow or ignore here. Because the result is local, an allow import needs no acknowledgement.
Editing the copy afterwards means editing the copy, not your original.
warden lists or warden blocklist?
Both families write the same object. warden lists add calls the same writer warden blocklist add does, so a list added either way is one [[blocklists]] row with the same validation and the same audit record.
warden lists … | warden blocklist … | |
|---|---|---|
| What it is | The subscription front door | Direct CRUD on [[blocklists]] rows |
| You name a list by | Catalog slug or URL | Its id |
| The id is | Derived for you | Yours to choose |
| Also covers | The catalog, refresh, the download cache | set-kind, set-trust, import-local |
| Reachability probe on add | Skipped, so a server that is down today does not block the subscription | Runs, unless --skip-head-check |
Note that the flag is spelled --kind while the field it writes is base. They are the same thing: the list’s own default direction.
Memory and list size
Warden holds every subscribed domain in memory. Memory grows with the number of unique domains, not with the number of lists: a domain on two lists counts once. warden status prints that number on its domains: line, and warden lists catalog shows each list’s count when it can fetch the catalog.
Measured on Warden 0.44.5 on 2026-10-24, on an x86_64 VM with 2 vCPU and 2.7 GB of RAM running Ubuntu 26.04:
| List set | Domains | Steady | Peak at a rebuild |
|---|---|---|---|
The default set: security/malicious, privacy/ads, privacy/tracking | 15.3 million | about 700 MB | about 1.4 GB |
privacy/ads alone | 7.1 million | about 330 MB | about 700 MB |
- Steady is the daemon’s resident memory once the lists are loaded: about 25 MB plus 45 MB per million domains. The first start and a restart stay close to it.
- Peak at a rebuild is a little over twice the steady figure, for a few seconds. A rebuild runs when a refresh finds a changed list, and at a reload that changes the list set or how any profile uses a list. A refresh that finds no change does not rebuild. Right after a rebuild, memory can sit about 20% above the steady figure.
Size the host for the peak, plus what the system and other services use. A host with less memory can install Warden and run for days, then lose the daemon to the kernel’s out-of-memory killer at the first list update. To size another set, add up the catalog counts of its lists and apply the same rule. Lists overlap, so the sum is an upper bound. On a machine with 1 GB of RAM, such as a small Raspberry Pi, even privacy/ads alone leaves little room at a rebuild: choose smaller lists.
The figures depend on the build and the host, so treat them as a guide. To check your own host:
warden status | grep domains:
grep -E 'VmRSS|VmHWM' /proc/$(systemctl show -p MainPID --value purge-warden)/statusVmRSS is the memory in use now. VmHWM is the highest value since the daemon started, so it shows the peak of the last rebuild.
CLI reference
| Verb | What it does |
|---|---|
warden lists catalog [--scope <s>] | Browse the purge.cc catalog, optionally one scope |
warden lists add <slug|url> | Subscribe. Derives the id, writes the [[blocklists]] row |
warden lists list | Every configured source, plus any inactive legacy entry |
warden lists refresh | Re-download every list now, and rebuild the filter if a list changed |
warden lists remove <slug|url|id> | Unsubscribe, whichever channel the entry lives in. It does not reload: run sudo systemctl reload purge-warden. warden lists refresh keeps downloading the removed list |
warden lists forget <slug|url> | Drop the cached body and headers; config untouched |
warden blocklist show <id> | One list in full, including which profiles enforce it |
warden blocklist add <id> --url <u> | Register a subscription with an id and flags you choose |
warden blocklist import-local <path> --id <id> --kind <deny|allow|ignore> | Copy a local file in and register it as trust = "local" |
warden blocklist set-kind <id> <deny|allow|ignore> | Change the list’s base |
warden profile list-policy set <profile> <list> <deny|allow|ignore> | Override base for one profile |
warden profile list-policy clear <profile> <list> | Drop the override; follow base again |
warden profile list-policy show <profile> | What is in force for every list, and where it came from |
In the TUI
Launch with warden dashboard, press 4 for the Filters section, then g i for the Lists leaf.
The leaf answers the same question warden blocklist show <id> does: for each list, the direction actually in force per profile, asked of effective_direction() rather than recomputed. If a list reaches nobody, both surfaces say so.
See also
- Profiles — the canonical page for the entity that decides a list’s direction.
- Create profiles — the next step: build the profiles whose policy you just set.
- Custom rules — for domains you write yourself, with the full rule grammar, instead of subscribing to a feed.
- Initialize — the three lists a fresh install already subscribes to.
- Principles — why the whole list set lives in memory.