From caca5e42e33543af61fef4932465d433ceb3c177 Mon Sep 17 00:00:00 2001 From: Chris O'Neil Date: Mon, 30 Mar 2026 22:03:15 +0100 Subject: [PATCH] feat: improve release process with signing, install scripts, and build fixes - Add ML-DSA-65 post-quantum signing for all release archives via ant-keygen - Add Windows install script (install.ps1) using irm | iex pattern - Replace winget manifest test with install.ps1 test in install-test workflow - Update release body with install scripts, verification instructions, and DigiCert EV code-signing mention for Windows - Fix musl builds: vendored OpenSSL (unix-only) and cross for aarch64-musl - Fix Windows build: add shell: bash to conditional build step - Skip cargo publish for RC versions - Update bootstrap peers with current production IPs - Update README installation section with install scripts - Add release-signing-key.pub for signature verification Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/ant-cli-release.yml | 130 ++++++++++++++++++++------ .github/workflows/install-test.yml | 70 ++++---------- Cargo.lock | 11 +++ README.md | 10 +- ant-core/Cargo.toml | 4 + install.ps1 | 98 +++++++++++++++++++ resources/bootstrap_peers.toml | 4 +- resources/release-signing-key.pub | Bin 0 -> 1952 bytes 8 files changed, 243 insertions(+), 84 deletions(-) create mode 100644 install.ps1 create mode 100644 resources/release-signing-key.pub diff --git a/.github/workflows/ant-cli-release.yml b/.github/workflows/ant-cli-release.yml index 5d30bb7..6edd566 100644 --- a/.github/workflows/ant-cli-release.yml +++ b/.github/workflows/ant-cli-release.yml @@ -41,21 +41,25 @@ jobs: with: targets: ${{ matrix.target }} - - name: install cross-compilation tools + - name: install cross if: matrix.target == 'aarch64-unknown-linux-musl' - run: | - sudo apt-get update - sudo apt-get install -y musl-tools gcc-aarch64-linux-gnu - echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV + run: cargo install cross --git https://github.com/cross-rs/cross - name: install musl tools if: matrix.target == 'x86_64-unknown-linux-musl' run: | sudo apt-get update sudo apt-get install -y musl-tools + echo "CC_x86_64_unknown_linux_musl=musl-gcc" >> $GITHUB_ENV - name: build - run: cargo build --release --target ${{ matrix.target }} --bin ant + shell: bash + run: | + if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then + cross build --release --target ${{ matrix.target }} --bin ant + else + cargo build --release --target ${{ matrix.target }} --bin ant + fi - name: determine version id: version @@ -206,9 +210,79 @@ jobs: path: ${{ github.workspace }}\smctl-signing.log if-no-files-found: ignore + sign-releases: + name: sign release artifacts + runs-on: ubuntu-latest + needs: [build, sign-windows] + steps: + - name: download all build artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: download signed windows artifact + uses: actions/download-artifact@v4 + with: + name: ant-x86_64-pc-windows-msvc-signed + path: artifacts-signed-win + + - name: replace windows archive with signed version + run: | + rm -f artifacts/ant-*-x86_64-pc-windows-msvc.zip + cp artifacts-signed-win/*.zip artifacts/ + + - name: download ant-keygen + run: | + gh release download --repo WithAutonomi/ant-keygen --pattern 'ant-keygen-linux-x64.tar.gz' --dir /tmp + tar -xzf /tmp/ant-keygen-linux-x64.tar.gz -C /tmp + chmod +x /tmp/ant-keygen + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: decode signing key + run: | + echo "${{ secrets.ANT_SIGNING_KEY }}" | xxd -r -p > /tmp/signing-key.secret + chmod 600 /tmp/signing-key.secret + + - name: sign all release files + run: | + for file in artifacts/ant-*.tar.gz artifacts/ant-*.zip; do + if [ -f "$file" ]; then + echo "Signing $file..." + /tmp/ant-keygen sign \ + --key /tmp/signing-key.secret \ + --input "$file" \ + --output "${file}.sig" \ + --context "ant-release-v1" + fi + done + + - name: clean up signing key + if: always() + run: shred -u /tmp/signing-key.secret 2>/dev/null || rm -f /tmp/signing-key.secret + + - name: generate checksums + run: | + cd artifacts + files=$(ls ant-*.tar.gz ant-*.zip ant-*.sig 2>/dev/null) + if [ -z "$files" ]; then + echo "ERROR: No release artifacts found to checksum" + exit 1 + fi + sha256sum $files > SHA256SUMS.txt + cat SHA256SUMS.txt + + - uses: actions/upload-artifact@v4 + with: + name: signed-releases + path: artifacts/* + retention-days: 1 + publish-crate: name: publish ant-core to crates.io runs-on: ubuntu-latest + if: ${{ !contains(github.ref_name, '-rc.') }} steps: - uses: actions/checkout@v4 @@ -223,7 +297,8 @@ jobs: release: name: create github release runs-on: ubuntu-latest - needs: [build, sign-windows, publish-crate] + needs: [sign-releases, publish-crate] + if: ${{ !cancelled() && needs.sign-releases.result == 'success' }} steps: - uses: actions/checkout@v4 @@ -243,23 +318,7 @@ jobs: - uses: actions/download-artifact@v4 with: - name: ant-x86_64-unknown-linux-musl - path: assets/ - - uses: actions/download-artifact@v4 - with: - name: ant-aarch64-unknown-linux-musl - path: assets/ - - uses: actions/download-artifact@v4 - with: - name: ant-x86_64-apple-darwin - path: assets/ - - uses: actions/download-artifact@v4 - with: - name: ant-aarch64-apple-darwin - path: assets/ - - uses: actions/download-artifact@v4 - with: - name: ant-x86_64-pc-windows-msvc-signed + name: signed-releases path: assets/ - name: extract changelog entry @@ -284,6 +343,12 @@ jobs: curl -fsSL https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.sh | bash ``` + ### Windows (quick-start) + + ```powershell + irm https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.ps1 | iex + ``` + ### Manual download Download the archive for your platform from the assets below, extract it, and place the `ant` binary on your `PATH`. Copy `bootstrap_peers.toml` to the appropriate config directory: @@ -294,12 +359,18 @@ jobs: | macOS | `~/Library/Application Support/ant/bootstrap_peers.toml` | | Windows | `%APPDATA%\ant\bootstrap_peers.toml` | - ### Windows (winget) + ## Verification - ```powershell - winget install Autonomi.ant + All release archives are signed with ML-DSA-65 (FIPS 204) post-quantum signatures. Download `ant-keygen` from [WithAutonomi/ant-keygen](https://github.com/WithAutonomi/ant-keygen/releases) and the public key from [`resources/release-signing-key.pub`](https://raw.githubusercontent.com/WithAutonomi/ant-client/main/resources/release-signing-key.pub), then verify: + + ```bash + ant-keygen verify --key release-signing-key.pub --input --signature .sig --context ant-release-v1 ``` + The Windows binary (`ant.exe`) is additionally signed with a DigiCert EV code-signing certificate. Windows will verify this signature automatically on download and execution. + + SHA256 checksums provided in `SHA256SUMS.txt`. + HEADER echo "## Detailed Changes" >> /tmp/release_body.md @@ -320,4 +391,7 @@ jobs: --title "ant ${{ steps.meta.outputs.version }}" \ --notes-file /tmp/release_body.md \ $prerelease_flag \ - assets/* + assets/*.tar.gz \ + assets/*.zip \ + assets/*.sig \ + assets/SHA256SUMS.txt diff --git a/.github/workflows/install-test.yml b/.github/workflows/install-test.yml index 6bb1551..b0b44ec 100644 --- a/.github/workflows/install-test.yml +++ b/.github/workflows/install-test.yml @@ -65,69 +65,33 @@ jobs: echo "Bootstrap config installed at $config_file" cat "$config_file" - test-winget-manifest: - name: validate winget manifest + test-install-script-windows: + name: test install.ps1 (Windows) runs-on: windows-latest steps: - uses: actions/checkout@v4 - - name: install wingetcreate + - name: run install script shell: pwsh - run: | - winget install wingetcreate --accept-source-agreements --accept-package-agreements - continue-on-error: true + env: + ANT_VERSION: ${{ inputs.version }} + INSTALL_DIR: ${{ runner.temp }}\ant\bin + run: . .\install.ps1 - - name: generate and validate manifest + - name: verify binary shell: pwsh run: | - $version = "${{ inputs.version }}" - $url = "https://github.com/WithAutonomi/ant-client/releases/download/ant-cli-v${version}/ant-${version}-x86_64-pc-windows-msvc.zip" - - # Download and hash - $tempFile = [System.IO.Path]::GetTempFileName() - try { - Invoke-WebRequest -Uri $url -OutFile $tempFile - $hash = (Get-FileHash -Path $tempFile -Algorithm SHA256).Hash - Write-Host "SHA256: $hash" - } finally { - Remove-Item $tempFile -ErrorAction SilentlyContinue - } - - # Substitute template - $template = Get-Content "resources\winget\Autonomi.ant.yaml" -Raw - $manifest = $template ` - -replace '\$\{VERSION\}', $version ` - -replace '\$\{SHA256\}', $hash - $manifest | Set-Content "winget\Autonomi.ant.installer.yaml" -Encoding UTF8 - - Write-Host "Generated manifest:" - Get-Content "winget\Autonomi.ant.installer.yaml" + $env:Path = "${{ runner.temp }}\ant\bin;$env:Path" + ant --help + Write-Host "Binary runs successfully" - - name: test download and extract + - name: verify bootstrap config shell: pwsh run: | - $version = "${{ inputs.version }}" - $url = "https://github.com/WithAutonomi/ant-client/releases/download/ant-cli-v${version}/ant-${version}-x86_64-pc-windows-msvc.zip" - $tempDir = Join-Path $env:RUNNER_TEMP "ant-test" - New-Item -ItemType Directory -Path $tempDir -Force | Out-Null - - Invoke-WebRequest -Uri $url -OutFile "$tempDir\ant.zip" - Expand-Archive "$tempDir\ant.zip" -DestinationPath $tempDir - - $antExe = Get-ChildItem -Path $tempDir -Recurse -Filter "ant.exe" | Select-Object -First 1 - if (-not $antExe) { - Write-Error "ant.exe not found in archive" - exit 1 - } - - & $antExe.FullName --help - Write-Host "Windows binary runs successfully" - - # Check bootstrap config is in archive - $config = Get-ChildItem -Path $tempDir -Recurse -Filter "bootstrap_peers.toml" | Select-Object -First 1 - if (-not $config) { - Write-Error "bootstrap_peers.toml not found in archive" + $configFile = Join-Path $env:APPDATA "ant\bootstrap_peers.toml" + if (-not (Test-Path $configFile)) { + Write-Error "bootstrap_peers.toml not found at $configFile" exit 1 } - Write-Host "Bootstrap config found in archive" - Get-Content $config.FullName + Write-Host "Bootstrap config installed at $configFile" + Get-Content $configFile diff --git a/Cargo.lock b/Cargo.lock index 80f5aa4..98e3231 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -865,6 +865,7 @@ dependencies = [ "libp2p", "lru", "multihash", + "openssl", "postcard", "rand 0.8.5", "reqwest 0.12.28", @@ -4359,6 +4360,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-src" +version = "300.5.5+3.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.111" @@ -4367,6 +4377,7 @@ checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] diff --git a/README.md b/README.md index eca8bde..317758d 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,16 @@ Data on Autonomi is **content-addressed**. Files are split into encrypted chunks ## Installation +### Linux / macOS + ```bash -cargo install --path ant-cli +curl -fsSL https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.sh | bash +``` + +### Windows + +```powershell +irm https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.ps1 | iex ``` ## Quick Start diff --git a/ant-core/Cargo.toml b/ant-core/Cargo.toml index 561698d..22fbc73 100644 --- a/ant-core/Cargo.toml +++ b/ant-core/Cargo.toml @@ -44,6 +44,10 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } [target.'cfg(unix)'.dependencies] libc = "0.2" +# Vendored OpenSSL ensures openssl-sys links statically for musl targets. +# Without this, the musl CI builds fail because the runners lack musl-compatible +# OpenSSL dev libraries. Not needed on Windows (uses schannel via native-tls). +openssl = { version = "0.10", features = ["vendored"] } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_System_Console", "Win32_System_Threading"] } diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..3d11248 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,98 @@ +# Quick-start installer for the Autonomi `ant` CLI. +# +# Usage: +# irm https://raw.githubusercontent.com/WithAutonomi/ant-client/main/install.ps1 | iex +# +# Environment variables: +# ANT_VERSION - install a specific version (e.g. "0.1.1"). Defaults to latest. +# INSTALL_DIR - override install directory (default: %LOCALAPPDATA%\ant\bin). +# +# Verification: +# Release archives are signed with ML-DSA-65 post-quantum signatures. +# Download ant-keygen from https://github.com/WithAutonomi/ant-keygen/releases +# and the public key from resources/release-signing-key.pub in the repository, then: +# ant-keygen verify --key release-signing-key.pub --input --signature .sig --context ant-release-v1 + +$ErrorActionPreference = "Stop" + +$Repo = "WithAutonomi/ant-client" +$BinaryName = "ant" + +# --- helpers ---------------------------------------------------------------- + +function Say($msg) { Write-Host $msg } +function Err($msg) { Write-Error $msg; exit 1 } + +function Get-LatestVersion { + $response = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" + if ($response.tag_name -match "^ant-cli-v(.+)$") { + return $Matches[1] + } + Err "Could not parse version from tag: $($response.tag_name)" +} + +function Get-DefaultInstallDir { + return Join-Path $env:LOCALAPPDATA "ant\bin" +} + +function Get-ConfigDir { + return Join-Path $env:APPDATA "ant" +} + +# --- main ------------------------------------------------------------------- + +$Version = if ($env:ANT_VERSION) { $env:ANT_VERSION } else { Get-LatestVersion } +$InstallDir = if ($env:INSTALL_DIR) { $env:INSTALL_DIR } else { Get-DefaultInstallDir } +$Target = "x86_64-pc-windows-msvc" + +if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") { + Say "WARNING: No native ARM64 build available. Installing x86_64 binary (runs under emulation)." +} + +Say "Installing ant $Version for $Target..." + +$Archive = "$BinaryName-$Version-$Target.zip" +$Url = "https://github.com/$Repo/releases/download/ant-cli-v$Version/$Archive" + +$TempDir = Join-Path ([System.IO.Path]::GetTempPath()) "ant-install-$([System.Guid]::NewGuid().ToString('N'))" +New-Item -ItemType Directory -Path $TempDir -Force | Out-Null + +try { + Say "Downloading $Url..." + Invoke-WebRequest -Uri $Url -OutFile (Join-Path $TempDir $Archive) -UseBasicParsing + + Say "Extracting..." + Expand-Archive -Path (Join-Path $TempDir $Archive) -DestinationPath $TempDir + + # Install binary + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + $ExtractedDir = Join-Path $TempDir "$BinaryName-$Version-$Target" + Copy-Item (Join-Path $ExtractedDir "$BinaryName.exe") -Destination (Join-Path $InstallDir "$BinaryName.exe") -Force + Say "Installed $BinaryName.exe to $InstallDir" + + # Install bootstrap config + $ConfigDir = Get-ConfigDir + New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null + $ConfigFile = Join-Path $ConfigDir "bootstrap_peers.toml" + if (-not (Test-Path $ConfigFile)) { + Copy-Item (Join-Path $ExtractedDir "bootstrap_peers.toml") -Destination $ConfigFile + Say "Installed bootstrap config to $ConfigFile" + } else { + Say "Bootstrap config already exists at $ConfigFile - skipping" + } + + # Add to user PATH if not already there + $UserPath = [Environment]::GetEnvironmentVariable("Path", "User") + if ($UserPath -notlike "*$InstallDir*") { + Say "" + Say "Adding $InstallDir to your user PATH..." + [Environment]::SetEnvironmentVariable("Path", "$InstallDir;$UserPath", "User") + $env:Path = "$InstallDir;$env:Path" + Say "Restart your terminal for the PATH change to take effect." + } +} finally { + Remove-Item -Path $TempDir -Recurse -Force -ErrorAction SilentlyContinue +} + +Say "" +Say "Done! Run 'ant --help' to get started." diff --git a/resources/bootstrap_peers.toml b/resources/bootstrap_peers.toml index 7315e60..4b7b50b 100644 --- a/resources/bootstrap_peers.toml +++ b/resources/bootstrap_peers.toml @@ -7,8 +7,8 @@ # Port range for ant-node: 10000-10999. peers = [ - "129.212.138.135:10000", - "134.199.138.183:10000", + "66.135.23.83:10000", + "149.248.9.2:10000", "167.235.201.229:10000", "178.156.129.121:10000", "207.148.94.42:10000", diff --git a/resources/release-signing-key.pub b/resources/release-signing-key.pub new file mode 100644 index 0000000000000000000000000000000000000000..ba0117d7125f5f5bc23e66a1ceda74cf0b5519ec GIT binary patch literal 1952 zcmV;R2VeNK&nhG_*|IvT>0bQ2^RXXJPzpxtgZys~i9rAxQr_IP$p`InI^n5;;r#!r z&Yyj&;#ZR3@~ojj({zee6;HoF>+ubbIWp^jr!IFs zRO3BjuIC9}``oGr+~Ek8yGx)h1ugFDni{U&=`8p!Lu`0U-tUo%l$}q31vKl^&9o;`X=sKM)N(-YAp@?<8=Z}vrY5X3~wuri!FuZ!WojqbZGa0)&EGwW|u#cV)fw>Y?f%xK) zvghWU5$j20IwjUqF5mnVGG)w%+x@$Ns8GzbF${3?wiSv{fUU8x5G!Xh_4YP3U#HT6 z)^(v66}}x_Gmeokj?Fs`S-e?OcJg!d(?acLM$>C!EkL6Z#zU{y1m7E>{T1h z+qP8*h7*5Isbk@XpnggF^rJ@49qjeSFX2N1(BxkmAR4EEy)hYTbk~|~J@~9my8&aQ zojl2GicewInDsy+sa}&_6Vu?^RI3f+h=qW9UN~fz#ObhI_ds86KfV;2e4>1Gr7|L6 zF{KTq%#-B(?Y!sm<=e&#;mA(z=5`zq8`x9S=m+iUk<=TJTqp&~;+)Z$L+#jVE7D|} z;S7ju4YF%xO$JFP|EaUEb_Bn_lJLL<2TtcH|% zhXt;;KoGK|`97nqTg{xxTYYoeRsGEGCZ+vHHN{I)5us zd37aQBOP$C+rxq%Ac)q-q!0^b95u;xQ;7w_!;~c5%wzxpWb>%!-wY`s+8G-lDN` zP1!xG{SLh{og?XQg--s>k^6#_?u*}Q*#V)(A{YUU5Y>l5(7hvdL{mnkt^iC9h)SqE zN-*FWq~$=k;1Zlf1N$yc^(os4gjL6E#vl3a3#5^Kc_&$Qm+We&_*JMy)YqoL#3wcA zPdSNwLG$*|w)v6kl<~V4Mx51JQv@v%d>3jqwd(U*mK*NQ#J~|+ybLMOLLC-k>K+6y z;}`8StX6Xj>x{SH5$B7YXPh#e_ z2x~PQma}_6E$#Eak>o@!$~RJm$YLeKI+1 ztzXLv6tC;R?1zP$6Saz%sge^LbnN}%!6F7-PX@=`#_+@e1g(E@ymU?d7^7A!MJOvT zyqma7=23Mh3?~lvl8njTGa6+m#{n{;Hi))l5Chh?pPh5zsmI^p3pp`&ypi!mn#GqW zz+uHBRITG97-{z>&|C6EV%(R`PC#}5-op7hLjBa&;f2`AIFZ45hgk6@8Yu$fRNh!* zo#u`+a8pA^knU~|jI*9cMf|AaQet#%qOT~O-yUfnR=i2U<<`WtTunJiOSJ4(RGJl( z3d^YwMgv?UFDdPEp;!7??;(Eji>Cxpv;$q(NWkTTwt8#L``Sn7p{`eSNx=C^Ydp2s zCp{gOISFNz+X17Cc#8*Z9E(dGok~y3xb`A*ihIcN3hx6Rw@r`0--n0=;R-P7-t)q? z06f&1AIVqYf+;V0YXIMK(8RP`U$!x9!k-vY}$UZ~&f~6uUA2 literal 0 HcmV?d00001