v1.0.0 - be your own indexer
Be your own indexer. One static binary, one command, a live indexed API in under two minutes — no subgraph to author, no Postgres to run, no third-party data dependency, ever.
In-place safe from 0.9.x: no data migration, no config changes.
What 1.0 means
A promise about stability, not a claim of completeness.
- Semantic versioning. Within 1.x we will not rename or remove a CLI flag, an HTTP route, a config key, or a generated column without a major bump.
- Upgrades stay a binary swap. No migration, no re-backfill. A newer binary reads an older release's hot store and sealed segments as they are — proven on a production box across 0.3.0 → 0.6.0 → 0.7.2, and in CI on every release since.
- MSRV 1.95, measured rather than asserted. Until this release
Cargo.tomlclaimed 1.85, whichcargo +1.85.0 checkrefutes in one command (cranelift-assembler-x64needs 1.94). A version nobody tests is not a promise. - Embedded mode is the production path.
devandroostrun in production today. Scaled mode is built and verified across real machines, but younger.
What is in it
Zero authoring. nuthatch init 0xAddr resolves the ABI (Sourcify → Etherscan, proxies included), generates the schema and decoders, and scaffolds the project. Your contract's events become per-event tables you query with real analytical SQL — the live tip and sealed history, one surface.
Deterministic core. Decode, reorg handling and entity derivation are re-executable: same inputs, same content-addressed output. Reorgs only ever touch the mutable hot store; sealed Parquet segments are strictly past finality and immutable.
A roost. Many nests in one runtime, across one or more chains, with one isolated cursor per chain and a footprint budget of ≤2 GB per cursor — CI-enforced, and a mount that would exceed it is refused rather than warned about. Nests mount and unmount live, without restarting their co-tenants.
Scaled mode. The same crates as three roles — a control plane, a writer pool taking one lease per cursor, and an independently-scaled query tier. Ownership is enforced by the store: every write carries a fence, and a stalled worker that wakes finds its writes refused rather than merely discouraged.
Nest bundles and a registry. Content-addressed, hash-verified, and shareable — a filesystem path or any S3-compatible bucket. Workers pull the nests they are assigned; with a bundle_hash pinned the fetch is by content address, so re-tagging a version in a registry cannot change what a fleet runs.
Safe upgrades. A compatible nest update hot-swaps with zero downtime and, when the decode is unchanged, mounts the old version's sealed segments instead of re-indexing history at all. A breaking one is served on a new endpoint alongside the old. Updating a nest is not a genesis resync.
AI-native, offline. An MCP server compiled into the binary, plus a builder skill whose CLI reference is generated from the binary and CI-checked for drift.
Since 0.9.3
A worker can run a nest it has never seen (RFC-0019). desired_nest had carried a version and a bundle_hash since RFC-0022; nothing on the worker side read them, so a worker could be assigned a nest it had no way to locate. Two things fell out of wiring it:
- A pinned hash bypasses the registry index entirely. Resolving
name@versionand then verifying the blob proves the registry served the blob it named — internal consistency, not authenticity. A registry that re-points1.0.0at other bytes passes that check every time. Pinned, the fetch goes straight to the content address. - Pulled bundles are cached by content address, not dropped into
--nest-root. Operator-placed nests outrank the registry, so a pulled copy sitting there would satisfy that rule forever and make re-pinning silently do nothing — no error, wrong data.
RFC-0013's benchmark gate was run rather than deferred again. DataFusion measured 1.6–2.7× DuckDB's latency on the fold that matters, with the gap widening as segments grow, at exact result parity. DuckDB stays in both modes. Two things we had recorded as fact were wrong: there is no arrow-version clash (DataFusion 54 shares our arrow 58), and the cost is +56 crates, not 70. The only real blocker was our own declared MSRV.
All seven pre-1.0 audit findings are closed, including the /sql arbitrary file read fixed in 0.9.3. One is closed as not ours to fix rather than fixed — DuckDB's allowed_directories does not enforce on the build we bundle, and a test now pins which control actually blocks a file read, so nobody weakens the denylist believing something sits behind it.
Documentation
New operator guides on the website: deploy, performance, security, and verifying a deployment.
Read the security page before exposing /sql to anyone you do not trust.
What is honestly not done
- The registry-pull runbook check has not been run on real machines.
init --from-subgraphis scaffolded and parked (#253).- reth ExEx ingestion (RFC-0003) and trace/state extraction (RFC-0014) are gated on a synced node.
- Scaled mode has not carried a production workload for anyone.
Verifying the download
curl -fsSLO https://github.com/nightswatchhq/nuthatch/releases/download/v1.0.0/nuthatch-x86_64-unknown-linux-gnu.tar.gz
curl -fsSLO https://github.com/nightswatchhq/nuthatch/releases/download/v1.0.0/nuthatch-x86_64-unknown-linux-gnu.tar.gz.sha256
sha256sum -c nuthatch-x86_64-unknown-linux-gnu.tar.gz.sha256| Artifact | Contains |
|---|---|
nuthatch-x86_64-unknown-linux-gnu.tar.gz · :1.0.0 · :latest |
embedded — no database driver |
nuthatch-scaled-x86_64-unknown-linux-gnu.tar.gz · :1.0.0-scaled |
scaled — worker, control, serve --hot-store |
be your own indexer.