ZEVM Internal Support: Startup And Configuration
ZEVM Internal Support: Startup And Configuration
Section titled “ZEVM Internal Support: Startup And Configuration”This page supports docs/specs/prd.md and docs/specs/json-rpc-contract.md.
1. Startup Model
Section titled “1. Startup Model”- one binary:
zevm - default runtime: trusted mode
- alternate runtime: light mode
- runtime selected by user-supplied
--modeor configmodebranch (config must include exactly one ofmode.trustedormode.light) - parser-provided CLI defaults are applied after CLI/config merge and do not create mode conflicts
- if user-supplied
--modeand configmodeare both present, they must agree
2. Shared CLI
Section titled “2. Shared CLI”| Flag | Type | Default |
|---|---|---|
--config | JSON path | none |
--mode | trusted or light | trusted (effective only when neither user-supplied --mode nor --config selects mode; if --config is provided it must include exactly one mode branch, and user-supplied --mode must match that branch) |
--host | bind host | 127.0.0.1 |
--port | TCP port | 8545 |
--engine-host | Engine API bind host | disabled unless Engine API is enabled |
--engine-port | Engine API TCP port | 8551 when Engine API is enabled |
3. Trusted CLI And Config
Section titled “3. Trusted CLI And Config”Trusted CLI flags:
--chain-id--coinbase-index--initial-balance--gas-price--base-fee--blob-base-fee--max-priority-fee-per-gas--block-gas-limit--mining--block-time--fork-url--fork-block-number--genesis--chain-rlp--engine-host--engine-port
Trusted config branch: mode.trusted
Resolved config sub-shapes:
- top-level
engineRpc: optional{ "host": "127.0.0.1", "port": 8551 }; enables a trusted-mode Engine API listener mining:{ "type": "auto" },{ "type": "manual" }, or{ "type": "interval", "blockTime": <u64> }hardfork: object of optional activation overrides using camelCase keys (homesteadBlock,daoBlock,tangerineWhistleBlock,spuriousDragonBlock,byzantiumBlock,petersburgBlock,istanbulBlock,muirGlacierBlock,berlinBlock,londonBlock,arrowGlacierBlock,grayGlacierBlock,mergeBlock,shanghaiTimestamp,cancunTimestamp,pragueTimestamp,osakaTimestamp,secondsPerSlot)fork:null,{ "url": "https://..." }, or{ "url": "https://...", "blockNumber": <u64> }genesis:nullor a path to a genesis JSON file whose top-levelallocobject seeds trusted-mode statechainRlp:nullor a path to a concatenated RLP block stream imported after genesis as query-only block history
Validation:
blockTimerequired only for interval miningblockTimeinvalid for auto and manual miningforkBlockNumberrequiresforkUrl- providing
forkUrlwithoutforkBlockNumberuses unpinned upstream-head (latest) fork semantics at startup coinbaseIndexmust be0..9- trusted startup fork block numbers use decimal
u64in CLI/config (--fork-block-number,mode.trusted.fork.blockNumber) - runtime
zevm_resetusesQuantityHexforforkConfig.blockNumber; example: startup decimal1000000corresponds to JSON-RPC"blockNumber": "0xf4240" - trusted hardfork activation values use decimal
u64in config; omittedhardforkfields inherit from the resolved chain default - default hardfork policy is explicit:
chainId = 1uses the mainnet activation schedule, while non-mainnet trusted defaults use a dev schedule with Cancun active from genesis and Prague/Osaka inactive until configured - trusted
genesispaths resolve by field precedence (--genesisovermode.trusted.genesis); when present, ZEVM imports accountbalance/wei,nonce,code, andstoragefrom the file instead of pre-funding the deterministic dev accounts - trusted
chainRlppaths resolve by field precedence (--chain-rlpovermode.trusted.chainRlp); when present, ZEVM imports the stream after genesis as query-only block history, sets the canonical head to the last imported block, and does not materialize imported state, receipts, or logs - trusted Engine API is disabled unless
--engine-host,--engine-port, or top-levelengineRpcis supplied; it is invalid in light mode
4. Light CLI And Config
Section titled “4. Light CLI And Config”Light CLI flags:
--network(mainnet,sepolia,holesky)--consensus-rpc-url--execution-rpc-url--checkpoint--checkpoint-dir--max-checkpoint-age-seconds(default1209600)--strict-checkpoint-agepresence flag (defaultfalse)
Light config branch: mode.light
Checkpoint-age defaults for light mode:
mode.light.maxCheckpointAgeSecondsdefaults to1209600mode.light.strictCheckpointAgedefaults tofalse- normative source:
docs/specs/prd.mdsection 5.3 (Light-mode CLI)
Naming/path bridge for light startup inputs:
- hyphenated CLI flags map to camelCase config keys:
--network->network,--consensus-rpc-url->consensusRpcUrl,--execution-rpc-url->executionRpcUrl,--checkpoint->checkpoint,--checkpoint-dir->checkpointDir,--max-checkpoint-age-seconds->maxCheckpointAgeSeconds,--strict-checkpoint-age->strictCheckpointAge - checkpoint-dir default template is
.zevm/checkpoints/<network>;<network>expands from resolved startupnetworkafter CLI/config merge - after CLI/config merge and
<network>expansion, any relativecheckpointDirvalue is resolved against the process current working directory at startup - persisted checkpoint startup input path is
${resolvedCheckpointDir}/checkpoint, whereresolvedCheckpointDiris the absolute path after that resolution step
Validation:
consensusRpcUrlis required in light modenetworkmust be one ofmainnet,sepolia,holeskyconsensusRpcUrlmust serve the same network selected bynetworkexecutionRpcUrlmust serve execution JSON-RPC for the same network when proof-backed reads are used--strict-checkpoint-ageCLI semantics are presence-based: present meanstrue, omitted meansfalse, and explicit assignment forms (for example--strict-checkpoint-age=false) are invalid- checkpoint startup inputs from CLI/config are nullable for precedence selection:
--checkpointabsent andmode.light.checkpointabsent ornullare treated as absent startup inputs - only non-null CLI/config checkpoint values (
--checkpoint,mode.light.checkpoint) must be0x-prefixed 32-byte hashes (Hash32) - selected startup checkpoint hash must resolve on the configured
networkviaconsensusRpcUrl; network mismatch is startup failure before opening the HTTP listener
Startup consensus-network handshake (before listener):
- resolve
network,consensusRpcUrl, and selected startup checkpoint from startup precedence - call
GET <consensusRpcUrl>/eth/v1/beacon/genesis - require HTTP
200and parsedata.genesis_validators_rootasHash32 - require root match for selected
network:mainnet->0x4b363db94e286120d76eb905340fdd4e54bfe9f06bf33ff6cf5ad27f511bfe95sepolia->0xd8ea171f3c94aea21ebc42a1ed61052acf3f9209c00e4efbaaddac09ed9b8078holesky->0x9143aa7c615a7f7115e2b6aac319c03529df8242ae705fba9df39b79c59fa8b1
- if handshake fails (request failure, non-
200, malformed payload, missing/invalid root, or root mismatch), ZEVM must fail before opening the HTTP listener
5. Config File Rules
Section titled “5. Config File Rules”- allowed top-level keys are
rpcandmode; unknown top-level keys are invalid - unknown keys inside
rpc,mode,mode.trusted,mode.light, and trusted structured objects (mining,hardfork,fork) are invalid rpcis optional; when omitted, defaults arehost = 127.0.0.1,port = 8545- when
rpcis present,hostandportdefault independently if omitted modemust contain exactly one oftrustedorlight- config with both is invalid
- config with neither is invalid
- explicit
--configload failure (missing file, unreadable file, malformed JSON, schema failure, or validation failure) is startup failure - explicit
--configload failures must emit one startup error record on processstderrnaming the config path and failure class before exiting non-zero
6. Precedence
Section titled “6. Precedence”Startup precedence:
- user-supplied CLI flag values
- config file values
- persisted light checkpoint (light mode only)
- mode defaults
Precedence scope clarification for light mode:
- precedence item 3 applies only to startup checkpoint selection
checkpointDir,maxCheckpointAgeSeconds, andstrictCheckpointAgeresolve independently per field as: user-supplied CLI value > config value > mode default- persisted
${resolvedCheckpointDir}/checkpointdoes not set or overridecheckpointDir,maxCheckpointAgeSeconds, orstrictCheckpointAge
Merge clarifications:
- only user-supplied CLI flags override config
- parser-provided CLI defaults fill missing fields only after CLI/config merge
- parser-provided CLI defaults are not treated as user overrides and cannot cause mode conflicts
Structured trusted-setting resolution:
miningresolves as one unit from CLI--miningand--block-time; if either flag is present, 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 present, ZEVM buildsforkfrom CLI and ignoresmode.trusted.forkgenesisresolves as one field from CLI--genesis, then configmode.trusted.genesis, then absentchainRlpresolves as one field from CLI--chain-rlp, then configmode.trusted.chainRlp, then absent- when no related CLI flags are present for that unit, ZEVM uses config value for that unit, then mode default
- resolved trusted
forkwithurland noblockNumberuses unpinned upstream-head (latest) semantics; resolved trustedforkwithblockNumberis pinned to that block
Light checkpoint selection precedence:
- user-supplied CLI checkpoint (
--checkpoint), when provided and non-null - config checkpoint (
mode.light.checkpoint), when set to a non-null value - persisted
${resolvedCheckpointDir}/checkpoint - baked network default
Precedence fallthrough is absence-driven only: ZEVM advances to a lower-precedence checkpoint source only when the higher-precedence source is absent. For CLI/config checkpoint inputs, absence includes omitted values and config null.
Once a checkpoint source is selected by precedence, that selected source is final for that startup attempt; any validation/derivation failure for the selected source must fail startup before opening the HTTP listener, and ZEVM must not fall back to lower-precedence checkpoint sources.
If CLI checkpoint, config checkpoint, and persisted ${resolvedCheckpointDir}/checkpoint are all absent, ZEVM selects the baked network default checkpoint and checkpointSource = "default". Here, “absent” includes config mode.light.checkpoint = null.
Baked default checkpoints are precedence inputs, not frozen public compatibility hashes.
Baked default checkpoint values are ZEVM bundled release/build inputs. For a given ZEVM release/build artifact and network, the selected baked default is deterministic.
Baked defaults are implementation-defined and may rotate across releases/builds.
Canonical release metadata artifact for baked defaults:
releaseIdentifiermust exactly equal the GitHub release tag name that carries metadata assets; tag-based identifiers match^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$, and commit-based identifiers match^commit-[0-9a-f]{40}$- each ZEVM
releaseIdentifierpublishes exactly one machine-readablelight-default-checkpoints.jsonasset athttps://github.com/evmts/zevm/releases/download/<releaseIdentifier>/light-default-checkpoints.json light-default-checkpoints.jsontop-level fields are exactlyschemaVersion,releaseIdentifier, anddefaultsschemaVersioniszevm-light-default-checkpoints.v1releaseIdentifiermatches the publishingreleaseIdentifierdefaultscontains exactlymainnet,sepolia, andholesky; each value is a0x-prefixed 32-byte hash (Hash32) equal to that release/build’s bundled baked default for the network- values are immutable per published release identifier; corrections publish a new release identifier with its own
light-default-checkpoints.json - correction releases include one release-notes supersession note under heading
## ZEVM Supersession Notewith required lines:schemaVersion,supersedesReleaseIdentifier,correctedArtifacts, andreason - publication-time validation gate is mandatory for canonical publication claims: before a release is treated or announced as canonical under this contract, CI/release automation validates both required artifacts (
release-tuple.json,light-default-checkpoints.json) from published release assets against PRD section 3.4 requirements - publication-time gate failure on either required artifact (missing, duplicate, unreadable, malformed, schema-mismatched, or value-mismatched) blocks canonical publication claims for that
releaseIdentifier; correction requires a newreleaseIdentifier(no in-place repair) - operators deterministically discover per-network baked defaults from that artifact; runtime probing via
zevm_lightSyncStatusis optional verification - deterministic baked-default discovery from release metadata is defined only for published release identifiers
- for unreleased commit builds without published
light-default-checkpoints.json, baked defaults remain implementation-defined and are not contract-discoverable from metadata - operators that require deterministic checkpoint selection for unreleased commit builds must provide an explicit checkpoint via CLI/config instead of relying on baked defaults
- phase 1 does not define a runtime JSON-RPC or CLI surface to directly report
releaseIdentifier; operators identify release/build boundaries from preserved provenance records - phase-1 source-build provenance has exactly two states: metadata-backed published-release provenance and operator-recorded unreleased-commit provenance
- metadata-backed published-release flow is strict and ordered: select one
releaseIdentifier-> fetch required assets for that identifier -> validate PRD section 3.4 invariants -> materialize pinned commits/toolchain -> build - unreleased-commit flow has no release-asset discovery step; operators derive and record
(zevmGitRevision, voltaireGitRevision, guillotineMiniGitRevision, zigVersion)from local state - operators must not mix metadata assets across
releaseIdentifiervalues and must not claim metadata-backed reproducibility for metadata-invalid identifiers or unreleased commit builds
Persisted checkpoint startup-input contract:
${resolvedCheckpointDir}/checkpointis read-only startup input in phase 1 (resolvedCheckpointDiris derived from mergedcheckpointDirby applying<network>expansion and then resolving relative paths against startup current working directory)- if
${resolvedCheckpointDir}does not exist at startup (including a missing expanded<network>directory), persisted checkpoint input is treated as absent and precedence falls through - if
${resolvedCheckpointDir}/checkpointis missing, persisted checkpoint input is treated as absent and precedence falls through - if
${resolvedCheckpointDir}/checkpointexists but is unreadable, startup fails before opening the HTTP listener - if the file is readable but trimmed content is malformed, startup fails before opening the HTTP listener
- ZEVM does not auto-create
${resolvedCheckpointDir}(including<network>directory expansion targets) during startup - ZEVM does not create, update, or delete
${resolvedCheckpointDir}/checkpointafter listener startup - seeding and management are operator-only workflows: operators seed
${resolvedCheckpointDir}/checkpointby creating/updating it before process start or between restarts - ZEVM does not seed
${resolvedCheckpointDir}/checkpointfrom baked defaults, explicit startup checkpoint inputs, orzevm_lightSyncStatus.lastCheckpoint - startup reads
${resolvedCheckpointDir}/checkpointonce during precedence resolution; runtime writes or external file changes after listener startup do not change the active process checkpoint source zevm_lightSyncStatus.lastCheckpointprogression is runtime state only and is not persisted to${resolvedCheckpointDir}/checkpointin phase 1- operators may update
${resolvedCheckpointDir}/checkpointbetween restarts; startup precedence is re-evaluated on each process start
Checkpoint age policy for the selected startup checkpoint:
ageis ZEVM’s startup-time freshness value for the selected startup checkpointageis 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 Beacon API data for the selected startup checkpoint hash on the selected network, usingconsensusRpcUrland not filesystem metadata/local file times- derivation steps are exact:
- call
GET <consensusRpcUrl>/eth/v1/beacon/genesis, require HTTP200, parsedata.genesis_timeas decimal unsigned integergenesisTimeSeconds - call
GET <consensusRpcUrl>/eth/v1/beacon/headers/{selectedCheckpointHash}, require HTTP200, parsedata.rootasHash32and require equality withselectedCheckpointHash, then parsedata.header.message.slotas decimal unsigned integercheckpointSlot - use
SECONDS_PER_SLOT = 12for phase-1 supported light networks and computecheckpointTimeSeconds = genesisTimeSeconds + (checkpointSlot * SECONDS_PER_SLOT)with integer arithmetic - use computed
checkpointTimeSecondsas integer Unix seconds in age evaluation
- call
- any request failure, non-
200, missing/malformed required field, checkpoint-root mismatch, or arithmetic overflow in this derivation is inability to resolvecheckpointTimeSecondsand is startup failure before listening age == maxCheckpointAgeSecondsis valid- only
age > maxCheckpointAgeSecondsis stale - stale +
strictCheckpointAge = false: emit one operator-facing startup warning before listening, then continue startup - non-strict stale warnings must be emitted on process
stderrduring startup and must not rely on JSON-RPC visibility - the non-strict stale warning must include: selected checkpoint hash,
checkpointSource,checkpointTimeSeconds,startupTimeSeconds, computedage,maxCheckpointAgeSeconds, andstrictCheckpointAge = false - stale +
strictCheckpointAge = true: startup failure before listening
Startup logging surface (phase 1):
- operator-facing startup warnings and startup-failure errors are emitted via process
stderr - phase 1 does not define dedicated CLI/config controls for startup log level, log file paths, or alternative log sinks
- capture/routing of startup
stderroutput is external process/shell responsibility
7. Startup Failure Contract
Section titled “7. Startup Failure Contract”ZEVM must fail before opening the HTTP listener for invalid startup input, including:
- unknown flags
- missing flag values
- malformed numeric or wei values
- malformed checkpoint input or malformed checkpoint file
- explicit
--modemismatch with config mode branch - trusted-only flags in light mode
- light-only flags in trusted mode
- missing required light consensus URL
- light consensus endpoint/network mismatch
- light 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/fork combinations
- invalid
coinbaseIndex - stale selected checkpoint when
strictCheckpointAge = true - explicit assignment form
--strict-checkpoint-age=false - inability to resolve
checkpointTimeSecondsfor the selected startup checkpoint - once a checkpoint source is selected by startup precedence, any selected-source validation/derivation failure is terminal for that startup attempt and must not trigger fallback to lower-precedence checkpoint sources
- startup-failure errors in this section are operator-facing startup logs emitted on process
stderr --configload failure (missing file, unreadable file, or invalid 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
For exact field-level and RPC-level behavior, use docs/specs/json-rpc-contract.md.