Lean Ethereum consensus client for devnets, implemented in modern C++.
qlean-mini follows the Lean Ethereum devnet specification:
This repository builds a small, modular node binary called qlean and a set of loadable modules. It uses CMake, Ninja, and vcpkg (manifest mode) for dependency management.
git clone https://github.com/qdrvm/qlean-mini.git
cd qlean-miniPrerequisites:
- CMake ≥ 3.25
- Ninja
- A C++23-capable compiler (AppleClang/Clang/GCC)
- Python 3 (required by the CMake build)
- vcpkg (manifest mode)
Note: Dependencies are also listed in the .ci/.env file for both macOS and Linux. To install on Debian Linux:
source .ci/.env
sudo apt update && sudo apt install -y $(echo $LINUX_PACKAGES)Make sure gcc-$GCC_VERSION is default compiler (where GCC_VERSION is defined in .ci/.env):
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-$GCC_VERSION 100
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-$GCC_VERSION 100For macOS:
source .ci/.env
brew install $(echo $MACOS_PACKAGES)# Clone vcpkg somewhere on your machine (e.g. in your home directory)
git clone https://github.com/microsoft/vcpkg.git "$HOME/vcpkg"
# Bootstrap vcpkg
"$HOME/vcpkg"/bootstrap-vcpkg.sh
# Export VCPKG_ROOT for the current shell session (and add to ~/.zshrc if use zsh for convenience)
export VCPKG_ROOT="$HOME/vcpkg"Notes:
- This project uses vcpkg in manifest mode with custom overlay ports from
vcpkg-overlay/and the registry settings invcpkg-configuration.json. - CMake presets expect
CMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake.
Use the provided CMake preset (generator: Ninja):
# From the repository root
cmake --preset default
cmake --build build -jYou can also build the project's Docker images with three-stage build:
# First time (full build)
make docker_build_all # ~25 min
# Daily development (fast rebuild)
make docker_build # ~3-5 min ⚡Three stages:
qlean-mini-dependencies:latest- vcpkg libs (~6-9 GB, rebuild rarely)- Safe optimization: excludes only temporary build files (~10-15 GB saved)
- Removed during build:
buildtrees/,downloads/,packages/(freed before copying) - Kept: complete
vcpkg_installed/(debug + release, .a + .so, all configs) - Zero risk: 100% safe, all dependencies intact, CMake configs work perfectly
qlean-mini-builder:latest- project code (~8-12 GB, rebuild often)qlean-mini:latest- runtime image (~240 MB, production)
When to rebuild dependencies:
vcpkg.jsonorvcpkg-configuration.jsonchanges (new libraries)- System dependencies update (
.ci/.env: cmake, gcc, rust versions) - Typically: once per month or when adding new dependencies
- Tip: Push dependencies to registry after rebuild for team reuse
- Note: Dependencies image keeps all files needed for compilation (no rebuild)
Main commands:
make docker_build_all # Full build (all 3 stages)
make docker_build # Fast rebuild (code only)
make docker_build_ci # CI/CD: pull deps + build (~4 min)Platform selection:
# ARM64 (default)
make docker_build_all
# AMD64 / x86_64
make docker_build_all DOCKER_PLATFORM=linux/amd64
# Multi-arch: CI/CD on native runners
# Job 1 (ARM64 runner):
DOCKER_PLATFORM=linux/arm64 make docker_build_all
make docker_push_platform # Push with -arm64 tag
# Job 2 (AMD64 runner):
DOCKER_PLATFORM=linux/amd64 make docker_build_all
make docker_push_platform # Push with -amd64 tag
# Job 3 (any machine):
make docker_manifest_create # Create multi-arch manifest for runtime image ONLY
# Run on specific platform
make docker_run DOCKER_PLATFORM=linux/amd64 ARGS='--version'Push/Pull:
make docker_push_dependencies # Push dependencies to registry (once)
make docker_push # Push all images (commit tag only)
make docker_pull_dependencies # Pull dependencies from registry
# Push with custom tag
DOCKER_PUSH_TAG=true DOCKER_IMAGE_TAG=v1.0.0 make docker_push # commit + v1.0.0
# Push with latest tag
DOCKER_PUSH_LATEST=true make docker_push # commit + latest
# Push all 3 tags (commit + custom + latest)
DOCKER_PUSH_TAG=true DOCKER_PUSH_LATEST=true DOCKER_IMAGE_TAG=v1.0.0 make docker_pushImage tagging:
Dependencies (platform-specific, one per architecture):
- Tags:
qlean-mini-dependencies:latest-arm64,qlean-mini-dependencies:latest-amd64 - Configurable via
DOCKER_DEPS_TAG(default:latest) - Examples:
v2-arm64,staging-amd64 - Note: Dependencies are NOT multi-arch (each platform uses its own image)
- Changes only when
vcpkg.jsonor system dependencies change
Builder & Runtime (per commit):
- Each build creates 2 local tags:
qlean-mini:608f5cc(commit) +qlean-mini:localBuild(default) - Push behavior:
- Commit tag (
608f5cc): always pushed to registry - Custom tag (
DOCKER_IMAGE_TAG): pushed ifDOCKER_PUSH_TAG=true(default:localBuild, not pushed) - Latest tag: pushed if
DOCKER_PUSH_LATEST=true
- Commit tag (
- Push up to 3 tags: commit + custom (v1.0.0) + latest
Utility:
make docker_run # Run node
make docker_run ARGS='--version'
make docker_verify # Test runtime image
make docker_clean # Clean builder + runtime (keep deps)
make docker_clean_all # Clean everything (including deps)For CI/CD:
# One command - pulls dependencies from registry, builds code
export DOCKER_REGISTRY=your-registry
make docker_build_ci # ~4 min
make docker_verify
make docker_push # Push commit tag only
# Production release with version and latest
DOCKER_PUSH_TAG=true DOCKER_PUSH_LATEST=true DOCKER_IMAGE_TAG=v1.0.0 make docker_push
# Pushes: qlean-mini:608f5cc + qlean-mini:v1.0.0 + qlean-mini:latest
# Only latest
DOCKER_PUSH_LATEST=true make docker_push # qlean-mini:608f5cc + qlean-mini:latest
# Staging environment
DOCKER_PUSH_TAG=true DOCKER_IMAGE_TAG=staging make docker_push # commit + stagingSee DOCKER_BUILD.md for details. See the Makefile for all Docker targets.
This project includes GitHub Actions workflow for automated multi-arch Docker builds:
- ✅ Auto-build on push to
ci/dockerbranch (or tags) - ✅ Manual builds via GitHub UI with flexible parameters
- ✅ Native multi-arch (ARM64 + AMD64) on free GitHub-hosted runners
- ✅ Fast builds (~20-30 min per architecture, native compilation)
- ✅ Smart caching (rebuilds dependencies only when
vcpkg.jsonchanges) - ✅ Flexible tagging (commit hash + custom tag + latest)
- ✅ Zero setup - works out of the box, 100% free for public repos
Quick actions:
# Push to ci/docker branch → auto-build and push
git push origin ci/docker
# Create tag → auto-build and push with tag
git tag v1.0.0 && git push origin v1.0.0Manual build via GitHub UI:
Go to: Actions → Docker Build → Run workflow
┌─────────────────────────────────────────────────────────┐
│ 📦 Dependencies Image (rebuild rarely) │
│ ☐ Build dependencies image (vcpkg libs) │
│ 📝 Dependencies tag: latest │
├─────────────────────────────────────────────────────────┤
│ 🏗️ Main Build Configuration │
│ ☑ Build linux/amd64 │
│ ☑ Build linux/arm64 │
├─────────────────────────────────────────────────────────┤
│ 🚀 Push & Tagging │
│ ☐ Push images to Docker Hub │
│ 📝 Custom tag: (e.g., v1.0.0, staging) │
│ ☐ Also push 'latest' tag │
└─────────────────────────────────────────────────────────┘
Common scenarios:
- Test build: Just select architectures, leave rest unchecked
- Production: Check
build_deps,push, fillcustom_tag, checkpush_latest - Hotfix: Select one arch, check
push, fillcustom_tag - Dependencies only: Check
build_depsandpush
See .github/workflows/README.md for CI/CD documentation.
This will:
- Configure the project into
./build/ - Build the main node executable at
./build/src/executable/qlean
Print help:
./build/src/executable/qlean --helpFor step-by-step instructions to run a local single-node devnet, see example/0-single/README.md. It includes the exact CLI command and an explanation of all flags.
The binary includes a helper subcommand to generate a node key and corresponding PeerId:
./build/src/executable/qlean key generate-node-keyThis prints two lines:
- The private key (hex)
- The PeerId (base58)
If TESTING is enabled (default ON in top-level CMakeLists), tests are built and can be run with CTest:
# After building
ctest --test-dir build --output-on-failureIndividual test binaries are placed under build/test_bin/.
SPDX-License-Identifier: Apache-2.0 — Copyright Quadrivium LLC