Add a Zagros Asset Hub history indexer for the explorer host

The explorer at explorer.pezkuwichain.io needs transfer history for Zagros
Asset Hub. It runs on the explorer host next to its own archive node, so the
manifest points at 127.0.0.1:9955 and the compose file binds everything
locally with memory limits and no published ports.

deploy-vps now ignores these files: a change to the Zagros indexer must not
rebuild or restart the mainnet stack and its bots on the indexer host.
This commit is contained in:
Satoshi Qazi Muhammed committed 2026-10-15 10:52:35 +04:00
1 parent e3ba45f449
commit 2476423362
5 files changed
+225

No files matched your search

+5
View File
@@ -2,6 +2,11 @@ name: Deploy to VPS
on:
push:
branches: [main]
# The Zagros history indexer runs on the explorer host, not here.
paths-ignore:
- zagros-assethub.yaml
- docker-compose.zagros.yml
- ops/zagros/**
concurrency:
group: deploy-vps
+1
View File
@@ -32,3 +32,4 @@ secrets/
# SubQuery
.data/
project.yaml
.env.zagros
+82
View File
@@ -0,0 +1,82 @@
# Zagros Asset Hub history for explorer.pezkuwichain.io.
#
# Runs on the explorer host next to its own archive node (systemd unit
# zagros-ah-archive, RPC on 127.0.0.1:9955), never on the mainnet indexer host:
# deploy-vps ignores this file and zagros-assethub.yaml.
#
# Everything uses the host network. Postgres binds 127.0.0.1; subql-node (3101)
# and subql-query (3100) cannot be told to, so the host firewall (default deny,
# only 22/80/443 open) keeps them local. Docker would bypass that firewall for
# published ports, so nothing is published.
# nginx exposes only POST /history/assethub (same origin as the explorer).
#
# printf 'ZAGROS_DB_PASS=%s\n' "$(openssl rand -hex 24)" > .env.zagros && chmod 600 .env.zagros
# docker compose -f docker-compose.zagros.yml --env-file .env.zagros up -d
#
# The mapping in dist/ must be built first (see ops/zagros/README.md).
name: zagros-history
services:
postgres:
image: postgres:16-alpine
network_mode: host
command: ["postgres", "-c", "listen_addresses=127.0.0.1", "-c", "port=5433"]
volumes:
- zagros-pgdata:/var/lib/postgresql/data
- ./docker/init:/docker-entrypoint-initdb.d
environment:
POSTGRES_PASSWORD: ${ZAGROS_DB_PASS:?set ZAGROS_DB_PASS in .env.zagros}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -h 127.0.0.1 -p 5433"]
interval: 5s
timeout: 5s
retries: 10
mem_limit: 1g
restart: unless-stopped
subquery-node-assethub:
build:
context: .
dockerfile: docker/Dockerfile.node
network_mode: host
depends_on:
postgres: { condition: service_healthy }
environment:
TZ: UTC
DB_USER: postgres
DB_PASS: ${ZAGROS_DB_PASS:?set ZAGROS_DB_PASS in .env.zagros}
DB_DATABASE: postgres
DB_HOST: 127.0.0.1
DB_PORT: 5433
volumes:
- ./:/app/project:ro
command:
- -f=/app/project/zagros-assethub.yaml
- --db-schema=subquery-zagros-assethub
- --disable-historical=true
- --batch-size=30
- --port=3101
mem_limit: 1g
restart: unless-stopped
graphql-engine-assethub:
image: onfinality/subql-query:v1.5.0
network_mode: host
depends_on:
- subquery-node-assethub
environment:
DB_USER: postgres
DB_PASS: ${ZAGROS_DB_PASS:?set ZAGROS_DB_PASS in .env.zagros}
DB_DATABASE: postgres
DB_HOST: 127.0.0.1
DB_PORT: 5433
command:
- --name=subquery-zagros-assethub
- --indexer=http://127.0.0.1:3101
- --port=3100
- --query-limit=100
mem_limit: 512m
restart: unless-stopped
volumes:
zagros-pgdata:
+38
View File
@@ -0,0 +1,38 @@
# Zagros Asset Hub history (explorer host)
Serves `HistoryElement` for Zagros Asset Hub to explorer.pezkuwichain.io. It
runs on the explorer host next to its own archive node, never on the mainnet
indexer host (`deploy-vps` ignores `zagros-assethub.yaml`,
`docker-compose.zagros.yml` and this directory).
## Pieces
| piece | where | listens |
|---|---|---|
| archive node `zagros-ah-archive` (systemd, `--state-pruning archive --blocks-pruning archive`, relay followed over RPC) | host | RPC 127.0.0.1:9955 |
| postgres 16 | compose | 127.0.0.1:5433 |
| subql-node, `zagros-assethub.yaml` | compose | 3101 (firewalled) |
| subql-query | compose | 3100 (firewalled); nginx exposes `POST /history/assethub` only |
## Build and start
```sh
git clone https://github.com/pezkuwichain/pezkuwi-subquery /opt/zagros-history
cd /opt/zagros-history
# Build the mapping (dist/) without installing Node on the host.
docker run --rm -v "$PWD":/w -w /w node:24-alpine sh -c \
'corepack enable && yarn install --immutable && yarn subql codegen zagros-assethub.yaml && yarn subql build zagros-assethub.yaml'
printf 'ZAGROS_DB_PASS=%s\n' "$(openssl rand -hex 24)" > .env.zagros && chmod 600 .env.zagros
docker compose -f docker-compose.zagros.yml --env-file .env.zagros up -d --build
```
## Check
```sh
curl -s -X POST 127.0.0.1:3100 -H 'content-type: application/json' \
-d '{"query":"{_metadata{lastProcessedHeight targetHeight}}"}'
```
`targetHeight - lastProcessedHeight` above 100 for ten minutes means the
indexer has stalled; the explorer then shows "on its way" for payments it has
not seen yet.
+99
View File
@@ -0,0 +1,99 @@
specVersion: 1.0.0
name: subquery-zagros-assethub
version: 1.0.0
runner:
node:
name: "@subql/node"
version: ">=4.6.6"
query:
name: "@subql/query"
version: "*"
description: Zagros Asset Hub SubQuery - history for explorer.pezkuwichain.io (same handlers as pezkuwi-assethub.yaml)
repository: https://github.com/pezkuwichain/pezkuwi-subquery
schema:
file: ./schema.graphql
network:
chainId: "0x063f739077881df9f291548de301b2e11d6a45cc545c24e7bf18d8a40f0ce182"
endpoint:
- ws://127.0.0.1:9955
dataSources:
- name: main
kind: substrate/Runtime
startBlock: 1
mapping:
file: ./dist/index.js
handlers:
# Block handler: initialize active pool stakers from chain state
- handler: handleBlock
kind: substrate/BlockHandler
# Signed extrinsics for history
- handler: handleHistoryElement
kind: substrate/CallHandler
filter:
isSigned: true
# Nomination Pools rewards
- handler: handlePoolReward
kind: substrate/EventHandler
filter:
module: nominationPools
method: PaidOut
# Pool member bonded (track active stakers)
- handler: handlePoolBonded
kind: substrate/EventHandler
filter:
module: nominationPools
method: Bonded
# Pool member unbonded (cleanup active stakers)
- handler: handlePoolUnbonded
kind: substrate/EventHandler
filter:
module: nominationPools
method: Unbonded
# Pool bonded slash
- handler: handlePoolBondedSlash
kind: substrate/EventHandler
filter:
module: nominationPools
method: PoolSlashed
# Pool unbonding slash
- handler: handlePoolUnbondingSlash
kind: substrate/EventHandler
filter:
module: nominationPools
method: UnbondingPoolSlashed
# Staking rewards on AH (validators + direct nominators)
- handler: handleAHRewarded
kind: substrate/EventHandler
filter:
module: staking
method: Rewarded
# Staking slashes on AH
- handler: handleAHSlashed
kind: substrate/EventHandler
filter:
module: staking
method: Slashed
# Era changes on AH (staking pallet lives here) - old format
- handler: handleAHNewEra
kind: substrate/EventHandler
filter:
module: staking
method: StakingElection
# Era changes on AH - new format (Polkadot 2.0)
- handler: handleAHStakersElected
kind: substrate/EventHandler
filter:
module: staking
method: StakersElected
# Native transfers
- handler: handleTransfer
kind: substrate/EventHandler
filter:
module: balances
method: Transfer
# Asset transfers
- handler: handleAssetTransfer
kind: substrate/EventHandler
filter:
module: assets
method: Transferred