Running the services

The network's chain-side services can be run by anyone: an archiver and a synchronizer are enough to verify the network's state from Ethereum chain data yourself, and a relayer lets you submit transactions with your own wallet.

To follow the public testnet, every service must use the network's blob address and the synchronizer must use its start slot; the archiver can start at or before it. Both are listed under Network parameters. The start slot changes over time, as the testnet is reset periodically during testing.

Running from source

Each service is run from a clone of the repository with a just recipe. The recipe builds the service in release mode and reads its configuration from services/<service>/.env; copy the service's .env.example there to start:

cp services/archiver/.env.example services/archiver/.env

The synchronizer and the relayer store their state in Postgres. Running just ensure-db creates the synchronizer and relayer databases on a local Postgres at localhost:5432 (user postgres).

Archiver

The archiver follows beacon blocks, stores the blobs sent to the blob address on its filesystem, and serves them over a beacon-compatible HTTP API, so that the synchronizer can read blobs older than the beacon's ~18-day retention window.

just archiver

Configure it in services/archiver/.env:

VariableRequiredDescription
RPC_URLyesEthereum execution-layer RPC endpoint
BEACON_URLyesBeacon API endpoint that serves blob sidecars
FILTER_ADDRESSyesBlob address; only blobs sent to it are archived
INIT_START_SLOTyesSlot to start archiving from on first run; afterwards it resumes from its stored progress. At or before the network's start slot
BLOBS_PATHyesDirectory where the blobs are stored (no database). Grows by ~128 KB per archived blob
HTTP_BINDnoAddress the HTTP API listens on (default 127.0.0.1:3001)

Synchronizer

The synchronizer reads the network's blobs from Ethereum, verifies their proofs, derives the network state (created objects, nullifiers, state roots), and serves it over HTTP to drivers, which use it to build and check their transactions.

just sync

Configure it in services/synchronizer/.env:

VariableRequiredDescription
RPC_URLyesEthereum execution-layer RPC endpoint
BEACON_URLyesBeacon API endpoint that serves blob sidecars
TO_ADDRESSyesBlob address the network's transactions are sent to
INIT_START_SLOTyesGenesis slot, where the state derivation starts on first run. Must be finalized and have an execution block. Use the network's start slot; it must not change once the database has synced
ARCHIVER_URLnoArchiver to read blobs from, e.g. http://localhost:3001. Unset, blobs are read from BEACON_URL, which only serves recent ones
STATE_LOG_DB_URLnoPostgres database holding the state log (default postgres://postgres@localhost:5432/synchronizer)
CONTAINER_DB_PATHnoRocksDB directory for the Merkle trees (default data/synchronizer-db). A rebuildable cache: Postgres holds the authoritative state
HTTP_BINDnoAddress the HTTP API listens on (default 127.0.0.1:3000)
RPC_RETRIESnoHow many times to ask again for a block or blobs a lagging node does not serve yet (default 6)
RPC_RETRY_MSnoDelay in ms between those requests (default 1000)
SYNC_DELAY_MSnoDelay in ms between slots once at chain head (default 173, derived from a 15 req/s RPC rate limit)
CATCHUP_BATCH_SIZEnoSlots fetched concurrently while catching up (default 7, derived from a 15 req/s RPC rate limit)

Once running, curl localhost:3000/sync-progress reports the last processed slot.

Relayer

The relayer accepts transaction proofs from drivers over HTTP and posts them to Ethereum as EIP-4844 blob transactions, signed and paid for by its own wallet.

just relayer

Configure it in services/relayer/.env:

VariableRequiredDescription
RPC_URLyesEthereum execution-layer RPC endpoint
TO_ADDRESSyesBlob address the transactions are sent to
PRIVATE_KEYyesPrivate key of the wallet that signs and pays for the blob transactions. Keep it funded
DB_URLnoPostgres database holding the relay job queue (default postgres://postgres@localhost:5432/relayer)
HTTP_BINDnoAddress the HTTP API listens on (default 127.0.0.1:3200)
MAX_ATTEMPTSnoSubmission attempts per proof before giving up (default 8)
RETRY_INITIAL_SECSnoInitial retry backoff in seconds (default 4)
RETRY_MAX_SECSnoMaximum retry backoff in seconds (default 300)
RECEIPT_POLL_SECSnoInterval between receipt polls in seconds (default 6)
RECEIPT_TIMEOUT_SECSnoTimeout for a submitted transaction's receipt (default: none)
WORKER_IDLE_SLEEP_MSnoWorker sleep when the queue is empty (default 1000)
MAX_FEE_PER_BLOB_GASnoOverride for the blob gas fee cap
FEE_BUMP_AFTER_SECSnoSeconds before replacing a stuck transaction with a higher fee (default: fee bumping disabled)
FEE_BUMP_MULTIPLIER_PCTnoFee increase per bump, in percent (default and minimum 100, i.e. 2x)
FEE_BUMP_MAXnoMaximum replacements per proof (default 5)

Running with Docker Compose

deploy/compose.yaml runs all three services from their published container images, together with a bundled Postgres. All services read their configuration from a single deploy/.env:

cp deploy/.env.example deploy/.env   # fill in the variables below
docker compose -f deploy/compose.yaml up -d

To only verify the network state, without a relayer or a wallet, start just the synchronizer (its archiver comes along):

docker compose -f deploy/compose.yaml up -d synchronizer postgres

The variables to fill in deploy/.env:

VariableRequiredDescription
TO_ADDRESSyesBlob address, shared by all three services (the archiver receives it as its FILTER_ADDRESS)
RPC_URLyesEthereum execution-layer RPC endpoint, used by all three services
BEACON_URLyesBeacon API endpoint that serves blob sidecars, used by the synchronizer and the archiver
INIT_START_SLOTyesStart slot for the synchronizer and the archiver. Use the network's start slot
PRIVATE_KEYrelayerRelayer wallet key; only needed when running the relayer
POSTGRES_PASSWORDnoPassword for the bundled Postgres (default postgres); change it when the host is exposed
IMAGE_TAGnoImage tag to run (default latest); pin a release for reproducible deployments
STATE_LOG_DB_URLnoUse an external Postgres for the synchronizer instead of the bundled one
DB_URLnoUse an external Postgres for the relayer instead of the bundled one
ARCHIVER_URLnoPoint the synchronizer at an external archiver instead of the bundled one
RUST_LOGnoLog filter (default info)

The services listen on 3000 (synchronizer), 3001 (archiver) and 3200 (relayer). The bind addresses, the RocksDB and blob store paths, and the database URLs are set by the compose file, with Docker volumes for their data. See deploy/README.md for hardware requirements and production notes.