MEV Configuration
MEV needs no flag. Before the Gloas (ePBS) fork, Anchor creates validator registrations for relays, and external builders are configured on your beacon node (builder or MEV-Boost connection). From Gloas onward, direct builder connections are configured on Anchor itself and use proposer preferences instead of validator registrations; see Direct builder connections below.
--proposer-delay-ms tunes when Anchor asks for a block. Asking later gives builders more time to
bid, at the cost of leaving less of the slot for everything that follows.
From the Gloas (ePBS) fork onward, --proposer-delay-epbs-ms takes over: each proposer duty uses
whichever value matches the fork at its slot. The two values are independent, with no fallback
between them, and the ePBS value also defaults to 0 (off).
How the delay works
The value is an offset from the start of the slot, not added latency. Set 300 and Anchor
requests the block 300ms into the slot. It does not add 300ms to whatever it would have done
otherwise.
Anchor has preparation work to do first. If that runs past your target, the request goes out as soon as it is ready and there is no wait, so a duty that is already late is never delayed further. That also means the setting can legitimately do nothing on a slow cluster, which the metrics below will tell you.
In a cluster, every operator holding a share requests its own candidate block and consensus picks between them, so set and monitor this fleet-wide rather than on a single node.
Which value applies is decided per duty by the Ethereum fork at the duty's slot: before Gloas,
--proposer-delay-ms; from Gloas onward, --proposer-delay-epbs-ms. An existing
--proposer-delay-ms setting therefore stops applying at the fork unless you also set the ePBS
value.
Choosing a value
--proposer-delay-ms (before Gloas)
| Value | Behaviour |
|---|---|
0 | Disabled. The default. |
300 | Recommended starting value, matching the SSV node. |
above 1000 | Refused unless you also pass --allow-dangerous-proposer-delay. |
above 4000 | Refused outright. The acknowledgement flag does not override it. |
--proposer-delay-epbs-ms (from Gloas onward)
| Value | Behaviour |
|---|---|
0 | Disabled. The default. |
up to 1000 | Applied from the first slot of the Gloas fork epoch. |
above 1000 | Refused outright. --allow-dangerous-proposer-delay does not override it; there is no escape hatch, matching the SSV node. |
anchor node --proposer-delay-ms 300 --proposer-delay-epbs-ms 300The higher the value, the higher the chance of missing a block proposal, since consensus, signing, and publication all still have to fit into the slot afterwards. Neither limit above is a safe maximum; both are guard rails.
Watching what it does
anchor_randao_reveal_completion_offset_secondsis how far into the slot pre-consensus finished, measured before any delay. Consistently larger than your configured value means the delay never bites.anchor_proposer_delay_applied_secondsis the wait actually applied, labelleddisabled,target_passed,waited, orclock_unavailable. Non-waiting outcomes record zero, so a flat zero line means the delay is not biting rather than that the metric is missing.- From Gloas onward, a flat zero with only
--proposer-delay-msconfigured means the fork switched which value applies, not that the delay is broken. The duty's span records which knob applied asproposer_delay_source(pre_gloasorgloas).
Coming from an SSV node
Same semantics as the SSV node's ProposerDelay, so an existing value carries over directly. The
SSV node reads it from a config file or the PROPOSER_DELAY environment variable; Anchor takes a
command line flag in milliseconds, so ProposerDelay: 300ms becomes --proposer-delay-ms 300.
The ePBS value maps the same way from the SSV node's ProposerDelayEPBS key or the
PROPOSER_DELAY_EPBS environment variable: ProposerDelayEPBS: 300ms becomes
--proposer-delay-epbs-ms 300. Both clients hard-cap it at one second.
Direct builder connections (from Gloas)
From the Gloas fork onward, Anchor can request bids from specific builders directly, alongside the
in-protocol p2p bids and the local build. This is configured per node in
<data_dir>/builder_definitions.yml. Anchor creates the file on first start with an empty
builders list; with no entries the feature is off and block production uses p2p bids and the local build only.
To get started, either start Anchor once and edit the template it creates, then restart; or place a populated file in the data directory before first start, which Anchor picks up as-is. The latter fits clusters best: every operator must deploy byte-identical entries (see below), so distribute one canonical file. There is no Anchor keymanager route for builder entries; the file is the whole reachable surface.
Edits apply on restart. There is no runtime reload, and the file is validated at every startup: an invalid file stops the node with an error category. Anchor-specific constraint errors also identify the offending entry, validator, or count. URLs and authentication data are omitted from store validation errors.
min_bid: 0
builder_boost_factor: 100
builders:
- enabled: true
url: "https://builder.example.com"
max_execution_payment: 1000000000The same file can override builders for one validator. Each validator_configs key is that
validator's compressed public key. If builders is omitted, the validator inherits the global
list. An empty list disables direct builders for that validator. A non-empty list replaces the
global list rather than adding to it:
validator_configs:
"0x<validator-public-key>":
min_bid: 10
builders:
- url: "https://validator-builder.example.com"
max_execution_payment: 1000000000
"0x<disabled-validator-public-key>":
builders: []Per-validator builder entries do not have an enabled field. Remove an entry from the list to
disable it, or use builders: [] to disable all direct builders for that validator.
Each entry supports:
| Field | Meaning |
|---|---|
enabled | Disabled entries never reach the wire, but the file must still validate; see Limits. |
url | The builder's URL, contacted by your beacon node. At most 2048 bytes. |
auth_data | Optional 0x-hex authentication bytes agreed with the builder out of band, at most 4096 bytes. When omitted, it defaults to the URL's lowercase ASCII hostname. Scheme, credentials, port, path, query, fragment, and trailing slash are not included. |
builder_pubkeys | Optional list of builder BLS public keys allowed to sign this builder's bids. Empty accepts any. At most 64 keys; see Limits. |
max_execution_payment | The maximum trusted (off-protocol) payment, in gwei, accepted from this builder. |
min_bid, builder_boost_factor | Optional per-builder overrides of the top-level values. |
The top-level min_bid and builder_boost_factor govern p2p bids and fill in any entry that
omits its own.
The configuration must be byte-identical across the cluster
Builder entries are cluster configuration. Every operator of a cluster MUST use byte-identical
entries, auth_data included. Each builder's authentication is threshold-signed by the cluster,
and the signed bytes include auth_data: a one-byte divergence fragments the signing roots, and a
builder is silently dropped wherever fewer than a quorum of operators share its exact bytes,
starting with the divergent operator's own proposals. The drop surfaces only in signing-failure
logs and reconstruction telemetry. The full entries must still match even when two URL spellings
derive the same hostname auth data, because the URL remains routing metadata sent to the beacon
node.
Treat the file as sensitive and restrict its permissions accordingly. A custom auth_data value
may be a shared secret agreed with the builder, so handle it as a credential. Request-auth signing
logs record the auth-data length, not its contents. Downstream systems may log builder URLs, so
do not embed credentials in the URL itself; authentication material belongs in auth_data.
Migrating default authentication and mixed-client clusters
This Lighthouse update changes omitted auth_data from the complete URL to its lowercase ASCII
hostname. For https://builder.example.com/, the default is now builder.example.com.
Older Anchor versions and the current go-ssv Gloas implementation still default to URL bytes.
Identical URLs with omitted auth therefore do not guarantee identical signing roots during
an upgrade or in a mixed-client cluster.
Before upgrading, configure explicit, identical authentication bytes on every operator. To pin
the standard hostname default in this example, use
auth_data: "0x6275696c6465722e6578616d706c652e636f6d" in Anchor and the same value for go-ssv's
AuthData. Confirm that the receiving builder supports the hostname default. An existing custom
value agreed with the builder can also be retained unchanged. Do not rely on omitted defaults
until every operator and the builder use the same rule.
Use the standard default or an agreed custom value
The builder checks the value it receives against the one it agreed with you, and rejects a mismatch, dropping itself from that proposal. Without a custom agreement, use the lowercase ASCII hostname default, either explicitly as above or by omitting the field once the whole cluster has compatible defaults. Explicitly pinning the standard default does not create a new custom token. A custom value the builder does not recognise fails every proposal. It fails per builder rather than failing the file, so nothing is refused at startup.
Limits
At most 8 global entries may be enabled, and each explicit per-validator builder list may
contain at most 8 entries (an SSV protocol cap; the beacon API itself allows 64). Inherited
global lists are checked once as globals, while builders: [] remains valid. Anchor refuses to
start above either cap: excess entries can exceed the per-slot signing budget other operators
enforce on gossip, silently dropping builders in a per-peer, nondeterministic way.
A zero-length auth_data ("0x" or an empty string) is also refused at startup, for disabled
entries too; omit the field instead to use the URL default. An entry listing more than 64
builder_pubkeys (a beacon-API bound) is likewise refused, disabled entries included: the file
would otherwise load, and the builder would then be omitted during block production.
Disabled global entries do not need a valid hostname when auth_data is omitted, because no
authentication is signed for them. Enabling such an entry still requires a valid HTTP(S) URL.
