Skip to content

Air-gapped Blockscout deploy + dedicated Archive-RPC node#8

Draft
owenwahlgren wants to merge 4 commits into
mainfrom
blockscout-poc
Draft

Air-gapped Blockscout deploy + dedicated Archive-RPC node#8
owenwahlgren wants to merge 4 commits into
mainfrom
blockscout-poc

Conversation

@owenwahlgren
Copy link
Copy Markdown
Collaborator

Summary

Adds an offline-friendly Blockscout deployment path and a dedicated Archive-RPC avalanchego node so Blockscout can index a benchmark L1 without exposing the bombard RPCs.

Two halves:

1. Blockscout packaging (podman-first, docker fallback)

  • scripts/blockscout-pack-images.sh pulls the 4 upstream images (backend, frontend, postgres, redis) for linux/amd64 and saves them into blockscout/images.tar.gz (~458 MB)
  • make pack-blockscout (in local/ and remote/) bundles that archive plus compose YAML + helper scripts alongside the existing benchmark payload
  • On the target, ./blockscout.sh up runs podman load -i on first call, then starts the compose stack — no registry traffic on the air-gapped machine
  • scripts/_blockscout_runtime.sh auto-detects podman vs docker and picks <runtime> compose vs the legacy <runtime>-compose
  • scripts/blockscout-up.sh resolves host.docker.internal to the actual host-gateway IP at runtime (Elixir's HTTP client bypasses /etc/hosts); also rewrites operator-local IPs so the container reaches the host without going through eth0
  • blockscout/docker-compose.yml indexer config tuned for subnet-evm (no TRACE_URL, HTTP-polling realtime fetcher, FIRST_BLOCK=0, INDEXER_BLOCK_RANGES)

2. Dedicated Archive-RPC avalanchego node

  • New L1ArchiveRPCNodeCount config (l1ArchiveRpcs JSON field) launches a node bound to 0.0.0.0 with --http-allowed-hosts=*
  • writeArchiveChainConfig overlays pruning-enabled: false + debug-tracer API onto the shared chain config — applied only to the Archive-RPC node, so bombard's RPCs stay loopback-bound and pruned
  • URLs written to a separate network_data/archive-rpcs.txt (bombard's rpcs.txt excludes them)
  • remote/03_deploy_l1_config.sh runs the equivalent on the bootstrap host (port 9656/9657) and persists ARCHIVE_RPC_URL to network.env
  • remote/blockscout.sh prefers ARCHIVE_RPC_URL over the bombard RPC

Test plan

  • Single-node local flow on Rocky 9.7 (l1ArchiveRpcs: 1): bombard via loopback rejects foreign Host headers, Blockscout via Archive-RPC indexes successfully (572 blocks / 15,794 txs / 1,803 addresses, 0 missing-trie-node errors)
  • Multi-node remote/ flow on a 3-node Rocky 9.7 cluster: 01..03 deploy + Archive-RPC verified, remote/blockscout.sh up indexes (690 blocks streaming) under sustained bombard
  • Bug fixes from multi-node validation: mkdir -p data/archive-rpc/{db,logs} in the Archive-RPC startup template, is_local_ip rewrite in to_internal_url
  • Real RHEL air-gap target (no internet) — only validated on Rocky with internet so far
  • Frontend customization / branding (out of scope here; tracked separately for a future fork)

Out of scope / follow-ups

  • Frontend host hardcoded to 127.0.0.1 in compose; external browser access currently requires manual sed. A --public-host flag in blockscout-up.sh would make this proper.
  • The Blockscout frontend has a custom proprietary license — we use the upstream image as-is. A custom Avalanche-branded frontend that consumes the API would be a separate project.
  • Backend fork into ava-labs (GPL-3, freely forkable) is also a separate project.

Why draft

Verified on Rocky 9.7 single-node and 3-node multi-node setups. Has not been validated on a true air-gapped RHEL host yet — needs a real customer-environment dry run before merge.

Bundles the Blockscout backend, frontend, postgres, and redis OCI images
into a single archive at blockscout/images.tar.gz on a build machine, then
extracts and runs the compose stack on the target via podman load. The
local and remote networks now optionally launch a dedicated Archive-RPC
avalanchego node bound to 0.0.0.0 with no pruning, used by Blockscout
while the bombard RPC nodes stay loopback-bound.

- scripts/blockscout-pack-images.sh pulls and saves the images
- make pack-blockscout (in local/ and remote/) wraps the standard tarball
  with the OCI archive plus compose + helper scripts
- local/internal/network adds L1ArchiveRPCNodeCount and writes
  archive-rpcs.txt; archive node uses a chain config with pruning-enabled
  false and debug-tracer API
- remote/03_deploy_l1_config.sh starts an Archive-RPC node on port 9656
  on the bootstrap host and writes ARCHIVE_RPC_URL to network.env
- scripts/blockscout-up.sh resolves host.docker.internal to the actual
  gateway IP at runtime so Elixir's HTTP client can reach the chain
- blockscout-{up,down}.sh detect podman vs docker and pick the right
  compose CLI
Two issues surfaced when validating the remote/ flow on a 3-node cluster
where the operator runs on the same host as the Archive-RPC node:

- `start-l1-archive-rpc.sh` was missing `mkdir -p data/archive-rpc/{db,logs}`,
  so the `nohup ... > data/archive-rpc/logs/avalanchego.out` redirect failed
  and avalanchego never started.
- `to_internal_url` only rewrote `localhost`/`127.0.0.1` to the host-gateway
  alias. When ARCHIVE_RPC_URL contains the operator's own eth0 IP, a
  rootless-podman container can't reach that IP directly. Added an
  `is_local_ip` helper (uses `ip -4 -o addr` or `ifconfig`) and a final
  pass that rewrites any host-bound IP through the same gateway resolver.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant