Learn how to configure challenger for your OP Stack chain.
This guide provides step-by-step instructions for setting up the configuration and monitoring options for op-challenger.The challenger is a critical fault proofs component that monitors dispute games and challenges invalid claims to protect your OP Stack chain. See the op-challenger explainer for a general overview of this fault proofs feature.The challenger is responsible for:
Monitoring dispute games created by the fault proof system
From the Karst upgrade, cannon-kona is the respected fault-proof game type, replacing op-program. Configure op-challenger with the cannon-kona trace type and a kona-client absolute prestate. A challenger still running the cannon / op-program trace type past Karst will not defend your chain.
op-geth has reached end-of-support (2026-05-31) and does not support the now-active Karst hardfork, so op-geth nodes can no longer follow the canonical chain. Migrate to op-reth, the primary supported execution client. See the op-geth deprecation notice for the full migration plan.
Before configuring your challenger, complete the following steps:
1
Deploy OP Stack chain with fault proofs enabled
L1 contracts deployed with dispute game factory
Fault proof system active on your chain
Access to your chain’s contract addresses
Generate an absolute prestate for your network version - This is critical as the challenger will refuse to interact with games if it doesn’t have the matching prestate
2
Set up required infrastructure access
L1 RPC endpoint (Ethereum, Sepolia, etc.)
L1 Beacon node endpoint (for blob access)
L2 archive node with debug API enabled
Rollup node (op-node) with historical data
3
Prepare configuration files
rollup.json - Rollup configuration file
genesis-l2.json - L2 genesis file
prestate.json - The absolute prestate file generated in step 1
To ensure you’re using the latest compatible versions of OP Stack components, always check the official releases page:OP Stack releases pageThis guide is verified against the following versions:
op-challenger — op-challenger/v1.9.4 (look for the latest op-challenger/v*).
op-reth — v2.2.5 (look for the latest op-reth release). op-reth is both the sequencer’s execution client and the archive node the challenger reads withdrawal proofs from.
kona-client — the absolute prestate is built from a tagged kona-client/v* release (e.g. kona-client/v1.6.0-rc.1). Use the tag matching the prestate registered on your chain; for governance-approved upgrades the version is named in the upgrade notice. See the kona-client prestate tutorial.
Always check the release notes to ensure you’re using compatible versions with your chain’s deployment. Using the op-challenger and kona-client versions named in the upgrade notice (or the latest matching releases) is the supported path.
Building from source gives you full control over the binaries and is the preferred approach for production deployments.Clone and build op-challenger
# Clone the optimism monorepogit clone https://github.com/ethereum-optimism/optimism.gitcd optimism# Check out the latest release of op-challengergit checkout op-challenger/v1.9.4# Generate the embedded superchain config bundle (initializes the# superchain-registry submodule; op-challenger embeds this file at compile time)just build-superchain-go# Install dependencies and buildjust op-challenger# Build the Cannon VM binary used to run dispute tracesjust cannon# Binaries will be available at ./op-challenger/bin/op-challenger# and ./cannon/bin/cannon
Build kona-hostThe challenger also needs kona-host, the pre-image oracle server for the cannon-kona game type. Build it from the kona-client release tag matching the absolute prestate registered on your chain (see Finding the current stable releases), so the server matches the kona-client build committed on chain:
# In a checkout of the tag your prestate was built fromcd rustcargo build --release --bin kona-host# Binary will be available at ./rust/target/release/kona-host
Check that you have properly installed the challenger component:
# Make sure you're in the optimism directory./op-challenger/bin/op-challenger --help# You should see the challenger help output with available commands and flags
After building the binaries, create your challenger working directory:
# Create challenger directory (this should be at the same level as optimism directory)mkdir challenger-nodecd challenger-node# Create necessary subdirectoriesmkdir scriptsmkdir challenger-data# Verify the optimism directory is accessible# Directory structure should look like:# /optimism/ (contains the built binaries)# /challenger-node/ (your working directory)
2
Copy configuration files
# Copy configuration files to your challenger directory# Adjust paths based on your deployment setupcp /path/to/your/rollup.json .cp /path/to/your/genesis-l2.json .
3
Set up environment variables
You’ll need to gather several pieces of information before creating your configuration. Here’s where to get each value:L1 network access:
L1 RPC URL: Your L1 node endpoint (Infura, Alchemy, or self-hosted)
L1 Beacon URL: Beacon chain API endpoint for blob access
L2 network access:
L2 RPC URL: Your op-reth archive node endpoint
Rollup RPC URL: Your op-node endpoint with historical data
Challenger wallet:
Private key for challenger operations (must be funded)
Network configuration:
Game factory address from your contract deployment
Network identifier (e.g., op-sepolia, op-mainnet, or custom)
Copy and paste in your terminal, to create your env file.
# Create .env file with your actual valuescat > .env << 'EOF'# L1 Configuration - Replace with your actual RPC URLsL1_RPC_URL=https://sepolia.infura.io/v3/YOUR_ACTUAL_INFURA_KEY# L2 Configuration - Replace with your actual node endpoints L2_RPC_URL=http://localhost:8545ROLLUP_RPC_URL=http://localhost:8547L1_BEACON=http://sepolia-cl-1:5051# Wallet configuration - Choose either mnemonic + HD path OR private keyMNEMONIC="test test test test test test test test test test test junk"HD_PATH="m/44'/60'/0'/0/0"# PRIVATE_KEY=0xYOUR_ACTUAL_PRIVATE_KEY # Alternative to mnemonic# Network configurationNETWORK=op-sepoliaGAME_FACTORY_ADDRESS=0xYOUR_GAME_FACTORY_ADDRESS# Trace configuration (cannon-kona is the respected game type from the Karst upgrade)TRACE_TYPE=permissioned,cannon-kona# Data directoryDATADIR=./challenger-data# Cannon VM binary (shared by cannon and cannon-kona; built from the optimism repo)CANNON_BIN=<PATH_TO_OPTIMISM_REPO>/cannon/bin/cannon# Configuration filesCANNON_ROLLUP_CONFIG=<PATH_TO_YOUR_ROLLUP_CONFIG>CANNON_L2_GENESIS=<PATH_TO_YOUR_L2_GENESIS># kona executable used as the pre-image oracle server, and the kona-client absolute prestateCANNON_KONA_SERVER=<PATH_TO_KONA_EXECUTABLE>CANNON_KONA_PRESTATE=<PATH_TO_YOUR_KONA_PRESTATE_FILE>EOF
Important: Replace ALL placeholder values (YOUR_ACTUAL_*) with your real configuration values.
4
Understanding key configuration flags
Show --l1-eth-rpc
This is the HTTP provider URL for a standard L1 node, can be a full node. op-challenger will be sending many requests, so chain operators need a node that is trusted and can easily handle many transactions.
Note: Challenger has a lot of money, and it will spend it if it needs to interact with games. That might risk not defending games or challenging games correctly, so chain operators should really trust the nodes being pointed at Challenger.
Show --l1-beacon
This is needed just to get blobs from.
In some instances, chain operators might need a blob archiver or L1 consensus node configured not to prune blobs:
If the chain is proposing regularly, a blob archiver isn’t needed. There’s only a small window in the blob retention period that games can be played.
If the chain doesn’t post a valid output root in 18 days, then a blob archiver running a challenge game is needed. If the actor gets pushed to the bottom of the game, it could lose if it’s the only one protecting the chain.
Show --l2-eth-rpc
This needs to be an op-reth archive node, with debug enabled.
Technically doesn’t need to go to bedrock, but needs to have access to the start of any game that is still in progress.
The withdrawal-proof data the challenger reads via eth_getProof is served by op-reth’s historical-proofs store. Enable it with --proofs-history --proofs-history.storage-version v2, set a persistent --proofs-history.storage-path, and size --proofs-history.window to cover the dispute game window (≥ 28 days). On permissioned chains, --rpc.eth-proof-window bounds how far back eth_getProof will serve. See Running op-reth with historical proofs.
Seed the proofs storage once before starting the node with --proofs-history, or op-reth refuses to start (the proofs-history ExEx panics with Proofs storage not initialized). With the node stopped, run op-reth proofs init --chain <chain-or-genesis> --datadir <reth-datadir> --proofs-history.storage-path <proofs-db-path> --proofs-history.storage-version v2. It snapshots the chain’s current state to seed the sidecar; the ExEx then indexes forward as the node syncs. Initialize at (or near) genesis so the whole fault-proof window is covered — a node seeded at the current tip only serves proofs for blocks after that point.
Show --rollup-rpc
This needs to be an op-node archive node because challenger needs access to output roots from back when the games start. See below for important configuration details:
Safe Head Database (SafeDB) Configuration for op-node:
The op-node behind the op-conductor must have the SafeDB enabled to ensure it is not stateless.
To enable SafeDB, set the --safedb.path value in your configuration. This specifies the file path used to persist safe head update data.
Example Configuration:
--safedb.path <path-to-safe-head-db> # Replace <path-to-safe-head-db> with your actual path
If this path is not set, the SafeDB feature will be disabled.
Never restore the SafeDB from a snapshot. The SafeDB records the safe head as derived from L1, and a snapshot may not reflect the actual on-chain derivation state. If the SafeDB is out of sync with L1, op-challenger will act on an incorrect safe head and may incorrectly attack valid outputs. op-challenger cannot detect this condition — the data it receives appears normal. If you suspect the SafeDB was restored from a snapshot or is otherwise corrupted, delete it and resync from a snapshot that is at least 30 days old — op-node does not backfill the SafeDB, so it will only populate it going forward from that point. A 30-day-old snapshot provides enough history to cover the 28-day dispute game window.
Ensuring Historical Data Availability:
Both op-node and op-reth must have data from the start of the games to maintain network consistency and allow nodes to reference historical state and transactions.
For op-node: Configure it to maintain a sufficient history of blockchain data locally or use an archive node.
For op-reth: Similarly, configure to store or access historical data.
Replace <op-node-archive-node-url> with the URL of your archive node and <path-to-safe-head-db> with the desired path for storing SafeDB data.
Show --private-key
Chain operators must specify a private key or use something else (like op-signer).
This uses the same transaction manager arguments as op-node , batcher, and proposer, so chain operators can choose one of the following options:
a mnemonic
a private key
op-signer endpoints
Show --network
This identifies the L2 network op-challenger is running for, e.g., op-sepolia or op-mainnet.
When using the --network flag, the --game-factory-address will be automatically pulled from the superchain-registry.
When the trace is generated, challenger needs the rollup config and the L2 genesis file. Both files are automatically loaded when a registry --network is used, but custom networks must specify both the L2 genesis and rollup config.
For custom networks not in the superchain-registry, the --game-factory-address and rollup must be specified, as follows:
--cannon-kona-rollup-config rollup.json \--cannon-kona-l2-genesis genesis-l2.json \# use this if running challenger outside of the docker image--cannon-kona-server ./kona \# version of kona-client deployed on chain# if you use the wrong one, you will lose the game# if you deploy your own contracts, you specify the hash, the root of the json file# OP Mainnet uses tagged versions of kona-client# build with: just reproducible-prestate-kona# challenger verifies that onchain --cannon-kona-prestate ./prestate.json \# load the game factory address from system config or superchain registry# point the game factory address at the dispute game factory proxy --game-factory-address
These options vary based on which --network is specified. Chain operators always need to specify a way to load prestates and must also specify the --cannon-kona-server whenever the docker image isn’t being used.
Show --datadir
This is a directory that op-challenger can write to and store whatever data it needs. It will manage this directory to add or remove data as needed under that directory.
If running in docker, it should point to a docker volume or mount point, so the data isn’t lost on every restart. The data can be recreated if needed but particularly if challenger has executed cannon as part of responding to a game it may mean a lot of extra processing.
Show --cannon-kona-prestate / --cannon-kona-prestates-url
The prestate is effectively the version of kona-client that is deployed on chain (run inside the Cannon VM as the cannon-kona game type). And chain operators must use the right version. op-challenger will refuse to interact with games that have a different absolute prestate hash to avoid making invalid claims. If deploying your own contracts, chain operators must specify an absolute prestate hash taken from the just reproducible-prestate-kona command during contract deployment, which will also build the required prestate file.All governance approved releases use a tagged version of kona-client. These can be rebuilt by checking out the version tag and running just reproducible-prestate-kona.
There are two ways to specify the prestate to use:
--cannon-kona-prestate: specifies a path to a single kona-client absolute-prestate file
--cannon-kona-prestates-url: specifies a URL to load prestates from. This enables participating in games that use different prestates, for example due to a network upgrade. The prestates are stored in this directory named by their hash.
# Make sure you're in the challenger-node directorycd challenger-node# Make script executablechmod +x scripts/start-challenger.sh# Start challenger./scripts/start-challenger.sh
The Docker setup provides a containerized environment for running the challenger. This method uses the official Docker image that includes the embedded kona server and Cannon executable.
1
Create environment file
First, create a .env file with your configuration values. This file will be used by Docker Compose to set up the environment variables:
# Create .env file with your actual valuescat > .env << 'EOF'# L1 Configuration - Replace with your actual RPC URLsL1_RPC_URL=https://sepolia.infura.io/v3/YOUR_ACTUAL_INFURA_KEYL1_BEACON=http://sepolia-cl-1:5051# L2 Configuration - Replace with your actual node endpoints L2_RPC_URL=http://localhost:8545ROLLUP_RPC_URL=http://localhost:8547# Wallet configuration - Choose either mnemonic + HD path OR private keyMNEMONIC="test test test test test test test test test test test junk"HD_PATH="m/44'/60'/0'/0/0"# Network configurationNETWORK=op-sepoliaGAME_FACTORY_ADDRESS=0xYOUR_GAME_FACTORY_ADDRESSEOF
Important: Replace ALL placeholder values (YOUR_ACTUAL_*) with your real configuration values.
2
Understanding configuration flags
Each environment variable maps to a specific challenger configuration flag. Here’s what each one does:
Show --l1-eth-rpc
This is the HTTP provider URL for a standard L1 node, can be a full node. op-challenger will be sending many requests, so chain operators need a node that is trusted and can easily handle many transactions.
Note: Challenger has a lot of money, and it will spend it if it needs to interact with games. That might risk not defending games or challenging games correctly, so chain operators should really trust the nodes being pointed at Challenger.
Show --l1-beacon
This is needed just to get blobs from.
In some instances, chain operators might need a blob archiver or L1 consensus node configured not to prune blobs:
If the chain is proposing regularly, a blob archiver isn’t needed. There’s only a small window in the blob retention period that games can be played.
If the chain doesn’t post a valid output root in 18 days, then a blob archiver running a challenge game is needed. If the actor gets pushed to the bottom of the game, it could lose if it’s the only one protecting the chain.
Show --l2-eth-rpc
This needs to be an op-reth archive node, with debug enabled.
Technically doesn’t need to go to bedrock, but needs to have access to the start of any game that is still in progress.
The withdrawal-proof data the challenger reads via eth_getProof is served by op-reth’s historical-proofs store. Enable it with --proofs-history --proofs-history.storage-version v2, set a persistent --proofs-history.storage-path, and size --proofs-history.window to cover the dispute game window (≥ 28 days). On permissioned chains, --rpc.eth-proof-window bounds how far back eth_getProof will serve. See Running op-reth with historical proofs.
Seed the proofs storage once before starting the node with --proofs-history, or op-reth refuses to start (the proofs-history ExEx panics with Proofs storage not initialized). With the node stopped, run op-reth proofs init --chain <chain-or-genesis> --datadir <reth-datadir> --proofs-history.storage-path <proofs-db-path> --proofs-history.storage-version v2. It snapshots the chain’s current state to seed the sidecar; the ExEx then indexes forward as the node syncs. Initialize at (or near) genesis so the whole fault-proof window is covered — a node seeded at the current tip only serves proofs for blocks after that point.
Show --rollup-rpc
This needs to be an op-node archive node because challenger needs access to output roots from back when the games start. See below for important configuration details:
Safe Head Database (SafeDB) Configuration for op-node:
The op-node behind the op-conductor must have the SafeDB enabled to ensure it is not stateless.
To enable SafeDB, set the --safedb.path value in your configuration. This specifies the file path used to persist safe head update data.
Example Configuration:
--safedb.path <path-to-safe-head-db> # Replace <path-to-safe-head-db> with your actual path
If this path is not set, the SafeDB feature will be disabled.
Never restore the SafeDB from a snapshot. The SafeDB records the safe head as derived from L1, and a snapshot may not reflect the actual on-chain derivation state. If the SafeDB is out of sync with L1, op-challenger will act on an incorrect safe head and may incorrectly attack valid outputs. op-challenger cannot detect this condition — the data it receives appears normal. If you suspect the SafeDB was restored from a snapshot or is otherwise corrupted, delete it and resync via consensus sync from a snapshot that is at least 30 days old — op-node does not backfill the SafeDB, so it will only populate it going forward from that point. A 30-day-old snapshot provides enough history to cover the 28-day dispute game window.
Ensuring Historical Data Availability:
Both op-node and op-reth must have data from the start of the games to maintain network consistency and allow nodes to reference historical state and transactions.
For op-node: Configure it to maintain a sufficient history of blockchain data locally or use an archive node.
For op-reth: Similarly, configure to store or access historical data.
Replace <op-node-archive-node-url> with the URL of your archive node and <path-to-safe-head-db> with the desired path for storing SafeDB data.
Show --private-key
Chain operators must specify a private key or use something else (like op-signer).
This uses the same transaction manager arguments as op-node , batcher, and proposer, so chain operators can choose one of the following options:
a mnemonic
a private key
op-signer endpoints
Show --network
This identifies the L2 network op-challenger is running for, e.g., op-sepolia or op-mainnet.
When using the --network flag, the --game-factory-address will be automatically pulled from the superchain-registry.
When the trace is generated, challenger needs the rollup config and the L2 genesis file. Both files are automatically loaded when a registry --network is used, but custom networks must specify both the L2 genesis and rollup config.
For custom networks not in the superchain-registry, the --game-factory-address and rollup must be specified, as follows:
--cannon-kona-rollup-config rollup.json \--cannon-kona-l2-genesis genesis-l2.json \# use this if running challenger outside of the docker image--cannon-kona-server ./kona \# version of kona-client deployed on chain# if you use the wrong one, you will lose the game# if you deploy your own contracts, you specify the hash, the root of the json file# OP Mainnet uses tagged versions of kona-client# build with: just reproducible-prestate-kona# challenger verifies that onchain --cannon-kona-prestate ./prestate.json \# load the game factory address from system config or superchain registry# point the game factory address at the dispute game factory proxy --game-factory-address
These options vary based on which --network is specified. Chain operators always need to specify a way to load prestates and must also specify the --cannon-kona-server whenever the docker image isn’t being used.
Show --datadir
This is a directory that op-challenger can write to and store whatever data it needs. It will manage this directory to add or remove data as needed under that directory.
If running in docker, it should point to a docker volume or mount point, so the data isn’t lost on every restart. The data can be recreated if needed but particularly if challenger has executed cannon as part of responding to a game it may mean a lot of extra processing.
Show --cannon-kona-prestate / --cannon-kona-prestates-url
The prestate is effectively the version of kona-client that is deployed on chain (run inside the Cannon VM as the cannon-kona game type). And chain operators must use the right version. op-challenger will refuse to interact with games that have a different absolute prestate hash to avoid making invalid claims. If deploying your own contracts, chain operators must specify an absolute prestate hash taken from the just reproducible-prestate-kona command during contract deployment, which will also build the required prestate file.All governance approved releases use a tagged version of kona-client. These can be rebuilt by checking out the version tag and running just reproducible-prestate-kona.
There are two ways to specify the prestate to use:
--cannon-kona-prestate: specifies a path to a single kona-client absolute-prestate file
--cannon-kona-prestates-url: specifies a URL to load prestates from. This enables participating in games that use different prestates, for example due to a network upgrade. The prestates are stored in this directory named by their hash.