Skip to main content

Reconcile customer-owned Ethereum validator indices against Optimum's validator registry (console API). Cron-friendly CLI that authenticates with an ovi_live_* operator API key, diffs the desired set against the registry, and applies the delta.

Project description

optimum-keysync

Reconcile a customer's Ethereum validator set against Optimum's validator registry. A one-shot CLI, safe to run on a cron / systemd timer / k8s CronJob: --dry-run by default, --apply to write.

Quickstart

pip install optimum-keysync

Point keysync at your Optimum API key and tell it which validators you manage. The simplest input is a JSON file of records (no beacon node needed); see examples/indices.json for the format:

export KEYSYNC_API_KEY=ovi_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export KEYSYNC_INDICES_FILE=/path/to/your-validators.json

keysync diff           # preview the planned changes, no writes
keysync sync --apply   # apply them

KEYSYNC_API_URL, KEYSYNC_AUTH_URL, KEYSYNC_NETWORK, and KEYSYNC_CHAIN_ID default to the public Optimum endpoints and Ethereum mainnet, so the snippet above omits them. Set them only to override, e.g. a testnet (KEYSYNC_NETWORK=hoodi with its KEYSYNC_CHAIN_ID).

Prefer to track validators by BLS pubkey or bare index? Point keysync at a beacon node and it resolves the rest:

export KEYSYNC_BEACON_URL=http://your-beacon-node:5052
export KEYSYNC_PUBKEYS_FILE=/path/to/validators.pubkeys   # one BLS pubkey per line

Get KEYSYNC_API_KEY (an ovi_live_* key) from the Optimum partners dashboard. That is the only secret; everything else is non-sensitive config. Re-runs are idempotent, so a steady state reports nothing to do.

Telling keysync which validators you own

Pick whichever list you already have. All three may be combined.

Source Env var Format Beacon node
BLS pubkeys KEYSYNC_PUBKEYS_FILE one pubkey per line required (resolves the index)
Validator indices KEYSYNC_INDEX_LIST_FILE one index per line required (resolves the pubkey)
Pre-computed records KEYSYNC_INDICES_FILE JSON of {validator_index, chain_id, validator_key} not used

KEYSYNC_INDICES_FILE is the no-lookup option for air-gapped setups, and it wins over a beacon-resolved hit for the same (validator_index, chain_id). See examples/ for a sample of each format.

Configuration

All settings come from environment variables, each with a matching flag override (the flag wins).

Env var Required Purpose
KEYSYNC_API_URL no Optimum console API base URL (validator endpoints). Default: https://console.getoptimum.io
KEYSYNC_AUTH_URL no Auth API base URL (used for GET /api/v1/me). Default: https://auth.getoptimum.io
KEYSYNC_API_KEY yes ovi_live_* from the partners dashboard
KEYSYNC_NETWORK no mainnet, hoodi, etc. Default: mainnet
KEYSYNC_CHAIN_ID no Corresponding chain ID string. Default: 0x1
KEYSYNC_BEACON_URL conditional Required when using a pubkeys or index-list file
KEYSYNC_PUBKEYS_FILE conditional One BLS pubkey per line; beacon resolves the index
KEYSYNC_INDEX_LIST_FILE conditional One validator index per line; beacon resolves the pubkey
KEYSYNC_INDICES_FILE conditional Pre-computed JSON records; no lookup
KEYSYNC_OPERATOR_ID no Auto-resolved via GET /api/v1/me. Set to skip the lookup or pin the scope.
KEYSYNC_LOG_FORMAT no console (default) or json

Commands

keysync diff                  # print the planned add/remove delta, no writes
keysync sync [--apply]        # reconcile; --dry-run by default, --apply to write
keysync show                  # print currently-assigned validators

keysync sync also takes --dry-run (forces no-write, wins over --apply), --max-deletes N (default 10), and --log-format json.

--max-deletes is the guardrail against a misconfigured input wiping out an operator's assignments: keysync refuses to unassign more than N validators in a single run. Bump it when a large exit is expected.

What it does

  1. Resolves your operator_id from the API key via GET /api/v1/me on optimum-auth (or uses KEYSYNC_OPERATOR_ID if set).
  2. Resolves the desired validator set from your input files, querying the beacon node's /eth/v1/beacon/states/head/validators endpoint as needed (pubkey to index, or index to pubkey).
  3. Lists your currently-assigned validators from the console API and computes the add / remove delta.
  4. Under --apply, registers new keys, assigns them, and unassigns anything that left the desired set. Each stage is idempotent server-side.

The ovi_live_* key is the Bearer on every console API call (no JWT exchange, no token caching), so revoking it at the dashboard takes effect on the next run.

Exit codes

Wire alerts on these:

Code Meaning
0 success
2 auth / identity failure
3 console API failure
4 configuration error
5 --max-deletes guardrail tripped
6 beacon node failure

Operating notes

  • Beacon node: must allow unauthenticated reads on /eth/v1/beacon/states/head/validators (Lighthouse: --http).
  • Retries: HTTP calls retry with exponential backoff on 5xx / 429 / network errors only. Other 4xx fails fast, so a misconfiguration does not retry into a rate limit.

Examples

examples/ has a pubkeys file, an index list, and an indices JSON, plus systemd timer and Kubernetes CronJob templates.

Development

pip install -e '.[dev]'
pytest

Tests are hermetic: respx stubs every outbound HTTP call, so no live API or beacon access is required.

Releasing

Publishing to PyPI is automated via .github/workflows/release.yml, which runs when a GitHub Release is published and uploads with PyPI Trusted Publishing (OIDC) — no token is stored in the repo. To cut a release:

  1. Bump version in pyproject.toml and merge to main.
  2. Publish a GitHub Release tagged v<version> (e.g. v1.0.0).

The workflow verifies the tag matches the package version, builds the sdist and wheel, and publishes. One-time setup: register the trusted publisher on PyPI (project optimum-keysync, workflow release.yml, environment pypi).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

optimum_keysync-1.1.1.tar.gz (41.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

optimum_keysync-1.1.1-py3-none-any.whl (30.8 kB view details)

Uploaded Python 3

File details

Details for the file optimum_keysync-1.1.1.tar.gz.

File metadata

  • Download URL: optimum_keysync-1.1.1.tar.gz
  • Upload date:
  • Size: 41.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for optimum_keysync-1.1.1.tar.gz
Algorithm Hash digest
SHA256 43b2d1f02cef3308b171b0e1e6ef19e744e6eafe2b5fc600d3cf81bace9fc6d7
MD5 a72d6437fb2b013e1b74dd6977a25868
BLAKE2b-256 cf19671f26e4d4b92277dc3dbdd412b9cf0fdcb98bd358b78210ecc2cb0f7b16

See more details on using hashes here.

Provenance

The following attestation bundles were made for optimum_keysync-1.1.1.tar.gz:

Publisher: release.yml on getoptimum/optimum-keysync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file optimum_keysync-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: optimum_keysync-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 30.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for optimum_keysync-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bef8901c4e7baac47991ef611cfa554fd27b721cfeb85db294ae0b65a33f3830
MD5 e89ca55fe33d752a478422bea9952061
BLAKE2b-256 80c1194d76b8601f388525ffef435f8cec28625f31dd56800c5c4111c5348417

See more details on using hashes here.

Provenance

The following attestation bundles were made for optimum_keysync-1.1.1-py3-none-any.whl:

Publisher: release.yml on getoptimum/optimum-keysync

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page