EN

Initialize

Under review — wording may still change.

Turn a bare binary into a running scaffold — system user, directories, and a first config.toml

What this step does

warden init provisions everything a fresh install needs before it can start: the purge-warden system user, the state directories, and a first config.toml. It requires root, and refuses outright if it isn’t: warden init requires root. Run with sudo.

It also seeds three blocklist subscriptions and turns filtering on immediately — a list’s base defaults to deny, and the profile init writes has no override, so it inherits base. No tags key is involved anywhere in the output; the tag mechanism that used to carry this was retired.

bash
sudo warden init

Interactive mode

With no --yes, init asks four questions in this order. Each accepts a bare Enter for its default:

#PromptDefault (Enter)
1Upstream resolverDetected from this machine’s /etc/resolv.conf / resolvectl status, plus any entries from an upstreams.toml menu next to the config. Type 0 to enter an address by hand.
2default_profile for unmapped sourcesdefault — type none to leave unmapped clients REFUSED instead.
3Blocklist subscriptionsThe three defaults — see The three seeded lists.
4Allowed client networks ([server].allow_from)RFC 1918 + loopback: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8.

Nothing writes upstreams.toml for you — create it by hand next to the config if you want a curated menu instead of the free-typed entry.

Non-interactive mode (--yes)

Scripted installs skip every prompt:

bash
sudo warden init --yes

--yes accepts the baked-in defaults for the profile (default), the three lists, and allow_from — but the upstream resolver still has to come from somewhere. With no --upstream flag, --yes adopts whatever this machine’s own resolver configuration already points at, and prints what it chose so an install transcript isn’t silent about it:

text
upstream: adopting 10.10.1.1:53 (detected on this machine)
warden never picks a resolver for you
There is no compiled-in default upstream — an earlier version shipped one, which meant every fresh install routed a household’s whole DNS stream to whichever provider warden chose, not the operator. So init detects what this machine already uses and offers it (interactively, as menu entry 1; under --yes, adopted automatically), and refuses rather than inventing a value if nothing usable is found. See Troubleshooting for both refusal messages.

What it writes

A fresh config.toml looks like this (list URLs and the admin token line omitted for space; see What init does not write below):

config.toml
toml
schema_version = 3

[server]
listen = "0.0.0.0:53"
allow_from = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8"]
log_level = "info"
default_profile = "default"

[upstream]
mode = "plain"
servers = ["10.10.1.1:53"]
timeout_ms = 5000

[lists]
sources = []
update_interval_secs = 43200

[[blocklists]]
id = "security-malicious"
display_name = "Security: malicious"
url = "https://lists.purge.cc/malicious.txt"

[[blocklists]]
id = "privacy-ads"
display_name = "Privacy: ads"
url = "https://lists.purge.cc/ads.txt"

[[blocklists]]
id = "privacy-tracking"
display_name = "Privacy: tracking"
url = "https://lists.purge.cc/tracking.txt"

[profiles.default]
display_name = "Default household profile"

[cache]
max_entries = 10000
# max_ttl_secs / min_ttl_secs / negative_ttl_secs also set, to sane defaults

[socket]
path = "/run/purge-warden/control.sock"

# [api]
# enabled = false  — commented out; enable and set a token to use the HTTP API

[lists].sources stays empty on purpose — subscriptions live as [[blocklists]] entities, not as slugs in that legacy channel.

The three seeded lists

--yes, or a bare Enter at the subscription prompt, subscribes these three catalog slugs. The id an entity renders as is the slug with / replaced by -:

Catalog slugEntity idURL
security/malicioussecurity-malicioushttps://lists.purge.cc/malicious.txt
privacy/adsprivacy-adshttps://lists.purge.cc/ads.txt
privacy/trackingprivacy-trackinghttps://lists.purge.cc/tracking.txt

Override the set with --lists <csv> of catalog slugs — raw URLs are refused (raw URLs are not accepted by warden init); add a URL-backed source after init with warden blocklist add <id> --url <url>. Browse the full catalog with warden lists catalog.

Filtering starts the moment the daemon starts, for all three: each [[blocklists]] row’s base defaults to deny, and [profiles.default] carries no override, so it inherits base. Nothing attaches a list to the profile — the direction comes entirely from that default.

The default-profile question

--yes hardcodes default_profile = "default". Interactively, a bare Enter also accepts default; typing none writes no [profiles.default] block at all and leaves default_profile commented out in [server] — which means a source that matches no device and no subnet is answered REFUSED, not filtered by some fallback profile.

What init does not write

Only commented examples — no live entries:

  • No [[devices]], no [[groups]], no [[subnets]].
  • No [[custom_lists]], no [custom_list_limits], and no packs/ directory on disk.

The commented [[devices]] example still shows a tags = ["mobile", "personal"] line — that’s a stale scaffold comment; Device has no tags field on current warden. Don’t uncomment it as written.

CLI reference

FlagArgumentMeaning
--yes(bool)Skip every prompt, accepting the baked-in defaults for profile, lists and allow_from.
--lists<slug>[,...]Comma-separated catalog slugs to subscribe, overriding the three defaults.
--force(bool)Overwrite an existing config at the target path; see Re-running init.
--upstream<addr:port>[,...]Comma-separated upstream resolvers, plain DNS. Wins over both detection and the prompt.
--upstream-catalog<path>Path to the upstreams.toml menu file. Defaults to upstreams.toml next to the target config.
--allow-from<cidr>[,...]Comma-separated CIDRs for [server].allow_from. Must be non-empty.
--listen<addr:port>Bind address for [server].listen. Defaults to 0.0.0.0:53.
--cluster-secondary(bool)Scaffold this node as a cluster secondary; requires --peer. See Cluster secondary.
--peer<url>Primary’s API base URL, e.g. https://10.10.1.94:8053. Only meaningful with --cluster-secondary.
--install-manpages(bool)Also generate manpages into /usr/local/share/man/man1/.
--man-dir<path>Target directory for --install-manpages.
--config<path>Root-level flag (precedes init, not after it) — moves the whole layout; see What it creates.

What it creates

With no --config, warden init provisions the historical layout:

WhatWhere
System userpurge-warden — no login shell, no home directory
State root/var/lib/purge-warden/
Downloaded lists/var/lib/purge-warden/lists/
Stats snapshots/var/lib/purge-warden/data/
Control socket + PID/run/purge-warden/
Master config/var/lib/purge-warden/config.toml, mode 0640 — written only if missing, or if you pass --force

Pass --config <path> and every one of those moves with it.

Re-running init (--force)

Without --force, init refuses before touching anything if the target config already exists:

text
config already exists: /var/lib/purge-warden/config.toml. Pass --force to overwrite
(the existing file will be renamed with a .pre-init-<ts> suffix).

With --force, the existing file is renamed aside first — config.toml becomes config.toml.pre-init-20260731T140502Z — so a mistaken re-init is a rename away from recovery, not a lost file. That rename protects the config file only; it does not protect anything else you’d want to compare or restore selectively. Run warden config backup first — see Backup & restore.

Cluster secondary

--cluster-secondary (with --peer <primary-api-url>) writes only node-local sections — no [upstream], no [[blocklists]], no [profiles.*]; those arrive from the primary. The result is deliberately not bootable until warden cluster join runs.

After init

init writes the config but creates no IPC token — every mutating or admin command refuses without one. The command prints the next steps itself:

next steps, as printed by init
next steps: 1. (optional) edit /var/lib/purge-warden/config.toml to customize lists 2. create the admin token — every mutating command needs one: warden --config /var/lib/purge-warden/config.toml token generate 3. start the daemon: sudo -u purge-warden warden --config /var/lib/purge-warden/config.toml start --daemon

If [server].listen binds a port below 1024 (the scaffold default, 0.0.0.0:53, does), the daemon needs CAP_NET_BIND_SERVICE to bind it:

bash
setcap cap_net_bind_service=+ep /usr/local/bin/warden

— or run the daemon from the packaged systemd unit instead, which grants the same capability via AmbientCapabilities= and needs no setcap step. Run the daemon from the packaged unit and the capability comes from AmbientCapabilities= instead.

Next: Import lists.

In the TUI

warden init is a root CLI command — there is no TUI equivalent, and nothing in the TUI runs it for you. Once it has run and the daemon is started, warden dashboard opens the TUI, and Configuration (5, or g e) is where the rest of setup continues.

Troubleshooting

no upstream resolver configuredinit --yes refuses outright. Nothing usable was found on this machine. Pass one explicitly:

bash
sudo warden init --yes --upstream 192.0.2.53:53

the only resolver(s) this machine uses are warden itself (…) — a different refusal, same command. This is the steady state when a working install is re-initialized: the network already points at warden, so adopting the detected address would make warden query itself. Pass --upstream with a resolver outside this host.

config already exists: … Expected — see Re-running init. Add --force, or point --config at a fresh path if you meant to keep the old one.

--lists rejects an entry with “raw URLs are not accepted”. --lists takes catalog slugs only. Subscribe by slug, then add a custom URL source afterwards with warden blocklist add <id> --url <url>.

A mutating command fails after init with a “no token” / authentication error. Expected — init never creates one. Run warden token generate as shown in After init.

For anything not listed here, warden config lint names the problem and the line it is on.

See also

  • Import lists — the next step in the base path.
  • Basic configuration — where the config lives, and how to validate and reload it after editing.
  • Backup & restorewarden config backup, before a --force re-init.
  • CLI reference — config-path discovery order and the rest of the warden config surface.
↑↓ to navigate · Enter to open · Esc to close See all results