{"id":47902838,"url":"https://github.com/tempoxyz/tidx","last_synced_at":"2026-04-27T23:00:35.067Z","repository":{"id":348366672,"uuid":"1139477625","full_name":"tempoxyz/tidx","owner":"tempoxyz","description":"tidx indexes Tempo chain data into a hybrid PostgreSQL + ClickHouse architecture for fast point lookups (OLTP) and lightning-fast analytics (OLAP).","archived":false,"fork":false,"pushed_at":"2026-04-21T14:53:37.000Z","size":4130,"stargazers_count":79,"open_issues_count":16,"forks_count":12,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-21T16:37:25.237Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://tidx.sh","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tempoxyz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-22T02:26:09.000Z","updated_at":"2026-04-21T14:52:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tempoxyz/tidx","commit_stats":null,"previous_names":["tempoxyz/tidx"],"tags_count":38,"template":false,"template_full_name":null,"purl":"pkg:github/tempoxyz/tidx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tempoxyz%2Ftidx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tempoxyz%2Ftidx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tempoxyz%2Ftidx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tempoxyz%2Ftidx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tempoxyz","download_url":"https://codeload.github.com/tempoxyz/tidx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tempoxyz%2Ftidx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32358509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-27T20:07:02.737Z","status":"ssl_error","status_checked_at":"2026-04-27T20:07:00.910Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T04:17:49.649Z","updated_at":"2026-04-27T23:00:35.058Z","avatar_url":"https://github.com/tempoxyz.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\".github/banner-dark.svg\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\".github/banner-light.svg\"\u003e\n    \u003cimg alt=\"tidx\" src=\".github/banner-light.svg\" width=\"100%\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#quickstart\"\u003eQuickstart\u003c/a\u003e •\n  \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e •\n  \u003ca href=\"#configuration\"\u003eConfiguration\u003c/a\u003e •\n  \u003ca href=\"#cli-reference\"\u003eCLI\u003c/a\u003e •\n  \u003ca href=\"#http-api\"\u003eAPI\u003c/a\u003e •\n  \u003ca href=\"#query-cookbook\"\u003eQueries\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n**tidx** indexes [Tempo](https://tempo.xyz) chain data into a hybrid PostgreSQL + ClickHouse architecture for fast point lookups (OLTP) and lightning-fast analytics (OLAP). \n\n## Features\n\n- **Dual Storage** — PostgreSQL (OLTP) + ClickHouse (OLAP), written in parallel\n- **Event/Function Decoding** — Query decoded events or function calldata by ABI signature (no pre-registration)\n- **HTTP API + CLI** — Query data via REST, SQL, or command line\n\n## Table of Contents\n\n- [Quickstart](#quickstart)\n- [Overview](#overview)\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [CLI](#cli)\n- [HTTP API](#http-api)\n- [Database Schema](#database-schema)\n- [Sync Architecture](#sync-architecture)\n- [Development](#development)\n- [License](#license)\n\n## Quickstart\n\n```bash\ncurl -L https://tidx.vercel.app/docker | bash\n```\n\n## Overview\n\nThe sync engine writes to both PostgreSQL and ClickHouse in parallel. Use the `engine` query parameter to choose which backend to query:\n\n```\n                                              ┌─────────────────────┐\n                                              │      /query         │\n                                              │                     │\n                                              │  ?signature=...     │◄─── Lazy event decoding\n                                              │  ?engine=...        │     (no pre-registration)\n                                              └──────────┬──────────┘\n                                                         │\n              ┌──────────────────────────────────────────┼──────────────────────────────────────────┐\n              │                                          │                                          │\n              ▼                                          ▼                                          ▼\n┌─────────────────────┐                    ┌─────────────────────┐                    ┌─────────────────────┐\n│    PostgreSQL       │                    │     ClickHouse      │                    │  Materialized Views │\n│    (OLTP)           │                    │      (OLAP)         │ ─────────────────► │  (auto-updated)     │\n│                     │                    │                     │                    │                     │\n└─────────┬───────────┘                    └─────────┬───────────┘                    └─────────────────────┘\n          │                                          │\n          └──────────────────┬───────────────────────┘\n                             │\n                     ┌───────┴───────┐\n                     │  Dual Sink    │\n                     └───────┬───────┘\n                             │\n                     ┌───────┴───────┐\n                     │  Sync Engine  │\n                     └───────────────┘\n```\n\n```bash\n# PostgreSQL (OLTP) - last 10 transfers from an address\ncurl \"https://tidx.example.com/query \\\n  ?chainId=4217 \\\n  \u0026signature=Transfer(address,address,uint256) \\\n  \u0026sql=SELECT * FROM Transfer WHERE from = '0x...' ORDER BY block_num DESC LIMIT 10\"\n\n# ClickHouse (OLAP) - same query, faster for large scans\ncurl \"https://tidx.example.com/query \\\n  ?chainId=4217 \\\n  \u0026engine=clickhouse \\\n  \u0026signature=Transfer(address,address,uint256) \\\n  \u0026sql=SELECT * FROM Transfer WHERE from = '0x...' ORDER BY block_num DESC LIMIT 10\"\n\n# ClickHouse (OLAP) - query pre-computed views\ncurl \"https://tidx.example.com/views?chainId=4217\"\n\u003e {\"ok\":true,\"views\":[{\"name\":\"top_holders\",\"columns\":[{\"name\":\"token\",\"type\":\"String\"},{\"name\":\"holder\",\"type\":\"String\"},{\"name\":\"balance\",\"type\":\"UInt256\"}]}]}\n\ncurl \"https://tidx.example.com/query \\\n  ?chainId=4217 \\\n  \u0026engine=clickhouse \\\n  \u0026sql=SELECT * FROM top_holders WHERE token = '0x...' LIMIT 10\"\n```\n\n## Installation\n\n### Docker\n\n```bash\ndocker pull ghcr.io/tempoxyz/tidx:latest\ndocker run -v $(pwd)/config.toml:/config.toml ghcr.io/tempoxyz/tidx up\n```\n\n### From Source\n\n```bash\ngit clone https://github.com/tempoxyz/tidx\ncd tidx\ncargo build --release\n```\n\n## Configuration\n\ntidx uses a `config.toml` file to configure the indexer.\n\n### Example\n\n```toml\n# config.toml\n\n[http]\nenabled = true\nport = 8080\nbind = \"0.0.0.0\"\ntrusted_cidrs = [\"100.64.0.0/10\"]   # Optional: trusted IPs for admin operations (e.g., Tailscale)\n\n[prometheus]\nenabled = true\nport = 9090\n\n[[chains]]\nname = \"mainnet\"\nchain_id = 4217\nrpc_url = \"https://rpc.tempo.xyz\"\npg_url = \"postgres://user@tidx.example.com:5432/tidx_mainnet\"\npg_password_env = \"TIDX_PG_PASSWORD\"  # Password from environment variable\nbatch_size = 100\n\n# Optional: ClickHouse for OLAP queries\n[chains.clickhouse]\nenabled = true\nurl = \"http://clickhouse:8123\"\n\n[[chains]]\nname = \"moderato\"\nchain_id = 42431\nrpc_url = \"https://rpc.testnet.tempo.xyz\"\npg_url = \"postgres://user@tidx.example.com:5432/tidx_moderato\"\npg_password_env = \"TIDX_PG_PASSWORD\"\n```\n\n### Reference\n\n```\n[http]                                             HTTP server configuration\n├── enabled                 bool      = true         Enable HTTP API server\n├── port                    u16       = 8080         HTTP server port\n├── bind                    string    = \"0.0.0.0\"    Bind address\n└── trusted_cidrs           string[]  = []           Trusted CIDRs for admin ops (e.g., Tailscale)\n\n[prometheus]                                       Prometheus metrics server\n├── enabled                 bool      = true         Enable metrics endpoint\n└── port                    u16       = 9090         Metrics server port\n\n[[chains]]                                         Chain configuration \n├── name                    string    (required)     Display name for logging\n├── chain_id                u64       (required)     Chain ID\n├── rpc_url                 string    (required)     JSON-RPC endpoint URL\n├── pg_url                  string    (required)     PostgreSQL connection string\n├── pg_password_env         string    (optional)     Env var name for PostgreSQL password\n├── api_pg_url              string    (optional)     Separate PostgreSQL URL for API (e.g., read replica)\n├── api_pg_password_env     string    (optional)     Env var name for API PostgreSQL password\n├── batch_size              u64       = 100          Blocks per RPC batch request\n└── [clickhouse]                                     ClickHouse OLAP settings\n    ├── enabled             bool      = false        Enable ClickHouse OLAP queries\n    └── url                 string    = \"http://clickhouse:8123\"  ClickHouse HTTP URL\n```\n\n## CLI\n\n```\nUsage: tidx \u003cCOMMAND\u003e\n\nCommands:\n  init         Initialize a new config.toml\n  up           Start syncing blocks from the chain (continuous) and serve HTTP API\n  status       Show sync status\n  query        Run a SQL query (use --signature to decode event logs)\n  views        Manage ClickHouse materialized views\n  upgrade      Update tidx to the latest version\n  help         Print this message or the help of the given subcommand(s)\n\nOptions:\n  -h, --help  Print help\n```\n\n### `tidx init`\n\n```\nInitialize a new config.toml\n\nUsage: tidx init [OPTIONS]\n\nOptions:\n  -o, --output \u003cOUTPUT\u003e  Output path for config file [default: config.toml]\n      --force            Overwrite existing config file\n  -h, --help             Print help\n```\n\n### `tidx up`\n\n```\nStart syncing blocks from the chain (continuous) and serve HTTP API\n\nUsage: tidx up [OPTIONS]\n\nOptions:\n  -c, --config \u003cCONFIG\u003e  Path to config file [default: config.toml]\n  -h, --help             Print help\n```\n\n### `tidx status`\n\n```\nShow sync status\n\nUsage: tidx status [OPTIONS]\n\nOptions:\n  -c, --config \u003cCONFIG\u003e  Path to config file [default: config.toml]\n  -w, --watch            Watch mode - continuously update status\n      --json             Output as JSON\n  -h, --help             Print help\n```\n\n### `tidx query`\n\n```\nRun a SQL query (use --signature to decode event logs)\n\nUsage: tidx query [OPTIONS] \u003cSQL\u003e\n\nArguments:\n  \u003cSQL\u003e  SQL query (SELECT only). Use event name from --signature as table\n\nOptions:\n  -u, --url \u003cURL\u003e              TIDX HTTP API URL (e.g., http://localhost:8080)\n  -n, --chain-id \u003cCHAIN_ID\u003e   Chain ID to query (uses first chain if not specified)\n  -e, --engine \u003cENGINE\u003e        Force query engine (postgres, clickhouse)\n  -f, --format \u003cFORMAT\u003e        Output format (table, json, csv, toon) [default: table]\n  -l, --limit \u003cLIMIT\u003e          Maximum rows to return [default: 10000]\n  -s, --signature \u003cSIGNATURE\u003e  Event signature to create a CTE\n  -t, --timeout \u003cTIMEOUT\u003e      Query timeout in milliseconds [default: 30000]\n  -c, --config \u003cCONFIG\u003e        Path to config file [default: config.toml]\n  -h, --help                   Print help\n```\n\n### `tidx views`\n\n```\nManage ClickHouse materialized views\n\nUsage: tidx views --url \u003cURL\u003e \u003cCOMMAND\u003e\n\nCommands:\n  list    List all views for a chain\n  get     Get view details\n  create  Create a new materialized view\n  delete  Delete a view\n\nOptions:\n      --url \u003cURL\u003e  TIDX HTTP API URL [env: TIDX_URL]\n  -h, --help       Print help\n```\n\n### `tidx upgrade`\n\n```\nUpdate tidx to the latest version\n\nUsage: tidx upgrade\n\nDownloads and replaces the current binary from GitHub releases.\n```\n\n### Examples\n\n```bash\n# Start with config\ntidx up --config config.toml\n\n# Watch sync status (updates every second)\ntidx status --watch\n\n# Run SQL query\ntidx query \"SELECT COUNT(*) FROM txs\"\n\n# Query with event decoding\ntidx query \\\n  --signature \"Transfer(address indexed from, address indexed to, uint256 value)\" \\\n  \"SELECT * FROM Transfer LIMIT 10\"\n\n# List views\ntidx views --url https://tidx.example.com list --chain-id 4217\n\n# Create a view (must be run from trusted IP)\ntidx views --url https://tidx.example.com create \\\n  --chain-id 4217 \\\n  --name top_holders \\\n  --sql \"SELECT holder, SUM(balance) as total FROM balances GROUP BY holder\" \\\n  --order-by holder\n\n# Self-update\ntidx upgrade\n```\n\n## HTTP API\n\ntidx exposes a HTTP API for querying the indexer.\n\n### Examples\n\n```bash\n# Point lookup (auto-routed to PostgreSQL)\ncurl \"https://tidx.example.com/query?chainId=4217\u0026sql=SELECT * FROM blocks WHERE num = 12345\"\n\u003e {\"columns\":[\"num\",\"hash\",\"timestamp\"],\"rows\":[[12345,\"0xabc...\",\"2024-01-01T00:00:00Z\"]],\"row_count\":1,\"engine\":\"postgres\",\"ok\":true}\n\n# Aggregation (auto-routed to ClickHouse)\ncurl \"https://tidx.example.com/query?chainId=4217\u0026sql=SELECT type, COUNT(*) FROM txs GROUP BY type\"\n\u003e {\"columns\":[\"type\",\"count\"],\"rows\":[[0,50000],[2,120000]],\"row_count\":2,\"engine\":\"clickhouse\",\"ok\":true}\n\n# Status\ncurl https://tidx.example.com/status\n\u003e {\"ok\":true,\"chains\":[{\"chain_id\":4217,\"synced_num\":567890,\"head_num\":567890,\"lag\":0}]}\n```\n\n### Reference\n\n```\nGET  /health                                             Health check\nGET  /status                                             Sync status for all chains\nGET  /query                                              Execute SQL query\n     ?sql                   string    (required)         SQL query (SELECT only)\n     ?chainId               number    (required)         Chain ID to query\n     ?signature             string                       Event signature for CTE generation\n     ?engine                string    = postgres         Query engine: postgres or clickhouse\n     ?live                  bool      = false            Enable SSE streaming (postgres only)\nGET  /views?chainId=                                     List materialized views\nGET  /views/{name}?chainId=                              Get view details\nPOST /views                                              Create view (trusted IP only)\nDELETE /views/{name}?chainId=                            Delete view (trusted IP only)\nGET  /metrics                                            Prometheus metrics\n```\n\n### Views API\n\nManage ClickHouse materialized views for pre-computed analytics. Views are stored in `analytics_{chainId}` database and auto-update on new data.\n\n**Note:** POST and DELETE require connection from a trusted IP (configured via `trusted_cidrs`).\n\n#### List Views\n\n```bash\ncurl \"https://tidx.example.com/views?chainId=42431\"\n```\n\n```json\n{\n  \"ok\": true,\n  \"views\": [\n    {\n      \"name\": \"token_holders\",\n      \"engine\": \"MaterializedView\",\n      \"database\": \"analytics_42431\",\n      \"columns\": [\n        {\"name\": \"token\", \"type\": \"String\"},\n        {\"name\": \"holder\", \"type\": \"String\"},\n        {\"name\": \"balance\", \"type\": \"UInt256\"}\n      ]\n    }\n  ]\n}\n```\n\n#### Create View (trusted IP only)\n\n```bash\ncurl -X POST \"https://tidx.example.com/views\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"chainId\": 42431,\n    \"name\": \"token_holders\",\n    \"sql\": \"SELECT token, holder, sum(balance) AS balance FROM token_balances GROUP BY token, holder HAVING balance \u003e 0\",\n    \"orderBy\": [\"token\", \"holder\"]\n  }'\n```\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `chainId` | yes | Target chain ID |\n| `name` | yes | View name (alphanumeric + underscore) |\n| `sql` | yes | SELECT statement for the view |\n| `orderBy` | yes | Primary key columns for table sorting |\n| `engine` | no | ClickHouse engine (default: `SummingMergeTree()`) |\n\nThis creates:\n1. Target table `analytics_{chainId}.{name}` with inferred schema\n2. Materialized view `analytics_{chainId}.{name}_mv` that auto-populates on inserts\n3. Backfills existing data from the source query\n\n#### Get View Details\n\n```bash\ncurl \"https://tidx.example.com/views/token_holders?chainId=42431\"\n```\n\n```json\n{\n  \"ok\": true,\n  \"view\": {\"name\": \"token_holders\", \"engine\": \"View\", \"database\": \"analytics_42431\"},\n  \"definition\": \"CREATE VIEW analytics_42431.token_holders AS SELECT ...\",\n  \"row_count\": 1234567\n}\n```\n\n#### Delete View (trusted IP only)\n\n```bash\ncurl -X DELETE \"https://tidx.example.com/views/token_holders?chainId=42431\"\n```\n\n```json\n{\n  \"ok\": true,\n  \"deleted\": [\"token_holders_mv\", \"token_holders\"]\n}\n```\n\n#### Query Views\n\nViews are auto-prefixed with `analytics_{chainId}` when using `engine=clickhouse`:\n\n```bash\n# Query the view (auto-prefixed)\ncurl \"https://tidx.example.com/query?chainId=42431\u0026engine=clickhouse\u0026sql=SELECT * FROM token_holders WHERE token = '0x...' ORDER BY balance DESC LIMIT 10\"\n```\n\n## Schemas\n\nAll tables use composite primary keys with timestamps for efficient range queries:\n\n### blocks\n\n| Column | Type | Description |\n|--------|------|-------------|\n| `num` | `INT8` | Block number |\n| `hash` | `BYTEA` | Block hash |\n| `parent_hash` | `BYTEA` | Parent block hash |\n| `timestamp` | `TIMESTAMPTZ` | Block timestamp |\n| `timestamp_ms` | `INT8` | Block timestamp (milliseconds) |\n| `gas_limit` | `INT8` | Gas limit |\n| `gas_used` | `INT8` | Gas used |\n| `miner` | `BYTEA` | Block producer |\n| `extra_data` | `BYTEA` | Extra data field |\n\n### txs\n\n| Column | Type | Description |\n|--------|------|-------------|\n| `block_num` | `INT8` | Block number |\n| `block_timestamp` | `TIMESTAMPTZ` | Block timestamp |\n| `idx` | `INT4` | Transaction index |\n| `hash` | `BYTEA` | Transaction hash |\n| `type` | `INT2` | Transaction type |\n| `from` | `BYTEA` | Sender address |\n| `to` | `BYTEA` | Recipient address |\n| `value` | `TEXT` | Transfer value (wei) |\n| `input` | `BYTEA` | Calldata |\n| `gas_limit` | `INT8` | Gas limit |\n| `max_fee_per_gas` | `TEXT` | Max fee per gas |\n| `max_priority_fee_per_gas` | `TEXT` | Max priority fee |\n| `gas_used` | `INT8` | Gas consumed |\n| `nonce_key` | `BYTEA` | Nonce key (2D nonces) |\n| `nonce` | `INT8` | Nonce value |\n| `fee_token` | `BYTEA` | Fee token address |\n| `calls` | `JSONB` | Batch call data |\n| `call_count` | `INT2` | Number of calls |\n| `valid_before` | `INT8` | Validity window start |\n| `valid_after` | `INT8` | Validity window end |\n| `signature_type` | `INT2` | Signature type |\n\n### logs\n\n| Column | Type | Description |\n|--------|------|-------------|\n| `block_num` | `INT8` | Block number |\n| `block_timestamp` | `TIMESTAMPTZ` | Block timestamp |\n| `log_idx` | `INT4` | Log index |\n| `tx_idx` | `INT4` | Transaction index |\n| `tx_hash` | `BYTEA` | Transaction hash |\n| `address` | `BYTEA` | Emitting contract |\n| `selector` | `BYTEA` | Event selector (topic0) |\n| `topics` | `BYTEA[]` | All topics |\n| `data` | `BYTEA` | Event data |\n\n### receipts\n\n| Column | Type | Description |\n|--------|------|-------------|\n| `block_num` | `INT8` | Block number |\n| `block_timestamp` | `TIMESTAMPTZ` | Block timestamp |\n| `tx_idx` | `INT4` | Transaction index |\n| `tx_hash` | `BYTEA` | Transaction hash |\n| `from` | `BYTEA` | Sender address |\n| `to` | `BYTEA` | Recipient address |\n| `contract_address` | `BYTEA` | Created contract (if deploy) |\n| `gas_used` | `INT8` | Gas consumed |\n| `cumulative_gas_used` | `INT8` | Cumulative gas in block |\n| `effective_gas_price` | `TEXT` | Actual gas price paid |\n| `status` | `INT2` | Success (1) or failure (0) |\n| `fee_payer` | `BYTEA` | Tempo fee payer (if sponsored) |\n\n### sync_state\n\n| Column | Type | Description |\n|--------|------|-------------|\n| `chain_id` | `INT8` | Chain identifier |\n| `head_num` | `INT8` | Remote chain head from RPC |\n| `synced_num` | `INT8` | Highest contiguous block (no gaps from backfill_num to here) |\n| `tip_num` | `INT8` | Highest block near chain head (realtime follows this) |\n| `backfill_num` | `INT8` | Lowest synced block going backwards (NULL=not started, 0=complete) |\n| `started_at` | `TIMESTAMPTZ` | Sync start time |\n| `updated_at` | `TIMESTAMPTZ` | Last update time |\n\n## Sync Architecture\n\ntidx uses two concurrent sync operations: **Realtime** follows the chain head, while **Gap Sync** fills all missing blocks from most recent to earliest.\n\n```\nBlock Numbers:  0                                                              HEAD\n                │                                                                │\n                ▼                                                                ▼\n    ════════════╪════════════════════════════════════════════════════════════════╪═══▶ time\n                │                                                                │\n    INDEXED:    ░░░░░░░░░░░████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░██████████\n                │          │               │                           │        │\n                ▼          ▼               ▼                           ▼        ▼\n              genesis    gap 2           gap 1                      tip_num   head_num\n               (0)     (fills 2nd)    (fills 1st)                   (1900)    (2000)\n                │                                                              │\n                │◄─────────────────── GAP SYNC ───────────────────────────────►│\n                │           Fills ALL gaps, most recent first                  │\n                │                                                    └─────────┘\n                │                                                     REALTIME\n                │                                                  (following head)\n                │\n                └─── Eventually reaches genesis (block 0)\n\nLegend:\n  ████  = indexed blocks\n  ░░░░  = gaps (missing blocks)\n```\n\n| Operation | Description |\n|-----------|-------------|\n| **Realtime** | Follows chain head immediately, maintains ~0 lag |\n| **Gap Sync** | Detects all gaps, fills from most recent to earliest |\n\nGap sync finds discontinuities via SQL and adds the gap from genesis to the first synced block. Gaps are sorted by end block descending (most recent first) and filled one at a time. Recent gaps are prioritized so users can query recent data during initial sync.\n\n## Development\n\n### Prerequisites\n\n- [Rust 1.75+](https://rustup.rs/)\n- [Docker](https://docs.docker.com/get-docker/)\n- [PostgreSQL](https://www.postgresql.org/download/)\n\n### Make Commands\n\n```bash\nmake up                Start services (use LOCALNET=1 for localnet)\nmake down              Stop all services\nmake logs              Tail indexer logs\nmake build             Build Docker image\nmake seed              Generate transactions\n\nmake bench             Run benchmarks\nmake check             Run clippy lints\nmake test              Run tests\n\nmake clean             Stop services and clean\n```\n\n## License\n\n[LICENSE](./LICENSE)\n\n## Acknowledgments\n\n- [golden-axe](https://github.com/indexsupply/golden-axe) — Inspiration for everything.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftempoxyz%2Ftidx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftempoxyz%2Ftidx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftempoxyz%2Ftidx/lists"}