Repository navigation
Checkpoint + restore a post-init stack to eliminate startup wait during dev #17
Description
Motivation
Current stack startup is dominated by on-chain initialization, not container boot:
- Each piri node registers with the delegator, registers with the PDP contract, waits for approval, and creates an on-chain data set.
- Every one of those operations waits ~6 epochs × 3s ≈ 18s minimum.
- With multiple piri nodes the waits serialize into multiple minutes.
For test iteration this is brutal — per-run stack startup is ~10× the duration of the test itself. The natural urge is "just reuse the stack" (e.g. SMELT_KEEP=1), but that only helps after the first boot of the day and risks state drift across runs.
The better fix is to checkpoint a fully-initialized network once, then resume from that checkpoint on subsequent boots. Startup collapses to container boot time; on-chain init runs zero times.
This is an extension of what we already do with systems/blockchain/state/anvil-state.json (chain + deployed contracts + funded addresses). We widen the snapshot to include post-piri-registration contract state and each service's persistent volume / keys.
Plan
Phase 0 — prerequisite cleanup
The snapshot logic for the PDP contracts currently lives in a shell script in the sibling repo:
That script is doing double duty (init + dump) and is too tangled to extend reliably. Clean it up first: separate init-mode from dump-mode paths, parameterize output location, add tests. Track as a separate issue in filecoin-services (out of scope for this repo, but blocks phase 2).
Phase 1 — manifest + CLI surface
Extend smelt.yml to declare what each service contributes to a checkpoint:
version: 1
piri:
nodes:
- storage: { db: sqlite, blob: filesystem }
checkpoint:
name: default # checkpoint ID; multiple checkpoints allowed
includes:
- blockchain # Anvil state.json (already implemented)
- piri # piri data volumes + keys
- delegator # UCAN store volume
- indexer # indexer state volumeAdd cmd/smelt subcommands:
smelt snapshot save [--name NAME]— stop the stack, dump anvil state, archive each listed service's volume + keys intogenerated/snapshots/<name>/, write a manifest descriptor.smelt snapshot load [--name NAME]— restore volumes + keys beforecompose up, point anvil at the saved state file.smelt snapshot list/smelt snapshot rm NAME— housekeeping.
Phase 2 — integrate with boot
make uplearns a--from-snapshot=NAMEpath (or env var) that callssnapshot loadbefore compose up.- Piri's entrypoint learns to skip the delegator/contract registration step when it detects a "already registered" marker restored from the snapshot. (This is the piece that actually saves minutes — restoring the volume isn't enough if piri re-runs init.)
- Document the two intended flows:
- Cold:
make fresh && make up— full init, ~N minutes. - Warm:
make up --from-snapshot=default— container boot time, seconds.
- Cold:
Phase 3 — Go SDK hook
Extend pkg/stack with WithSnapshot(name string) so e2e tests can opt into snapshot restore for local iteration:
s := stack.MustNewStack(t, stack.WithSnapshot("default"))CI must not use snapshots (defeats the point — it needs cold-boot coverage). Gate by env var or explicit opt-in; never default.
Out of scope
- CI using snapshots. Never — CI needs cold-boot coverage and can't rely on dev-produced artifacts.
SMELT_KEEP=1-style "reuse the running stack". Orthogonal concern; might be worth a follow-up but this issue is about boot time, not container reuse.- Automatic snapshot invalidation when
smelt.yml/ images / contracts change. Start manual (smelt snapshot rm+ re-save). Add staleness detection later if it becomes a footgun. - Cross-machine snapshots. Treat snapshots as local to the developer's checkout.
Acceptance criteria
smelt snapshot save --name defaulton a healthy stack producesgenerated/snapshots/default/with the chain state, service volumes, and keys.- A fresh
make clean && make up --from-snapshot=defaultbrings the stack to a "piri registered, ready to upload" state without any on-chain init waits. - A smoke test (
tests/e2e/smoke_test.goor similar) that normally takes ~3 min to start completes startup in <30s with a snapshot. - Docs updated:
docs/GETTING_STARTED.mdand/or a newdocs/SNAPSHOTS.mdcovering the two flows and how to invalidate.
Prior art referenced
systems/blockchain/state/anvil-state.json— existing chain snapshot; this plan generalizes the idea.filecoin-services/service_contracts/localdev/entrypoint.sh— "Mode 2 dump mode"; source of the snapshot primitive for PDP contract state.
Activity