> ## Documentation Index
> Fetch the complete documentation index at: https://docs.volmarket.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Deployment

> What runs where — the Anchor programs on devnet, the frontend on Vercel, and the keeper on an always-on host.

Volmarket is four pieces with four homes. They deploy independently.

| Piece                                 | Where                                                                                         | Why                                                                                                                                              |
| ------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `frontend-react/`                     | **Vercel** — live at [volmarket.xyz](https://volmarket.xyz)                                   | Vite build; talks only to public RPC and read-only chain state.                                                                                  |
| `signal_markets/` + `mock_validator/` | **Solana devnet**                                                                             | The programs live where the data is.                                                                                                             |
| `keeper/`                             | **An always-on host** — Railway in this deployment; Fly.io or a VPS work too. **Not** Vercel. | A long-lived process that watches a stream, polls the chain, and serves the frontend's signal feed. Serverless functions will not keep it alive. |
| the repository                        | **GitHub**                                                                                    | Source of truth the others build from.                                                                                                           |

## Programs → Solana devnet

```bash theme={null}
cd signal_markets
anchor build
anchor deploy --provider.cluster devnet
```

`anchor build` produces `signal_markets/target/idl/signal_markets.json`. Note the deployed program id and set it as `PROGRAM_ID` for the keeper.

Deploy `mock_validator` as well (see its README) and point `TXLINE_PROGRAM_ID` at it.

The program accepts exactly two validator addresses for `resolve_market`: the real TxLINE `txoracle` (`6pW64gN1…`) and `mock_validator` (`FPnwSSp2…`). Which one is used depends on two keeper settings that must agree — see [selecting a path](/settlement#selecting-a-path).

To settle through the real validator:

1. Set `MOCK_VALIDATOR=false` on the keeper, so it fetches a real proof instead of a stub.
2. Point `TXLINE_PROGRAM_ID` at the `txoracle` address — this is already the devnet default, so leaving it blank also works.
3. Raise the resolve transaction's compute budget — verification costs \~234k CU.

To run the mock path, set `TXLINE_PROGRAM_ID` explicitly to the `mock_validator` address and leave `MOCK_VALIDATOR=true`. Leaving `TXLINE_PROGRAM_ID` blank here would send a stub proof to the real validator, which rejects it.

## Frontend → Vercel

Point a Vercel project at the repository. The root `vercel.json` already sets the build:

* **Build command:** `cd frontend-react && npm install && npm run build`
* **Output directory:** `frontend-react/dist`

Set the app's `VITE_*` environment variables in the Vercel project settings: RPC URL, USDC mint, Privy app id, fund endpoint, and fee recipient. These are client-side and non-secret. Vite bakes them in at build time, so redeploy after changing one.

## Keeper → always-on host

A persistent Node process. Deploy it somewhere that keeps a process running.

```bash theme={null}
cd keeper
npm ci
npm run build && npm start
```

The keeper carries a self-contained `signal_markets.idl.json`, so it runs without the `signal_markets/` workspace beside it — `IDL_PATH` defaults to `./signal_markets.idl.json`.

### Environment

Set these on the host, starting from `keeper/.env.example`:

| Variable                   | Value                                                                                                                                                                    |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `TXLINE_NETWORK`           | `devnet`                                                                                                                                                                 |
| `SOLANA_RPC_URL`           | Your own RPC endpoint. Blank falls back to public devnet. Supply your own key; none is committed to the repository.                                                      |
| `PROGRAM_ID`               | Your deployed `signal_markets` id                                                                                                                                        |
| `IDL_PATH`                 | `./signal_markets.idl.json` (default)                                                                                                                                    |
| keeper signing key         | Via the host's secret manager — see [Secrets](#secrets)                                                                                                                  |
| `TXLINE_BASE_URL`          | Feed host. Blank uses the network default — `txline-dev.txodds.com` on devnet.                                                                                           |
| `TXLINE_PROGRAM_ID`        | The validator program the keeper CPIs into. Blank defaults to the **real** `txoracle` on devnet; set it to `FPnwSSp2…` for the mock path.                                |
| `TXLINE_SERVICE_LEVEL`     | `1` (World Cup and international friendlies, free)                                                                                                                       |
| `BOOTSTRAP_LIQUIDITY_USDC` | Opposing-pool seed per new market when both sides are empty (default `10`). `0` disables seeding entirely.                                                               |
| `BOOTSTRAP_MAX_USDC`       | Cap on the house seed per pool (default `1000`), bounding exposure. Past the cap, delivered odds fall below true odds and the frontend caps the displayed odds to match. |
| `APP_USDC_MINT`            | The app's USDC mint                                                                                                                                                      |
| `MOCK_VALIDATOR`           | `true` (default) builds a stub proof; `false` fetches a real TxLINE Merkle proof. This selects the *proof payload*, not the program — pair it with `TXLINE_PROGRAM_ID`.  |
| `REPLAY_FILE`              | Optional — replay a recorded TxLINE capture instead of the live stream, e.g. `replay/odds-capture.json`                                                                  |

For two-sided markets to pay out, the keeper must be **running** and **funded**: SOL for transaction fees, plus USDC if `BOOTSTRAP_LIQUIDITY_USDC > 0`, which it spends seeding empty pools.

### Container hosts

Ready-made artifacts live in `keeper/`: `Dockerfile`, `.dockerignore`, `.railwayignore`, and `fly.toml`.

<Tabs>
  <Tab title="Railway">
    Create a new service with root directory `keeper/`, set the environment variables, and deploy. Running `railway up` from `keeper/` also works.
  </Tab>

  <Tab title="Fly.io">
    ```bash theme={null}
    fly launch --no-deploy --copy-config
    # set secrets
    fly deploy
    ```
  </Tab>
</Tabs>

<Warning>
  **IPv6 DNS on container hosts.** Some container hosts have broken IPv6 egress while Node 24 resolves DNS IPv6-first. Every RPC `fetch` then fails with a bare `TypeError: fetch failed`, which crash-loops the keeper on startup.

  `src/index.ts` forces IPv4-first via `setDefaultResultOrder("ipv4first")`. Keep `NODE_OPTIONS=--dns-result-order=ipv4first` set as well.
</Warning>

<Note>
  **RPC rate limits.** If the RPC endpoint returns 429s, the keeper's crash guards absorb them and let its retry timers resume the work rather than restarting. A restart would replay the full startup burst — market scan, board seed, bootstrap deposits, resolve and claim — against the same throttled endpoint, which earns the next 429. Use a dedicated RPC endpoint for anything beyond light testing.
</Note>

## Secrets

* The keeper's signing key is set **only** through the host's secret manager. Never commit it, and never bake it into an image.
* Never commit `.env` — only `.env.example`, with placeholders — or any Solana keypair (`*id.json`, `.fee-wallet.json`, `.treasury.json`). `.gitignore` covers these.
* Never commit an RPC URL containing an API key. Config defaults in the repository are public endpoints only; supply your own key through environment variables on the host.
* The frontend holds no secrets — only public RPC URLs, program ids, and mints.

<Warning>
  A leaked keeper key lets someone submit resolutions as you, wasting fees. A leaked wallet key can drain funds. If either is exposed, rotate immediately.
</Warning>
