Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Configuration

zecd is configured by a TOML file plus Bitcoin-Core-style CLI flags and a handful of environment variables. This page is the complete reference: every TOML section and key with its type, default, and semantics, plus the CLI flags and environment variables. The zecd.example.toml file in the repository root is a fully commented starting point.

File location and precedence

The config file is <datadir>/zecd.toml, overridable with --conf <FILE>. Like bitcoind, the file is located before its own datadir key can apply: the lookup uses only the --datadir flag and the ZECD_DATADIR environment variable, never a datadir set inside the file. If the file does not exist, built-in defaults apply.

Unknown keys anywhere in the file are a startup error (fail-fast), not a silent ignore: a typo cannot quietly disable a setting.

General precedence, highest first:

  1. CLI flag (some flags read an environment variable as a fallback; see Environment variables)
  2. TOML key
  3. Built-in default

Per-key exceptions are noted inline below (the RPC password has a three-way precedence; rpcauth entries accumulate rather than override; per-wallet keys override global [pools] keys).

Top-level keys

KeyTypeDefaultDescription
networkstring"test"Chain to run on: "main"/"mainnet", "test"/"testnet", or "regtest". Overridden by --network, --testnet, --regtest.
datadirpath"./zecd-data"Parent directory for per-wallet subdirectories, the RPC cookie file, the datadir lock, and (by default) the age identity. Overridden by --datadir / ZECD_DATADIR.
default_walletstring"default"Wallet served when a request hits / rather than /wallet/<name> (see multiwallet routing).

The default network is testnet; mainnet must be selected explicitly. On mainnet, zecd additionally refuses to start while [rpc] password is still the example placeholder change-me (case-insensitive), since the RPC password is spend authority.

[wallets.<name>]

One section per wallet; each wallet is an independent seed, SQLite database, and directory, served at /wallet/<name>. If no wallet section is declared, an implicit entry for default_wallet is created at <datadir>/<name>. Every [pools] key can be overridden per wallet here.

KeyTypeDefaultDescription
dirpath<datadir>/<name>Directory holding this wallet's data.sqlite, keys.toml, and blocks/.
keys_filepath<dir>/keys.tomlLocation of this wallet's keys.toml (the encrypted seed), independent of dir (for example a read-only mounted Kubernetes Secret while dir stays a disposable cache). For the default wallet, [keys] keys_file / ZECD_KEYS_FILE / --keys-file set this too, but an explicit per-wallet keys_file wins over all of them.
poolsarray of stringglobal [pools] enabledOverride of the enabled shielded pools for this wallet.
default_receiversarray of stringsee belowOverride of the default UA receivers. A wallet that overrides pools but not default_receivers receives into everything it enabled; a wallet that overrides neither inherits the global default. Must be a subset of the wallet's enabled pools.
transparentboolglobal valueOverride of [pools] transparent.
transparent_defaultboolglobal valueOverride of [pools] transparent_default.
transparent_gap_limitintegerglobal valueOverride of [pools] transparent_gap_limit.
transparent_initial_scanintegerglobal valueOverride of [pools] transparent_initial_scan.
transparent_allow_beyond_recovery_windowboolglobal valueOverride of [pools] transparent_allow_beyond_recovery_window.
transparent_gap_warn_thresholdintegerglobal valueOverride of [pools] transparent_gap_warn_threshold.

At most one loaded wallet may hold spending keys; any number of watch-only (UFVK) wallets may run alongside it; see Watch-only wallets.

[backend]

The chain upstream: a single self-hosted Zebra node's JSON-RPC. See A Zebra-only backend for the deployment model and the cleartext-credential gate.

KeyTypeDefaultDescription
serverstring"zebra"Upstream endpoint. "zebra" means a local zebrad at 127.0.0.1:8234 (mainnet) or 127.0.0.1:18234 (testnet/regtest); set zebrad's rpc.listen_addr accordingly. Any explicit zebra://host:port (or bare host:port) works. Overridden by --server.
connect_timeout_secsinteger10Per-attempt dial timeout (seconds); clamped to at least 1.
reconnect_base_secsinteger1Reconnect backoff base delay (seconds); clamped to at least 1. Backoff is exponential with full jitter.
reconnect_max_secsinteger60Reconnect backoff cap (seconds); clamped to at least reconnect_base_secs.
rfc1918_is_localbooltrueTreat private / non-globally-routable addresses (RFC1918, link-local, CGNAT, IPv6 ULA/link-local) as "local" for the cleartext-credential gate (the Docker/LAN norm). Set false for a strict loopback-only posture.
allow_remote_cleartextboolfalseEscape hatch: allow [zebra] credentials to travel in plaintext to a globally-routable host. Only set this when the hop is secured out-of-band (SSH/WireGuard tunnel, private overlay).

[zebra]

Credentials for the zebrad endpoint. Omit the whole section when zebrad runs with enable_cookie_auth = false. A cookie file wins over user/password; nothing set means no authentication.

KeyTypeDefaultDescription
rpc_userstringunsetRPC username for zebrad.
rpc_passwordstringunsetRPC password for zebrad.
rpc_cookiepathunsetPath to zebrad's cookie file; re-read on every reconnect (zebrad regenerates it at startup). Wins over rpc_user/rpc_password.

[rpc]

zecd's own JSON-RPC server (the Bitcoin-Core-dialect surface; see Conventions & wire format).

KeyTypeDefaultDescription
bindstring (IP)"127.0.0.1"Listen address. Overridden by --rpcbind.
portinteger8232 main / 18232 test+regtestListen port. Overridden by --rpcport.
userstringunsetHTTP Basic auth username. Overridden by --rpcuser.
passwordstringunsetHTTP Basic auth password. Precedence: --rpcpassword / ZECD_RPC_PASSWORD > password_file > this key. If no user/password pair is configured, cookie auth is used instead.
password_filepathunsetRead the RPC password from this file (trailing newline/CR trimmed), keeping the spend-equivalent secret out of a ConfigMap-bound TOML. A configured file that cannot be read is a fatal startup error.
autharray of string[]Bitcoin-Core-style rpcauth entries (<user>:<salt>$<hmac-sha256 hex>), each an additional accepted credential. Generate with zecd rpcauth <user> [password]. Entries from --rpcauth flags and this key accumulate (all are accepted), matching bitcoind.
cookiefilepath<datadir>/.cookieWhere the bitcoind-style cookie is written when no user/password is set: zecd mints a random secret at startup and writes __cookie__:<random> (mode 0600).
work_queueinteger100Max concurrent in-flight requests before returning HTTP 503 (Bitcoin Core's -rpcworkqueue); clamped to at least 1.
allowed_methodsarray of string[]RPC method safelist. Empty means every method is served; non-empty serves only the listed methods, anything else returning -32601 ("Method not found") exactly as if it did not exist. Names are validated against the implemented method set at startup, so a typo fails fast. A coarse server-wide gate, not per-user.

[keys]

Seed custody and unlock behavior. See Key custody for the two at-rest custody models (age identity vs. passphrase).

KeyTypeDefaultDescription
age_identitypath<datadir>/identity.txtage identity file used to decrypt the wallet seed for unattended sending (the identity-file custody model). Overridden by --age-identity / ZECD_AGE_IDENTITY.
auto_unlockbooltrueDecrypt the seed at startup so sends need no walletpassphrase (identity-file wallets only; passphrase-encrypted wallets always start locked).
keys_filepathunsetLocation of the default wallet's keys.toml, independent of the datadir (mount it as a Secret). Equivalent to [wallets.<default>] keys_file; overridden by --keys-file / ZECD_KEYS_FILE, and by an explicit per-wallet keys_file.
bootstrap_from_keysbooltrueWhen a wallet's keys.toml exists but its data.sqlite has no account, recreate the account from the seed on boot and rescan from the wallet's birthday: the setting that lets the data directory be a disposable cache. Set false to fail fast on an empty datadir instead. Watch-only wallets have no seed and are not covered.

[pools]

Global defaults for which value pools each wallet uses; every key here can be overridden per wallet in [wallets.<name>]. See Addresses & shielded pools and Transparent support.

KeyTypeDefaultDescription
enabledarray of string["orchard"]Shielded pools the wallet receives into and spends from; supported values are "sapling" and "orchard". Change goes to the strongest enabled pool (Orchard if enabled). Must be non-empty.
default_receiversarray of string= enabledReceivers included in the Unified Addresses getnewaddress hands out when no per-call override is given. Must be a subset of enabled (a violation is a startup error).
transparentboolfalseAllow bare transparent (t1…/tm…) receiving addresses via getnewaddress "" "transparent". Off keeps zecd shielded-only (address_type = "transparent" is rejected with -8).
transparent_defaultboolfalseMake a bare transparent address the no-argument getnewaddress default. Requires transparent = true (validated at startup).
transparent_gap_limitinteger20External transparent gap limit: how far past the last funded receiving address a from-seed restore keeps scanning. Unlike shielded funds (always recoverable by trial decryption), transparent funds are only rediscovered within this window. Must be at least 1.
transparent_initial_scaninteger0Initial scan depth: pre-expose external transparent indices 0..N at startup/restore so the receive scan covers all of them, independent of the (small) steady-state gap limit. Set to your issuance high-water mark; 0 disables pre-exposure.
transparent_allow_beyond_recovery_windowbooltrueWhat getnewaddress "" "transparent" does once the recovery window is exhausted: true issues the address anyway with a loud warning that funds sent there may be unrecoverable from seed; false fails the call with an actionable -4 error (fail-closed).
transparent_gap_warn_thresholdinteger5Warn when fewer than this many in-window transparent address slots remain, giving lead time to widen the limits. 0 warns only on actual exhaustion.

[sync]

KeyTypeDefaultDescription
interval_secsinteger20How often to poll Zebra for new blocks (seconds); clamped to at least 1.
rebroadcast_secsinteger60How often (at most) to re-broadcast the wallet's own transactions that are unmined and unexpired (seconds); clamped to at least 1.

[spend]

Send policy: confirmations, privacy, and the proving pipeline. See Privacy policy for the four-rung ladder and its enforcement points.

KeyTypeDefaultDescription
trusted_confirmationsinteger3Confirmations before the wallet's own outputs (change) are spendable (ZIP 315 default). Clamped to at least 1.
untrusted_confirmationsinteger10Confirmations before third-party outputs are spendable (ZIP 315 default). Must be at least trusted_confirmations (validated at startup). Anchors balances and spend proposals; getbalance's explicit minconf overrides per call.
privacy_policystring"AllowRevealedRecipients"What sends may reveal on-chain: "FullPrivacy", "AllowRevealedAmounts", "AllowRevealedRecipients", or "AllowFullyTransparent". z_sendmany's per-call privacyPolicy overrides it.
orchard_action_limitinteger50Cap on Orchard actions (max(inputs, outputs)) a single send may build; bounds memory/proving cost and yields a clean -8 for oversized sends. 0 disables the cap.
cache_proving_keybooltrueBuild the Orchard proving key once at startup and prove sends through the PCZT path, instead of rebuilding the key (~seconds of keygen) on every transaction. Both paths produce identical transactions.
pipeline_provingboolfalseRun a send's proving step off the single-writer actor so a long proof no longer freezes background sync and status. Sends still serialize. Only engages on the cached-Orchard PCZT path (cache_proving_key = true, Orchard-only spends).

[health]

Unauthenticated liveness/readiness probes on a separate port; see the operations runbook.

KeyTypeDefaultDescription
enabledbooltrueServe /healthz, /readyz, /status.
bindstring (IP)"127.0.0.1"Probe listen address (0.0.0.0 to expose off-host).
portinteger9233Probe listen port (all networks).
readinessstring"connected"What /readyz gates on: "connected" (backend connected and its tip past the wallet's birthday; does not wait for scanning) or "synced" (additionally scanned to within max_scan_lag blocks of the tip).
max_scan_laginteger4Maximum chain_tip - fully_scanned gap at which /readyz reports ready. Only consulted in "synced" mode.

[log]

KeyTypeDefaultDescription
levelstring"info"Default tracing filter; overridden entirely by RUST_LOG when set.
formatstring"text""text" (human-readable) or "json" (structured, for log aggregation). Logs go to stderr.

CLI flags

Flags use Bitcoin-Core-style names and always win over the corresponding TOML key.

FlagOverridesDescription
--conf <FILE>file locationPath to the TOML config (default <datadir>/zecd.toml).
--datadir <DIR>datadirData directory. Falls back to ZECD_DATADIR, then the file, then ./zecd-data.
--testnetnetworkUse testnet.
--regtestnetworkUse regtest (a local Zebra regtest chain). Wins over --testnet and --network.
--network <NET>network"main", "test", or "regtest".
--rpcbind <ADDR>[rpc] bindRPC bind address.
--rpcport <PORT>[rpc] portRPC port.
--rpcuser <USER>[rpc] userRPC username.
--rpcpassword <PASS>[rpc] password / password_fileRPC password; also readable from ZECD_RPC_PASSWORD. Passing it on the command line triggers a startup warning: argv is world-readable via ps / /proc/<pid>/cmdline. Prefer the environment variable or password_file.
--rpcauth <USER:SALT$HASH>accumulates with [rpc] authAdditional rpcauth credential; may be repeated.
--server <SERVER>[backend] serverChain upstream: zebra or zebra://host:port.
--age-identity <FILE>[keys] age_identityage identity file; also readable from ZECD_AGE_IDENTITY.
--keys-file <FILE>[keys] keys_fileDefault wallet's keys.toml path; also readable from ZECD_KEYS_FILE. An explicit [wallets.<name>] keys_file still wins.
--versionPrint the version and exit.

Subcommands

Running zecd with no subcommand (or zecd run) starts the daemon. The global flags above are accepted on every invocation and must precede the subcommand (zecd --datadir ./data --testnet init). init and export-ufvk honor the datadir/network/keys flags; the RPC flags are inert for them. rpcauth runs before config resolution and ignores all of them.

SubcommandFlagsDescription
init--wallet <NAME> (default default), --restore, --mnemonic-file <FILE>, --encrypt, --ufvk <UFVK>, --birthday <HEIGHT>Create and initialize a wallet, then exit. --restore reads the mnemonic from ZECD_MNEMONIC, else --mnemonic-file, else stdin. --encrypt reads the passphrase from ZECD_WALLET_PASSPHRASE, else prompts. --ufvk creates a watch-only wallet and conflicts with --restore/--encrypt. --birthday defaults to the current chain tip for new wallets; a restore without it scans from Sapling activation.
export-ufvk--wallet <NAME> (default default)Print a wallet's Unified Full Viewing Key (reads the wallet DB; no identity/passphrase needed, and not blocked by a running daemon's datadir lock).
rpcauth <username> [password]Generate a salted [rpc] auth credential line. Omitting the password generates a strong random one, printed once. Needs no datadir or config.
runRun the JSON-RPC daemon (the default when no subcommand is given).

Environment variables

VariableUsed byDescription
ZECD_DATADIRdaemon + subcommandsData directory. Precedence: --datadir > ZECD_DATADIR > file datadir > ./zecd-data.
ZECD_RPC_PASSWORDdaemonRPC password; equivalent to --rpcpassword and wins over [rpc] password_file and inline password. Preferred over the flag (not visible in ps).
ZECD_KEYS_FILEdaemon + initDefault wallet's keys.toml path; equivalent to --keys-file.
ZECD_AGE_IDENTITYdaemon + initage identity file path; equivalent to --age-identity.
ZECD_MNEMONICinit --restoreThe seed phrase for a non-interactive restore. Takes precedence over --mnemonic-file and stdin.
ZECD_WALLET_PASSPHRASEinit --encryptThe at-rest passphrase for a non-interactive encrypted init; otherwise prompted twice on stdin.
ZECD_ALLOW_CORE_DUMPSdaemon + subcommandsSet to exactly 1 to opt out of the core-dump/ptrace hardening (RLIMIT_CORE=0 + PR_SET_DUMPABLE=0) for crash debugging. Any other value, including 0 or empty, keeps hardening on. The seed mlock is unaffected.
RUST_LOGdaemon + subcommandsStandard tracing filter; overrides [log] level when set.

Minimal example

A testnet daemon against a local zebrad with cookie auth on both hops:

network = "test"
datadir = "./data"

[backend]
server = "zebra"          # zebra://127.0.0.1:18234 on testnet

[zebra]
rpc_cookie = "/var/lib/zebrad/.cookie"

# No [rpc] user/password: zecd writes its own cookie to ./data/.cookie,
# and local clients authenticate with it like bitcoin-cli does.