A roost is one runtime hosting many nests. Nests on the same chain share a single cursor and
one getLogs per window - N nests for roughly one nest’s RPC cost - and a roost can also span
multiple chains, running one isolated cursor per chain: a Base nest and an Arbitrum nest in one
process. Each cursor has its own tip, finality, and reorg boundary, and a per-cursor footprint
budget.
Layout
A roost is a directory with a roost.toml and a nests/ folder of ordinary nests:
my-roost/
roost.toml
nests/
usdc/ # a normal nest - nuthatch.toml, abis/, views/, …
weth/
[roost]
name = "my-roost"
chain = "mainnet"
chain_id = 1
rpc_urls = ["https://…"]
nests = ["usdc", "weth"] # subdirectories under nests/ to mount
max_rss_mb = 2048 # optional per-cursor RAM ceiling
A nest can’t tell it’s co-hosted: its config, storage, and routes are identical to a solo dev.
Mount an existing nest by copying (or nest load-ing) it into nests/
and adding its name.
Run it
nuthatch roost dev
This brings up every mounted nest and serves them behind one listener (--listen, default
127.0.0.1:8288):
GET /nests- the roster: each nest’s name, chain, registry hash, table count, and footprint.GET /<name>/…- each nest’s full API under its prefix:/usdc/sql,/usdc/tables,/weth/_admin/, and so on. Byte-identical routes to a solo nest, just prefixed.
The backfill flags you know from dev apply to every mounted nest: --backfill N,
--seal-direct, --concurrency, --window, --rpc overrides, --no-admin.
Multichain
To span more than one chain, drop the top-level chain/chain_id/rpc_urls and list chains under
[[chains]] (a top-level array beside [roost]); each nest declares its own chain in its
nuthatch.toml:
[roost]
name = "my-roost"
nests = ["usdc", "base-app"] # usdc → mainnet, base-app → base
max_rss_mb = 2048 # per-cursor; a roost's total budget is Σ cursors
[[chains]]
chain = "mainnet"
chain_id = 1
rpc_urls = ["https://…"]
[[chains]]
chain = "base"
chain_id = 8453
rpc_urls = ["https://…"]
Exactly one form: top-level chain or [[chains]], never both or neither.
Isolation
Chain identity is shared per cursor; stores are per-nest and isolated - each nest keeps its own redb hot store and its own sealed segments. A reorg rolls back every nest on that cursor’s hot store together (same chain, same boundary); sealed history is immutable everywhere. The roster and per-nest metrics let you see each nest’s own progress and footprint rather than one blended number.
One rule to keep: one cursor per chain, one chain per cursor. To index a second chain, add a
second [[chains]] cursor (or run a second process) - never try to multiplex chains behind one
cursor.