Configuration Reference

dvb-WarpPool has two configuration files plus a set of env vars for optional subsystems:

  • config.toml — all non-sensitive settings (pool behavior, Stratum, VarDiff, notifier schema)
  • secrets.toml — auth hashes, RPC credentials, Sv2 keys; always chmod 600
  • env vars — opt-in subsystems (auto-update, health-check interval, notifier secrets, debounce tuning)

Defaults are defined in crates/config/src/lib.rs; this page is the operator's view.

config.toml

[branding]

[branding]
status_brand_name = "dvb-WarpPool"
server_location = "self-hosted"
fiat_currency = "EUR"
pool_donation_address = ""

Controls the strings shown in the UI and in the Prometheus build_info metric. No effect on functionality.

[logging]

[logging]
debug = false
net_debug = false
json = true        # JSON-formatted for Loki/Promtail; set false for readable stdout

Runtime override via env var: RUST_LOG=warppool=debug takes precedence over the config setting.

[mining]

[mining]
payout_address = "bc1q..."           # REQUIRED — block-reward recipient
pool_fee_percent = 0.0                # Default = no pool fee (solo)
pool_fee_address = ""                 # Required when fee_percent > 0
pooltag_prefix = "dvbprojekt-WarpPool"  # 19 chars max, written to coinbase scriptSig
operator_donation_address = ""
operator_donation_percent = 0.0

For multi-user-solo (hosting-service setup): set pool_fee_percent = 1.0 and pool_fee_address. The sum of fee + donation must not exceed 99%.

[node]

[node]
rpc_url = "http://127.0.0.1:8332"
zmq_hashblock_addr = "tcp://127.0.0.1:28332"
zmq_rawblock_addr = "tcp://127.0.0.1:28333"
rpc_cookie_path = "/home/bitcoin/.bitcoin/.cookie"   # optional, alternative to user/pass in secrets.toml

The cookie takes precedence over user/pass in secrets.toml. Leaving the ZMQ fields empty enables poll-only mode (slower but functional).

[server]

[server]
pool_listen = ":3333"
status_listen = ":18334"
status_tls_listen = ":18443"          # optional
status_public_url = "https://pool.example.com"   # for push notifications
trust_proxy_headers = false           # only set true when the daemon runs behind Caddy/nginx

status_public_url is required by the UI to build absolute URLs for web push and email links. Leave empty when not running behind a reverse proxy.

[stratum]

[stratum]
stratum_tls_listen = ":3443"          # optional, V1 TLS
stratum_password = ""                 # empty = address-format auth only
safe_mode = true                      # Conservative validator
tls_cert_path = "/etc/dvb-warppool/cert.pem"
tls_key_path = "/etc/dvb-warppool/key.pem"
sv2_listen = ":3334"                  # optional, Stratum V2
sv2_max_connections = 1024

Sv2 requires secrets.sv2_static_priv_key_hex. If it is missing, Sv2 is disabled with a warning (the daemon still starts).

[tuning]

[tuning]
max_saved_workers_per_user = 64
max_saved_workers_pool_total = 1024

Storage cap. Above these thresholds, old/idle workers are evicted (FIFO by last_seen_at).

[profile]

[profile]
kind = "klein"                  # klein | mittel | gross | enterprise; omit = auto
expected_miner_count = 5        # for auto: estimate used by auto-detection

The profile affects defaults and resource limits. Hot-switchable via POST /api/admin/profile — the config value is only the fallback at first start.

[vardiff]

[vardiff]
enabled = true
target_seconds_per_share = 30.0
window = 16
min_diff = 1.0
max_diff = 65536.0
retarget_after_n_shares = 8
hysteresis = 0.30          # 30% — a new diff that deviates > 30% from the old one triggers a retarget
max_step = 4.0             # max 4× per retarget
initial_diff = 1.0

EMA-based, with persisted snapshots in the vardiff_state table. Tests: crates/stratum-v1/src/vardiff.rs.

[ratelimit]

[ratelimit]
enabled = true
connects_per_sec = 5.0     # per peer IP
connect_burst = 20
auths_per_sec = 1.0
auth_burst = 10
idle_evict_secs = 300      # after 5 min without traffic → bucket state is evicted

Per-peer-IP token bucket. Triggered limits return mining.authorize with result: false (no disconnect — the miner should not get stuck in a reconnect loop).

[notifier]

See Notifications for detailed sink setup guides.

[notifier]
webpush_subscriptions_path = "/var/lib/dvb-warppool/webpush.json"

[notifier.ntfy]
topic_url = "https://ntfy.sh/my-pool-topic-secret"
on_block_found = true
on_miner_disconnect = false
on_rpc_down = true

[notifier.telegram]
bot_token_env = "TELEGRAM_BOT_TOKEN"
chat_id = "123456789"
on_block_found = true

[notifier.discord]
webhook_url_env = "DISCORD_WEBHOOK_URL"
on_block_found = true

[notifier.slack]
webhook_url_env = "SLACK_WEBHOOK_URL"
on_block_found = true
on_miner_disconnect = false
on_rpc_down = true

[notifier.email]
smtp_url = "smtps://pool@mail.example.com:465"
from = "pool@example.com"
to = ["operator@example.com"]
password_env = "POOL_SMTP_PASSWORD"
on_block_found = true
on_rpc_down = true

All sinks are optional. The daemon starts fine without a [notifier] section at all. Sinks whose env vars are missing are skipped with a notifier sink skipped warning.

secrets.toml (chmod 600)

rpc_user = "warppool"            # optional, alternative to cookie
rpc_pass = "..."                  # optional, ditto
admin_username = "admin"          # default "admin"
admin_password_hash = "$argon2id$..."   # via `dvb-warppool-cli hash-password`
jwt_secret = "..."                # min 32 bytes recommended; empty → admin auth disabled
sv2_static_priv_key_hex = "..."  # via `dvb-warppool-cli gen-sv2-key`

An empty admin_password_hash or empty jwt_secret makes every /api/admin/* endpoint return 503 "auth disabled". Useful for a read-only setup without an admin UI.

Phase 21: VAPID Web Push Secrets

vapid_public_key = "B..."      # via `dvb-warppool-cli gen-vapid-keys`
vapid_private_key = "..."      # same command
vapid_contact = "mailto:operator@example.org"   # optional, default mailto:operator@localhost

The public key is served via /api/push/vapid-public-key (public, no-auth — the UI needs it for PushManager.subscribe). The private key stays daemon-side only. If either value is empty, web push is disabled — the daemon log reports "web-push disabled (no vapid keys)".

Environment Variables

Bitcoin Health Check

VarDefaultDescription
WARPPOOL_HEALTH_CHECK_INTERVAL_SECONDS60Interval of the daemon's periodic Bitcoin health check. 0 = off.

When the check is enabled, the daemon emits HealthSnapshot SSE events and fires RpcDown/RpcRecovered notifications on transitions.

Auto-Update

VarDefaultDescription
WARPPOOL_AUTOUPDATE_REPO(unset)e.g. dvb-projekt/dvb-WarpPool. When set, /api/admin/update is active.
WARPPOOL_AUTOUPDATE_INTERVAL_HOURS24How often to poll GitHub for new releases. 0 = off.
WARPPOOL_COSIGN_BIN(unset)Path to the cosign binary for verify-blob. Required when cosign_verify=true in the update request.

Stratum / Notifier Tuning

VarDefaultDescription
WARPPOOL_DISCONNECT_DEBOUNCE_SECS30Per-worker debounce for MinerDisconnect notifications.

Solar/PV Provider (Phase 20.5)

The electricity tariff switches to excess_rate_eur_kwh (typically 0.0) when the PV array produces more than house + pool consume together. Currently implemented: Home Assistant via its REST API.

[mining.electricity.solar]
enabled = true
kind = "home_assistant"
url_env = "WARPPOOL_HA_URL"            # e.g. http://homeassistant.local:8123
token_env = "WARPPOOL_HA_TOKEN"        # Long-Lived Access Token
pv_entity_id = "sensor.pv_power_total"
consumption_entity_id = "sensor.grid_load_total"   # optional
poll_interval_secs = 60                # 60s is enough to track PV trends
surplus_buffer_w = 200                 # safety buffer
excess_rate_eur_kwh = 0.0              # self-generated power = free
stale_after_secs = 300                 # fall back after 5 min without a snapshot

Create the token in HA: Profile → Long-Lived Access Tokens → Create Token. Entity IDs are listed under Developer Tools → States.

If consumption_entity_id is omitted, the pool compares the PV value directly against its own consumption (not the whole-house total).

/api/energy reports current_rate_source = "solar-excess" and a solar object containing pv_w / consumption_w / excess_w / age_seconds.

Vendor Probes (Phase 22)

VarDefaultDescription
WARPPOOL_AUTO_PROBE_DISCOVERED(off)1/true/yes: in addition to DB miners, devices found via mDNS are polled every 30s. Telemetry values land in an in-memory cache (no DB write) and are exposed in /metrics with label="discovered".

Health Anomaly Check (Phase 20.3b)

VarDefaultDescription
WARPPOOL_ANOMALY_CHECK_INTERVAL_SECS300Interval of the periodic anomaly detection. 0 = off.
WARPPOOL_ANOMALY_DEBOUNCE_SECS1800Per-(miner, alert_kind) debounce. While a symptom persists, the notifier does not fire on every tick — only again after this interval.

Critical alerts (FanStuck, StaleData) are sent to every sink with on_health_alert = true (default). Warnings (ThermalThrottling, VoltageDrop, HashrateDrop) are UI-only via /api/miners/:id/alerts.

Notifier Secrets

The variable names are FREELY CHOSEN — you set the name in config.toml, and the daemon reads that env var.

Typical names:

  • TELEGRAM_BOT_TOKENnotifier.telegram.bot_token_env
  • DISCORD_WEBHOOK_URLnotifier.discord.webhook_url_env
  • SLACK_WEBHOOK_URLnotifier.slack.webhook_url_env
  • POOL_SMTP_PASSWORDnotifier.email.password_env

Test/Debug

VarDefaultDescription
RUST_LOGinfoStandard tracing-subscriber filter. Use warppool=debug,info for selective debug.
WARPPOOL_E2E_REGTEST_*Set by scripts/regtest-up.sh. See Testing.

CLI Override

dvb-warppool-daemon --help lists the CLI flags that can override individual config values. Common ones:

dvb-warppool-daemon \
  --config /etc/dvb-warppool/config.toml \
  --secrets /etc/dvb-warppool/secrets.toml \
  --db-url sqlite:///var/lib/dvb-warppool/pool.db \
  --ui-dir /usr/share/dvb-warppool/ui \
  --no-zmq                          # debug-only: force poll-only mode

Reload Behavior

There is currently no hot reload for config.toml. Restart the daemon after every change:

systemctl restart dvb-warppool

The only hot-switchable items are:

  • Profile (POST /api/admin/profile) — affects display and defaults, not the max_connections semaphore
  • Admin tokens (POST /api/admin/tokens) — effective immediately
  • 2FA (POST /api/auth/2fa/enable) — effective immediately

See Also