Configuration Overview
Configuration Overview
Section titled “Configuration Overview”ZEVM supports two runtime modes:
Normative sources:
docs/specs/prd.mdanddocs/specs/json-rpc-contract.md.This page is an operational summary. For product-contract disputes, follow those normative sources.
- trusted mode: writable local Ethereum dev node
- light mode: read-only proof-backed, consensus-anchored client
Forking is a trusted-mode configuration, not a third mode.
Phase 1 Scope (Public Contract)
Section titled “Phase 1 Scope (Public Contract)”Use canonical scope sources instead of repeating the full phase-1 checklist:
- product/runtime and transport boundaries: Canonical Specs
- startup/config behavior and precedence: this page and mode pages in this section
- release/build pins and baked defaults: Release Metadata Runbook
- deferred-surface promotion and phase-1 boundary/graduation criteria: Specs And Process
Phase 1 remains source-first; release artifact jobs may publish CLI/C ABI/npm artifacts, while installer UX, signing/notarization, and byte-identical binary reproducibility guarantees are out of scope.
Shared CLI Flags
Section titled “Shared CLI Flags”| Flag | Type | Default |
|---|---|---|
--config | JSON file path | none (when provided, config must include exactly one mode branch) |
--mode | trusted or light | trusted (effective only when neither --config nor explicit --mode is supplied) |
--host | bind host | 127.0.0.1 |
--port | TCP port | 8545 |
--engine-host | Engine API bind host | disabled unless --engine-host, --engine-port, or top-level engineRpc is supplied |
--engine-port | Engine API TCP port | 8551 when Engine API is enabled |
RPC Exposure And Hardening
Section titled “RPC Exposure And Hardening”ZEVM phase 1 serves JSON-RPC over plain HTTP only. ZEVM does not provide built-in TLS, JWT, or JSON-RPC authentication.
--host 127.0.0.1 keeps the listener local to the machine.
Binding --host to a non-loopback interface (for example, 0.0.0.0) exposes RPC to any network path that can reach that interface.
The optional Engine API listener is trusted-mode only and is disabled by default. Enable it with CLI --engine-host / --engine-port or a top-level config engineRpc object. Exposing the Engine API on a reachable interface is unsafe without an external authenticated proxy or isolated network.
Recommended phase-1 operator pattern:
- keep ZEVM bound to loopback (
--host 127.0.0.1) - place a reverse proxy or gateway in front of ZEVM for TLS termination and authentication
- enforce source-IP allowlists at ingress and host firewall layers
- deny direct external access to the ZEVM RPC port
Ingress/IP allowlist recommendations:
- allow HTTPS ingress only from known operator/bastion CIDRs (for example, office/VPN egress ranges)
- if ZEVM is not on loopback, allow ZEVM RPC port ingress only from the proxy host/private subnet
- apply explicit deny-all defaults for all non-allowlisted sources
- do not expose ZEVM RPC directly to the public Internet
Example reverse-proxy configuration (NGINX, TLS + basic auth + source-IP allowlist):
server { listen 443 ssl; server_name zevm-rpc.example.com;
ssl_certificate /etc/letsencrypt/live/zevm-rpc.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/zevm-rpc.example.com/privkey.pem;
auth_basic "ZEVM RPC"; auth_basic_user_file /etc/nginx/.htpasswd;
allow 203.0.113.10; # operator bastion/VPN egress IP allow 198.51.100.0/24; # trusted corporate CIDR deny all;
location / { proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://127.0.0.1:8545; }}This pattern keeps TLS/auth/IP filtering in the proxy layer while ZEVM remains plain HTTP on local/private transport in phase 1. For a light-mode operator checklist, see Light-Mode Configuration -> RPC Exposure And Hardening.
Phase-1 transport limits are fixed, not configurable: 64 active TCP connections, 15,000 ms read/write socket timeouts, 8,192 byte HTTP header buffer, and 1,048,576 byte request body limit. Larger request bodies return HTTP 413 without a JSON-RPC body.
Startup Logging Surfaces (Phase 1)
Section titled “Startup Logging Surfaces (Phase 1)”Phase 1 has no dedicated ZEVM startup log-level or log-path knobs in the CLI/config contract.
Operator startup diagnostics come from process stderr capture in shell redirection or service-manager logging.
Explicit --config load failures emit one startup error record before exit. The record names the config path and includes a failureClass such as missing-file, unreadable-file, malformed-json, schema, or validation.
For concrete capture examples and failure triage flow, see Troubleshooting -> Capture Startup Logs (Phase 1).
Config File Schema
Section titled “Config File Schema”--config loads one JSON file with shared RPC settings and exactly one mode branch.
Trusted-mode config shape
Section titled “Trusted-mode config shape”{ "rpc": { "host": "127.0.0.1", "port": 8545 }, "engineRpc": { "host": "127.0.0.1", "port": 8551 }, "mode": { "trusted": { "chainId": 31337, "coinbaseIndex": 0, "initialBalance": "10000000000000000000000", "gasPrice": "2000000000", "baseFee": "1000000000", "blobBaseFee": "1", "maxPriorityFeePerGas": "1000000000", "blockGasLimit": 30000000, "mining": { "type": "auto" }, "hardfork": { "cancunTimestamp": 0, "pragueTimestamp": 9223372036854775807, "osakaTimestamp": 9223372036854775807 }, "fork": null, "genesis": null, "chainRlp": null } }}Trusted hardfork policy is explicit and owned by the runtime. chainId = 1 defaults to the mainnet activation schedule; any other trusted chain defaults to a dev schedule with Cancun active from genesis and Prague/Osaka inactive. mode.trusted.hardfork can override any activation field with decimal u64 values: homesteadBlock, daoBlock, tangerineWhistleBlock, spuriousDragonBlock, byzantiumBlock, petersburgBlock, istanbulBlock, muirGlacierBlock, berlinBlock, londonBlock, arrowGlacierBlock, grayGlacierBlock, mergeBlock, shanghaiTimestamp, cancunTimestamp, pragueTimestamp, osakaTimestamp, and secondsPerSlot.
mode.trusted.genesis or CLI --genesis points at a genesis JSON file and imports its alloc object at startup. Alloc entries may use balance or legacy wei, plus optional nonce, code, and storage. When a genesis file is supplied, ZEVM seeds state from that allocation instead of pre-funding the deterministic dev accounts; the managed signing accounts remain available through eth_accounts.
mode.trusted.chainRlp or CLI --chain-rlp points at a concatenated RLP block stream. ZEVM imports those blocks after genesis as query-only block history, makes the imported head canonical, and keeps decoded transaction bodies available for block and transaction-by-position reads. It does not execute imported transactions or materialize imported state, receipts, or logs in phase 1.
Light-mode config shape
Section titled “Light-mode config shape”{ "rpc": { "host": "127.0.0.1", "port": 8545 }, "mode": { "light": { "network": "mainnet", "consensusRpcUrl": "https://beacon.example", "executionRpcUrl": "https://execution.example", "checkpoint": null, "checkpointDir": ".zevm/checkpoints/mainnet", "maxCheckpointAgeSeconds": 1209600, "strictCheckpointAge": false } }}Mode-branch Rules
Section titled “Mode-branch Rules”modemust contain exactly one oftrustedorlight- config containing both branches is invalid
- config containing neither branch is invalid
- without explicit
--mode, runtime mode is selected by the config branch when--configis provided - explicit
--modewith no--configselects runtime mode directly - when both explicit
--modeand--configare provided, explicit--modemust match the single config mode branch or startup fails before opening the HTTP listener - trusted default applies only when no explicit
--modeis supplied and no--configfile is loaded
Config Key Validation
Section titled “Config Key Validation”- allowed top-level keys are
rpc,engineRpc, andmode; unknown top-level keys are invalid - unknown keys inside
rpc,engineRpc,mode,mode.trusted,mode.light, and trusted structured objects (mining,hardfork,fork) are invalid rpcis optional; when omitted, shared defaults apply (host = 127.0.0.1,port = 8545)- when
rpcis present,hostandportdefault independently if omitted engineRpcis optional and trusted-mode only; when present, omittedhostdefaults to the main RPC host and omittedportdefaults to8551
Startup Precedence
Section titled “Startup Precedence”For non-checkpoint startup options across modes:
- explicitly supplied CLI flags
- config file values
- defaults for any field still unresolved after CLI/config merge
CLI defaults are not treated as explicit overrides over config values.
Parser-provided defaults are applied only after explicit CLI/config merge and do not count as explicit inputs.
Because of that ordering, parser defaults cannot create a mode-branch conflict with --config; only explicitly supplied --mode can conflict with the config mode branch.
Structured trusted-setting resolution:
miningresolves as one unit from CLI--miningand--block-time; if either flag is explicitly supplied, ZEVM buildsminingfrom CLI and ignoresmode.trusted.mininghardforkhas no phase-1 CLI flags; ZEVM starts from the default schedule for the resolvedchainId, then applies anymode.trusted.hardforkfield overridesforkresolves as one unit from CLI--fork-urland--fork-block-number; if either flag is explicitly supplied, ZEVM buildsforkfrom CLI and ignoresmode.trusted.forkgenesisresolves by normal field precedence from CLI--genesis, thenmode.trusted.genesis, then absentchainRlpresolves by normal field precedence from CLI--chain-rlp, thenmode.trusted.chainRlp, then absent- when no related CLI flags are present for that unit, ZEVM uses config value for that unit, then mode default
Light-Mode Checkpoint Resolution
Section titled “Light-Mode Checkpoint Resolution”Checkpoint resolution applies only to selecting the initial light checkpoint:
- user-supplied CLI checkpoint (
--checkpoint), when provided - config checkpoint (
mode.light.checkpoint), when set to a non-null value - persisted startup checkpoint input (
checkpointDir/checkpoint) - baked network default checkpoint
checkpointDir, maxCheckpointAgeSeconds, and strictCheckpointAge are non-checkpoint controls.
They resolve by normal startup precedence (explicit CLI flag > config value > default) and are not part of the four-step checkpoint selection order above.
mode.light.checkpoint may be omitted or set to null; both are treated as absent for checkpoint precedence.
--strict-checkpoint-age is a presence flag: present means true, omitted means false, and assignment forms like --strict-checkpoint-age=false are invalid.
When step 1 or step 2 wins, zevm_lightSyncStatus.checkpointSource is explicit for both CLI-supplied and config-supplied checkpoints.
checkpointSource is startup-source metadata and does not change when lastCheckpoint advances.
This precedence is strict: CLI checkpoint > config checkpoint > persisted checkpoint file > baked default.
If checkpointDir is missing at startup (including missing network-expanded directory), step 3 is treated as absent input and selection falls through by precedence.
If checkpointDir/checkpoint is missing, step 3 is treated as absent input and selection falls through by precedence.
If checkpointDir/checkpoint exists but is unreadable, startup fails before opening the HTTP listener.
When CLI checkpoint, config checkpoint, and persisted checkpointDir/checkpoint are all absent, ZEVM selects the baked network default and zevm_lightSyncStatus.checkpointSource reports default.
The baked default checkpoint is a startup precedence input, not a frozen public compatibility constant.
Baked default values are bundled per ZEVM release/build and may rotate across releases.
This docs page specifies selection behavior and precedence, not immutable checkpoint constants.
For published release identifiers, release-specific baked defaults are published in light-default-checkpoints.json; use Release Metadata Runbook and ZEVM release metadata for exact release/build boundaries, provenance, and default-checkpoint audit flow.
If a build has no published light-default-checkpoints.json, treat it as an unpublished source-build boundary and provide an explicit checkpoint when deterministic startup selection is required.
Phase-1 persisted checkpoint contract: checkpointDir/checkpoint is startup input only. ZEVM does not create, update, or delete it during runtime, and runtime lastCheckpoint progression is not persisted there.
Operators who want persisted-source startup behavior (checkpointSource = "persisted") must provision and manage checkpointDir/checkpoint themselves; ZEVM does not auto-populate it.
ZEVM does not auto-create checkpointDir during startup.
Selected startup checkpoint input must match the selected light network; mismatch is startup failure before opening the HTTP listener.
Checkpoint age policy for the selected startup checkpoint:
ageis how old the selected startup checkpoint is when ZEVM startsageis evaluated once during startup, after checkpoint selection and before stale-policy decisionageis measured in whole seconds:age = max(0, startupTimeSeconds - checkpointTimeSeconds)startupTimeSecondsis sampled at age-check timecheckpointTimeSecondsis derived deterministically from the selected startup checkpoint hash on the selected network- derivation steps: call
GET <consensusRpcUrl>/eth/v1/beacon/genesisand parsedata.genesis_time; callGET <consensusRpcUrl>/eth/v1/beacon/headers/{selectedCheckpointHash}and parsedata.rootanddata.header.message.slot(withdata.rootrequired to equalselectedCheckpointHash) - compute summary:
checkpointTimeSeconds = genesisTimeSeconds + (checkpointSlot * 12)(integer Unix seconds, with phase-1SECONDS_PER_SLOT = 12) - this derivation is anchored to the selected startup checkpoint input; filesystem metadata like
checkpointDir/checkpointmtime is not used age == maxCheckpointAgeSecondsis valid- only
age > maxCheckpointAgeSecondsis stale - stale +
strictCheckpointAge = false: emit one startup/operator warning and continue startup (listener still opens) - non-strict stale warning channel is operator startup logging surfaces (captured process
stderrvia shell redirection or service manager); it is not a JSON-RPC response - phase 1 provides no dedicated ZEVM startup log-level or log-path knobs in CLI/config
- non-strict stale warning payload must include: selected checkpoint hash,
checkpointSource,checkpointTimeSeconds,startupTimeSeconds, computedage,maxCheckpointAgeSeconds, andstrictCheckpointAge = false - stale +
strictCheckpointAge = true: startup failure before opening the HTTP listener - inability to resolve
checkpointTimeSecondsfor the selected startup checkpoint: startup failure before opening the HTTP listener - stale-checkpoint warnings are startup/operator surfaces (not runtime JSON-RPC method errors)
Precedence interaction summary:
- checkpoint selection precedence decides only which startup checkpoint hash is selected (
explicit/persisted/default) - resolved
checkpointDirvalue determines the filesystem path used for step 3 (checkpointDir/checkpoint) - resolved
maxCheckpointAgeSecondsandstrictCheckpointAgevalues are applied after checkpoint selection during startup stale-policy evaluation
Light-Mode Readiness Contract
Section titled “Light-Mode Readiness Contract”In light mode:
eth_chainIdis always callable, including whileready = false- while
ready = false,eth_blockNumberfails with-32011 - while
ready = false, proof-backed account/code/storage/nonce reads fail with-32011 - phase-1 operator-facing startup inputs are
network,consensusRpcUrl, andexecutionRpcUrl, with optional checkpoint controls (checkpoint,checkpointDir,maxCheckpointAgeSeconds,strictCheckpointAge) status = "syncing"means startup validation passed and listener is live, but readiness gate remains closedstatus = "synced"is the only state whereready = true; readiness transitions totrueonly after synced state with verified optimistic, safe, and finalized heads- while
ready = true, verified heads maintainfinalized <= safe <= latest - on any sync-state degradation from ready conditions (including slot coherence no longer holding:
finalizedSlot <= safeSlot <= optimisticSlot), ZEVM setsready = falseimmediately before serving readiness-gated calls status = "error"keepsready = false; if persistent, operators should verifynetwork,consensusRpcUrl,executionRpcUrl, and checkpoint inputs, then restart
Startup Failures
Section titled “Startup Failures”ZEVM fails before opening the HTTP listener for invalid startup input, including:
- unknown flags
- missing flag values
- invalid integer or wei values
- invalid mode combinations
- invalid trusted/light flag mixing
- missing required light-mode consensus URL
- light-mode consensus endpoint/network mismatch
- light-mode startup consensus-network handshake failure (
GET /eth/v1/beacon/genesisrequest failure, non-200, malformed payload, missing/invaliddata.genesis_validators_root, or root mismatch with selected network) - selected startup checkpoint/network mismatch
- invalid mining option combinations
- invalid fork option combinations
- invalid
coinbaseIndex - stale selected checkpoint when
strictCheckpointAge = true - inability to resolve
checkpointTimeSecondsfor the selected startup checkpoint - malformed checkpoint input or malformed checkpoint file
--configload failure (missing file, unreadable file, or malformed JSON): startup fails before opening the HTTP listener, exits non-zero, reports an operator-facing error naming the config path and failure class, and does not fall back to defaults
Light-mode startup includes a network-validation handshake before listener bind:
- call
GET <consensusRpcUrl>/eth/v1/beacon/genesis - require HTTP
200and parsedata.genesis_validators_rootasHash32 - require root match for selected light
networkusing the canonical chain-id/root mapping - if handshake fails (request failure, non-
200, malformed payload, missing/invaliddata.genesis_validators_root, or root mismatch), startup fails before opening the HTTP listener