Blockchain monitoring, alerting, and cross-domain message (XDM) transfer indexing for Subspace networks.
- Save the Slack token to a file named
slack-secret - Restrict permissions (Unix):
chmod 400 slack-secret docker run --mount type=bind,source=/path/to/slack-secret,target=/slack-secret,readonly ghcr.io/autonomys/chain-alerter --rpc-url wss://rpc.mainnet.autonomys.xyz/ws --slack-bot-name "My Bot" --slack-channel-name chain-alerts
Requires a PostgreSQL instance:
cargo run -p indexer -- --db-uri postgres://user:pass@localhost:5432/indexerConnects to a Subspace node via WebSocket and monitors for:
- Block events: known account transfers (deposits, withdrawals), domain upgrades, fraud proofs, operator slashing/offline, sudo calls, runtime code updates
- Chain stalls and reorgs: detects when blocks stop being produced or when forks exceed a depth threshold
- Slot timing: monitors per-slot and average slot duration via Proof-of-Time from the P2P network
- Uptime: optional Uptime Kuma health check pushes
Alerts are posted to a Slack channel. The network (Mainnet, Chronos Testnet, etc.) is auto-detected from node metadata, and the corresponding accounts and bootnodes are loaded from alerter/networks.toml.
REST API for querying cross-domain message (XDM) transfers, backed by PostgreSQL:
GET /health— last processed block per chainGET /v1/xdm/transfers/{address}— XDM transfers for an addressGET /v1/xdm/recent— recent XDM transfers (configurable limit)
Processes blocks in parallel from both the consensus chain and Auto-EVM domain.
The shared/subspace.rs module (Subspace::listen_for_all_blocks) manages block tracking and reorg detection. It subscribes to all imported blocks and maintains a header metadata cache (HeadersMetadataCache) of the last 100 blocks.
- Subscribe to all blocks via
subscribe_all(). On startup, load the lastCACHE_HEADER_DEPTH(100) canonical block headers into the cache. - For each received block:
a. Add its header to the cache.
b. Check if it is the canonical block at that height by querying the node RPC (
is_canonical_block). Fork blocks are logged and skipped. c. Compute the tree route from the previous best block to the new best block usingsp_blockchain::tree_route, which walks the cached headers to find enacted (new best path) and retracted (old best path) blocks relative to a common ancestor. d. If headers are missing from the cache during tree route computation, fetch them from RPC and retry (recursive_tree_route). - Broadcast the enacted blocks as
BlocksExtvia atokio::broadcastchannel. If blocks were retracted, includeReorgData(enacted blocks, retracted blocks, common ancestor). - After broadcasting, prune cached headers older than 100 blocks behind the current best.
The stall_and_reorg alerter listens on the broadcast stream and:
- If
BlocksExtcontainsReorgDatawith retracted blocks exceeding the configured--reorg-depth-threshold(default 6), it sends a reorg alert to Slack. - If no blocks arrive within
--non-block-import-threshold(default 60s), it sends a chain stall alert. When blocks resume, it sends a recovery alert.
If the block subscription closes due to an RPC error, the listener automatically re-subscribes and reloads the header cache from the new connection.
For details of the fork choice algorithm, see the subspace protocol specification.
- The Slack OAuth token is loaded from a file and must be readable only by the current user.
- On Unix, the process enforces
0400or0600permissions. Other modes cause a panic at startup.
- On Unix, the process enforces
- The token is wrapped and zeroized on drop to reduce in-memory exposure.
- Do not commit or log the token. The
.gitignoreandDebugimpl handle this by default.
The Slack bot has permission to read and post in channels it is invited into by Slack users.
The Slack bot can be managed via your Slack login on the Slack apps portal:
- The Autonomys Slack team ID is T03LJ85UR5G and the App ID is A09956363PY (these are not secrets)
- Slack bot tokens can be created and revoked on the Install App screen, and the bot's permissions can be changed
- Bot tokens look like
xoxb-(numbers)-(numbers)-(base64)
- Rust nightly (pinned in
rust-toolchain.toml) - A Slack bot token with permission to post to the target channel in the Autonomys workspace (for alerter)
- PostgreSQL (for indexer)
- A running Subspace node (a local non-archival node is fine for the alerter)
- Get the current Slack "bot token" from Install App on the Slack apps portal
- Save it to a file named
slack-secret - Restrict permissions (Unix):
chmod 400 slack-secret(orchmod 600 slack-secret)
cargo run -p alerter -- \
--rpc-url wss://rpc.mainnet.autonomys.xyz/ws \
--slack-bot-name "My Bot" \
--slack-channel-name chain-alerts \
--slack-secret-path ./slack-secret| Argument | Required | Default | Description |
|---|---|---|---|
--rpc-url |
Yes | — | Node WebSocket RPC endpoint |
--network-config-path |
No | /networks.toml |
Path to TOML file with accounts and bootnodes |
--slack-bot-name |
Yes | — | Bot display name in Slack |
--slack-channel-name |
Yes | — | Target Slack channel |
--slack-secret-path |
No | /slack-secret |
Path to file containing bot token |
--slack-bot-icon |
No | robot_face |
Bot emoji icon |
--slack-team-id |
No | T03LJ85UR5G |
Slack workspace ID |
--uptimekuma-url |
No | — | Uptime Kuma push URL |
--uptimekuma-interval |
No | 60s |
Health check push frequency |
--non-block-import-threshold |
No | 60s |
Alert after no blocks for this duration |
--reorg-depth-threshold |
No | 6 |
Reorg depth to trigger alert |
--per-slot-threshold |
No | 1.2s |
Max acceptable per-slot duration |
--avg-slot-threshold |
No | 1.1s |
Max acceptable average slot duration |
cargo run -p indexer -- \
--db-uri postgres://indexer:password@localhost:5432/indexer?sslmode=disable| Argument | Required | Default | Description |
|---|---|---|---|
--migrations-path |
No | ./indexer/migrations |
Path to SQL migration files |
--consensus-rpc |
No | wss://rpc.mainnet.autonomys.xyz/ws |
Consensus chain RPC |
--auto-evm-rpc |
No | wss://auto-evm.mainnet.autonomys.xyz/ws |
Auto-EVM domain RPC |
--db-uri |
No | postgres://indexer:password@localhost:5434/indexer?sslmode=disable |
PostgreSQL connection string |
--process-blocks-in-parallel |
No | 5000 |
Number of blocks to process concurrently |
All indexer arguments can also be set via environment variables (uppercase, e.g. DB_URI, CONSENSUS_RPC).
RUST_LOG can be used to filter logs. See EnvFilter docs.
Running a local node gives low latency for block subscriptions, best block checks, and event retrieval. See the Subspace monorepo for build/run instructions.
alerter/— Chain event alerting servicemain.rs: multi-task orchestrator usingtokio::JoinSetcli.rs: command-line configuration (clap)events.rs: block event monitoring (transfers, domain events, fraud proofs, operator events, sudo, code updates)stall_and_reorg.rs: chain stall detection and reorg monitoringslots.rs: slot timing monitoring via Proof-of-Timep2p_network.rs: libp2p peer discovery and PoT stream collectionslack.rs: Slack API integration with secure token handlinguptime.rs: Uptime Kuma health check pusherevent_types.rs: alert event type definitionsmd_format.rs: markdown formatting for alert messagesnetworks.toml: per-network configuration (known accounts, bootstrap nodes)
indexer/— XDM transfer indexer and REST APImain.rs: Actix-web server + dual-chain block processorsapi.rs: REST endpoints (health, XDM transfers)xdm.rs: XDM event processing and indexingstorage.rs: PostgreSQL database layer (sqlx)types.rs: domain models (ChainId, transfer types)migrations/: SQL migration files
shared/— Shared blockchain client utilitiessubspace.rs: subxt-based chain interaction (block streaming, metadata, events)
docker/— Multi-platform Dockerfiles (alerter.Dockerfile,indexer.Dockerfile)
- Subspace Protocol reference implementation (node): autonomys/subspace