Local SSV testnet in ~4 minutes. Kurtosis-based devnet for developing and testing SSV nodes.
- Docker (or OrbStack on macOS)
- Kurtosis CLI (
brew install kurtosis-tech/tap/kurtosis-cli)
Recommended: 8+ CPU cores, 16GB+ RAM allocated to Docker.
git clone https://github.com/ssvlabs/ssv-mini.git && cd ssv-mini
make prepare # Clone SSV repo + build Docker image (~5 min first time)
make run # Start the testnet (~4 min)That's it. Run make show to see services and ports, make logs to tail SSV node logs.
SSV_COMMIT=my-feature-branch make prepare
make runcd ../ssv && docker build -t node/ssv .
cd ../ssv-mini && make restart-ssv-nodesOr use the ssv-mini CLI tool from the SSV repo:
# Install (one time, from ssv-mini repo):
ln -sf "$(pwd)/scripts/ssv-mini" ~/bin/ssv-mini
# Then from the SSV repo:
ssv-mini # Create testnet or push code to running one
ssv-mini restart # Rebuild + restart SSV nodes only
ssv-mini logs # Tail SSV node 0 logsmake help
| Command | Description |
|---|---|
make run |
Start testnet (default: Fulu at genesis) |
make run-boole |
Start with Boole fork at epoch 3 |
make run-gloas |
Start with Gloas/ePBS (EIP-7732) fork at epoch 2 |
make reset |
Clean + restart from genesis |
make show |
Show running services and ports |
make logs |
Tail ssv-node-0 logs (SERVICE=ssv-node-1 for others) |
make clean |
Remove all enclaves |
make restart-ssv-nodes |
Restart SSV nodes (after rebuilding image) |
make prepare |
Clone SSV repo + build Docker image |
make prepare-anchor |
Clone Anchor repo + build Docker image; useful when using custom branch |
make prepare-monitor |
Clone E2M repo + build Docker image |
make prepare-all |
Build SSV + Anchor + Monitor images |
make generate-keys |
Regenerate static operator keys + keyshares |
| Command | Description |
|---|---|
make stop-el |
Stop geth (simulate EL crash) |
make start-el |
Restart stopped geth |
make swap-el EL_IMAGE=<img> |
Hot-swap geth to custom image |
make restore-el |
Restore default geth |
make test-faulty-el |
Bloom filter cross-check test |
Use EL_SERVICE=el-2-geth-lighthouse to target the second EL node.
Image pins and shared chain constants (genesis mnemonic, validator owner, SSV contract addresses) live in constants.env, the single source of truth that the Starlark package, the Makefile and the scripts all read. Bump an image there once and every network picks it up.
Edit params.yaml to customize the network:
nodes:
ssv:
count: 4 # Valid: 4, 7, 10, 13 (3f+1 for BFT)
anchor:
count: 0 # Anchor consensus client nodes
# images: # optional: per-network overrides of the constants.env images
# anchor: "node/anchor" # e.g. a local `make prepare-anchor` build
network:
network_params:
fulu_fork_epoch: 0 # 0 = active at genesis (default); set large (e.g. 100000) to defer
boole_epoch: 3 # Omit for pre-Boole
use_static_keys: true # false = regenerate keys at runtime (~40s slower)Pre-built configs:
params.yaml— Fulu at genesis (default)params-boole.yaml— Alan→Boole fork transitions; needsSSV_COMMIT=integration/boole-convergence make prepareparams-gloas.yaml— Fulu→Gloas (ePBS/EIP-7732) transition; needsSSV_COMMIT=epbs-gloas make prepareand ethpandaops glamsterdam-devnet-8 client images (digest-pinned; monitor/E2M enabled - prepare it beforehand)
make run PARAMS_FILE=params-boole.yaml┌─────────────┐ ┌─────────────┐
│ Geth (EL) │────▶│ Lighthouse │
│ ×2 nodes │ │ (CL) ×2 │
└──────┬──────┘ └──────┬──────┘
│ │
┌────┴────┐ ┌────┴────┐
│ SSV │ │Validator│
│ Contracts│ │ Clients │
└────┬────┘ └─────────┘
│
┌────┴──────────────────┐
│ SSV Nodes ×4 │
│ (operator clusters) │
└───────────────────────┘
- Ethereum layer: 2× Geth + 2× Lighthouse + validators (74 total)
- SSV layer: 4 operator nodes in a BFT cluster with 10 SSV validators
- Contracts: SSV Network contracts deployed via Hardhat (ssv-network v2.0.0)
See CLAUDE.md for detailed architecture and development notes.
