Skip to content

Configuration

Zephyr persists state across a few files in the working directory:

  • config.toml: your preferences. RPC endpoints, processor toggles, buy/sell transaction params, and the [trade] defaults used to seed new mints.
  • positions.json: runtime state. The currently-active mint, per-mint trade params (entry %, exit %, target change delay) that override [trade] defaults once a mint has a record, and per-mint trade stats / PnL history.
  • wallet.enc + wallet.key: your trading wallet, encrypted. Created on first start from the private_key you put in config.toml (which is then removed). Back up both together — one without the other is useless.

When you change settings from the UI:

  • Processor toggles, tips, delays, and buy/sell tx params → saved to config.toml.
  • Active mint → saved to positions.json (top-level active_mint), never touches config.toml.
  • Entry %, exit %, or target change delay with an active mint → saved to that mint's record in positions.json. Without an active mint → saved to config.toml [trade] defaults.

Buy amount is not persisted. Each UI client tracks its own last-used amount and sends it with every buy command.

You edit config.toml manually for initial setup and fields that aren't exposed in the UI (private_key, rpc_url, optionally grpc_url).

Full config.toml

This is the same config shipped in Getting Started. Copy this as your starting point and fill in your details.

toml
[trade]
# Defaults used to seed new mints. Per-mint overrides live in positions.json.
entry_pct = -10.0                                # Entry target as % from reference price (negative = wait for dip)
exit_pct = 2.0                                   # Take-profit target as % above buy price
target_change_delay = 1000                       # Ms between reference price updates (auto-rebuy)

[buy]
slippage = 150                                   # Slippage in bps (150 = 1.5%)
max_block_difference = 3                         # Transaction freshness window in slots
nonce_delay = 25                                 # Nonce rotation interval in ms
panic_slippage = 1000                            # Slippage for panic buy in bps

[buy.dynamic_fee]
percentile = 7500                                # Fee percentile (×100, so 7500 = p75)
fee_multiplier = 1.0                             # Multiply raw fee by this factor
fee_cap = 500000                                 # Max fee in microlamports (0 = no cap)

[buy.dynamic_tip]
percentile = 8500                                # Must be in [general].tip_percentiles
tip_multiplier = 1.0                             # Multiply raw tip by this factor
tip_floor = 0.003                                # Minimum SOL tip (stream-fallback)
tip_cap = 0.005                                  # Maximum SOL tip; set == tip_floor to pin

[sell]
slippage = 200
max_block_difference = 5
nonce_delay = 25
panic_slippage = 2000

[sell.dynamic_fee]
percentile = 8500
fee_multiplier = 1.0
fee_cap = 800000

[sell.dynamic_tip]
percentile = 8500
tip_multiplier = 1.0
tip_floor = 0.003
tip_cap = 0.007

[network]
rpc_url = "<your-rpc-url>"                       # Solana RPC endpoint (read-only) — leave empty to use the Zephyr-provided default
grpc_url = ""                                    # gRPC endpoint for real-time streaming (leave empty to use the Zephyr-provided default; Helius LaserStream NY/Frankfurt auto-picked by region)
grpc_token = ""                                  # Auth token for the gRPC endpoint (ignored when grpc_url is empty)
processor = "all"                                # "all" or a specific one: jito, helius, etc.

[network.node]
enabled = true                                   # Enable anonymous RPC sender
urls = [
    "http://rpc-slc1.thornode.io",
    "http://rpc-va-zesty.thornode.io",
    "http://rpc-ny.thornode.io",
    "https://rpc.shyft.to?api_key=<your-shyft-key>",
]                                                # Send endpoints. MUST NOT reuse network.rpc_url
buy_delay = 50
sell_delay = 50

[network.jito]
enabled = true
buy_delay = 100
sell_delay = 100
# Optional per-processor override. Empty = use Zephyr's default fan-out.
# Non-empty = exact list that fires (good for non-NY VPS — restrict to
# nearby regions). Available on every processor and each bifrost subprovider.
# endpoints = [
#   "https://frankfurt.mainnet.block-engine.jito.wtf",
#   "https://amsterdam.mainnet.block-engine.jito.wtf",
# ]

[network.nextblock]
enabled = true
buy_delay = 25
sell_delay = 25

[network.helius]
enabled = true
buy_delay = 25
sell_delay = 25

[network.zeroslot]
enabled = true
buy_delay = 25
sell_delay = 25

[network.nozomi]
enabled = true
buy_delay = 25
sell_delay = 25

# Lunar has no Zephyr-provided key — keep it disabled unless you add your
# own api key below (an enabled Lunar with no key sends nothing, silently).
[network.lunar]
enabled = false
buy_delay = 25
sell_delay = 25

[network.astralane]
enabled = true
buy_delay = 25
sell_delay = 25

[network.stellium]
enabled = true
buy_delay = 25
sell_delay = 25

[network.falcon]
enabled = true
buy_delay = 25
sell_delay = 25

[network.bifrost_bloxroute]
enabled = true
buy_delay = 50
sell_delay = 50

[network.bifrost_astralane]
enabled = false
buy_delay = 50
sell_delay = 50

[network.bifrost_blockrazor]
enabled = true
buy_delay = 100
sell_delay = 100

[network.bifrost_lunarlander]
enabled = false
buy_delay = 100
sell_delay = 100

# Beam (RPC Fast) — one account, three relays. Needs your own Beam key.
# Enable the relays you want; the rate limit is per ACCOUNT, so split your
# account TPS across their api_keys[].tps rather than repeating it.
[network.beam_astralane]
enabled = false
buy_delay = 25
sell_delay = 25

[network.beam_bloxroute]
enabled = false
buy_delay = 25
sell_delay = 25

[network.beam_falcon]
enabled = false
buy_delay = 25
sell_delay = 25

# API keys live as array-of-tables. Each [[...api_keys]] adds one key with
# its own TPS budget. First-fit rotation; aggregate processor TPS = sum of
# per-key `tps` values. A send must clear BOTH the processor's
# buy_delay/sell_delay AND the selected key's `1000/tps`-ms interval.

[[network.jito.api_keys]]
api_key = "<your-jito-key>"
tps = 10

[[network.nextblock.api_keys]]
api_key = "<your-nextblock-key>"
tps = 50

# Optional: Zephyr provides a default Helius Sender key (per-IP TPS slice
# from a shared pool), no api key required. Add your own here only if you
# need more capacity (it's used as spillover).
# [[network.helius.api_keys]]
# api_key = "<your-helius-key>"
# tps = 50

# Optional — Zephyr provides 40 TPS on 0slot by default, no api key
# required. Add your own here only if you need more capacity (it's used
# as spillover).
# [[network.zeroslot.api_keys]]
# api_key = "<your-zeroslot-key>"
# tps = 50

# Optional: Zephyr provides 40 TPS on Nozomi by default, no api key
# required. Add your own here only if you need more capacity (it's used
# as spillover).
# [[network.nozomi.api_keys]]
# api_key = "<your-nozomi-key>"
# tps = 50

# Optional — Lunar has no Zephyr-provided default key. To use it, add your
# own Lunar api key here.
# [[network.lunar.api_keys]]
# api_key = "<your-lunar-key>"
# tps = 50

# Optional: Zephyr provides a default Astralane key, no api key required.
# Add your own here only if you need more capacity (it's used as spillover).
# [[network.astralane.api_keys]]
# api_key = "<your-astralane-key>"
# tps = 50

[[network.stellium.api_keys]]
api_key = "<your-stellium-key>"
tps = 50

# Optional — Zephyr provides 40 TPS on Falcon by default, no api key
# required. Add your own here only if you need more capacity (it's used
# as spillover).
# [[network.falcon.api_keys]]
# api_key = "<your-falcon-key>"
# tps = 50

# Beam needs your own key (no Zephyr default). One Beam key works for all
# three relays; split your account's TPS budget across them rather than
# repeating the full amount.
# [[network.beam_astralane.api_keys]]
# api_key = "<your-beam-key>"
# tps = 1
# [[network.beam_bloxroute.api_keys]]
# api_key = "<your-beam-key>"
# tps = 1
# [[network.beam_falcon.api_keys]]
# api_key = "<your-beam-key>"
# tps = 1

[general]
private_key = "<your-base58-private-key>"                      # Set once; encrypted to wallet.enc + wallet.key on first start, then removed from this file
access_code = "<your-access-code>"                             # Required: bot refuses to start if empty. Use a long random string
fee_percentiles = [5500, 6500, 7000, 7500, 8000, 8500, 9000]   # Fee percentiles surfaced in the fee bar
tip_percentiles = [7000, 7500, 8000, 8500, 9000, 9800]         # Tip percentiles surfaced in the tip bar
log_level = "info"                                             # Log verbosity: error|warn|info|debug|trace ("trace" = full per-tx firehose)
nonces = []                                                    # Nonce pubkeys (populated by --generate-nonces)

[general]

FieldDescription
private_keyYour wallet's base58-encoded private key. Set it once to load (or replace) your wallet: on the next start it's encrypted into wallet.enc + wallet.key, removed from config.toml, and read from the encrypted files on every startup after. Replacing it backs up the previous wallet first. Back up both files together.
fee_percentilesPriority fee percentiles (×100) the backend subscribes to, displayed in the fee bar
tip_percentilesTip percentiles (×100) the backend subscribes to, displayed in the tip bar
access_codePassword the extension uses to authenticate with the backend. Required — the bot refuses to start if it's empty, since the web interface is reachable from your machine's public IP. Use a long random string.
log_levelLog verbosity for the zephyr target: error | warn | info | debug | trace (default info). debug adds lifecycle logs; trace adds the full per-broadcast firehose.
noncesDurable nonce account pubkeys (populated by --generate-nonces)

[network]

FieldDescription
rpc_urlSolana RPC endpoint for read-only calls (account lookups, nonce recovery). Leave empty to use the Zephyr-provided default.
grpc_urlgRPC endpoint for real-time streaming. Leave empty to use the Zephyr-provided default (Helius LaserStream — NY or Frankfurt region auto-selected at startup based on your machine's location)
grpc_tokenAuth token for the gRPC endpoint. Ignored when grpc_url is empty; an empty token is honored for providers that don't authenticate
processorWhich processors to use: all or a specific one (jito, helius, etc.)

WARNING

Never duplicate rpc_url in network.node.urls. rpc_url is the read lane, node.urls is the send lane for the Node processor. Pointing them at the same endpoint turns your read RPC into a hot send target and burns rate limit quickly.

[trade]

These three values act as defaults used to seed each new mint's record in positions.json the first time Zephyr fetches its MintCache. Once a mint has a record, its per-mint values override these defaults, and any change made in the UI while that mint is active is saved to that record, not back to config.toml.

FieldDescription
entry_pctDefault entry target as % from reference price. Negative = wait for dip (e.g., -12.0 = buy when price drops 12%).
exit_pctDefault take-profit target as % above buy price (e.g., 5.0 = sell at +5%). Triggers when net holdings (after protocol fees) would be worth amount × (1 + exit_pct/100) SOL.
target_change_delayDefault ms between reference price updates in auto-rebuy mode.

Session state not in [trade]:

  • Active mint: stored in positions.json top-level active_mint, so Zephyr resumes on the same mint after a restart.
  • Buy amount: kept in UI memory only. Flows to the backend with every bot_buy / bot_auto_rebuy command. A backend restart re-seeds amount from open_cost_sol of the active mint so the take-profit target stays correct mid-Hold.

Buy / sell params

[buy] and [sell] share the same structure.

FieldDescription
slippageSlippage tolerance in bps (100 = 1%, 200 = 2%)
max_block_differenceTransaction freshness window in slots
nonce_delayNonce rotation interval in ms
panic_slippageSlippage for panic sell (- key) in bps. Default 1000 = 10%

Compute unit limit is set per-AMM in src/bot/execute.rs::cu_limit (PumpFun ~80k, PumpSwap ~120k, Raydium CPMM ~140k, Meteora DAMM ~160k). Values were sized from observed consumption + ~15% headroom so priority fee paid isn't wasted on unused CU budget.

Dynamic fee

FieldDescription
percentileFee percentile (×100). Example: 7500 = p75. Should be one of [general].fee_percentiles; if not, it is snapped to the nearest configured value.
fee_multiplierMultiply the fee by this factor. 1.0 = as-is
fee_capMax fee in microlamports. 0 = no cap

Dynamic tip

Every tip-paying processor (Jito, Nextblock, Helius, Zeroslot, Nozomi, Lunar, Astralane, Stellium, Falcon, all four Bifrost subproviders, and all three Beam subproviders) draws its tip from a single dynamic value sourced from sol-fee-tracker's aggregate tip pool. The legacy per-processor tip = 0.005 field has been removed.

FieldDescription
percentileTip percentile (×100). Should be one of [general].tip_percentiles (snapped to the nearest if not)
tip_multiplierMultiply the raw tip by this factor
tip_floorMinimum SOL tip (also stream-fallback when pool has fewer than 20 non-dust samples)
tip_capMaximum SOL tip. Set equal to tip_floor to pin a constant

The tip is the live value at percentile, multiplied by tip_multiplier, then clamped to [tip_floor, tip_cap].

Leader aware sizing

Optionally, [buy.leader_aware] / [sell.leader_aware] tune the tip and fee separately based on whether the upcoming slot leader orders by tip or by priority fee, so you stop paying for a lever that wins nothing. Off by default. See Leader Aware Tip & Fee Sizing.

Pump guard

Optionally, [buy.pump_guard] stops auto-rebuy when it detects a price spike so you do not fill a buy at the top of a pump-and-dump, leaving it to you to re-arm. Off by default. Manual Buy presses are never affected. See Pump Guard.


Processors

Each processor sends your transaction through a different block builder or RPC provider. Enable the ones you have access to.

Two rate-limiting gates

Every send passes through two gates in series:

  1. Processor-level delay (buy_delay / sell_delay in ms): minimum interval between consecutive sends from this processor, regardless of which key is used.
  2. Per-key TPS (keyed processors only): each [[network.<proc>.api_keys]] has its own tps budget; the throttle walks the list first-fit and picks the next key whose 1000/tps-ms interval has elapsed. Aggregate processor TPS = sum of per-key values, so adding more keys scales throughput horizontally.

Both gates must clear per send. On Too Many Requests / 429 errors, either lower the key's tps or increase the processor's buy_delay / sell_delay.

Direct processors

ProcessorRecommended TPSNotes
noden/aAnonymous RPC sender. Multi-URL via urls = [...], no api_keys block
jito10
nextblock50
helius50Helius Sender. Zephyr provides a default key (no api key required); your per-IP TPS slice is carved from a shared pool. Adding your own [[network.helius.api_keys]] extends capacity (it's used as spillover)
zeroslot400slot. Zephyr provides 40 TPS by default — no api key required. Adding your own [[network.zeroslot.api_keys]] extends capacity (it's used as spillover)
nozomi40Temporal (nozomi.temporal.xyz). Zephyr provides 40 TPS by default (no api key required). Adding your own [[network.nozomi.api_keys]] extends capacity (it's used as spillover)
lunar50Lunar Lander. No Zephyr-provided key — add your own [[network.lunar.api_keys]] to use it
astralane50Iris2 gateway. Plain-text body (base64 of signed tx, no JSON-RPC envelope); api-key and method go in the URI query string. Shares astraXXX tip accounts with the Bifrost /astralane subroute. Zephyr provides a default key (no api key required). Adding your own [[network.astralane.api_keys]] extends capacity (it's used as spillover)
stellium50Per-customer regional endpoints; api key is the trailing path component. Min tip 0.001 SOL (silently dropped below — tip_floor covers it)
falcon40corvus-labs.io / falcon.wtf. NYC region only. Min tip 0.001 SOL (tip_floor covers it). Zephyr provides 40 TPS by default — no api key required. Adding your own [[network.falcon.api_keys]] extends capacity (it's used as spillover)

Standard shape for keyed processors:

toml
[network.<name>]
enabled = true
buy_delay = 100
sell_delay = 100

[[network.<name>.api_keys]]
api_key = "your-api-key"
tps = <see table>

Node is slightly different (multi-URL, no api_keys):

toml
[network.node]
enabled = true
urls = ["https://your-rpc-endpoint"]
buy_delay = 100
sell_delay = 100

Multiple keys per processor

Each processor accepts any number of [[network.<proc>.api_keys]] blocks. Four 5-TPS Nextblock keys combine to a 20-TPS fleet:

toml
[[network.nextblock.api_keys]]
api_key = "key1"
tps = 5

[[network.nextblock.api_keys]]
api_key = "key2"
tps = 5

[[network.nextblock.api_keys]]
api_key = "key3"
tps = 5

[[network.nextblock.api_keys]]
api_key = "key4"
tps = 5

With buy_delay = 50 (matching the 20 TPS total) the processor sends every 50 ms, rotating keys so no single key exceeds its 5 TPS budget.

Bifrost proxies

Bifrost routes through underlying providers. IP-allowlisted, no api_keys needed.

toml
[network.bifrost_bloxroute]
enabled = true
buy_delay = 50
sell_delay = 50

# Same shape for bifrost_astralane, bifrost_blockrazor, bifrost_lunarlander

Beam (RPC Fast)

Beam is one endpoint that routes to three relays — beam_astralane, beam_bloxroute, and beam_falcon — each its own toggleable subprovider. Unlike Bifrost, Beam needs an API key (one Beam account works for all three), so each subprovider takes its own api_keys block:

toml
[network.beam_bloxroute]
enabled = true
buy_delay = 25
sell_delay = 25

[[network.beam_bloxroute.api_keys]]
api_key = "your-beam-key"
tps = 1

# Same shape for beam_astralane, beam_falcon

Beam's rate limit is per account, not per relay. The three subproviders rate-limit independently, so when you enable more than one with the same key, split your account's TPS budget across their tps values rather than repeating the full amount (e.g. a ~3 TPS account → tps = 1 on each).

Endpoint override

Every processor (including each Bifrost and Beam subprovider) accepts an optional endpoints list to override the baked-in regional fan-out. Empty / missing = use Zephyr's defaults (today's behavior). Non-empty = exact list that fires.

The audience is operators running a non-NY VPS — by default Zephyr fans out to NY-biased endpoints, which adds 80–150 ms WAN latency from Frankfurt or Tokyo. Set endpoints to nearby regions only:

toml
[network.jito]
enabled = true
buy_delay = 100
sell_delay = 100
endpoints = [
  "https://frankfurt.mainnet.block-engine.jito.wtf",
  "https://amsterdam.mainnet.block-engine.jito.wtf",
  "https://london.mainnet.block-engine.jito.wtf",
]

Entries are origins only — Zephyr appends each processor's auth scheme (path component, query param, header) the same way it does for the defaults.

Nonce vs blockhash (tx_source)

Each processor accepts an optional tx_source = "nonce" (default) or "blockhash", applied to both buy and sell:

toml
[network.nozomi]
enabled = true
tx_source = "nonce"      # durable-nonce txs (never expire)

[network.jito]
enabled = true
tx_source = "blockhash"  # recent-blockhash txs (expire ~60s)

With a nonce pool configured ([general].nonces), every processor defaults to resting on a durable nonce. Set tx_source = "blockhash" on a processor to send recent-blockhash transactions instead. With no nonce pool configured, a nonce processor is skipped — it sends nothing rather than downgrade to blockhash (which would bleed fees on reverting spam); set tx_source = "blockhash" explicitly to trade without durable nonces. Every variant carries the same on-chain dedup id, so exactly one transaction lands regardless of the mix.

Default endpoints

The lists Zephyr fans out to when endpoints is empty / missing. NY-biased by default. Override per processor as shown above for non-NY VPS deployments.

ProcessorDefault origins
jitohttps://{ny,slc,amsterdam,frankfurt,london,singapore,tokyo}.mainnet.block-engine.jito.wtf
heliushttp://{ewr,slc,fra,ams,lon,sg,tyo}-sender.helius-rpc.com
nextblockhttp://{ny,slc,fra,tokyo,london}.nextblock.io
nozomihttps://{pit1,ewr1,ash1,lax1,ams1,lon1,sgp1}.nozomi.temporal.xyz (tyo1 disabled — unstable)
zeroslothttp://ny1.0slot.trade
lunarhttp://nyc.lunar-lander.hellomoon.io (NYC only — Lunar concentrates landing there)
astralanehttps://edge.astralane.io, http://ny.gateway.astralane.io, http://la.gateway.astralane.io
stelliumhttp://ewr1.flashrpc.com
falconhttp://nyc.falcon.wtf (NYC region only — additional regions added as Falcon expands)
bifrost_*http://bifrost-ny.thornode.io (single Thor deployment; each subprovider — bloxroute, astralane, blockrazor, lunarlander — is a path on this origin)
beam_*https://beam.rpcfast.com (single endpoint; the relay — astralane, bloxroute, falcon — is chosen via a query param on this origin)

If a region you'd expect (Lunar EU/Asia, Bifrost AMS/DE/AP, Astralane FR/AMS/JP) isn't in this list, it's off by default — either underperforming today or requiring per-customer allowlisting. Set endpoints = [...] to opt in.


Nonce pool

Required when multiple processors are enabled. Reduces wasted transaction fees.

Create nonce accounts:

bash
./zephyr --generate-nonces 64

Pubkeys are appended to config.toml automatically. Run again to add more. Rent is reclaimable. If you see Nonce pool exhausted warnings, add more.

nonce_delay in [buy] / [sell] controls rotation interval (ms). Nonces are automatically refilled when transactions land on-chain.

Zephyr Docs

Enter the access code to continue.