Everything that CI and the release build download is pinned to a reviewed version, and the pin is checked on every push and pull request. Anything not listed here is not allowed into CI or a release. This document lists the pinned inputs, how to bump them, and how the release toolchain is recorded.
Enforced by .github/scripts/check_supply_chain_pins.py (the Supply Chain / pins job):
| Input | Rule |
|---|---|
GitHub Actions (uses:) |
Full 40-character commit SHA plus a # vX.Y.Z comment. Local ./ actions are exempt. |
Runner images (runs-on:) |
Fixed image (ubuntu-24.04), never *-latest. |
| Toolchain actions | Explicit exact version input (see the inventory below). No stable, nightly, latest. |
cargo install |
--locked and an exact --version. |
| Downloaded release archives | Verified with sha256sum -c before use. |
curl ... | sh |
Not allowed. Download, verify, then execute. |
Dockerfile FROM |
Tag plus @sha256: index digest. |
Cargo.lock |
Every crate resolves from crates.io. |
package-lock.json, pnpm-lock.yaml |
Every package resolves from registry.npmjs.org. No git or tarball URLs. |
Dependency changes are reviewed by .github/scripts/dependency_review.py (the
Supply Chain / dependency-review job, pull requests only). It diffs every
tracked Cargo.lock, package-lock.json and pnpm-lock.yaml between the base
and head commits, lists added and removed packages in the job summary, and
queries OSV for advisories against the newly introduced
versions. MODERATE, HIGH, CRITICAL and unrated advisories fail the job; LOW and
informational (RustSec "unmaintained") advisories are warnings. It does not
depend on the repository dependency graph, so it works on forks.
Lockfile integrity is checked by the Supply Chain / lockfiles job:
pnpm install --frozen-lockfile, npm ci followed by npm audit signatures
(registry signatures and provenance attestations), and cargo metadata --locked
for each Cargo workspace.
Declared once per workflow in a top-level env: block headed
# Reviewed toolchain pins.
| Tool | Pin | Where | Verified by |
|---|---|---|---|
| Rust (stable) | 1.98.1 |
ci.yml, coverage.yml, sac-smoke.yml, integration-futurenet.yml |
rustup channel manifest signatures |
| Rust (nightly, fuzz) | nightly-2026-09-01 |
ci.yml |
rustup channel manifest signatures |
| Rust (Solana) | 1.91.0 action / 1.89.0 solana/rust-toolchain.toml |
ci.yml |
rustup channel manifest signatures |
| Node.js | 22.23.2 |
ci.yml, audit-freeze.yml, stellar-verification.yml, supply-chain.yml |
actions/setup-node release checksums |
| pnpm | 10.28.2 |
same as Node.js, and packageManager in package.json |
corepack npm registry signature |
| stellar-cli (CI) | 26.1.0 |
ci.yml (STELLAR_CLI_SHA256) |
sha256sum -c against the pinned hash |
| stellar-cli (Futurenet) | 28.0.0 |
integration-futurenet.yml |
cargo install --locked (crates.io checksums) |
| Foundry | v1.8.3 |
ci.yml |
foundry-rs/foundry-toolchain |
| slither-analyzer | 0.11.4, solc 0.8.28 |
ci.yml |
PyPI via crytic/slither-action |
| Kani | 0.68.0 |
ci.yml |
crates.io via model-checking/kani-github-action |
| Anchor / Solana CLI | 0.30.1 / 1.18.17, Node 20.15.0 |
ci.yml |
heyAyushh/setup-anchor |
| cargo-fuzz | 0.12.0 |
ci.yml |
cargo install --locked |
| cargo-tarpaulin | 0.37.4 |
coverage.yml |
cargo install --locked |
| cosign | v2.2.4 |
stellar-attestation.yml |
sigstore/cosign-installer checksum |
The stellar job builds the contract WASM, optimizes it, regenerates
stellar/bindings/typescript/** and stellar/abi/*.json, and fails the PR if
either set of checked-in artifacts moves. Nothing in that chain is
continue-on-error any more, so the three constraints below decide the pins.
- The wasm32 target set must stay free of
soroban-sdk/testutils. The SDK guards that feature withcompile_error!on wasm, and a plaincargo buildin a virtual workspace selects every member, including the host-onlybench,bench-crossoverandintegration-testscrates that enable it.default-membersinstellar/Cargo.tomlis what keepscargo build --target wasm32-unknown-unknown --releasebuilding only the ten deployable members (nine cdylibs and thewraith-metricshelper library, which emits no WASM). A new member that enablestestutilshas to stay out of that list; otherwise the wasm build fails with hundreds of errors inside the SDK rather than in the new crate, which reads like a toolchain bug. - Contract code may not use host-only SDK APIs. Converting an
Addressinto anxdr::ScAddressexists only behindcfg(not(target_family = "wasm")), and linkingallocunconditionally leaves the cdylib without a global allocator. Both failures surface in this blocking step, not incargo test. contract optimizeneeds a wasm-opt that accepts bulk memory. rustc emitsmemory.copy/memory.fillfor four of the nine contracts on every toolchain tested (1.88.0 and 1.98.1), and stellar-cli 22.0.1's bundled optimizer rejects them during validation, so the size gate could never pass for those artifacts. 26.1.0 validates all nine and is the CI pin.
The upgrade path, in short:
- 26.1.0 is the version that produced the bindings already committed here. Its
template emits
@stellar/stellar-sdk^14.5.0and nonetworksblock, which is what every checked-in client looks like. Pinning a different CLI therefore regenerates all five clients and theirpackage.json, so a CLI bump is an artifact-regeneration PR with reviewable output, never a one-line config bump. - Do not move past 26.x without checking both outputs. stellar-cli 28.1.0 renames
the
type_key totypeincontract info interface --output json-formatted, which rewrites everystellar/abi/*.jsonsnapshot, and markscontract bindings typescriptdeprecated in favour of the JavaScript SDK generator. Each needs a decision and a fresh baseline before the pin moves. 28.1.0's optimizer produces the same payloads as 26.1.0, so the size gate is not what holds the pin back. - The release container (next section) still builds with stellar-cli 22.0.0,
which cannot validate those four contracts, so
stellar/build/build.shneeds the same bump. That changes deployed hashes, so it belongs to a planned redeploy rather than to this check; tracked as a known gap below.
| Input | Pin | Where |
|---|---|---|
| Base image | debian:bookworm-20260918-slim@sha256:3783cc01769c7b2b1b83a5c5ad96c815348e28ed7da68e2e3687004faa906251 |
stellar/build/Dockerfile |
| rustup-init | 1.29.1, sha256:dda7234360b7f578ca8b0ddcb80145646fa61a67c1720a5abc7051b35c9fcb71 |
stellar/build/Dockerfile |
| Rust | 1.88.0, target wasm32-unknown-unknown |
stellar/build/rust-toolchain.toml |
| stellar-cli | 22.0.0, cargo install --locked, compiled with the Rust pin above |
stellar/build/Dockerfile |
| soroban-sdk | as resolved in stellar/Cargo.lock |
stellar/Cargo.lock |
The release build pins an older Rust than CI on purpose: changing the compiler
changes the WASM bytes, which changes the hashes that stellar/build/verify.js
compares against deployed contracts. Bump it only as part of a planned
redeploy.
Pinned by SHA in every workflow. Dependabot proposes bumps weekly
(.github/dependabot.yml).
| Action | Version | SHA |
|---|---|---|
actions/cache |
v4.3.0 | 0057852bfaa89a56745cba8c7296529d2fc39830 |
actions/checkout |
v4.4.0 | 11d5960a326750d5838078e36cf38b85af677262 |
actions/configure-pages |
v5.0.0 | 983d7736d9b0ae728b81ab479565c72886d7745b |
actions/deploy-pages |
v4.0.5 | d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e |
actions/setup-node |
v4.4.0 | 49933ea5288caeca8642d1e84afbd3f7d6820020 |
actions/upload-artifact |
v4.6.2 | ea165f8d65b6e75b540449e92b4886f43607fa02 |
actions/upload-pages-artifact |
v3.0.1 | 56afc609e74202658d3ffba0e8f6dda462b719fa |
crytic/slither-action |
v0.4.0 | f197989dea5b53e986d0f88c60a034ddd77ec9a8 |
dawidd6/action-download-artifact |
v6 | bf251b5aa9c2f7eeb574a96ee720e24f801b7c11 |
docker/setup-buildx-action |
v3.12.0 | 8d2750c68a42422c14e847fe6c8ac0403b4cbd6f |
dorny/paths-filter |
v3.0.4 | 0e4a8c6effa4802afeda77dc8d303f8176d7dfad |
dtolnay/rust-toolchain |
master | 02cb101ec7c40f2c49e1d9714d64511d8e1b74de |
foundry-rs/foundry-toolchain |
v1.9.1 | 908c540300062bd5a7e473851cdb4282204cee09 |
github/codeql-action/upload-sarif |
v3.38.2 | 1190a975f95ce23525efb6a3fc21ea29567c1b52 |
heyAyushh/setup-anchor |
v4.2 | 039028505349e846df1e57a5691d6d744474ae9b |
model-checking/kani-github-action |
v1 | 69d357bade1eb7b32bc3fc3d5c3d173c5bfa5237 |
sigstore/cosign-installer |
v3.5.0 | 59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 |
softprops/action-gh-release |
v2.6.2 | 3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 |
stefanzweifel/git-auto-commit-action |
v5.2.0 | b863ae1933cb653a53c021fe36dbb774e1fb9403 |
Every Stellar release carries attestation.json, produced inside the pinned
container by stellar/build/build.sh and signed with cosign by
stellar-attestation.yml. Its toolchain object records what built the
artifacts:
"toolchain": {
"rust": "1.88.0",
"cargo": "1.88.0",
"rustup": "1.29.1",
"stellar-cli": "22.0.0",
"soroban-sdk": "22.0.11",
"target": "wasm32-unknown-unknown",
"base_image": "debian:bookworm-20260918-slim@sha256:3783cc01...",
"cargo_lock_sha256": "<sha256 of stellar/Cargo.lock>"
}To confirm a release, rebuild from its commit (see
audit-prep/05-reproducible-builds.md) and
compare both the WASM hashes and the toolchain object.
- Open a dedicated PR. One tool or action family per PR, titled
chore(deps): bump <tool> <old> -> <new>. Dependabot PRs already follow this. - Read what changed. Release notes and the diff between the old and new
SHA or tag. For an action, check that the new SHA is reachable from the
upstream tag (
git ls-remote https://github.com/<owner>/<repo> refs/tags/<tag>) and not from a fork. Look for new network calls, new permissions, or new transitive actions. - Update the pin and its companions together:
- Action: the SHA and the
# vX.Y.Zcomment. - Toolchain: the
env:value in every workflow that declares it (grep -rn '<NAME>:' .github/workflows). - Downloaded binary: the version and its
*_SHA256. Compute the hash from the official release asset. If upstream publishes checksums or build provenance (gh attestation verify), check against those as well. - Base image: the dated tag and the digest together
(
docker buildx imagetools inspect <image>:<tag>).
- Action: the SHA and the
- Update the inventory in this file in the same PR.
- CI must pass, including
Supply Chain / pins. - Maintainer review.
.github/is owned by@truthixifyvia CODEOWNERS. Pin bumps need that approval before merge.
A pin bump is routine. The following need a full re-audit, recorded in the PR description:
- Release build inputs (
stellar/build/**, the release Rust toolchain, stellar-cli in the Dockerfile, the base image). Rebuild twice from a clean checkout, confirm identical WASM hashes, and state in the PR whether hashes moved against the last releaseattestation.json. Any movement requires a planned redeploy. - A new third-party action or tool. Justify it, confirm the maintainer and
the repository, prefer first-party (
actions/*) alternatives, and add it to the inventory. - A new dependency source (git dependency, non-default registry). The pins check rejects these; allowing one is a policy change to this document.
- A security advisory against any pinned input. Bump to a fixed version, or record why it is not reachable.
- Quarterly, even with no advisories. Walk the inventory, bump anything more than one minor version behind, and re-verify the release build.
If an introduced package has an advisory that is not reachable (for example a
dev-only CLI in the subgraph toolchain), add its id to
.github/supply-chain/osv-allowlist.txt with a comment giving the reason, the
reviewer, and a revisit date. Exceptions need the same CODEOWNERS approval as
any other .github/ change.
apt-getpackages instellar/build/Dockerfileand the CKB job come from the Debian and Ubuntu archives at build time. They are signed by the distribution but not version-pinned. The contract WASM does not link against them.model-checking/kani-github-actionandheyAyushh/setup-anchorcall further actions by moving tag internally. We pin the outer action. Upgrading those inner actions requires bumping the outer SHA.- stellar-cli
22.0.1, the previous CI pin, published neither checksums nor build provenance, and its optimizer cannot validate the workspace's bulk-memory contracts.26.1.0publishes a sha256 digest per release asset;STELLAR_CLI_SHA256is that upstream digest, and CI re-checks the downloaded archive against it. - The release container's stellar-cli
22.0.0has the same optimizer as the old CI pin, sostellar/build/build.shaborts on the four contracts that use bulk memory. Bumping it changes the optimized bytes, which changes the hashesstellar/build/verify.jscompares against deployed contracts, so it belongs to a planned redeploy rather than to the CI pin above. - The repository dependency graph is not enabled. Enabling it (Settings → Code
security) would additionally allow
actions/dependency-review-actionfor license policy. The OSV review above does not require it.