Skip to content

Configuration

Prerequisites: Quick Start complete, including a credential — an API key, or a join key if you use Gateway Self-Enrollment.

Basic Setup

Everything that identifies your gateway — gateway_id, chain, and validator scope — comes from your credential. Use an API key (ogw_) for single-gateway deployments, or a join key (ojk_) for fleet self-enrollment. The only operational fields you set in YAML are cluster ID, ports, telemetry, and identity directories.

Create config/app_conf.yml:

yaml
log_level: info
gateway_cluster_id: optimum_ethereum_hoodi_v0_1   # REQUIRED — assigned by Optimum

agent_lib_p2p_port: 33212
agent_mump2p_port: 33213
telemetry_enable: true
telemetry_port: 48123
identity_libp2p_dir: /tmp/libp2p
identity_mump2p_dir: /tmp/mump2p

Credentials via environment, not YAML. Set OPT_API_KEY=ogw_live_... or OPT_JOIN_KEY=ojk_live_... in the environment — never in YAML or image layers. api_key and join_key are mutually exclusive; setting both is a startup error.

Authentication

Your gateway authenticates only with Optimum's auth service (https://auth.getoptimum.io), never with peer gateways. On start (and periodically after), it obtains two short-lived JWTs and keeps them refreshed automatically. There is no token to manage yourself.

API key (default)

Your gateway sends its API key to POST /api/v1/auth/token. The key determines your chain (Hoodi vs Mainnet), gateway ID, and validator list — there is no chain or gateway_id field for partners to set on this path.

Join key (fleet self-enrollment)

With OPT_JOIN_KEY, the gateway enrolls once on first boot (POST /api/v1/gateways/enroll), persists an asymmetric credential to disk, and mints JWTs with a client assertion on every boot after that. Chain, type, and cluster scope come from the join key. Set a unique OPT_GATEWAY_ID per host as the enrollment label; runtime gateway_id in metrics and /health comes from the JWT sub claim after enroll.

See Gateway Self-Enrollment for minting, storage, and fleet rollout.

  • Services token: attached as an Authorization: Bearer header on calls to Optimum's central services, namely bootstrap registration and heartbeat, and (when enabled) Loki/Mimir remote push.
  • Peer handshake token: presented during peer-to-peer (libp2p) handshakes with other gateways, so each side can confirm the other is a legitimate gateway on the same chain before exchanging traffic.

Both tokens are ES256-signed and valid for 6 hours; the gateway refreshes them well before expiry. Peers verify each other's handshake token locally against Optimum's published JWKS, with no per-connection callback to the auth service. If your API key or enrolled credential is revoked or suspended, refresh stops and the gateway loses access at the next token expiry.

Networks (Hoodi / Mainnet)

The network is selected by your credential, not by config. A Hoodi API key or join key runs Hoodi; a Mainnet one runs Mainnet. Set the matching gateway_cluster_id you were assigned during onboarding (Hoodi partners use optimum_ethereum_hoodi_v0_1; Mainnet cluster ID is provided by Optimum during onboarding). To move a gateway to Mainnet, obtain a Mainnet credential from Optimum and set the assigned Mainnet gateway_cluster_id, then restart.

Confirm the active network after start:

sh
curl -s http://localhost:48123/api/v1/self_info | jq '.chain, .fork_digest'

Lighthouse and Nimbus do not automatically reconnect to the gateway after a gateway restart. Configure direct_cl_peers so the gateway runs a dedicated goroutine that retries the connection until the CL peer is reachable again.

yaml
direct_cl_peers:
  - /ip4/192.168.1.2/tcp/9000/p2p/16Uiu2HAmGj6AoMKe7fNrghXwwRgivXLpji3Hkm4QEGVpsHZYKwPQ

Replace the IP, port, and peer ID with your CL node's libp2p multiaddr.

Why this matters: Prysm re-dials peers on its own, but Lighthouse and Nimbus do not reliably hold the gateway link after a gateway restart. Without direct_cl_peers, the CL can stay disconnected until the next manual intervention. We recommend all partners add their CL nodes as direct peers.

Peer allowlist: When direct_cl_peers is set, the gateway disconnects any libp2p peer whose ID is not in that list. When empty, no peer-ID filtering is applied; firewall agent_lib_p2p_port (default 33212) to your CL client.

Lighthouse beacon node CLI: upstream help marks --libp2p-addresses as deprecated; use --boot-nodes for the same comma-delimited multiaddrs (multiaddr or ENR). See Beacon Node help.

To find your CL node's multiaddr:

  • Lighthouse: curl -s http://localhost:5052/eth/v1/node/identity | jq '.data.p2p_addresses[0]'
  • Prysm: curl -s http://localhost:3500/eth/v1/node/identity | jq '.data.p2p_addresses[0]'
  • Nimbus: curl -s http://localhost:9596/eth/v1/node/identity | jq '.data.p2p_addresses[0]'

Topics

Topic subscription is baked into the binarybeacon_block plus all 64 attestation subnets (beacon_attestation_0 through beacon_attestation_63). There is no eth_topics_subscribe field to configure. The full topic paths (with fork digest) are resolved automatically.

Gateway Pairing Mode

The gateway exposes a paired_with field (visible in /api/v1/self_info) that declares what the gateway is paired with. This is derived from your API key type and controls whether inbound beacon blocks from mump2p are re-forwarded to the local CL. For partner deployments the value is partner — no action needed.

Attestation forwarding is identical in all modes.

Attestation Subnet Boost

When attestation subnets are subscribed, the gateway forwards to mump2p only attestations from your partner's known validators — not every attestation the CL sees. The partner validator list is derived from your API key (via the auth mint) and refreshed periodically.

  • Non-partner attestations: dropped before reaching mump2p (saves bandwidth vs forwarding everything)
  • Stale attestations (older than the current slot gate): dropped
  • Inbound attestations from mump2p: always forwarded to CL (trust upstream filter)

This is fully automatic — no extra config. Until the first successful validator sync, the gateway forwards no attestations.

Remote Push

Remote push streams your gateway's logs to Optimum's Loki and metrics to Optimum's Mimir, giving the Optimum team visibility to help support you. Both telemetry_enable and remote_push_enable must be true. v1.3.2 uses standard Prometheus remote write for metrics push — same setup as v1.1.1.

yaml
telemetry_enable: true
remote_push_enable: true

Remote push authenticates with the gateway's services token (the short-lived JWT minted from your API key or enrolled credential; see Authentication), attached as an Authorization: Bearer header. There are no separate remote_push_client_id / remote_push_client_secret to configure anymore. The push endpoints are baked into the binary. Requirements: telemetry_enable: true, remote_push_enable: true, a valid credential (OPT_API_KEY or OPT_JOIN_KEY), and outbound HTTPS (443).

Dynamic Configuration

The gateway receives automatic config updates from bootstrap.

  • Polls for updates periodically
  • Changes apply without restart
  • Dynamic config includes: propagation toggle, self-message skip

Config Reference

KeyEnv VariableDefaultDescription
api_keyOPT_API_KEY(empty)Gateway API key (ogw_live_...). Set via env, not YAML. Set this or join_key; with neither, the gateway starts with authentication disabled and cannot join the Optimum mesh. Drives gateway_id, chain, and validator scope
join_keyOPT_JOIN_KEY(empty)Org-wide join key (ojk_live_...). Set via env, not YAML. Mutually exclusive with api_key. See Gateway Self-Enrollment
enroll_cred_dirOPT_ENROLL_CRED_DIRidentity_mump2p_dirEnrollment credential directory (enrollment.json). Must be persistent
gateway_idOPT_GATEWAY_IDdev-gatewayJoin-key path only: enrollment label at first boot (unique per host). Overwritten by JWT sub after mint. Ignored for API-key path
gateway_cluster_idOPT_GATEWAY_CLUSTER_ID(required)Cluster ID assigned by Optimum during onboarding
agent_lib_p2p_portOPT_AGENT_LIB_P2P_PORT33212CL clients connect here (inbound)
agent_mump2p_portOPT_AGENT_MUMP2P_PORT33213mump2p agent port (inbound)
telemetry_enableOPT_ENABLE_TELEMETRYfalseEnable metrics / health endpoint
telemetry_portOPT_TELEMETRY_PORT48123Telemetry HTTP port (/health, /metrics, /api/v1/self_info)
identity_libp2p_dirOPT_IDENTITY_LIBP2P_DIR/tmp/libp2plibp2p identity dir — persist as a volume
identity_mump2p_dirOPT_IDENTITY_MUMP2P_DIR/tmp/mump2pmump2p identity dir — persist as a volume
direct_cl_peersOPT_DIRECT_CL_PEERS(empty)CL node multiaddrs for auto-reconnect; when set, only listed peer IDs may stay connected
log_levelOPT_LOG_LEVELdebugdebug / info
remote_push_enableOPT_REMOTE_PUSH_ENABLEfalseOptional Loki/Mimir push (requires telemetry_enable: true)
stream_enableOPT_STREAM_ENABLEfalseEnable the consumer block stream (WebSocket + gRPC)
stream_onlyOPT_STREAM_ONLYfalseSkip CL host/ingest; never publishes. Requires stream_enable
stream_addrOPT_STREAM_ADDR127.0.0.1:9600WebSocket listener (own port, off /metrics); loopback by default
stream_grpc_addrOPT_STREAM_GRPC_ADDR127.0.0.1:9601gRPC listener; loopback by default
stream_require_authOPT_STREAM_REQUIRE_AUTHtrueVerify consumer JWTs; false only on a loopback bind
stream_max_connsOPT_STREAM_MAX_CONNS256Global connection cap
stream_max_conns_per_subOPT_STREAM_MAX_CONNS_PER_SUB8Per-consumer-key connection cap
stream_buffer_sizeOPT_STREAM_BUFFER_SIZE64Per-connection ring buffer (drop-on-overflow)
stream_heartbeat_interval_secOPT_STREAM_HEARTBEAT_INTERVAL_SEC20In-band liveness frame; 0 disables, leaving a stalled feed indistinguishable from a quiet one
stream_keepalive_min_time_secOPT_STREAM_KEEPALIVE_MIN_TIME_SEC20Shortest accepted client ping interval; faster pings get GOAWAY too_many_pings
stream_reauth_modeOPT_STREAM_REAUTH_MODEobserveoff | observe | enforce; re-verify the presented token mid-stream
stream_reauth_interval_secOPT_STREAM_REAUTH_INTERVAL_SEC60How often the presented token is re-verified

See Consumer Block Stream for minting consumer tokens and opening a stream.

Validation

sh
curl http://localhost:48123/health
curl http://localhost:48123/metrics | grep gateway_id
docker logs optimum-gateway | grep "subscribed to topic"

Common Issues

  • Wrong network — Chain comes from the credential, not YAML. If /api/v1/self_info shows the wrong chain, you are using the wrong API key or join key. Get the right credential from Optimum
  • Missing gateway_cluster_id — Required; use the ID assigned during onboarding
  • API key or join key in YAML / image — Move credentials to OPT_API_KEY or OPT_JOIN_KEY in the environment
  • Both OPT_API_KEY and OPT_JOIN_KEY set — Startup error; use exactly one credential mode
  • Port conflicts — Ensure 33212, 33213, 48123 are free
  • Peer ID changes after restart — Persist identity_libp2p_dir and identity_mump2p_dir as volumes

See Troubleshooting for more.