You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Charon currently ignores the VC's proposer config: prepare_beacon_proposer is a no-op (core/validatorapi/router.go, submitProposalPreparations) and register_validator is ignored (core/validatorapi/validatorapi.go, SubmitValidatorRegistrations). Charon injects the lock's fee recipient itself. As a result the VC-side fee recipient / gas limit are unmanaged and irrelevant today — CDVN VCs set none, and Teku even hardcodes --validators-proposer-default-fee-recipient 0x0000…0000.
Post-gloas the VC signsfee_recipient and target_gas_limit into SignedProposerPreferences (#4691), so its local config becomes load-bearing and must match the lock cluster-wide or the partial signatures won't aggregate. This issue: give Charon a way to push the cluster-canonical fee recipient + gas limit (lock FeeRecipientAddresses / TargetGasLimit) into the VCs.
This has independent, pre-gloas value — it makes VCs hold the correct fee recipient rather than relying on Charon's override — so it can land before the gloas duty.
No standard config file exists. The ecosystem is standardizing an API instead — keymanager-APIs#87 (/eth/v1/validator/config, closed/contentious, not ratified), explicitly to kill the "different config dialect per client" burden it names for dappnode/eth-docker.
Per-client config surfaces:
Prysm / Teku / Lodestar — one central proposer_config JSON: --proposer-settings-file / --validators-proposer-config / --proposerSettingsFile.
Applying updates (matters for coordinated gas_limit bumps, which are applied staggered across operators): file rewrite + VC restart everywhere. File hot-reload exists only on Teku (--Xvalidators-proposer-config-refresh-enabled, per epoch) and Nimbus (per-validator files read live); Prysm/Lodestar re-read on restart; the CDVN Lighthouse adapter regenerates validator_definitions.yml from proposer-config.json on every container start, so a restart applies updates there too.
Mechanism
File-based, exclusively. The keymanager API is ruled out permanently (decision 2026-09-17) — charon generates proposer-config.json, per-client adapters derive native formats from it, and updates apply via file rewrite + VC restart. Post-gloas this same file is the alignment surface for the values VCs sign (SignedProposerPreferences, and later the builder url/auth_data signed into BuilderRequestAuth), so it must be byte-consistent across the cluster.
Tasks
Decide mechanism: (B) per-client file generation — one JSON parsed natively by Prysm/Teku/Lodestar (JSON is valid YAML); Nimbus + Lighthouse via generate-from-JSON adapters (LH: author the whole validator_definitions.yml — a supported workflow — generate, not merge). Always-on (no feature flag — the diff is small and generation is best-effort/create-once, decided in review of app: generate proposer config file for validator clients #4700).
Resolve canonical config from the lock: keyed by this node's pubshares (the identities the VC manages); fee_recipient from FeeRecipientAddresses, gas_limit from TargetGasLimit with registration/30M fallback (app: generate proposer config file for validator clients #4700)
keymanager path ruled out permanently — file-based is the mechanism
charon edit-in-sync flow: extend charon alpha feerecipient fetch (or sibling vc-config sync) to rewrite proposer-config.json from the effective quorum-gated state — the existing sign → Obol-API-quorum → fetch flow is the sync point; sign should print the follow-through (fetch on every node + VC restart on Prysm/Lodestar/Lighthouse). Impl notes: export/move the unexported generator helpers from app for cmd/ use; rewrite via temp-file+rename (atomic vs Teku's periodic re-read); core: add gloas proposer preferences duty #4693's mismatch metric detects laggards
🎯 Problem to be solved
Charon currently ignores the VC's proposer config:
prepare_beacon_proposeris a no-op (core/validatorapi/router.go,submitProposalPreparations) andregister_validatoris ignored (core/validatorapi/validatorapi.go,SubmitValidatorRegistrations). Charon injects the lock's fee recipient itself. As a result the VC-side fee recipient / gas limit are unmanaged and irrelevant today — CDVN VCs set none, and Teku even hardcodes--validators-proposer-default-fee-recipient 0x0000…0000.Post-gloas the VC signs
fee_recipientandtarget_gas_limitintoSignedProposerPreferences(#4691), so its local config becomes load-bearing and must match the lock cluster-wide or the partial signatures won't aggregate. This issue: give Charon a way to push the cluster-canonical fee recipient + gas limit (lockFeeRecipientAddresses/TargetGasLimit) into the VCs.This has independent, pre-gloas value — it makes VCs hold the correct fee recipient rather than relying on Charon's override — so it can land before the gloas duty.
Sub-task of the gloas epic #4324.
🛠️ Proposed solution
Findings
/eth/v1/validator/config, closed/contentious, not ratified), explicitly to kill the "different config dialect per client" burden it names for dappnode/eth-docker.proposer_configJSON:--proposer-settings-file/--validators-proposer-config/--proposerSettingsFile.<validators-dir>/<pubkey>/suggested_fee_recipient.hex+suggested_gas_limit.json.validator_definitions.yml(keystore-entangled; standalone fee-recipient file removed); CDVN profile authors it fromproposer-config.jsonon every container start (Add lighthouse validator client with proposer config charon-distributed-validator-node#487).gas_limitbumps, which are applied staggered across operators): file rewrite + VC restart everywhere. File hot-reload exists only on Teku (--Xvalidators-proposer-config-refresh-enabled, per epoch) and Nimbus (per-validator files read live); Prysm/Lodestar re-read on restart; the CDVN Lighthouse adapter regeneratesvalidator_definitions.ymlfromproposer-config.jsonon every container start, so a restart applies updates there too.Mechanism
File-based, exclusively. The keymanager API is ruled out permanently (decision 2026-09-17) — charon generates
proposer-config.json, per-client adapters derive native formats from it, and updates apply via file rewrite + VC restart. Post-gloas this same file is the alignment surface for the values VCs sign (SignedProposerPreferences, and later the builderurl/auth_datasigned intoBuilderRequestAuth), so it must be byte-consistent across the cluster.Tasks
validator_definitions.yml— a supported workflow — generate, not merge). Always-on (no feature flag — the diff is small and generation is best-effort/create-once, decided in review of app: generate proposer config file for validator clients #4700).fee_recipientfromFeeRecipientAddresses,gas_limitfromTargetGasLimitwith registration/30M fallback (app: generate proposer config file for validator clients #4700)keymanager pathruled out permanently — file-based is the mechanismproposer-config.jsonnext to the lock, create-if-missing, best-effort (app: generate proposer config file for validator clients #4700)run.shlayer (all 5 VCs) — Prysm/Teku direct flag (native parse); Lodestar flag + extension shim (cpto.yml: itsreadFilethrowsUnsupportedFileFormatfor.json, but content parses — unknownbuilder.enabledignored,gas_limitmust be string; verified inpackages/cli/src/util/proposerConfig.ts); Nimbus renderer → per-validatorsuggested_fee_recipient.hex+suggested_gas_limit.json; Lighthouse (new CDVN profile) renderer → fullvalidator_definitions.ymlgeneration (replaces import;--disable-auto-discover). Shared: wait-for-file guard (docker creates a directory when bind-mounting a missing file path), restart-semantics docs (hot: Teku w/ refresh flag, Nimbus; restart: Prysm/Lodestar/Lighthouse)charonedit-in-sync flow: extendcharon alpha feerecipient fetch(or siblingvc-config sync) to rewriteproposer-config.jsonfrom the effective quorum-gated state — the existingsign→ Obol-API-quorum →fetchflow is the sync point;signshould print the follow-through (fetch on every node + VC restart on Prysm/Lodestar/Lighthouse). Impl notes: export/move the unexported generator helpers fromappforcmd/use; rewrite via temp-file+rename (atomic vs Teku's periodic re-read); core: add gloas proposer preferences duty #4693's mismatch metric detects laggards🔗 References