Casper Devnet Launcher
Casper Devnet Launcher is a Rust tool for running a local Casper network, based on and heavily
inspired by the NCTL workflow, to make local devnets quick and easy for smart contract developers.
It embeds the essential node-launcher behavior in-process, so you only need the casper-node
and (optionally) casper-sidecar binaries.

Why this exists
NCTL is fantastic for core protocol development and for building assets from source trees, but it comes with a large shell script surface, external process supervision, and multi-step UX. This tool targets application and contract developers who want a repeatable, portable devnet for development, CI, and tests.
Comparison with NCTL
| Area | NCTL | Casper Devnet Launcher (this repo) |
|---|---|---|
| Primary audience | Core protocol development | Smart contract/app developers, CI/tests |
| Process control | External supervisor (supervisord) | In-process process control |
| Setup workflow | Multiple commands | Single command: casper-devnet start |
| Implementation | Large shell script | Rust binary (portable) |
| Node launcher | External casper-node-launcher |
Embedded launcher logic |
| Requirements | Node + launcher + sidecar + scripts | Assets bundle (node + sidecar + templates) |
| Keys/accounts | Random keys, friction to name/locate | Deterministic accounts and one-shot inherited pipe consensus keys from a seed (BIP32 paths) |
| macOS devnet start | Often requires extra local compilation | Download pre-built cross-platform bundles |
| Network feedback | Extra commands to watch blocks/txs | Persistent SSE connection with live output |
Installation
Docker usage
Pull the image:
Run a devnet with the default data location (persist assets and network state with a volume):
Use a custom data directory by overriding XDG_DATA_HOME and mounting it:
The exposed ports map to node-1 services: RPC (11101), REST (14101), SSE (18101), network gossip
(22101), binary protocol (28101), and diagnostics websocket proxy
(ws://127.0.0.1:32000/diagnostics/node-1/). The diagnostics proxy also accepts HTTP POST requests
to /diagnostics/node-1/ for non-websocket clients and streams NDJSON responses.
Diagnostics HTTP Proxy
The diagnostics proxy is useful in environments where you cannot or do not want to keep a websocket connection open. It accepts plain HTTP POST requests, forwards them to the node's diagnostics Unix socket, and returns line-delimited JSON responses. This is handy for automation or for setting failure points and collecting detailed runtime state.
Set a failure point (stop at a specific block height):
Dump network info:
Dump queues:
For interactive workflows, use websockets so you can send commands and immediately see responses without re-establishing connections:
Usage
Add a local assets bundle:
Add a custom override asset (symlink-backed local paths):
Custom asset names are write-once: reusing an existing name returns an error instead of replacing the asset directory.
List installed protocol bundles and custom assets:
Print absolute path to a custom asset directory (shell-substitution friendly):
Custom assets install only symlink-backed asset files. Hooks are network-scoped and live under the managed network directory.
List managed network directories:
Remove a managed network directory from disk:
Print the staged per-node config directories for a protocol version:
Print the network root:
Download assets from the latest release:
Supported host architectures:
aarch64-apple-darwinaarch64-unknown-linux-gnux86_64-apple-darwinx86_64-unknown-linux-gnu
See also https://github.com/veles-labs/devnet-launcher-assets/releases/.
Force re-download:
Override the target triple:
Security note
casper-devnet assets pull downloads pre-built binaries from
https://github.com/veles-labs/devnet-launcher-assets/releases.
If you are not comfortable running pre-built binaries, download the assets repo and rebuild the
binaries locally using the provided scripts before installing them with assets add.
List available protocol versions:
Start a devnet:
Start from a specific installed bundle, or from a custom asset:
Override the chainspec protocol version while using the selected asset files:
Patch chainspec values before fresh genesis setup:
--chainspec-override uses key.path=<toml-value> syntax and may be repeated. Quote
values that contain shell-sensitive characters or spaces, especially arrays and strings.
Overrides are applied before launcher defaults, so --protocol-version, --delay,
--network-name, and --node-count still control their generated chainspec fields.
Overrides only apply to a fresh setup; use --force-setup when the network already exists.
Stage a protocol upgrade from a versioned or custom asset:
Derive deterministic account material from a seed and BIP32 path:
Print a random live endpoint for a running node:
When the network is actively managed by casper-devnet, this command prefers the live control
socket to discover currently running nodes before choosing an endpoint. If that live query is
unavailable or unresponsive, it falls back to state.json instead of hanging indefinitely.
Add managed non-genesis nodes to a running network:
network <name> add-nodes is live-only: it requires the foreground start or MCP-managed process
to be running so it can prepare assets, spawn the new node and sidecar processes, track them in
state.json, and stop them during normal shutdown. During expansion, the manager reads a recent
trusted hash from an existing node's REST /status, writes it to [node].trusted_hash, and uses
the active config's joining sync mode ([node].sync_handling = "ttl", or
sync_to_genesis = false for legacy configs).
Added nodes inherit the currently active protocol version and any higher protocol versions already
staged on existing nodes, so a node added before an activation era can follow the pending upgrade.
The foreground start manager logs the full endpoint summary for nodes added through the control
plane and prints node reactor state changes observed by polling each node's REST /status every
500ms.
If a managed process is running (from start or MCP), staging runs in live mode and restarts
sidecars. Otherwise, staging runs in offline mode and only writes versioned
nodes/node-*/bin/<version> and nodes/node-*/config/<version> assets.
Live staging control uses a per-network Unix socket at /tmp/<network-name>.socket
for runtime stage requests. Managed node processes serve consensus secret keys through
inherited pipe file descriptors, deriving the PEM from the network seed and writing it once
after each child process starts. Managed on-disk configs still reference
keys/secret_key.pem, but the embedded launcher starts validators and migrate-data with
temporary configs whose consensus key paths point at /proc/self/fd/<fd> on Linux or
/dev/fd/<fd> on macOS. The generated PEM is not served again for that config read, and
fresh pipes are created before later migrate-data runs or validator restarts.
Node and sidecar log aliases (for example node-1.stdout) are atomically repointed to
versioned log files during protocol transitions; use tail -F to follow across alias swaps.
If networks/<network>/hooks/pre-stage-protocol exists, it runs after the target version's
per-node bin/<version> and config/<version> directories have been staged, and before
post-stage metadata is queued, with argv
<network_name> <protocol_version> <activation_point>. Use
casper-devnet network <network> path <protocol_version> inside the hook to locate each staged
per-node config directory. If the hook fails, the newly staged version directories are removed and
post-stage-protocol is not queued.
If networks/<network>/hooks/post-stage-protocol exists, it runs once later at the real upgrade
boundary, after the launcher starts the target validator version, with argv
<network_name> <protocol_version>.
If networks/<network>/hooks/pre-genesis exists, it runs after assets have been prepared
for a fresh network but before the network is started, with argv
<network_name> <protocol_version>.
If networks/<network>/hooks/post-genesis exists, it runs once after the fresh network
produces its first block, with argv <network_name> <protocol_version>.
If networks/<network>/hooks/block-added exists, it runs on each observed new block with
argv <network_name> <protocol_version> and the block event JSON payload on stdin.
Each hook runs in its own working directory under networks/<network>/hooks/work/<hook-name>/, so
hooks can leave files behind for later hooks to inspect.
Hook stdout/stderr are streamed line by line through casper-devnet stderr as
<hook_name> stdout: ... and <hook_name> stderr: .... Non-zero exits are still reported, but
successful exit code 0 is quiet. The raw hook streams are also written under
networks/<network>/hooks/logs/.
The generated sample hooks live under networks/<network>/hooks/*.sample. The samples show how
to call casper-devnet network <network> port --rpc, issue an info_get_status JSON-RPC
request, consume block-added JSON from stdin, and use casper-devnet network <network> path [<protocol_version>] to locate the network root or staged per-node config directories.
Run MCP control plane server (STDIO + HTTP):
Run MCP in HTTP-only mode:
Check whether a devnet has produced blocks (useful for CI):
Create assets without starting processes:
Use --setup-only when you want to tweak chainspecs or node configs before launching.
Use --chainspec-override with --setup-only to apply repeatable TOML value patches during
fresh asset setup.
--setup-only writes configs that reference keys/secret_key.pem, but it does not create
keys/ directories or write regular consensus secret key PEM files. Managed start/MCP runs
use one-shot inherited pipe key delivery at runtime.
Rebuild assets:
MCP workflow
casper-devnet mcp does not auto-start a network. Use MCP tools in this order:
spawn_network(defaults toforce_setup=truefor fresh setup; setforce_setup=falseto resume existing assets).wait_network_ready(waits for running processes, healthy/status,reactor_state=Validate, and first observed block).- Call network tools (RPC/status/block/log/SSE/transactions).
MCP server defaults:
transport=bothhttp_bind=127.0.0.1:32100http_path=/mcp
MCP tools require network_name; node-scoped tools also require node_id.
Managed networks are stopped automatically when the MCP server exits.
Use managed_processes to inspect managed node/sidecar processes, with optional process-name filtering and running_only control.
Use stage_protocol to stage versioned-asset or custom-asset upgrades for managed networks
(live_mode=true) or discovered stopped networks (live_mode=false).
rpc_query_global_state auto-resolves the latest block hash when both block_id and state_root_hash are omitted.
For transaction construction, use MCP tools (make_transaction_package_call, make_transaction_contract_call, make_transaction_session_wasm) with send_transaction_signed instead of invoking external casper-client binaries.
session_args supports full CLType strings (including nested types such as Option<List<U512>>, Map<String,U64>, tuples, and ByteArray[32]). Scalars can be passed as string/number/bool, null maps to None for Option<T>, and composite values should be provided as hex bytes (0x...). Pass this field as JSON (array/object), not an escaped JSON string. Legacy session_args_json is still accepted for compatibility.
send_transaction_signed.transaction should be a typed JSON object. Field name transaction_json is not accepted, and encoded JSON strings are not supported.
Use MCP transaction query tools (get_transaction, wait_transaction) instead of shelling out to curl for info_get_transaction calls.
Valid session_args examples:
[{"name":"value","type":"I32","value":"1"}][{"name":"items","type":"List<U64>","value":"0x03000000010000000000000002000000000000000300000000000000"}]Unsupported formats:{"value":1}(object shorthand)["value:i32=1"](casper-client CLI arg string format)
Codex CLI stdio MCP example (~/.codex/config.toml):
[]
= "casper-devnet"
= ["mcp", "--transport", "stdio"]
Or add it via Codex CLI:
If casper-devnet is not on PATH, set command to an absolute binary path.
Claude CLI config example: PRs welcome.
Common flags
--asset <version>: Versioned asset bundle to use from the assets store (accepts2.1.3orv2.1.3; defaults to newest bundle)--custom-asset <name>: Custom asset underassets/custom/<name>to use instead of a versioned bundle--protocol-version <version>: Override the chainspec protocol version; when omitted,startuses the selected asset's chainspec value--chainspec-override <key.path=value>: Patch a generated chainspec value before genesis setup; repeatable; value must be valid TOML, for example'core.test_values=[1, 10]'; requires a fresh network or--force-setup--network-name <name>: Network name for configs/paths (default:casper-dev)--net-path <path>: Override the network runtime root (default: platform data dir.../networks)--node-count <n>: Number of nodes (aliases:--nodes,--validators; default: 4)--users <n>: Number of user accounts (default: node count)--delay <seconds>: Genesis activation delay (default: 3). Keep it short for local devnets; increase if you need more time to attach tooling before genesis.--log-level <level>: Child process log level (default:info)--node-log-format <format>: Node logging format in config (default:json)--setup-only: Build assets and exit--force-setup: Rebuild assets even if they exist, while preservingnetworks/<network>/hooks/--seed <string>: Seed for deterministic devnet keys (default:default)
casper-devnet mcp flags:
--transport <stdio|http|both>: MCP transport mode (default:both)--http-bind <addr:port>: HTTP bind address for streamable MCP (default:127.0.0.1:32100)--http-path <path>: HTTP mount path for MCP endpoint (default:/mcp)--net-path <path>: Override network runtime root (same behavior asstart)
casper-devnet network <network> stage-protocol flags:
[asset]: Optional positional shorthand for--asset <version>--asset <version>: Versioned asset bundle to stage from--custom-asset <name>: Custom asset underassets/custom/<name>to stage from--protocol-version <version>: Protocol version to stage (required)--activation-point <era-id>: Future era id for activation (required)--chainspec-override <key.path=value>: Patch the staged chainspec before pre-stage hooks run; repeatable; value must be valid TOML, for example'core.test_values=[1, 10]'--net-path <path>: Override network runtime root (same behavior asstart)
Exactly one of [asset], --asset, or --custom-asset is required for staging.
casper-devnet derive flags:
<path>: BIP32 derivation path to resolve--secret-key: Print or write the derived secret key PEM--public-key: Print or write the derived public key hex--account-hash: Print or write the derived account hash--seed <string>: Deterministic seed for derivation (default:default)-o, --output <path>: Output directory, or-for stdout
Exactly one of --secret-key, --public-key, or --account-hash must be provided.
casper-devnet network <network> path flags:
[protocol_version]: Optional protocol version to inspect--net-path <path>: Override network runtime root (same behavior asstart)
When no protocol version is provided, this prints the network root directory. When a protocol version is provided, it prints one staged config directory per known node, one path per line.
casper-devnet network <network> add-nodes flags:
--count <n>: Number of managed non-genesis nodes to add to the live network--net-path <path>: Override network runtime root (same behavior asstart)
casper-devnet network <network> port flags:
--rpc: Print one random running node RPC URL--sse: Print one random running node SSE URL--rest: Print one random running node REST URL--binary: Print one random running node binary-port address--diagnostics: Print one random running node diagnostics socket path--net-path <path>: Override network runtime root (same behavior asstart)
Exactly one of --rpc, --sse, --rest, --binary, or --diagnostics must be provided.
casper-devnet network <network> status flags:
--node-id <id>: Node id whose REST/statusendpoint should be queried--net-path <path>: Override network runtime root (same behavior asstart)
casper-devnet network <network> is-ready flags:
--net-path <path>: Override network runtime root (same behavior asstart)
Assets bundle layout
The bundle is extracted into the platform data directory and should include a versioned root with the following shape:
v2.1.1/bin/casper-node
v2.1.1/bin/casper-sidecar
v2.1.1/chainspec.toml
v2.1.1/sidecar-config.toml
v2.1.1/node-config.toml
Custom override assets are stored separately under assets/custom/<name>/ as symlinks to local
casper-node, casper-sidecar, chainspec.toml, node-config.toml, and sidecar-config.toml.
Custom assets do not install or execute hooks.
Network hooks live under each managed network directory. Samples are generated as:
networks/<network>/hooks/pre-genesis.sample
networks/<network>/hooks/post-genesis.sample
networks/<network>/hooks/block-added.sample
networks/<network>/hooks/pre-stage-protocol.sample
networks/<network>/hooks/post-stage-protocol.sample
Only exact active hook filenames are executed:
networks/<network>/hooks/pre-genesis
networks/<network>/hooks/post-genesis
networks/<network>/hooks/block-added
networks/<network>/hooks/pre-stage-protocol
networks/<network>/hooks/post-stage-protocol
The .sample files are boilerplate only and are never executed directly.
For manual rebuilds and bundle scripts, see https://github.com/veles-labs/devnet-launcher-assets/.
Notes
- The launcher runs the node directly and manages processes internally; no external supervisor is required.
- The embedded launcher state is handled within the process; only the node/sidecar binaries are required.
- Assets are stored under the platform data directory (e.g.,
~/.local/share/xyz.veleslabs.casper-devneton Linux or~/Library/Application Support/xyz.veleslabs.casper-devneton macOS), withassets/for bundles andnetworks/for runtime assets.