Skip to content

feat(config): Per-chain configuration and runtime chain registry #216

Description

@Emmyt24

Depends on: #213. Blocks: #227.

Description

Configuration assumes exactly one chain. AppState holds a single
network: StellarNetwork, one horizon: Horizon, one horizon_url, one friendbot_url; the
ingest supervisor takes one Horizon URL. There is no way to express "Stellar mainnet and Base
and Arbitrum, each with its own endpoint, confirmation depth, and enable flag."

Replace the flat configuration with a per-chain structure that bin/server loads at startup and
resolves into the ChainRegistry from #213.

Requirements and context

  • Support N chains where N ≥ 1, configured without recompiling.
  • Fail fast and loudly at startup. A chain configured with a bad RPC URL must abort boot with a
    clear message, not fail lazily on the first customer deposit. Follow the existing precedent in
    CloudinaryConfig::from_env — but note that disabled-when-absent
    is right for image uploads and wrong for a chain that has live customer balances.
  • Env-var-only config does not scale to N chains with ~8 settings each. Prefer a config file
    (TOML) with env-var override for secrets, and document the precedence order.
  • Preserve the existing resilience wiring: RetryPolicy and CircuitBreaker are currently
    per-process, and must become per-chain — a degraded Base RPC must not open the circuit on
    Stellar. See crates/ingest/tests/supervisor_network_isolation_tests.rs
    for the isolation property already asserted.
  • Security: RPC URLs frequently embed API keys (Alchemy, Infura). They must never appear in
    logs, error messages, or health endpoints. Wrap in a redacting Debug impl.

Suggested execution

Branch: feat/per-chain-configuration

Implement changes

  • Define ChainConfig { chain_id, rpc_url (redacted Debug), enabled, confirmation_depth, poll_interval, retry, circuit, faucet_url }
    and AppConfig { chains: Vec<ChainConfig>, .. }.
  • Refactor AppState to hold Arc<ChainRegistry> instead of network/horizon/horizon_url,
    keeping accessors for the Stellar path so route handlers change minimally in this PR.
  • Add startup validation: chain ids are well-formed and unique, at least one chain is enabled, every
    enabled chain's RPC answers a liveness probe within a timeout.
  • Move friendbot_url into the Stellar chain config (it is a Stellar-only concept and does not
    belong in shared state).
  • Add a /health detail per chain — reachability and last successful poll, reusing
    LastPollTracker. Redact credentials.

Test and commit

  • Test config parse: valid multi-chain file; duplicate chain id rejected; unknown chain id rejected;
    no-enabled-chains rejected.
  • Test env-var override precedence explicitly.
  • Test that a redacted RPC URL never appears in Debug/Display output or in a /health
    response
    — assert on the API-key substring being absent.
  • Test that one chain's circuit breaker opening does not affect another's.
  • Update .env.example, README.md, and the deployment section of
    docs/architecture.md with a worked multi-chain example.

Example commit message

feat(config): per-chain configuration and runtime registry

Replaces single-chain AppState fields with a ChainRegistry built from
per-chain config, each carrying its own RPC endpoint, confirmation
depth, and resilience policy so a degraded RPC on one chain cannot
open the circuit on another.

RPC URLs use a redacting Debug impl — provider URLs embed API keys.

Refs #216

Guidelines

Keep this PR mechanical. Do not add EVM chains here — this issue delivers the shape, and #217/#218
deliver an adapter to put in it.


Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third Campaignarea/backendBackend crates: api, store, ingest, webhooks, bin/serverdifficulty/mediumMedium difficultytype/epicTracking issue for a multi-issue epic

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions