From 62b03332676eec75f140ae7527c12337f41cdeed Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 21:14:53 +0000 Subject: [PATCH 1/9] ci: use draft releases to support immutable GitHub releases --- .github/workflows/manual-publish.yml | 23 +++++++------ .github/workflows/release-please.yml | 51 +++++++++++++++++++++++----- release-please-config.json | 1 + 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index 50fde11..76f69d5 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -14,6 +14,7 @@ jobs: permissions: id-token: write contents: read + attestations: write # Needed for actions/attest outputs: gem-hash: ${{ steps.publish.outputs.gem-hash}} steps: @@ -37,13 +38,15 @@ jobs: with: dry_run: ${{ inputs.dry_run }} - release-provenance: - needs: [ 'build-publish' ] - permissions: - actions: read - id-token: write - contents: write - uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0 - with: - base64-subjects: "${{ needs.build-publish.outputs.gem-hash }}" - upload-assets: ${{ !inputs.dry_run }} + - name: Generate checksums file + if: ${{ !inputs.dry_run }} + env: + HASHES: ${{ steps.publish.outputs.gem-hash }} + run: | + echo "$HASHES" | base64 -d > checksums.txt + + - name: Attest build provenance + if: ${{ !inputs.dry_run }} + uses: actions/attest@v4 + with: + subject-checksums: checksums.txt diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 7d6fc2c..92d68dd 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -12,6 +12,7 @@ jobs: id-token: write # Needed if using OIDC to get release secrets. contents: write # Contents and pull-requests are for release-please to make releases. pull-requests: write + attestations: write # Needed for actions/attest outputs: release-created: ${{ steps.release.outputs.release_created }} upload-tag-name: ${{ steps.release.outputs.tag_name }} @@ -25,6 +26,22 @@ jobs: with: fetch-depth: 0 # Full history is required for proper changelog generation + - name: Create release tag + if: ${{ steps.release.outputs.releases_created == 'true' }} + env: + TAG_NAME: ${{ steps.release.outputs.tag_name }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if gh api "repos/${{ github.repository }}/git/ref/tags/${TAG_NAME}" >/dev/null 2>&1; then + echo "Tag ${TAG_NAME} already exists, skipping creation." + else + echo "Creating tag ${TAG_NAME}." + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag "${TAG_NAME}" + git push origin "${TAG_NAME}" + fi + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0 if: ${{ steps.release.outputs.releases_created == 'true' }} name: 'Get rubygems API key' @@ -51,15 +68,31 @@ jobs: with: token: ${{secrets.GITHUB_TOKEN}} - release-provenance: - needs: [ 'release-package' ] + - name: Generate checksums file + if: ${{ steps.release.outputs.releases_created == 'true' }} + env: + HASHES: ${{ steps.publish.outputs.gem-hash }} + run: | + echo "$HASHES" | base64 -d > checksums.txt + + - name: Attest build provenance + if: ${{ steps.release.outputs.releases_created == 'true' }} + uses: actions/attest@v4 + with: + subject-checksums: checksums.txt + + publish-release: + needs: ['release-package'] if: ${{ needs.release-package.outputs.release-created == 'true' }} + runs-on: ubuntu-latest permissions: - actions: read - id-token: write contents: write - uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0 - with: - base64-subjects: "${{ needs.release-package.outputs.gem-hash }}" - upload-assets: true - upload-tag-name: ${{ needs.release-package.outputs.upload-tag-name }} + steps: + - name: Publish release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: ${{ needs.release-package.outputs.upload-tag-name }} + run: > + gh release edit "$TAG_NAME" + --repo ${{ github.repository }} + --draft=false diff --git a/release-please-config.json b/release-please-config.json index 4eedbc1..d09c561 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -6,6 +6,7 @@ "versioning": "default", "include-component-in-tag": false, "include-v-in-tag": false, + "draft": true, "extra-files": ["PROVENANCE.md", "lib/ldclient-otel/version.rb"] } } From 4884b204051462743c06f227f514254a2d104ea2 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 21:42:17 +0000 Subject: [PATCH 2/9] ci: add force-tag-creation and publish_release option --- .github/workflows/manual-publish.yml | 25 +++++++++++++++++++++++++ release-please-config.json | 6 +++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index 76f69d5..ec65db5 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -6,6 +6,15 @@ on: description: 'Is this a dry run. If so no package will be published.' type: boolean required: true + tag: + description: 'Tag of an existing draft release to upload artifacts to.' + type: string + required: false + publish_release: + description: 'Publish (un-draft) the release after all artifacts are uploaded?' + type: boolean + required: false + default: true jobs: build-publish: @@ -50,3 +59,19 @@ jobs: uses: actions/attest@v4 with: subject-checksums: checksums.txt + + publish-release: + needs: ['build-publish'] + if: ${{ !inputs.dry_run && inputs.publish_release }} + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Publish release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: ${{ inputs.tag }} + run: > + gh release edit "$TAG_NAME" + --repo ${{ github.repository }} + --draft=false diff --git a/release-please-config.json b/release-please-config.json index d09c561..8c001c5 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -7,7 +7,11 @@ "include-component-in-tag": false, "include-v-in-tag": false, "draft": true, - "extra-files": ["PROVENANCE.md", "lib/ldclient-otel/version.rb"] + "force-tag-creation": true, + "extra-files": [ + "PROVENANCE.md", + "lib/ldclient-otel/version.rb" + ] } } } From e06bec127cb03ebc8de3201cd377e2ea8a1e1b12 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 22:06:51 +0000 Subject: [PATCH 3/9] ci: simplify for attestation-only releases (no draft needed) Since actions/attest@v4 stores attestations via GitHub's attestation API (not as release assets), repos that only use attestation don't need draft releases. Release-please can publish the release directly. Changes: - Remove draft:true from release-please-config.json - Remove create-tag job/steps (force-tag-creation handles this) - Remove publish-release job (release is published directly) - Remove publish_release input from manual workflows --- .github/workflows/manual-publish.yml | 21 ------------------ .github/workflows/release-please.yml | 32 ---------------------------- release-please-config.json | 1 - 3 files changed, 54 deletions(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index ec65db5..7384878 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -10,11 +10,6 @@ on: description: 'Tag of an existing draft release to upload artifacts to.' type: string required: false - publish_release: - description: 'Publish (un-draft) the release after all artifacts are uploaded?' - type: boolean - required: false - default: true jobs: build-publish: @@ -59,19 +54,3 @@ jobs: uses: actions/attest@v4 with: subject-checksums: checksums.txt - - publish-release: - needs: ['build-publish'] - if: ${{ !inputs.dry_run && inputs.publish_release }} - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Publish release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG_NAME: ${{ inputs.tag }} - run: > - gh release edit "$TAG_NAME" - --repo ${{ github.repository }} - --draft=false diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 92d68dd..91eed3c 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -26,22 +26,6 @@ jobs: with: fetch-depth: 0 # Full history is required for proper changelog generation - - name: Create release tag - if: ${{ steps.release.outputs.releases_created == 'true' }} - env: - TAG_NAME: ${{ steps.release.outputs.tag_name }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if gh api "repos/${{ github.repository }}/git/ref/tags/${TAG_NAME}" >/dev/null 2>&1; then - echo "Tag ${TAG_NAME} already exists, skipping creation." - else - echo "Creating tag ${TAG_NAME}." - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git tag "${TAG_NAME}" - git push origin "${TAG_NAME}" - fi - - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0 if: ${{ steps.release.outputs.releases_created == 'true' }} name: 'Get rubygems API key' @@ -80,19 +64,3 @@ jobs: uses: actions/attest@v4 with: subject-checksums: checksums.txt - - publish-release: - needs: ['release-package'] - if: ${{ needs.release-package.outputs.release-created == 'true' }} - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Publish release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG_NAME: ${{ needs.release-package.outputs.upload-tag-name }} - run: > - gh release edit "$TAG_NAME" - --repo ${{ github.repository }} - --draft=false diff --git a/release-please-config.json b/release-please-config.json index 8c001c5..369563d 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -6,7 +6,6 @@ "versioning": "default", "include-component-in-tag": false, "include-v-in-tag": false, - "draft": true, "force-tag-creation": true, "extra-files": [ "PROVENANCE.md", From 0be9c9e750f11830ae5983fef5daf7920c19e0d6 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 22:33:44 +0000 Subject: [PATCH 4/9] ci: remove force-tag-creation from attestation-only repo force-tag-creation only operates in conjunction with draft releases. Since this repo does not use draft releases (attestation-only, no artifact uploads to the release), force-tag-creation is not needed. --- release-please-config.json | 1 - 1 file changed, 1 deletion(-) diff --git a/release-please-config.json b/release-please-config.json index 369563d..7a1bf9e 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -6,7 +6,6 @@ "versioning": "default", "include-component-in-tag": false, "include-v-in-tag": false, - "force-tag-creation": true, "extra-files": [ "PROVENANCE.md", "lib/ldclient-otel/version.rb" From f3518a5dd9aee389caae128a65bf27e9acc3820a Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 23:12:17 +0000 Subject: [PATCH 5/9] ci: switch from subject-checksums to subject-path for attestation --- .github/actions/publish/action.yml | 10 ---------- .github/workflows/manual-publish.yml | 11 +---------- .github/workflows/release-please.yml | 10 +--------- 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/.github/actions/publish/action.yml b/.github/actions/publish/action.yml index 275c2d9..a9c9483 100644 --- a/.github/actions/publish/action.yml +++ b/.github/actions/publish/action.yml @@ -4,10 +4,6 @@ inputs: dry_run: description: 'Is this a dry run. If so no package will be published.' required: true -outputs: - gem-hash: - description: "base64-encoded sha256 hashes of distribution files" - value: ${{ steps.gem-hash.outputs.gem-hash }} runs: using: composite @@ -16,12 +12,6 @@ runs: shell: bash run: gem build launchdarkly-server-sdk-otel.gemspec - - name: Hash gem for provenance - id: gem-hash - shell: bash - run: | - echo "gem-hash=$(sha256sum launchdarkly-server-sdk-otel-*.gem | base64 -w0)" >> "$GITHUB_OUTPUT" - - name: Publish Library shell: bash if: ${{ inputs.dry_run == 'false' }} diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index 7384878..bd6c823 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -19,8 +19,6 @@ jobs: id-token: write contents: read attestations: write # Needed for actions/attest - outputs: - gem-hash: ${{ steps.publish.outputs.gem-hash}} steps: - uses: actions/checkout@v4 @@ -42,15 +40,8 @@ jobs: with: dry_run: ${{ inputs.dry_run }} - - name: Generate checksums file - if: ${{ !inputs.dry_run }} - env: - HASHES: ${{ steps.publish.outputs.gem-hash }} - run: | - echo "$HASHES" | base64 -d > checksums.txt - - name: Attest build provenance if: ${{ !inputs.dry_run }} uses: actions/attest@v4 with: - subject-checksums: checksums.txt + subject-path: 'launchdarkly-server-sdk-otel-*.gem' diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 91eed3c..8073970 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -16,7 +16,6 @@ jobs: outputs: release-created: ${{ steps.release.outputs.release_created }} upload-tag-name: ${{ steps.release.outputs.tag_name }} - gem-hash: ${{ steps.publish.outputs.gem-hash}} steps: - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4 id: release @@ -52,15 +51,8 @@ jobs: with: token: ${{secrets.GITHUB_TOKEN}} - - name: Generate checksums file - if: ${{ steps.release.outputs.releases_created == 'true' }} - env: - HASHES: ${{ steps.publish.outputs.gem-hash }} - run: | - echo "$HASHES" | base64 -d > checksums.txt - - name: Attest build provenance if: ${{ steps.release.outputs.releases_created == 'true' }} uses: actions/attest@v4 with: - subject-checksums: checksums.txt + subject-path: 'launchdarkly-server-sdk-otel-*.gem' From c16576acb77f0ff16f9e566c30c33bb1dd789e7e Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Wed, 1 Apr 2026 16:34:26 +0000 Subject: [PATCH 6/9] ci: remove unused tag input and orphaned job outputs --- .github/workflows/manual-publish.yml | 4 ---- .github/workflows/release-please.yml | 3 --- 2 files changed, 7 deletions(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index bd6c823..647418f 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -6,10 +6,6 @@ on: description: 'Is this a dry run. If so no package will be published.' type: boolean required: true - tag: - description: 'Tag of an existing draft release to upload artifacts to.' - type: string - required: false jobs: build-publish: diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 8073970..070d20e 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -13,9 +13,6 @@ jobs: contents: write # Contents and pull-requests are for release-please to make releases. pull-requests: write attestations: write # Needed for actions/attest - outputs: - release-created: ${{ steps.release.outputs.release_created }} - upload-tag-name: ${{ steps.release.outputs.tag_name }} steps: - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4 id: release From b5470763733fab520a5693c20ed3612f1a431e23 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Wed, 1 Apr 2026 19:08:03 +0000 Subject: [PATCH 7/9] ci: use format() for dry_run conditions to handle both string and boolean inputs --- .github/workflows/manual-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index 647418f..ae70951 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -37,7 +37,7 @@ jobs: dry_run: ${{ inputs.dry_run }} - name: Attest build provenance - if: ${{ !inputs.dry_run }} + if: ${{ format('{0}', inputs.dry_run) == 'false' }} uses: actions/attest@v4 with: subject-path: 'launchdarkly-server-sdk-otel-*.gem' From 6577c65a8640674d8e61a1ae04a6de6eddc23a64 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Wed, 1 Apr 2026 21:51:29 +0000 Subject: [PATCH 8/9] docs: update PROVENANCE.md and README.md for GitHub artifact attestations --- PROVENANCE.md | 46 ++++++++++++++++++++++++++-------------------- README.md | 4 ++-- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/PROVENANCE.md b/PROVENANCE.md index 08e24fe..3b641de 100644 --- a/PROVENANCE.md +++ b/PROVENANCE.md @@ -1,14 +1,14 @@ -## Verifying SDK build provenance with the SLSA framework +## Verifying SDK build provenance with GitHub artifact attestations -LaunchDarkly uses the [SLSA framework](https://slsa.dev/spec/v1.0/about) (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages. +LaunchDarkly uses [GitHub artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published SDK packages. -As part of [SLSA requirements for level 3 compliance](https://slsa.dev/spec/v1.0/requirements), LaunchDarkly publishes provenance about our SDK package builds using [GitHub's generic SLSA3 provenance generator](https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/generic/README.md#generation-of-slsa3-provenance-for-arbitrary-projects) for distribution alongside our packages. These attestations are available for download from the GitHub release page for the release version under Assets > `multiple-provenance.intoto.jsonl`. +LaunchDarkly publishes provenance about our SDK package builds using [GitHub's `actions/attest` action](https://github.com/actions/attest). These attestations are stored in GitHub's attestation API and can be verified using the [GitHub CLI](https://cli.github.com/). -To verify SLSA provenance attestations, we recommend using [slsa-verifier](https://github.com/slsa-framework/slsa-verifier). Example usage for verifying SDK packages is included below: +To verify build provenance attestations, we recommend using the [GitHub CLI `attestation verify` command](https://cli.github.com/manual/gh_attestation_verify). Example usage for verifying SDK packages is included below: ``` -# Set the version of the SDK to verify +# Set the version of the library to verify VERSION=1.1.1 ``` @@ -17,27 +17,33 @@ VERSION=1.1.1 # Download gem $ gem fetch launchdarkly-server-sdk-otel -v $VERSION -# Download provenance from Github release -$ curl --location -O \ - https://github.com/launchdarkly/ruby-server-sdk-otel/releases/download/${VERSION}/launchdarkly-server-sdk-otel-${VERSION}.gem.intoto.jsonl - -# Run slsa-verifier to verify provenance against package artifacts -$ slsa-verifier verify-artifact \ ---provenance-path launchdarkly-server-sdk-otel-${VERSION}.gem.intoto.jsonl \ ---source-uri github.com/launchdarkly/ruby-server-sdk-otel \ -launchdarkly-server-sdk-otel-${VERSION}.gem +# Verify provenance using the GitHub CLI +$ gh attestation verify launchdarkly-server-sdk-otel-${VERSION}.gem --owner launchdarkly ``` Below is a sample of expected output. ``` -Verified signature against tlog entry index 83653185 at URL: https://rekor.sigstore.dev/api/v1/log/entries/24296fb24b8ad77a7df0bbf87a7d5fcaafa551a2101d9f993d251a56a918bb113e81d2c575dc7e25 -Verified build using builder "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.10.0" at commit 14c48a68c45871c27409591969e7f4c0ebdcdf62 -Verifying artifact launchdarkly-server-sdk-otel-1.0.0.gem: PASSED +Loaded digest sha256:... for file://launchdarkly-server-sdk-otel-1.1.1.gem +Loaded 1 attestation from GitHub API + +The following policy criteria will be enforced: +- Predicate type must match:................ https://slsa.dev/provenance/v1 +- Source Repository Owner URI must match:... https://github.com/launchdarkly +- Subject Alternative Name must match regex: (?i)^https://github.com/launchdarkly/ +- OIDC Issuer must match:................... https://token.actions.githubusercontent.com + +✓ Verification succeeded! + +The following 1 attestation matched the policy criteria -PASSED: Verified SLSA provenance +- Attestation #1 + - Build repo:..... launchdarkly/ruby-server-sdk-otel + - Build workflow:. .github/workflows/release-please.yml + - Signer repo:.... launchdarkly/ruby-server-sdk-otel + - Signer workflow: .github/workflows/release-please.yml ``` -Alternatively, to verify the provenance manually, the SLSA framework specifies [recommendations for verifying build artifacts](https://slsa.dev/spec/v1.0/verifying-artifacts) in their documentation. +For more information, see [GitHub's documentation on verifying artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds#verifying-artifact-attestations-with-the-github-cli). -**Note:** These instructions do not apply when building our libraries from source. +**Note:** These instructions do not apply when building our libraries from source. diff --git a/README.md b/README.md index 4dc232a..c4fb936 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,10 @@ Contributing We encourage pull requests and other contributions from the community. Check out our [contributing guidelines](CONTRIBUTING.md) for instructions on how to contribute to this library. -Verifying library build provenance with the SLSA framework +Verifying library build provenance with GitHub artifact attestations ------------ -LaunchDarkly uses the [SLSA framework](https://slsa.dev/spec/v1.0/about) (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published library packages. To learn more, see the [provenance guide](PROVENANCE.md). +LaunchDarkly uses [GitHub artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published library packages. To learn more, see the [provenance guide](PROVENANCE.md). About LaunchDarkly ----------- From c457cc84aade42ee9641399a4acbbb8c02d7d539 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Wed, 1 Apr 2026 22:11:54 +0000 Subject: [PATCH 9/9] docs: restore original SLSA framework text in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c4fb936..4dc232a 100644 --- a/README.md +++ b/README.md @@ -57,10 +57,10 @@ Contributing We encourage pull requests and other contributions from the community. Check out our [contributing guidelines](CONTRIBUTING.md) for instructions on how to contribute to this library. -Verifying library build provenance with GitHub artifact attestations +Verifying library build provenance with the SLSA framework ------------ -LaunchDarkly uses [GitHub artifact attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published library packages. To learn more, see the [provenance guide](PROVENANCE.md). +LaunchDarkly uses the [SLSA framework](https://slsa.dev/spec/v1.0/about) (Supply-chain Levels for Software Artifacts) to help developers make their supply chain more secure by ensuring the authenticity and build integrity of our published library packages. To learn more, see the [provenance guide](PROVENANCE.md). About LaunchDarkly -----------