Releasing Shekyl
This document describes how to create a new Shekyl release.
Prerequisites
- Push access to Shekyl-Foundation/shekyl-core
- All CI checks passing on
dev - Physical possession of the Shekyl Foundation release-signing hardware
token (OpenPGP applet holding signing subkey
6914D74823DDA8DC) and its Signature PIN. Seedocs/SIGNING.mdfor full key material and the signing ceremony.
Build Dependencies
The CI workflows install all dependencies automatically. For local builds:
- C++ toolchain: GCC or Clang with C++17 support
- Rust toolchain: stable Rust via rustup (builds
libshekyl_ffi.a) - CMake >= 3.14 (required for
FetchContent) - Google Test: system
libgtest-devis preferred; if absent, CMake fetches GoogleTest v1.16.0 at configure time - Boost, OpenSSL, libsodium and other standard Monero-derived dependencies
Branch Strategy
dev-- continuous development and staging branch. All feature work, bug fixes, and CI improvements land here first.main-- protected release branch. Only receives merge commits fromdevat release time. Tags are always created onmain.
Creating a Release
-
Prepare the changelog on
dev. Indocs/CHANGELOG.md, rename the## Unreleasedsection to## [X.Y.Z] - YYYY-MM-DDand add a fresh empty## Unreleasedsection above it. Commit ondev. -
Promote
devtomainby pull request, titledRelease: vX.Y.Z, and merge it with GitHub's Create a merge commit — never squash, rebase or fast-forward. The merge commit preserves branch topology and is the release boundary.RELEASE_PROMOTION.md§4 is the full sequence, including the pre-flight on the frozen SHA; this pull request is also the first full build and test run on it. (git merge --no-ff devon a localmainproduces the same commit, butmainis protected and takes it through the pull request.) -
Tag the release on
mainwith the Foundation institutional signing key, following the ceremony indocs/SIGNING.md(§"Release-tag signing ceremony"). The version is derived automatically from the tag name by CMake (seecmake/GitVersion.cmake), so there is no need to manually editsrc/version.cpp.in.Version symbols: The canonical C++ identifiers are
SHEKYL_VERSION,SHEKYL_VERSION_TAG,SHEKYL_RELEASE_NAME,SHEKYL_VERSION_FULL, andSHEKYL_VERSION_IS_RELEASE(declared insrc/version.h, defined in the generatedversion.cpp). LegacyMONERO_*macro aliases exist for backward compatibility with upstream cherry-picks and will be removed after v4 RingPQC stabilisation.Summary of the ceremony (see SIGNING.md for full checks and failure modes — this is a summary, not a substitute):
# YubiKey inserted, then warm the GPG agent — DO NOT SKIP. # Without this, the first signing attempt fails with a # misleading "No secret key" error even when the card is plugged in. gpg --card-status # The merge happened on the remote. Fetch, and tag the merge commit by # name: a local main, or an origin/main from before the fetch, is the # previous release. git fetch origin # Sign with the Foundation release-signing subkey explicitly. # -u overrides git config user.signingkey for this single command, # so your personal commit-signing key stays configured for normal work. git tag -u 6914D74823DDA8DC -a -s v3.0.3-RC1 origin/main -m "Shekyl v3.0.3-RC1" # Verify BEFORE pushing. A wrong signer can still be undone locally. git verify-tag v3.0.3-RC1 # Expected: "Good signature from 'Shekyl Foundation (Release Signing Key) ...'" # Expected: Primary key fingerprint F5F7 5A47 70C9 4FE1 D5A5 AE59 844E 424F 9866 4F44For pre-releases use a suffix:
v3.0.3-RC1,v3.1.0-alpha.5,v3.1.0-beta.1, etc. -
Push the tag. CI triggers on tag push.
mainis already on the remote, because the merge happened there:git merge-base --is-ancestor v3.0.3-RC1 origin/main && git push origin v3.0.3-RC1Important: The tag must point to a commit that is already on the remote's
mainbranch. -
(Optional) Bump the dev default -- after tagging, update
SHEKYL_VERSION_DEFAULTincmake/Version.cmaketo the next planned version so that un-tagged development builds show the correct series. -
devis not synced withmain. The promotion is a non-fast-forward merge, sodev's history is unchanged and work continues on it; no sync step is needed (.cursor/rules/06-branching.mdc, release flow step 6). A reverse-merge pull request frommainintodevis not part of the flow. Whiledevhas not moved since the freeze it can be fast-forwarded to the release merge commit, which keepsgit log origin/main ^origin/devempty;v3.1.0-alpha.8andv3.1.0-alpha.9did that. It is optional, and oncedevhas moved it is not done. -
GitHub Actions takes over. The
gitianworkflow automatically:- Builds reproducible, deterministic binaries for Linux (x86_64, aarch64, riscv64), Windows x64, macOS (x86_64, aarch64), and FreeBSD x86_64 inside isolated Docker containers
- Packages Linux x86_64 and aarch64 binaries as
.deband.rpm - Builds a Windows NSIS installer (
.exe) - Creates a source archive with all submodules
- Generates
SHA256SUMSfor all artifacts - Publishes everything as a GitHub Release
-
Verify the release at https://github.com/Shekyl-Foundation/shekyl-core/releases
-
Sign the asset manifest. The release job publishes an unsigned
SHA256SUMS. With the token inserted, run the manifest ceremony (docs/SIGNING.md§"Release assets"):python3 scripts/release/sign_release_assets.py vX.Y.Z --download --upload --clobberUntil this has run, a downloader can check hashes but not who published them.
Tag Naming
- Release tags:
v3.1.0,v3.2.0,v4.0.0 - Pre-release tags, in the canonical form
docs/VERSIONING.mddefines:v3.1.0-alpha.9,v3.1.0-beta.1,v3.1.0-rc.1 - The release job marks a tag as a pre-release when its name contains
RC,alphaorbeta(.github/workflows/gitian.yml, theprerelease:expression). GitHub'scontains()is not case sensitive, so the canonical lowercaserc.Nis covered as well as the older-RC1spelling.
Release Artifacts
Each release produces these files (all binaries are Gitian reproducible builds):
| File | Description |
|---|---|
shekyl-x86_64-linux-gnu-vX.Y.Z.tar.bz2 | Linux x86_64 binaries |
shekyl-aarch64-linux-gnu-vX.Y.Z.tar.bz2 | Linux ARM64 binaries |
shekyl_X.Y.Z_amd64.deb | Debian/Ubuntu x86_64 package with systemd unit |
shekyl_X.Y.Z_arm64.deb | Debian/Ubuntu ARM64 package with systemd unit |
shekyl-X.Y.Z-1.x86_64.rpm | RPM x86_64 package for Fedora/RHEL/SUSE |
shekyl-X.Y.Z-1.aarch64.rpm | RPM ARM64 package for Fedora/RHEL/SUSE |
shekyl-x86_64-w64-mingw32-vX.Y.Z.zip | Windows x64 binaries (portable zip) |
shekyl-vX.Y.Z-win-x64-setup.exe | Windows installer (NSIS) |
shekyl-x86_64-apple-darwin11-vX.Y.Z.tar.bz2 | macOS Intel binaries |
shekyl-aarch64-apple-darwin11-vX.Y.Z.tar.bz2 | macOS Apple Silicon binaries |
shekyl-x86_64-unknown-freebsd-vX.Y.Z.tar.bz2 | FreeBSD x86_64 binaries |
shekyl-vX.Y.Z-source.tar.gz | Complete source with submodules |
SHA256SUMS | Checksums for all artifacts |
SHA256SUMS.asc | Detached signature over SHA256SUMS, added by the manifest ceremony (step 9) |
The Gitian Linux build also produces a RISC-V 64-bit tarball; the release job does not publish it (see "Future Platforms").
Linux Package Details
The .deb and .rpm packages are available for both x86_64 and ARM64:
- Install
shekyld,shekyl-cli, andshekyl-wallet-rpcto/usr/local/bin/ - Include a systemd service unit for
shekyld - After install:
sudo systemctl enable --now shekyld - Data directory:
/var/lib/shekyl - Logs: systemd journal (
journalctl -u shekyld)
Windows Installer Details
The NSIS installer:
- Installs binaries to
Program Files\Shekyl - Optionally adds to system PATH
- Creates Start Menu shortcuts
- Registers a standard Windows uninstaller
Gitian Deterministic Builds
The gitian workflow is the sole release pipeline. It runs automatically
on every tag push and produces reproducible builds for all platforms, then
packages and publishes the GitHub Release. It can also be triggered manually
via the GitHub Actions UI (workflow_dispatch), which allows overriding the
tag and repository URL -- useful for testing against a fork.
The pipeline has two phases:
- Build -- 4 parallel Gitian jobs (Linux, Windows, macOS, FreeBSD) produce deterministic tarballs inside isolated Docker containers.
- Package & Publish -- a lightweight job downloads the Gitian artifacts,
creates
.deb/.rpmpackages and a Windows NSIS installer from the pre-built binaries, generates a source archive andSHA256SUMS, then publishes the GitHub Release.
Each Gitian build descriptor installs the Rust toolchain via rustup,
pinned to the workspace toolchain (--default-toolchain 1.94.0, kept in
lockstep with rust/rust-toolchain.toml) with the minimal profile and the
appropriate cross-compilation targets (aarch64, RISC-V for Linux; 64-bit
MinGW for Windows; Darwin targets for macOS). The pin is serial and
up-front by design: the parallel make build invokes many cargo
processes at once, and letting them auto-install the toolchain concurrently
races ~/.rustup/downloads and aborts the build (component download failed … could not rename). The minimal profile omits clippy/rustfmt —
a release build compiles the Rust FFI, it never lints. 32-bit targets were
permanently retired in v3.1.0-alpha.5 (Chore #3) on PQC constant-time
grounds — see docs/CHANGELOG.md entry "Retired 32-bit build targets".
Re-running a Failed Gitian Build
If a tag needs to be moved (e.g. to include a last-minute fix):
git tag -d v3.0.3-RC1 # delete local
git push origin :refs/tags/v3.0.3-RC1 # delete remote
git fetch origin # the fix reached main by pull request
gpg --card-status # warm the agent
git tag -u 6914D74823DDA8DC -a -s v3.0.3-RC1 origin/main \
-m "Shekyl v3.0.3-RC1" # recreate on the new merge commit, signed
git verify-tag v3.0.3-RC1 # MUST pass before pushing
git push origin v3.0.3-RC1
Re-tagging goes through the same signing ceremony as a fresh release;
docs/SIGNING.md §"Release-tag signing ceremony" is the full procedure.
Or trigger manually from the Actions tab without retagging.
Testing Against a Fork
# From the Actions tab, use "Run workflow" with:
# tag: v3.0.3-RC1
# repo_url: https://github.com/youruser/shekyl-core
Future Platforms
- macOS
.dmginstaller -- native disk image with drag-to-Applications UX. - Linux AppImage -- single-file portable binary for desktop distributions.
- RISC-V 64-bit is available via Gitian deterministic builds and may be
promoted to the main release if demand warrants. 32-bit platforms are
permanently out of scope on security grounds — see
docs/CHANGELOG.mdentry "Retired 32-bit build targets".