From 452b954d2b59cf41294401186461c583a8350c38 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Thu, 13 Aug 2026 18:58:02 +0000 Subject: [PATCH 1/8] Add build verification guidance for mainnet contracts Nothing on-chain links a deployed WASM to its source, and the SEP-55 route to fixing that was missing from the skills entirely. Adds a "Verify your build" section to the smart-contracts skill covering the source_repo/home_domain metadata and the soroban-build-workflow reusable workflow, points the pre-mainnet checklists at it, and expands the one-line SEP-0055 entry in the standards skill. Framed as a standard step for every mainnet deploy rather than something reserved for user-facing contracts. --- .../04-build-verification.json | 13 ++++++ skills/smart-contracts/SKILL.md | 40 ++++++++++++++++++- skills/smart-contracts/security.md | 1 + skills/standards/SKILL.md | 2 +- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 evals/scenarios/smart-contracts/04-build-verification.json diff --git a/evals/scenarios/smart-contracts/04-build-verification.json b/evals/scenarios/smart-contracts/04-build-verification.json new file mode 100644 index 0000000..84e9e5f --- /dev/null +++ b/evals/scenarios/smart-contracts/04-build-verification.json @@ -0,0 +1,13 @@ +{ + "skills": [ + "smart-contracts" + ], + "query": "I'm deploying my Soroban contract to mainnet next week. How do I make it so users can check that the deployed WASM really came from my GitHub repo?", + "expected_behavior": [ + "Points at SEP-55 build verification — source_repo metadata in the WASM plus a GitHub build attestation — rather than improvising a scheme like publishing the hash in the README", + "Names either stellar contract build --meta source_repo=github:/ or the soroban-build-workflow reusable workflow", + "Any workflow it writes sets id-token: write, contents: write, and attestations: write permissions", + "Says to deploy the WASM attached to the release the workflow produced, because a local rebuild may not match the attested hash", + "States the limit: the attestation proves which workflow run and commit built the binary, not that the code is safe or reviewed" + ] +} diff --git a/skills/smart-contracts/SKILL.md b/skills/smart-contracts/SKILL.md index 99903bd..6fa52ad 100644 --- a/skills/smart-contracts/SKILL.md +++ b/skills/smart-contracts/SKILL.md @@ -186,6 +186,44 @@ stellar contract invoke \ To upload WASM without instantiating (e.g. for factories or upgrades), use `stellar contract upload` (the older `stellar contract install` is a deprecated alias). +## Verify your build + +Nothing on-chain ties a deployed WASM to its source. [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md) (Draft) closes that gap: build in GitHub Actions, stamp the source repository into the WASM's `contractmetav0` section, and publish a [GitHub build attestation](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) over the binary — anyone can then walk from the on-chain WASM hash back to the commit and the workflow run that produced it. Make it a standard step before **any** mainnet deploy; it costs one workflow file, so there's no reason to reserve it for user-facing contracts. + +Two metadata entries carry it (`stellar contract build --meta key=value`, read back with `stellar contract info meta --wasm `): + +- `source_repo=github:/` — where the source lives +- `home_domain=` — domain serving your SEP-1 `stellar.toml`, for org/token lookup (optional; bare domain, no scheme or path) + +[soroban-build-workflow](https://github.com/stellar-expert/soroban-build-workflow) does the whole job — build with the metadata, optimize, attest, publish a release with the WASM attached, and register the build with stellar.expert's contract validation: + +```yaml +# .github/workflows/release.yml — verified build on every version tag +name: Build and Release +on: + push: + tags: ["v*"] + +permissions: # all three are required to attest + id-token: write + contents: write + attestations: write + +jobs: + release: + uses: stellar-expert/soroban-build-workflow/.github/workflows/release.yml@v27.0.0 # pin a tag, not @main + with: + release_name: ${{ github.ref_name }} + home_domain: example.com # optional + package: my-contract # optional — omit to build the working directory + secrets: + release_token: ${{ secrets.GITHUB_TOKEN }} # created by GitHub, nothing to configure +``` + +Then deploy **the WASM attached to that release**, not a local rebuild — compilation environments vary, and a hash that differs by one byte matches no attestation. + +The result shows up in [Stellar Lab's contract explorer](https://developers.stellar.org/docs/tools/lab/smart-contracts/contract-explorer#build-info) and on stellar.expert. Be precise with users about what it proves: a specific workflow run built this binary from this commit. It says nothing about whether that code is safe or was ever reviewed. + ## Minimal test ```rust @@ -211,7 +249,7 @@ Auth mocking, event assertions, fuzzing, fork tests, and CI setup: [testing.md]( ## Before mainnet -Work through the checklists in [security.md](security.md) — authorization, reinitialization, arithmetic, storage TTLs, and cross-contract validation are the recurring failure modes. +Work through the checklists in [security.md](security.md) — authorization, reinitialization, arithmetic, storage TTLs, and cross-contract validation are the recurring failure modes. Ship the deploy through a [verified build](#verify-your-build) so the on-chain WASM hash traces back to a reviewable commit. ## Documentation diff --git a/skills/smart-contracts/security.md b/skills/smart-contracts/security.md index 75334df..ad68f8f 100644 --- a/skills/smart-contracts/security.md +++ b/skills/smart-contracts/security.md @@ -172,6 +172,7 @@ Any contract that takes a token address must assume it may be native XLM's SAC, - [ ] Events emitted for auditable state changes (and error codes never renumbered) - [ ] Upgrade path gated, tested (happy + failure), and replay-safe - [ ] Emergency controls (pause) and incident runbook defined for value-bearing contracts +- [ ] Deployed WASM traceable to its source — SEP-55 build metadata plus a GitHub attestation, deployed from the release artifact ([SKILL.md](SKILL.md#verify-your-build)) ### Client-side diff --git a/skills/standards/SKILL.md b/skills/standards/SKILL.md index 9cae054..453f3d3 100644 --- a/skills/standards/SKILL.md +++ b/skills/standards/SKILL.md @@ -56,7 +56,7 @@ Treat this file as a routing map, not a source of final governance/status truth. - [SEP-0048](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0048.md): Contract interface specification - [SEP-0049](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0049.md): Upgradeable-contract guidance - [SEP-0050](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0050.md): NFT standard work -- [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md): Contract build verification +- [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md): Contract build verification — a `source_repo` metadata entry in the WASM plus a GitHub build attestation let anyone trace a deployed contract hash back to the commit and workflow run that built it. Automate it with [soroban-build-workflow](https://github.com/stellar-expert/soroban-build-workflow); setup in [`../smart-contracts/SKILL.md`](../smart-contracts/SKILL.md#verify-your-build) - [SEP-0056](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0056.md): Vault-style tokenized products - [SEP-0057](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0057.md): Regulated token patterns (T-REX) From a5921875c3c7c63886f5a6204b5dd55b0ebf9b90 Mon Sep 17 00:00:00 2001 From: kaankacar Date: Fri, 14 Aug 2026 07:21:09 +0000 Subject: [PATCH 2/8] Resolve the build workflow version instead of pinning one in docs Pinning v27.0.0 in the example was wrong twice over: that tag has no published release (v25.1.0 is the latest), and a version tag is mutable, so a retarget would silently change a workflow that runs with the repository token, OIDC, and attestation permissions. Document resolving the current release, reading its release.yml, and pinning that commit SHA instead, matching how this repo pins pr-preview-action. Also attribute each permission to the step that needs it, note that the trust chain only works on a public repo since attestations are read unauthenticated, and restore the standards list to its one-line format with the routing pointer moved to Related skills. --- .../04-build-verification.json | 5 ++-- skills/smart-contracts/SKILL.md | 24 +++++++++++++------ skills/smart-contracts/security.md | 2 +- skills/standards/SKILL.md | 3 ++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/evals/scenarios/smart-contracts/04-build-verification.json b/evals/scenarios/smart-contracts/04-build-verification.json index 84e9e5f..266e084 100644 --- a/evals/scenarios/smart-contracts/04-build-verification.json +++ b/evals/scenarios/smart-contracts/04-build-verification.json @@ -6,8 +6,9 @@ "expected_behavior": [ "Points at SEP-55 build verification — source_repo metadata in the WASM plus a GitHub build attestation — rather than improvising a scheme like publishing the hash in the README", "Names either stellar contract build --meta source_repo=github:/ or the soroban-build-workflow reusable workflow", - "Any workflow it writes sets id-token: write, contents: write, and attestations: write permissions", + "If it reuses soroban-build-workflow, resolves the current release itself and pins a full commit SHA rather than a version tag copied from documentation", + "Grants contents: write for publishing the release and id-token: write plus attestations: write for the attestation, without presenting all three as attestation requirements", "Says to deploy the WASM attached to the release the workflow produced, because a local rebuild may not match the attested hash", - "States the limit: the attestation proves which workflow run and commit built the binary, not that the code is safe or reviewed" + "States the limits: the source repo must be public to be checkable, and the attestation proves which workflow run and commit built the binary, not that the code is safe" ] } diff --git a/skills/smart-contracts/SKILL.md b/skills/smart-contracts/SKILL.md index 6fa52ad..1f62f51 100644 --- a/skills/smart-contracts/SKILL.md +++ b/skills/smart-contracts/SKILL.md @@ -188,14 +188,23 @@ To upload WASM without instantiating (e.g. for factories or upgrades), use `stel ## Verify your build -Nothing on-chain ties a deployed WASM to its source. [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md) (Draft) closes that gap: build in GitHub Actions, stamp the source repository into the WASM's `contractmetav0` section, and publish a [GitHub build attestation](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) over the binary — anyone can then walk from the on-chain WASM hash back to the commit and the workflow run that produced it. Make it a standard step before **any** mainnet deploy; it costs one workflow file, so there's no reason to reserve it for user-facing contracts. +Nothing on-chain ties a deployed WASM to its source. [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md) (Draft) closes that gap: build in GitHub Actions, stamp the source repository into the WASM's `contractmetav0` section, and publish a [GitHub build attestation](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) over the binary — anyone can then walk from the on-chain WASM hash back to the commit and the workflow run that produced it. Verifiers read the attestation from `api.github.com/repos///attestations/sha256:` unauthenticated, so the source repository has to be public for that "anyone" to hold; on a private repo the provenance exists but no user can check it. Make it a standard step before **any** mainnet deploy; it costs one workflow file, so there's no reason to reserve it for user-facing contracts. Two metadata entries carry it (`stellar contract build --meta key=value`, read back with `stellar contract info meta --wasm `): - `source_repo=github:/` — where the source lives - `home_domain=` — domain serving your SEP-1 `stellar.toml`, for org/token lookup (optional; bare domain, no scheme or path) -[soroban-build-workflow](https://github.com/stellar-expert/soroban-build-workflow) does the whole job — build with the metadata, optimize, attest, publish a release with the WASM attached, and register the build with stellar.expert's contract validation: +[soroban-build-workflow](https://github.com/stellar-expert/soroban-build-workflow) does the whole job — build with the metadata, optimize, attest, publish a release with the WASM attached, and register the build with stellar.expert's contract validation. + +Resolve its version yourself instead of copying a pin out of any documentation, this file included. Pins go stale, and the newest tag in that repo is not always a published release: + +```bash +gh release view --repo stellar-expert/soroban-build-workflow --json tagName # latest release +gh api repos/stellar-expert/soroban-build-workflow/commits/ --jq .sha # its commit +``` + +Read that release's `release.yml` before wiring it in — it should check out the tagged commit, build from source, and attest the same file it uploads. If it doesn't, stop and tell the user rather than pinning it anyway. Then pin the full commit SHA, not the tag: tags are mutable, and this workflow runs with your repository token, OIDC, and attestation permissions. ```yaml # .github/workflows/release.yml — verified build on every version tag @@ -204,14 +213,15 @@ on: push: tags: ["v*"] -permissions: # all three are required to attest - id-token: write - contents: write - attestations: write +permissions: # inherited by the called workflow, which cannot elevate them + contents: write # create the release, attach the WASM + id-token: write # sign the attestation + attestations: write # publish it jobs: release: - uses: stellar-expert/soroban-build-workflow/.github/workflows/release.yml@v27.0.0 # pin a tag, not @main + # full SHA of the release you just reviewed; keep the version in the comment + uses: stellar-expert/soroban-build-workflow/.github/workflows/release.yml@ # vX.Y.Z with: release_name: ${{ github.ref_name }} home_domain: example.com # optional diff --git a/skills/smart-contracts/security.md b/skills/smart-contracts/security.md index ad68f8f..aa56fe9 100644 --- a/skills/smart-contracts/security.md +++ b/skills/smart-contracts/security.md @@ -172,7 +172,7 @@ Any contract that takes a token address must assume it may be native XLM's SAC, - [ ] Events emitted for auditable state changes (and error codes never renumbered) - [ ] Upgrade path gated, tested (happy + failure), and replay-safe - [ ] Emergency controls (pause) and incident runbook defined for value-bearing contracts -- [ ] Deployed WASM traceable to its source — SEP-55 build metadata plus a GitHub attestation, deployed from the release artifact ([SKILL.md](SKILL.md#verify-your-build)) +- [ ] Deployed WASM traceable to its source — SEP-55 build metadata plus a GitHub attestation from a public repo, deployed from the release artifact ([SKILL.md](SKILL.md#verify-your-build)) ### Client-side diff --git a/skills/standards/SKILL.md b/skills/standards/SKILL.md index 453f3d3..343b9ee 100644 --- a/skills/standards/SKILL.md +++ b/skills/standards/SKILL.md @@ -28,6 +28,7 @@ This file carries the SEP/CAP standards routing map. The other two live alongsid - Frontend SEP-7 / SEP-10 flows → `../dapp/SKILL.md` - CAPs for cryptography (BLS, BN254, Poseidon) → `../zk-proofs/SKILL.md` - x402/MPP protocol context → `../agentic-payments/SKILL.md` +- SEP-55 verified builds for mainnet contracts → `../smart-contracts/SKILL.md` --- @@ -56,7 +57,7 @@ Treat this file as a routing map, not a source of final governance/status truth. - [SEP-0048](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0048.md): Contract interface specification - [SEP-0049](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0049.md): Upgradeable-contract guidance - [SEP-0050](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0050.md): NFT standard work -- [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md): Contract build verification — a `source_repo` metadata entry in the WASM plus a GitHub build attestation let anyone trace a deployed contract hash back to the commit and workflow run that built it. Automate it with [soroban-build-workflow](https://github.com/stellar-expert/soroban-build-workflow); setup in [`../smart-contracts/SKILL.md`](../smart-contracts/SKILL.md#verify-your-build) +- [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md): Contract build verification - [SEP-0056](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0056.md): Vault-style tokenized products - [SEP-0057](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0057.md): Regulated token patterns (T-REX) From 3904affc97d561da85be3d35dd28167405f528b4 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Mon, 31 Aug 2026 14:49:46 +0000 Subject: [PATCH 3/8] Point at the current attestation doc and scope the private-repo caveat --- skills/smart-contracts/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/smart-contracts/SKILL.md b/skills/smart-contracts/SKILL.md index 1f62f51..8b9db74 100644 --- a/skills/smart-contracts/SKILL.md +++ b/skills/smart-contracts/SKILL.md @@ -188,7 +188,7 @@ To upload WASM without instantiating (e.g. for factories or upgrades), use `stel ## Verify your build -Nothing on-chain ties a deployed WASM to its source. [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md) (Draft) closes that gap: build in GitHub Actions, stamp the source repository into the WASM's `contractmetav0` section, and publish a [GitHub build attestation](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) over the binary — anyone can then walk from the on-chain WASM hash back to the commit and the workflow run that produced it. Verifiers read the attestation from `api.github.com/repos///attestations/sha256:` unauthenticated, so the source repository has to be public for that "anyone" to hold; on a private repo the provenance exists but no user can check it. Make it a standard step before **any** mainnet deploy; it costs one workflow file, so there's no reason to reserve it for user-facing contracts. +Nothing on-chain ties a deployed WASM to its source. [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md) (Draft) closes that gap: build in GitHub Actions, stamp the source repository into the WASM's `contractmetav0` section, and publish a [GitHub build attestation](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations) over the binary — anyone can then walk from the on-chain WASM hash back to the commit and the workflow run that produced it. Verifiers read the attestation from `api.github.com/repos///attestations/sha256:` unauthenticated, so the source repository has to be public for that "anyone" to hold. A private repo still gets an attestation, but only accounts with repository access can read it, so no user of your contract can. Make it a standard step before **any** mainnet deploy; it costs one workflow file, so there's no reason to reserve it for user-facing contracts. Two metadata entries carry it (`stellar contract build --meta key=value`, read back with `stellar contract info meta --wasm `): From db7690c6ac1de7c54132d7c70556c2a6bc577c99 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Mon, 31 Aug 2026 14:53:40 +0000 Subject: [PATCH 4/8] Make the version-resolution commands copy-pastable --- skills/smart-contracts/SKILL.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skills/smart-contracts/SKILL.md b/skills/smart-contracts/SKILL.md index 8b9db74..10c8e85 100644 --- a/skills/smart-contracts/SKILL.md +++ b/skills/smart-contracts/SKILL.md @@ -200,8 +200,9 @@ Two metadata entries carry it (`stellar contract build --meta key=value`, read b Resolve its version yourself instead of copying a pin out of any documentation, this file included. Pins go stale, and the newest tag in that repo is not always a published release: ```bash -gh release view --repo stellar-expert/soroban-build-workflow --json tagName # latest release -gh api repos/stellar-expert/soroban-build-workflow/commits/ --jq .sha # its commit +REPO=stellar-expert/soroban-build-workflow +TAG=$(gh release view --repo "$REPO" --json tagName --jq .tagName) # latest release +gh api "repos/$REPO/commits/$TAG" --jq .sha # its commit ``` Read that release's `release.yml` before wiring it in — it should check out the tagged commit, build from source, and attest the same file it uploads. If it doesn't, stop and tell the user rather than pinning it anyway. Then pin the full commit SHA, not the tag: tags are mutable, and this workflow runs with your repository token, OIDC, and attestation permissions. From 1a49d897075663bfd1714d0311d3930d37a85170 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Mon, 31 Aug 2026 14:55:55 +0000 Subject: [PATCH 5/8] State that the workflow pin does not cover the actions it calls --- evals/scenarios/smart-contracts/04-build-verification.json | 3 ++- skills/smart-contracts/SKILL.md | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/evals/scenarios/smart-contracts/04-build-verification.json b/evals/scenarios/smart-contracts/04-build-verification.json index 266e084..cd9b08a 100644 --- a/evals/scenarios/smart-contracts/04-build-verification.json +++ b/evals/scenarios/smart-contracts/04-build-verification.json @@ -9,6 +9,7 @@ "If it reuses soroban-build-workflow, resolves the current release itself and pins a full commit SHA rather than a version tag copied from documentation", "Grants contents: write for publishing the release and id-token: write plus attestations: write for the attestation, without presenting all three as attestation requirements", "Says to deploy the WASM attached to the release the workflow produced, because a local rebuild may not match the attested hash", - "States the limits: the source repo must be public to be checkable, and the attestation proves which workflow run and commit built the binary, not that the code is safe" + "States the limits: the source repo must be public to be checkable, and the attestation proves which workflow run and commit built the binary, not that the code is safe", + "Says the commit pin covers release.yml only, not the actions release.yml calls on mutable tags, rather than presenting the pin as closing the whole supply chain" ] } diff --git a/skills/smart-contracts/SKILL.md b/skills/smart-contracts/SKILL.md index 10c8e85..09dde63 100644 --- a/skills/smart-contracts/SKILL.md +++ b/skills/smart-contracts/SKILL.md @@ -207,6 +207,8 @@ gh api "repos/$REPO/commits/$TAG" --jq .sha # its commit Read that release's `release.yml` before wiring it in — it should check out the tagged commit, build from source, and attest the same file it uploads. If it doesn't, stop and tell the user rather than pinning it anyway. Then pin the full commit SHA, not the tag: tags are mutable, and this workflow runs with your repository token, OIDC, and attestation permissions. +Tell the user where that pin stops. It freezes `release.yml`, not the actions `release.yml` calls — `v25.1.0` reaches `actions/checkout@v4`, `stellar/stellar-cli@v25.1.0`, `actions/setup-node@v4` and `actions/attest-build-provenance@v1`, all mutable tags resolved at run time under the same permissions. A caller cannot pin those. Fork the workflow if that risk is unacceptable. + ```yaml # .github/workflows/release.yml — verified build on every version tag name: Build and Release From c78e37b07dd2cb43e3f2a4b7ecc9e3151a2b2af0 Mon Sep 17 00:00:00 2001 From: Kaan Kacar Date: Mon, 31 Aug 2026 14:56:23 +0000 Subject: [PATCH 6/8] Keep concrete versions out of the pin caveat --- skills/smart-contracts/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/smart-contracts/SKILL.md b/skills/smart-contracts/SKILL.md index 09dde63..e6c75ef 100644 --- a/skills/smart-contracts/SKILL.md +++ b/skills/smart-contracts/SKILL.md @@ -207,7 +207,7 @@ gh api "repos/$REPO/commits/$TAG" --jq .sha # its commit Read that release's `release.yml` before wiring it in — it should check out the tagged commit, build from source, and attest the same file it uploads. If it doesn't, stop and tell the user rather than pinning it anyway. Then pin the full commit SHA, not the tag: tags are mutable, and this workflow runs with your repository token, OIDC, and attestation permissions. -Tell the user where that pin stops. It freezes `release.yml`, not the actions `release.yml` calls — `v25.1.0` reaches `actions/checkout@v4`, `stellar/stellar-cli@v25.1.0`, `actions/setup-node@v4` and `actions/attest-build-provenance@v1`, all mutable tags resolved at run time under the same permissions. A caller cannot pin those. Fork the workflow if that risk is unacceptable. +Tell the user where that pin stops. It freezes `release.yml`, not the actions `release.yml` calls. Those sit on mutable tags of their own (`actions/checkout@v4`, the CLI, the attest action) and resolve at run time under the same permissions, and a caller cannot pin them. Fork the workflow if that residual risk is unacceptable. ```yaml # .github/workflows/release.yml — verified build on every version tag From a6eb6aa56c30196375bdf4a68ddfabb68c8e60f1 Mon Sep 17 00:00:00 2001 From: kaankacar Date: Mon, 31 Aug 2026 15:05:18 +0000 Subject: [PATCH 7/8] Scope the private-repo caveat to the plans that support attestations --- skills/smart-contracts/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/smart-contracts/SKILL.md b/skills/smart-contracts/SKILL.md index e6c75ef..f21cede 100644 --- a/skills/smart-contracts/SKILL.md +++ b/skills/smart-contracts/SKILL.md @@ -188,7 +188,7 @@ To upload WASM without instantiating (e.g. for factories or upgrades), use `stel ## Verify your build -Nothing on-chain ties a deployed WASM to its source. [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md) (Draft) closes that gap: build in GitHub Actions, stamp the source repository into the WASM's `contractmetav0` section, and publish a [GitHub build attestation](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations) over the binary — anyone can then walk from the on-chain WASM hash back to the commit and the workflow run that produced it. Verifiers read the attestation from `api.github.com/repos///attestations/sha256:` unauthenticated, so the source repository has to be public for that "anyone" to hold. A private repo still gets an attestation, but only accounts with repository access can read it, so no user of your contract can. Make it a standard step before **any** mainnet deploy; it costs one workflow file, so there's no reason to reserve it for user-facing contracts. +Nothing on-chain ties a deployed WASM to its source. [SEP-0055](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0055.md) (Draft) closes that gap: build in GitHub Actions, stamp the source repository into the WASM's `contractmetav0` section, and publish a [GitHub build attestation](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations) over the binary — anyone can then walk from the on-chain WASM hash back to the commit and the workflow run that produced it. Verifiers read the attestation from `api.github.com/repos///attestations/sha256:` unauthenticated, so the source repository has to be public for that "anyone" to hold. On the Free, Pro, and Team plans a private or internal repo cannot publish an attestation at all. Enterprise Cloud can, but only accounts with repository access read it, so no user of your contract can. Make it a standard step before **any** mainnet deploy; it costs one workflow file, so there's no reason to reserve it for user-facing contracts. Two metadata entries carry it (`stellar contract build --meta key=value`, read back with `stellar contract info meta --wasm `): From e6cfe919db32984d8bc58a53c3d4f2a78287d46e Mon Sep 17 00:00:00 2001 From: kaankacar Date: Mon, 31 Aug 2026 15:13:04 +0000 Subject: [PATCH 8/8] Review release.yml at the resolved SHA, not at the tag --- .../scenarios/smart-contracts/04-build-verification.json | 2 +- skills/smart-contracts/SKILL.md | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/evals/scenarios/smart-contracts/04-build-verification.json b/evals/scenarios/smart-contracts/04-build-verification.json index cd9b08a..3b80d76 100644 --- a/evals/scenarios/smart-contracts/04-build-verification.json +++ b/evals/scenarios/smart-contracts/04-build-verification.json @@ -6,7 +6,7 @@ "expected_behavior": [ "Points at SEP-55 build verification — source_repo metadata in the WASM plus a GitHub build attestation — rather than improvising a scheme like publishing the hash in the README", "Names either stellar contract build --meta source_repo=github:/ or the soroban-build-workflow reusable workflow", - "If it reuses soroban-build-workflow, resolves the current release itself and pins a full commit SHA rather than a version tag copied from documentation", + "If it reuses soroban-build-workflow, resolves the current release itself, reads release.yml at that commit rather than at the tag, and pins the full commit SHA rather than a version tag copied from documentation", "Grants contents: write for publishing the release and id-token: write plus attestations: write for the attestation, without presenting all three as attestation requirements", "Says to deploy the WASM attached to the release the workflow produced, because a local rebuild may not match the attested hash", "States the limits: the source repo must be public to be checkable, and the attestation proves which workflow run and commit built the binary, not that the code is safe", diff --git a/skills/smart-contracts/SKILL.md b/skills/smart-contracts/SKILL.md index f21cede..f725145 100644 --- a/skills/smart-contracts/SKILL.md +++ b/skills/smart-contracts/SKILL.md @@ -202,10 +202,12 @@ Resolve its version yourself instead of copying a pin out of any documentation, ```bash REPO=stellar-expert/soroban-build-workflow TAG=$(gh release view --repo "$REPO" --json tagName --jq .tagName) # latest release -gh api "repos/$REPO/commits/$TAG" --jq .sha # its commit +SHA=$(gh api "repos/$REPO/commits/$TAG" --jq .sha) # its commit +gh api "repos/$REPO/contents/.github/workflows/release.yml?ref=$SHA" \ + -H "Accept: application/vnd.github.raw" # the file you will pin ``` -Read that release's `release.yml` before wiring it in — it should check out the tagged commit, build from source, and attest the same file it uploads. If it doesn't, stop and tell the user rather than pinning it anyway. Then pin the full commit SHA, not the tag: tags are mutable, and this workflow runs with your repository token, OIDC, and attestation permissions. +Read `release.yml` at that SHA, never at the tag. A tag can move between the two steps, and then you review one commit and pin another. It should check out the tagged commit, build from source, and attest the same file it uploads. If it doesn't, stop and tell the user rather than pinning it anyway. Then pin that full SHA, because this workflow runs with your repository token, OIDC, and attestation permissions. Tell the user where that pin stops. It freezes `release.yml`, not the actions `release.yml` calls. Those sit on mutable tags of their own (`actions/checkout@v4`, the CLI, the attest action) and resolve at run time under the same permissions, and a caller cannot pin them. Fork the workflow if that residual risk is unacceptable. @@ -223,7 +225,7 @@ permissions: # inherited by the called workflow, which cannot elevate jobs: release: - # full SHA of the release you just reviewed; keep the version in the comment + # the SHA you resolved and reviewed; keep the version in the comment uses: stellar-expert/soroban-build-workflow/.github/workflows/release.yml@ # vX.Y.Z with: release_name: ${{ github.ref_name }}