On this page
Key takeaways
- Chainwatch is a free, self-hosted dashboard for rindexer: a setup wizard, a live event explorer, and signed webhook or email alerts.
- Events are stored in a PostgreSQL you own. There are no accounts, no telemetry and no hosted service.
- rindexer runs unmodified, as a supervised process. Chainwatch only writes its configuration and reads its tables.
- It is free for personal, non-commercial use, and it has honest limits that we list below.
Every team that touches payments or tokens ends up needing the same thing: a reliable record of what happened on-chain, in a database they control. We wrote about the hard parts of that in our note on indexing blockchain payments, and we have built it by hand more than once. So when we found rindexer, an open-source indexer written in Rust that turns a YAML file into decoded events in PostgreSQL, the engine was exactly what we wanted. The part we kept missing was somewhere to look at the result.
Chainwatch is that part. It is a small web app that sits next to rindexer and Postgres, and it is now a free download from the studio.
What it does
- 01
A wizard instead of YAML
Pick a chain, paste a contract address, choose the events and where to start. Chainwatch checks your RPC, loads the ABI (pasted, or fetched with your own Etherscan key) and writes the rindexer configuration.
- 02
A live overview
Engine status, events per second, the indexed block against the chain head, and lag for every indexer, with a ticker across the top.
- 03
An event explorer
Browse decoded events as they land. Search any address or hash, page through history, copy values and export CSV. Token amounts use the decimals you set, so a USDC transfer reads as dollars instead of a 12-digit integer.
- 04
Alerts you can verify
Rules such as “Transfer where value is greater than X”, delivered as a webhook, an email, or both. Every webhook is signed with HMAC-SHA256 and a timestamp.
Where your data lives
rindexer writes decoded events into PostgreSQL tables, one schema per contract and one table per event. Chainwatch reads those tables back. That is the whole integration. Your RPC URLs are handed to rindexer through environment variables and are never written into the configuration file, so there is nothing sensitive to commit by accident. Deleting an indexer keeps its data on purpose; dropping it is a deliberate SQL command.
Verifying an alert
A webhook that anyone can forge is worse than no webhook. Each Chainwatch delivery carries a timestamp and a signature over the timestamp and the raw body. Your receiver recomputes it, compares in constant time, and rejects anything old.
import crypto from "node:crypto";
export function verify(headers, rawBody, secret) {
const ts = headers["x-chainwatch-timestamp"];
const sig = headers["x-chainwatch-signature"] ?? "";
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}What went wrong while we built it
We tested Chainwatch against the real thing: USDC on Base and on Ethereum, indexed into a local Postgres. Four lessons are worth passing on, because you may hit the same ones.
- rindexer's official image is linux/amd64 only. On Apple Silicon, the default QEMU emulation crashed Node outright. Rosetta emulation fixed it, and the README now says so.
- Our first Dockerfile installed rindexer with its curl installer and hid the failure behind a fallback. The image built and then had no engine in it. We now copy the binary from the official image and fail the build if it does not run.
- Decoded address and hash columns come back as fixed-width, space-padded text, and the log index is stored as text. Comparisons and ordering silently misbehaved until we cast and trimmed them.
- A new alert rule that starts from the beginning of history fires on every old event. Rules now start from the latest row at the moment you create them.
It is a dashboard, not a data warehouse
Chainwatch is only as accurate as your RPC provider, and a public endpoint is fine for trying it and not for relying on it. Alerts are delivered once per matching event and a failed delivery is logged, not retried. There is a single admin password and no roles, so keep it on a private network or behind your own authentication. Changing an indexer restarts the engine and briefly pauses indexing.
Getting it
Chainwatch is a free download on the freebies page. Enter your email and we send a private link. It is licensed for personal, non-commercial use; if you want to use it for a team or a client, write to us. It needs Docker and an RPC endpoint, and the quick start is three commands.
Before you start
- Docker with Compose installed (Rosetta emulation on Apple Silicon).
- An RPC endpoint for each chain you want to index.
- A strong ADMIN_PASSWORD and a random SESSION_SECRET in your .env.
- A reverse proxy with HTTPS if you will reach it from anywhere but your own machine.
- Optional: an Etherscan API key for automatic ABIs, and an SMTP URL for email alerts.
Chainwatch is not affiliated with the rindexer project. rindexer is MIT-licensed and its notice ships in the download. If this is useful to you, the best thanks is a star on the rindexer repository.
References & further reading
- 1rindexer — Josh Stevens and contributors, GitHub, MIT licence
- 2rindexer documentation — rindexer.xyz
About the author
Product behaviour described here reflects what is implemented and tested; anything else is marked as planned. Code samples are illustrative.
All writing