Skip to content
Join the waitlistWaitlist

Config keys

Every key of guard.toml with its type, default and bounds, as Guard's code checks them.

guard.toml lives in Guard’s home (~/.zunder-guard unless --home or ZUNDER_GUARD_HOME says otherwise; --config names another file). zunder-guard init writes it; zunder-guard check-config checks it and prints its rules.

  • Fractions are fractions. 0.02 means 2%. Basis points end in _bps.
  • Decimals are strings. max_loss_at_stop = "0.02", not 0.02. A bare float is refused, and so is a TOML date, so no value is rounded on the way in.
  • Unknown keys are refused, so a typo is an error, not a silently ignored setting.
  • Every value is checked at start, against an upper bound as well as a lower one. A value out of bounds is refused, never clamped.
  • A missing key takes its default, except network, account and [auth] clients, which have none.

What init writes for a paper-mode Guard that reads a testnet account, with the default rules. Your file has your own addresses.

network = "testnet"
mode = "paper"
account = "0xYourAccountAddress"
allow_mainnet = false
listen = "127.0.0.1:8547"
state_dir = "."
sync_seconds = 5
ip_share = "1"
pairing_sha3 = "…64 hex digits…"
[auth]
clients = ["0xTheClientKeysAddress"]
nonce_max_age_ms = 30000
nonce_max_ahead_ms = 5000
[policy]
max_leverage = "5"
max_loss_at_stop = "0.02"
stop = "attach"
default_stop_distance = "0.02"
min_liquidation_distance = "0.10"
max_position_of_account = "2"
max_open_risk = "0.06"
daily_loss_stop = "0.06"
drawdown_halt = "0.25"
markets = "all"
entry_price_bound = "0.005"
fee_bps = "4.5"
slippage_bps = "1"
stop_slippage = "0.10"
exit_slippage = "0.05"

GuardConfig in crates/zunder-guard/src/config.rs.

KeyTypeDefaultBounds and checks
networkstringnone: required"testnet" or "mainnet": the network of the account
modestring"paper""paper", "testnet" or "mainnet". A sending mode must be the account’s network. run sends only with the same --network on its command line
accountstringnone: required0x and 40 hex digits: the main wallet’s address, not the API wallet’s
api_walletstringunset0x and 40 hex digits. Needed to send: the key Guard is given must be this API wallet’s. key check records it
allow_mainnetbooleanfalsetrue only in a mainnet config; refused in a testnet config
listenstring"127.0.0.1:8547"an IP address with a port, loopback only. run --listen overrides it at start
state_dirpath"guard-state" (init writes ".")not empty. A relative path is taken from the config file’s directory. Holds the journals and the kill file kill
sync_secondsinteger55 to 300: seconds between two reads of the account (Guard reads less often when its ip_share or its HIP-3 dexes need it)
ip_sharedecimal"1"above 0, at most 1: the part of the IP address’s request weight (Hyperliquid’s 1,200 a minute) this Guard may spend. Several Guards on one machine: 1/N each, as a decimal ("0.5", "0.3333"). Every budget is fitted to it; a share too small to keep Guard safe is refused: below 0.291 with the main dex alone (three Guards per address at most), 0.466 with one HIP-3 dex (two), 0.749 with two (one). init --ip-share writes it, run --ip-share overrides it
emergency_dirpath<state_dir>/emergencynot state_dir itself. Where Guard records the protective actions it sends (flattening, stops, closes) while the decision journal cannot be written, in emergency- and the journal’s file name; best a directory on another disk that Guard may write. A relative path is taken from the config file’s directory
licencestringunseta licence key (zgl1_…); not a secret
pairing_sha3stringunset64 hex digits: the hash of the pairing code init or pair showed once

AuthConfig in crates/zunder-guard-core/src/auth.rs.

KeyTypeDefaultBounds
clientsarray of stringsnone: at least one1 to 64 addresses, 0x and 40 hex digits each: the client keys’ addresses (init, pair and client add add them)
nonce_max_age_msinteger300001,000 to 300,000: how old a request’s nonce may be
nonce_max_ahead_msinteger5000100 to 60,000: how far ahead of Guard’s clock a nonce may be

Policy in crates/zunder-guard-core/src/policy.rs. The first ten keys are the nine rules (the stop rule has two); they travel in a rules code. The rest are Guard’s cost and execution assumptions, which no rules code carries.

KeyTypeDefaultBoundsRule
max_leveragedecimal"5"above 0, at most 10, at most four decimal places(c) max leverage
max_loss_at_stopdecimal"0.02"above 0, at most 0.05; at most max_open_risk(g) max loss at the stop
stopstring"attach""attach" or "refuse"(b) an entry without a stop
default_stop_distancedecimal"0.02"above 0, at most 0.5; below min_liquidation_distance(b) where an attached stop goes
min_liquidation_distancedecimal"0.10"0.01 to 0.5; above default_stop_distance(d)
max_position_of_accountdecimal"2"above 0, at most 10; at most max_leverage(f) max position, 2 is 200% of equity
max_open_riskdecimal"0.06"above 0, at most 0.2; at least max_loss_at_stop(e)
daily_loss_stopdecimal"0.06"above 0, at most 0.15(h)
drawdown_haltdecimal"0.25"above 0, at most 0.5(i)
markets"all" or array of strings"all""all", or 1 to 32 market names (1 to 32 characters each)(a)
entry_price_bounddecimal"0.005"0.0005 to 0.05how far beyond the mid an entry’s limit may lie; a limit further out is pulled in
fee_bpsdecimal"4.5"0 to 50taker fee per side, counted into each entry’s risk
slippage_bpsdecimal"1"0 to 100expected slippage per side, counted likewise
stop_slippagedecimal"0.10"0.05 to 0.2how far beyond its trigger Guard’s market stop may fill; the liquidation must lie beyond that
exit_slippagedecimal"0.05"0.01 to 0.1how far from the mid Guard’s own closing orders may fill when it flattens
max_trading_equity_usddecimalunset (no cap)above 0, at most 2500the equity cap, in USDC

max_loss_at_stop, default_stop_distance, min_liquidation_distance, max_position_of_account, max_open_risk, daily_loss_stop and drawdown_halt take at most six decimal places (four in percent, as a rules code writes them).

A config with mode = "mainnet" is checked further (check_mainnet in config.rs):

  • allow_mainnet = true, account, api_wallet and max_trading_equity_usd are all required;
  • no rule may be looser than the mainnet ceiling (Policy::within_mainnet_ceiling; today equal to the defaults, but pinned on its own, so changing a default never loosens mainnet): max_leverage, max_loss_at_stop, max_open_risk, daily_loss_stop, drawdown_halt, max_position_of_account, entry_price_bound and default_stop_distance at most the ceiling; min_liquidation_distance, fee_bps, slippage_bps, stop_slippage and exit_slippage at least it; no HIP-3 market.

A config alone never sends to mainnet: run also needs --network mainnet, the key on standard input and ZUNDER_MAINNET_CONFIRM naming the account (Paper, testnet and mainnet).

There is no [risk] section, no relay and no telemetry setting. The keys risk_per_trade, drawdown_stop, stop_policy, max_position_fraction, round_trip_cost_bps and allowed_coins belong to the risk engine’s own config and to the website’s judge, not to Guard; in guard.toml they are refused as unknown keys.

This page as plain Markdown, for people and LLMs: /docs/reference/config.md