Dubhe Rust Indexer
dubhe-indexer is a high-performance Rust binary that listens to Sui blockchain events emitted by your Dubhe contract and writes them into a PostgreSQL database. It exposes a built-in GraphQL endpoint and a gRPC endpoint for querying indexed data in real time.
How it works
The indexer is event-driven — it processes SetRecord, SetField, and DeleteRecord events emitted by the Dubhe framework. Only events whose original_package_id matches your config and whose table name is listed in your dubhe.config.json are indexed.
Important: The indexer can only process events it has seen. If
start_checkpointindubhe.config.jsonis set later than your deployment checkpoint, events before that point are permanently missed and will not appear in the database.
Installation
Install dubhe-indexer using the Dubhe CLI:
pnpm dubhe doctorThe doctor command checks your environment and installs required binaries, including dubhe-indexer. You can also download the binary directly from GitHub releases.
Prerequisites
- PostgreSQL — the indexer stores all indexed data in PostgreSQL.
dubhe.config.json— generated fromdubhe.config.tsviadubhe convert-json.- A running Sui node or access to a remote checkpoint store (testnet/mainnet).
Configuration file
Generate dubhe.config.json from your TypeScript config before starting the indexer:
dubhe convert-json --config-path dubhe.config.ts --output-path dubhe.config.jsonThe JSON file must include start_checkpoint — the checkpoint from which the indexer begins reading. Set it to the checkpoint at or just before your contract was deployed:
{
"name": "my_game",
"description": "My on-chain game",
"packageId": "0x123...",
"originalPackageId": "0x123...",
"start_checkpoint": "1000000",
"resources": {
"level": "u32",
"stats": {
"fields": { "attack": "u32", "hp": "u32" }
}
}
}
start_checkpointis read from the config file. The--first-checkpointCLI argument is present in--helpoutput but is not used by the current binary — always set it indubhe.config.json.
CLI reference
dubhe-indexer [OPTIONS]
Options:
-c, --config-json <PATH> Path to dubhe.config.json [default: dubhe.config.json]
--database-url <URL> PostgreSQL connection string [default: postgres://postgres@localhost:5432/postgres]
--checkpoint-url <URL> Checkpoint source: local directory (e.g. .chk) or
remote http URL (e.g. https://checkpoints.testnet.sui.io)
[default: .chk]
--use-rpc-ingestion Use gRPC to fetch checkpoints from --rpc-url instead
of local path / remote store [default: false]
--rpc-url <URL> gRPC endpoint (used with --use-rpc-ingestion)
[default: http://localhost:9000]
--port <PORT> Proxy server port (GraphQL + gRPC via proxy, health, playground)
[default: 8080]
--grpc-port <PORT> Direct gRPC backend port [default: 8085]
--graphql-port <PORT> Independent GraphQL backend port [default: 8089]
--force Clear the database before starting [default: false]Service ports
When the indexer starts, it launches three services:
| Service | Default port | Description |
|---|---|---|
| Proxy server | 8080 | Serves /graphql, /playground, /health, /metadata, /welcome; forwards gRPC requests |
| gRPC backend | 8085 | Direct gRPC endpoint for DubheGrpcClient |
| GraphQL backend | 8089 | Independent PostGraphile GraphQL service |
For most use cases, connect your client to the proxy at port 8080:
- GraphQL queries:
http://localhost:8080/graphql - GraphQL playground:
http://localhost:8080/playground - gRPC (via proxy):
http://localhost:8080(prefix/dubhe_grpc.*) - Direct gRPC:
http://localhost:8085
--grpc-portand--graphql-portmust be different values. The binary exits immediately if they match.
Checkpoint sources
The --checkpoint-url argument controls where the indexer reads checkpoints from:
| Value | Source | When to use |
|---|---|---|
.chk (default) | Local directory | Localnet — the directory where dubhe node writes checkpoint files |
https://checkpoints.testnet.sui.io | Remote store | Testnet |
https://checkpoints.mainnet.sui.io | Remote store | Mainnet |
--use-rpc-ingestion + --rpc-url | gRPC from a full node | When you have a full node with gRPC checkpoint API |
Localnet
Use pnpm dubhe node to start the local Sui node. It writes checkpoint files to .chk by default, and the indexer reads from .chk by default — no extra flags needed:
# Terminal 1: start local node (writes checkpoints to .chk)
pnpm dubhe node
# Terminal 2: start indexer (reads from .chk by default)
dubhe-indexer \
--config-json dubhe.config.json \
--database-url postgres://postgres:postgres@localhost:5432/postgresTo clear the database and start fresh (useful after redeploying):
dubhe-indexer \
--config-json dubhe.config.json \
--database-url postgres://postgres:postgres@localhost:5432/postgres \
--forceTestnet / Mainnet
Pass the remote checkpoint store URL via --checkpoint-url:
# Testnet
dubhe-indexer \
--config-json dubhe.config.json \
--checkpoint-url https://checkpoints.testnet.sui.io \
--database-url postgres://user:password@localhost:5432/dubhe_prod
# Mainnet
dubhe-indexer \
--config-json dubhe.config.json \
--checkpoint-url https://checkpoints.mainnet.sui.io \
--database-url postgres://user:password@localhost:5432/dubhe_prodQuerying indexed data
GraphQL client
import { createDubheGraphqlClient } from '@0xobelisk/graphql-client';
const client = createDubheGraphqlClient({
endpoint: 'http://localhost:8080/graphql',
subscriptionEndpoint: 'ws://localhost:8080/graphql'
});
const players = await client.getAllTables('player', {
filter: { hp: { greaterThan: '50' } },
orderBy: [{ field: 'hp', direction: 'DESC' }],
first: 10
});gRPC client
import { DubheGrpcClient } from '@0xobelisk/grpc-client';
const grpc = new DubheGrpcClient({ baseUrl: 'http://localhost:8085' });
const result = await grpc.dubheGrpcClient.queryTable({
tableId: 'player',
filters: [],
pagination: { page: 0, pageSize: 20 }
});Via createClient
If you use @0xobelisk/client, both clients are created automatically:
import { createClient } from '@0xobelisk/client/sui';
const { graphqlClient, grpcClient } = createClient({
network: 'localnet',
packageId: PackageId,
metadata,
endpoints: {
graphql: 'http://localhost:8080/graphql',
grpc: 'http://localhost:8085'
}
});Monitoring and troubleshooting
Logging
# Info level (recommended)
RUST_LOG=info dubhe-indexer --config-json dubhe.config.json --database-url ...
# Debug level (verbose)
RUST_LOG=debug dubhe-indexer --config-json dubhe.config.json --database-url ...Common issues
No data indexed, no errors
- Check that
start_checkpointindubhe.config.jsonis at or before the checkpoint of your contract deployment. - Check that
originalPackageIdmatches the package ID from your first deployment (not an upgraded package ID). - Check that resource names in
dubhe.config.jsonmatch the table names emitted by the contract.
Database connection refused
- Verify PostgreSQL is running:
pg_isready -h localhost. - Pass the connection string via
--database-url, not via an environment variable — the indexer binary reads only the CLI argument.
Checkpoints not arriving (localnet)
- Confirm
pnpm dubhe nodeis running and writing to the.chkdirectory. - If you restarted the node, run with
--forceto clear stale data.
Production deployment
Docker
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY dubhe-indexer /usr/local/bin/dubhe-indexer
RUN chmod +x /usr/local/bin/dubhe-indexer
COPY dubhe.config.json /app/dubhe.config.json
WORKDIR /app
CMD ["dubhe-indexer", \
"--config-json", "dubhe.config.json", \
"--checkpoint-url", "https://checkpoints.mainnet.sui.io", \
"--database-url", "postgres://user:password@db-host:5432/dubhe_prod", \
"--port", "8080"]Logging in production
RUST_LOG=info dubhe-indexer \
--config-json dubhe.config.json \
--checkpoint-url https://checkpoints.mainnet.sui.io \
--database-url postgres://user:password@db-host:5432/dubhe_prod