Atlas is a single-chain EVM explorer for ev-node based chains. The backend is one Rust process, atlas-server, that combines the indexer, HTTP API, live event stream, and background workers.
JSON-RPC
+----------------+
| EVM / ev-node |
+-------+--------+
|
v
+-------------------------------------------------------------------+
| atlas-server |
| |
| +--------------+ +----------------+ +-------------+ |
| | Block fetch | ----> | Batch builder | ----> | Binary COPY | |
| | workers | | logs/tokens | | writer | |
| +--------------+ +----------------+ +------+------+ |
| | |
| +----------------+ +----------------+ +-------------+-------+|
| | Metadata | | Gap fill | | DA worker ||
| | worker | | worker | | optional ev-node ||
| +----------------+ +----------------+ +-------------+-------+|
| | |
| +----------------+ +----------------+ +-------------+-------+|
| | Axum REST API | | SSE /api/events| | Prometheus metrics ||
| +----------------+ +----------------+ +---------------------+|
+-----------------------------+-------------------------------------+
|
v
+----------+
| Postgres |
+----------+
^
|
+---------+----------+
| atlas-frontend |
| nginx + Vite build |
+--------------------+
atlas/
+-- backend/
| +-- Cargo.toml
| +-- crates/
| | +-- atlas-common/ # Shared models, errors, pagination, DB helpers
| | +-- atlas-server/ # API, indexer, CLI, workers, metrics
| +-- migrations/ # SQLx migrations
+-- frontend/ # Vite app, Bun, Tailwind, Preact compatibility
+-- branding/ # Optional mounted logo/static branding assets
+-- docs/
+-- docker-compose.yml
+-- .env.example
atlas-server run starts:
- A migration pass before serving traffic.
- Separate SQLx pools for API and indexer work.
- The block fetch pipeline with configurable batch size, fetch workers, RPC batch size, and RPC rate limit.
- A binary COPY writer using a direct
tokio-postgresconnection. - An Axum router with REST endpoints, Etherscan-compatible API, metrics, and health probes.
- An in-process
HeadTrackerused by/api/heightand/api/events. - The missed-block gap-fill worker.
- The NFT metadata worker.
- Optional DA tracking, faucet backend, and snapshot scheduler.
The check subcommand validates configuration and DB/RPC connectivity. The migrate subcommand runs migrations and exits. The db subcommands provide dump, restore, and indexed-data reset operations.
- The indexer resumes from stored state or
START_BLOCK. - Fetch workers pull blocks and receipts from the configured EVM JSON-RPC endpoint.
- The batch builder extracts blocks, transactions, addresses, event logs, ERC-20 transfers/balances, NFT transfers/ownership, contract metadata, and failed block state.
- The writer persists batches with binary COPY and ordinary SQL where appropriate.
- Committed batches update the
HeadTrackerand publish live block events. - Failed blocks are recorded for the gap-fill worker, which retries with backoff and clears recovered rows atomically.
The fresh-chain block-zero path is supported; recent underflow fixes ensure initial local-chain indexing works from START_BLOCK=0.
NFT tokens move through a metadata state machine:
pendingfetchedretryable_errorpermanent_error
The worker resolves IPFS, Arweave, data: URI, direct image, and HTTP metadata references, applies SSRF and payload protections, classifies failures, stores retry timestamps, and exposes metadata status through NFT APIs.
When ENABLE_DA_TRACKING=true, Atlas queries ev-node using EVNODE_URL for Celestia header and data inclusion heights. The worker backfills known blocks, updates pending entries, stores results in block_da_status, and emits da_batch SSE events for visible UI updates.
When SNAPSHOT_ENABLED=true, a scheduler runs daily pg_dump snapshots at SNAPSHOT_TIME UTC, writes custom-format dump files into SNAPSHOT_DIR, and keeps the newest SNAPSHOT_RETENTION completed dumps.
The API is built with Axum. Most routes have:
- 10 second HTTP timeout returning
408. - API pool
statement_timeout = '10s'. - HTTP request metrics collected by route.
- CORS allowing all origins by default or a single
CORS_ORIGINwhen configured.
Routes excluded from the 10 second HTTP timeout:
/api/events, because SSE connections are long-lived./api/contracts/{address}/verify, because Solidity compilation can take longer and accepts up to 50 MiB request bodies.
PostgreSQL stores canonical explorer history and derived views. Large tables are optimized for explorer access patterns:
- Blocks avoid large
OFFSETscans by using a cursor derived from the highest indexed block and the clamped page limit. - Large table counts use
pg_class.reltuplesestimates where appropriate, with exact counts for small tables. - Transactions use lookup tables and partition-aware queries where needed.
- Migrations run with a dedicated one-connection pool that is not constrained by the API statement timeout.
The frontend is a Vite build served by unprivileged nginx:
- Container port
8080, exposed as host port80in Docker Compose. /api/and exact/apiare proxied toatlas-server:3000./api/eventsis proxied with buffering disabled./branding/serves mounted branding assets.- SPA routes fall back to
index.html.
In development, Vite serves on 5173 and proxies browser /api/... requests to localhost:3000.
/metricsexposes Prometheus text format./health/livereports process liveness./health/readychecks DB connectivity and recent indexer state.- Logs support text or JSON format through
LOG_FORMAT. - Metrics include indexer head, missing block state, HTTP route metrics, and SSE connection accounting.