A Rust + Postgres system that indexes Livepeer protocol events on Arbitrum One, prices every monetary event at block-level precision via Uniswap V3 TWAP × Chainlink, and exposes the result over an HTTP API plus a single-page web UI.
The load-bearing correctness guarantee is byte-deterministic replay: a database wipe followed by a re-run from cached RPC inputs and the seeded SQLite produces an identical output database (SPEC §1.4 / §12.4).
Built by Cloud SPE.
The Livepeer protocol emits a lot of monetary events on-chain — bonds, unbonds, rewards, ticket redemptions, gateway deposits. None of them carry a USD value at the point of emission, and pricing them after the fact non-deterministically (e.g., via a third-party REST API at query time) makes historical accounting unreliable.
This system solves that by:
- Indexing every Livepeer protocol log on Arbitrum One into an immutable
raw_protocol_eventstable. - Pricing each finalized monetary event at its own block, using on-chain oracles only (Uniswap V3 30-minute TWAP for LPT, Chainlink ETH/USD aggregator for the L1 reference rate, Chainlink L2 sequencer-uptime feed as a liveness gate).
- Caching every RPC call into
rpc_call_cacheso the entire pipeline is replayable bit-for-bit from cold storage. - Aggregating the priced rows into daily rollups for payouts, rewards, tickets, and event metrics.
- Serving the result over a versioned HTTP API (
/api/v1/...) and a Lit-based SPA bundled into the same Axum process.
Arbitrum One RPC (Chainstack + secondary)
│
▼
┌───────────────────────────────────────────────────────────────────────┐
│ livepeer-indexer pulls logs in chunks, decodes via vendored │
│ ABIs, writes raw_protocol_events (append) │
│ livepeer-reorg-watcher validates parent-hash chain continuity │
│ livepeer-finality-watcher advances finality field on L1 batch posts │
└───────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────┐
│ livepeer-valuator prices finalized events via │
│ Chainlink + Uniswap V3 TWAP → │
│ event_valuations (immutable, versioned) │
│ livepeer-staker stake_balances_by_block, gateway_*, │
│ orchestrator/broadcaster profile │
└───────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────┐
│ livepeer-rollups orch_payouts_daily, orch_rewards_daily, │
│ tickets_daily, event_metrics_daily │
│ livepeer-enricher ENS name + avatar resolution (L1 lookup) │
└───────────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────────┐
│ livepeer-api Axum HTTP server on :8080 │
│ • /api/v1/* business endpoints │
│ • /health /metrics /openapi.json │
│ • Serves the SPA bundle as same-origin │
└───────────────────────────────────────────────────────────────────────┘
livepeer-daemon supervises the live-mode loop in production; livepeer-orchestrator is the one-shot CLI for replays, bootstraps, and migration-only runs. livepeer-seed-migrator is a one-time SQLite-to-Postgres import for trusted historical valuations (SPEC §8.1 seed pass).
Requires Docker (or a local Postgres 17), Rust 1.94.1 (rustup will install automatically via the toolchain pin), Node 22+, and a Chainstack Arbitrum One RPC URL.
# 1. Configure
cp .env.example .env # then fill in CHAINSTACK_RPC_URL, etc.
# 2. Start Postgres
docker compose up -d postgres
# 3. Build everything (Rust + frontend)
cargo build --release --workspace
(cd frontend-ui && npm install && npm run build)
# 4. Apply migrations
target/release/livepeer-orchestrator migrate-only
# 5. Bring up the worker fleet (HTTP API on :8080, daemon, rollups, staker, enricher)
bash scripts/resume-catchup-all.sh
# 6. Smoke-check
curl -s http://127.0.0.1:8080/health # → ok
open http://127.0.0.1:8080/ # → SPA dashboardStop everything with bash scripts/stop-all.sh (workers) and docker compose down (Postgres — preserves the postgres_data volume).
Pinned in rust-toolchain.toml to Rust 1.94.1. CI matches via dtolnay/rust-toolchain@1.94.1. Dependencies are pinned exactly (=x.y.z) — bumps go through PR review.
.envat the repo root — secrets (CHAINSTACK_RPC_URL,POSTGRES_*, alert tokens). Gitignored. Use.env.exampleas a template.config/arbitrum.yaml— static, chain-level config (contract addresses, ABI paths, pricing parameters). Committed.config/env/{dev,staging,prod}.yaml— environment overrides (RPC concurrency, batch sizes, etc.). Committed.FE_STATIC_DIRenv var (defaults to/opt/livepeer/frontend-ui/distfor the Docker image) — points the API at the SPA bundle. The local-dev scriptscripts/resume-catchup-all.shsets it to$PWD/frontend-ui/distautomatically.
| Binary | Role |
|---|---|
livepeer-indexer |
Pull logs for one contract over a block range and decode them. |
livepeer-reorg-watcher |
Validate parent-hash continuity and route reorg mutations. |
livepeer-finality-watcher |
Advance the finality column when L1 batches post. |
livepeer-valuator |
Price finalized events. Subcommands: backfill-from-seed, backfill-eth-onchain, backfill-lpt-onchain, backfill-multi-asset, backfill-all, backfill-eth-prices. |
livepeer-staker |
Compute stake/gateway/profile derived tables. Subcommands: backfill, gateway-backfill, profile-follow, tx-receipts-follow, refresh-pending. |
livepeer-rollups |
Daily aggregate workers. Subcommands: orch-payouts-daily, orch-rewards-daily, tickets-daily, event-metrics-daily (each accepts --follow for live mode). |
livepeer-enricher |
ENS L1 name + avatar resolver for orchestrators and gateways. |
livepeer-daemon |
Live-mode supervisor — runs all the workers on internal cadences. |
livepeer-orchestrator |
One-shot CLI: migrate-only, replay, bootstrap, backfill-cuts. |
livepeer-seed-migrator |
One-time SQLite → seeded_event_prices import. |
livepeer-api |
Axum HTTP server. Serves /api/v1/*, operational endpoints, and the SPA. |
livepeer-alert-bot |
Telegram + Discord alerting on indexer-health metrics. |
livepeer-mcp-diag |
Read-only production diagnostics MCP server (SELECT-only diag_ro role + GET-only docker-socket proxy). See crates/livepeer-mcp-diag/README.md. |
:8080/— SPA (Livepeer Protocol Explorer dashboard).:8080/api/v1/*— business endpoints (events, valuations, rollups, gateways, orchestrators, rounds, governance, network stats). Full schema:/openapi.json.:8080/health,:8080/metrics,:8080/config.json,:8080/backfills/status— operational, unversioned.
Restated from AGENTS.md — the authoritative repo map. Load-bearing invariants any change must respect:
- Byte-deterministic replay. Drop the DB, replay from
rpc_call_cache+ seed → identical output. The Determinism CI workflow enforces this. - Raw events are immutable. Only permitted mutation is a reorg-induced
block_number/block_hashswap, fully audited. - Valuations are immutable and versioned. New pricing logic = new
valuation_version; never edit existing rows. - No external pricing APIs in the primary path. Only on-chain TWAP/Chainlink and the trusted SQLite seed.
- Migrations are forward-only and immutable once merged.
- All deps pinned exactly. Bumps go through PR review.
- Single-instance workers. No claim mechanism in v1.
The ci.yml workflow runs four jobs on every push to main and every PR, all with RUSTFLAGS="-D warnings":
| Job | Command |
|---|---|
| fmt | cargo fmt --all --check |
| clippy | cargo clippy --workspace --all-targets -- -D warnings |
| build | cargo build --workspace --all-targets |
| test | cargo test --workspace |
The determinism.yml workflow is manual / on-demand only (workflow_dispatch) — it spins up Postgres, replays each fixture in tests/fixtures/, and diffs the SHA-256 hash table against expected_hashes.json. Run it from the GitHub Actions UI or:
gh workflow run Determinism --ref mainThe cargo test --workspace suite excludes integration tests that require a live Postgres. Those are tagged #[ignore] with a reason string. To run them locally:
docker compose up -d postgres
target/release/livepeer-orchestrator migrate-only
cargo test -p livepeer-api -- --ignoredOr for the full sweep (regular + integration):
cargo test --workspace -- --include-ignored- Plans go in
docs/exec-plans/active/(move tocompleted/when done). - Decisions go in
docs/design-docs/with status (draft/accepted/superseded) and averifieddate. - Tech debt is tracked in
docs/exec-plans/tech-debt-tracker.md. - Spec changes require explicit approval and a version bump (SPEC v1.0 → v1.1).
.
├── crates/ 14 Cargo workspace members (see AGENTS.md)
├── abi/ vendored Livepeer ABI JSON (hash-verified at boot)
├── config/ static + env-overlay YAML config
├── migrations/ sqlx-cli forward-only migrations
├── frontend-ui/ Lit + TypeScript SPA (Vite)
├── tests/fixtures/ determinism fixtures (rpc_cache, seed, hashes)
├── scripts/ operator scripts (resume-catchup-all.sh, stop-all.sh, …)
├── docs/ spec, design docs, runbook, deployment, exec plans
├── ops/ Prometheus + Alertmanager configs
├── Dockerfile multi-stage; one image, all binaries
├── docker-compose.yml local dev — single-host shape
├── docker-compose.prod.yml production — pulls tztcloud/livepeer-protocol-explorer
└── rust-toolchain.toml pinned 1.94.1
See AGENTS.md for the canonical, per-crate map.
| File | Purpose |
|---|---|
| AGENTS.md | Repo map + load-bearing invariants. The canonical entry point for contributors and agents. |
| docs/product-specs/v1-livepeer-indexer.md | The authoritative spec (the encyclopedia). |
| docs/RUNBOOK.md | Operational procedures (SPEC §19). |
| docs/DEPLOYMENT.md | Production deployment shape. |
| docs/DETERMINISM.md | Determinism replay-test contract (SPEC §12.4). |
| docs/POSTGRES_MAINTENANCE.md | DB upgrade + maintenance procedures. |
| docs/CLOUDFLARE_TUNNEL.md | External-access tunnel setup. |
| docs/design-docs/index.md | Per-decision design records. |
| docs/design-docs/core-beliefs.md | Load-bearing operating principles. |
| docs/exec-plans/tech-debt-tracker.md | Known shortcuts + deferred work. |
MIT — Copyright (c) 2026 Cloud SPE.