Skip to content

feat: localdev MODE=init|load with graceful-shutdown state dump - #16

Merged
frrist merged 2 commits into
mainfrom
frrist/snapshots
Apr 21, 2026
Merged

frrist merged 2 commits into
mainfrom
frrist/snapshots

Conversation

@frrist

@frrist frrist commented Apr 21, 2026 •

Copy link
Copy Markdown
Member

Rewrites the localdev container's entrypoint around two explicit modes so external orchestrators (storacha/smelt's snapshot feature) can drive post-deploy, post-registration state capture.

Modes:

  • MODE=init: starts anvil with --dump-state, deploys contracts via deploy-local.sh, launches the mockrpc Filecoin proxy, and stays alive. On SIGTERM, a trap tears down mockrpc then anvil (in that order — mockrpc first so no late-arriving txs land in the snapshot), waits briefly for fs sync, then atomically copies anvil-state.json and deployed-addresses.json into $OUTPUT_DIR (default /output). The previous behavior killed anvil immediately after deploy — no window for external services to register before the dump.

  • MODE=load: loads a pre-deployed state file, starts mockrpc, stays alive. Also runs anvil with --dump-state so graceful shutdown captures the current (possibly post-registration) state. When /output is mounted writable, the trap copies the dump there too; otherwise it's a silent no-op. Lets an orchestrator snapshot a running chain by issuing docker stop.

  • MODE unset: autodetects (load if /app/anvil-state.json exists, else init). Keeps zero-config behavior working.

Other changes:

  • Healthcheck in docker-compose.yml switched to test -f /tmp/ready. The sentinel is touched only after contracts are deployed (init) or state is loaded (load) AND mockrpc is accepting requests — more accurate than the previous eth_blockNumber port probe, which succeeded before contracts were available in init mode.
  • OUTPUT_DIR is configurable (default /output).
  • SIGTERM during deploy refuses to dump (DEPLOY_IN_PROGRESS guard + deploy-as-background-subprocess pattern so bash's trap fires promptly on signals rather than waiting for forge/cast children). Exits non-zero with a clear message rather than snapshotting a partially-deployed chain.
  • Breaking change: the old DUMP_STATE=true env var is removed. Replaced by MODE=init.

README documents both modes and the opt-in dump-on-shutdown semantics of load mode.

Breaking change: the previous DUMP_STATE=true env var has been removed.
Use MODE=init instead. The critical behavioral difference is that init mode
now stays alive after deploying contracts (so external services can register
against the chain) and dumps state on graceful shutdown (SIGTERM), rather than
dumping immediately after deploy.

Rewrites the localdev container's entrypoint around two explicit modes
so external orchestrators (storacha/smelt's snapshot feature) can drive
post-deploy, post-registration state capture.

Modes:

- MODE=init: starts anvil with --dump-state, deploys contracts via
  deploy-local.sh, launches the mockrpc Filecoin proxy, and stays
  alive. On SIGTERM, a trap tears down mockrpc then anvil (in that
  order — mockrpc first so no late-arriving txs land in the snapshot),
  waits briefly for fs sync, then atomically copies anvil-state.json
  and deployed-addresses.json into $OUTPUT_DIR (default /output).
  The previous behavior killed anvil immediately after deploy — no
  window for external services to register before the dump.

- MODE=load: loads a pre-deployed state file, starts mockrpc, stays
  alive. Also runs anvil with --dump-state so graceful shutdown
  captures the current (possibly post-registration) state. When
  /output is mounted writable, the trap copies the dump there too;
  otherwise it's a silent no-op. Lets an orchestrator snapshot a
  running chain by issuing docker stop.

- MODE unset: autodetects (load if /app/anvil-state.json exists, else
  init). Keeps zero-config behavior working.

Other changes:

- Healthcheck in docker-compose.yml switched to `test -f /tmp/ready`.
  The sentinel is touched only after contracts are deployed (init)
  or state is loaded (load) AND mockrpc is accepting requests — more
  accurate than the previous eth_blockNumber port probe, which
  succeeded before contracts were available in init mode.
- OUTPUT_DIR is configurable (default /output).
- SIGTERM during deploy refuses to dump (DEPLOY_IN_PROGRESS guard +
  deploy-as-background-subprocess pattern so bash's trap fires
  promptly on signals rather than waiting for forge/cast children).
  Exits non-zero with a clear message rather than snapshotting a
  partially-deployed chain.
- Breaking change: the old DUMP_STATE=true env var is removed.
  Replaced by MODE=init.

README documents both modes and the opt-in dump-on-shutdown semantics
of load mode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@alanshaw alanshaw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread service_contracts/localdev/README.md Outdated
> Use `MODE=init` instead. The critical behavioral difference is that init mode
> now stays alive after deploying contracts (so external services can register
> against the chain) and dumps state on graceful shutdown (SIGTERM), rather than
> dumping immediately after deploy.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to PR description.

remove breaking changes section
@frrist
frrist merged commit bcfafa2 into main Apr 21, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants