<!-- https://zunderlabs.com/docs/concepts/networks · Markdown version of the page -->

# Paper, testnet and mainnet

The three modes Guard runs in, what each one sends where, and why mainnet needs you to type it.

Every strategy and every Guard goes through the same three steps, in order. Zunder holds itself to this: backtest, paper, testnet, live.

| | Paper | Testnet | Mainnet |
|---|---|---|---|
| Prices | Hyperliquid mainnet, real | Hyperliquid testnet | Hyperliquid mainnet |
| Orders sent | none | to `api.hyperliquid-testnet.xyz` | to `api.hyperliquid.xyz` |
| Money | none | test USDC, no value | real |
| Stops rest on the venue | no (simulated) | yes | yes |
| How you get there | the default | `--network testnet` | type the network, confirm the account |

## Paper

Paper mode reads real prices and judges every order exactly as it would on mainnet, then records the decision instead of sending it.

**Example.** Your bot buys ETH. Guard answers "resized to 0.41 ETH, rule: max loss at the stop", writes it to the journal and the event log, and sends nothing.

:::note[Planned]
Guard's paper mode is planned. Zunder's own paper books are different and run today: they replay the backtest on live candles (`docs/paper.md`).
:::

## Testnet

Testnet is a separate Hyperliquid network with test money. Orders are real orders there: stops rest on the venue and fire.

This is where Zunder runs today. Its runner (`zunder-runner testnet`) trades one book on a testnet account, with the risk engine, the journal, stops on the venue and the kill switch (`docs/testnet.md`).

Things that differ from mainnet (`docs/testnet.md`, "What differs between the networks"):

- **Signatures.** Orders are signed for one network. A testnet signature is rejected on mainnet and the reverse: the "phantom agent" carries source `"b"` on testnet and `"a"` on mainnet.
- **API wallets, asset ids and nonces** are per network. An API wallet approved on testnet does not exist on mainnet.
- **Prices** on testnet are not mainnet's.

## Mainnet

Mainnet trades real money. Guard never gets there by itself.

Zunder's own code has a mainnet path, built and reviewed, **never run, and switched off**. It refuses to start unless every one of these holds at once (`docs/testnet.md`, "The guards, all at once"):

- the command names mainnet, and the config says `network = "mainnet"` (there is no default network);
- `allow_mainnet = true`, an equity cap, the account and the API wallet in the config;
- a confirmation that names the traded account, read at every start, before the key is read;
- the key arrives on standard input only, and its address must be the configured API wallet;
- the risk journal was started for mainnet and this account.

:::note[Planned]
Guard follows the same idea with fewer steps:

```sh
zunder-guard init --interactive    # answer the mode with "mainnet" in full, then the account again
ZUNDER_GUARD_MAINNET_CONFIRM=0xYourAccountAddress zunder-guard run --network mainnet
```

Guard refuses mainnet unless you typed the network in full and the account again during the setup, and every start names the account in `ZUNDER_GUARD_MAINNET_CONFIRM` (the installer writes it to a file only root can read; deleting that file stops mainnet). A config file alone never moves Guard to mainnet. A testnet journal never opens on mainnet.
:::

## Changing mode

Each mode has its own journal. Moving from testnet to mainnet starts a new journal with a new peak. Your limits carry over in the config; the risk state does not.
