Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
#
# SPDX-License-Identifier: Apache-2.0

[alias]
check-wasm = "check --workspace --target wasm32-unknown-unknown --no-default-features --features wasm"
23 changes: 23 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
#
# SPDX-License-Identifier: Apache-2.0

version: 2
updates:
- package-ecosystem: cargo
directory: /
schedule:
interval: weekly
day: monday
time: "04:00"
timezone: Europe/Copenhagen
open-pull-requests-limit: 10

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
time: "04:00"
timezone: Europe/Copenhagen
open-pull-requests-limit: 10
224 changes: 197 additions & 27 deletions .github/workflows/crates-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,56 +7,226 @@ name: Crates.io Release
on:
workflow_dispatch:
inputs:
publish:
description: Publish reallyme-cose to crates.io after the dry run succeeds
preflight_run_id:
description: Successful Release Preflight workflow run ID that reviewed this release
required: true
type: boolean
default: false
type: string
release_sha:
description: Exact current main commit SHA certified by the preflight run
required: true
type: string
version:
description: Exact crate version certified by the preflight run
required: true
type: string

concurrency:
group: crates-release-${{ inputs.version }}-${{ inputs.release_sha }}
cancel-in-progress: false

permissions:
actions: read
contents: read

jobs:
dry-run:
name: crates.io publish preflight
runs-on: ubuntu-latest
permissions:
contents: read
verify-preflight:
name: verify reviewed preflight
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
outputs:
release_sha: ${{ steps.verify-attestation.outputs.release_sha }}
version: ${{ steps.verify-attestation.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Verify requested release identity and preflight run
shell: bash
env:
GH_TOKEN: ${{ github.token }}
PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }}
RELEASE_SHA: ${{ inputs.release_sha }}
RELEASE_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
if [[ ! "$PREFLIGHT_RUN_ID" =~ ^[1-9][0-9]*$ ]]; then
echo "preflight_run_id must be a positive integer" >&2
exit 1
fi
if [[ ! "$RELEASE_SHA" =~ ^[0-9a-f]{40}$ ]]; then
echo "release_sha must be a lowercase 40-character Git SHA" >&2
exit 1
fi
if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "version must be a stable semantic version" >&2
exit 1
fi
if [[ "$GITHUB_SHA" != "$RELEASE_SHA" ]]; then
echo "release_sha does not match the release workflow head" >&2
exit 1
fi
current_main_sha="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/heads/main" --jq '.object.sha')"
if [[ "$current_main_sha" != "$RELEASE_SHA" ]]; then
echo "release_sha is not the current origin/main commit" >&2
exit 1
fi
expected_workflow_id="$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/release-preflight.yml" --jq '.id')"
run_json="$(gh api "repos/$GITHUB_REPOSITORY/actions/runs/$PREFLIGHT_RUN_ID")"
if ! jq --exit-status \
--arg release_sha "$RELEASE_SHA" \
--argjson workflow_id "$expected_workflow_id" \
'
.workflow_id == $workflow_id and
.event == "workflow_dispatch" and
.head_branch == "main" and
.head_sha == $release_sha and
.status == "completed" and
.conclusion == "success" and
.run_attempt == 1 and
(.path | startswith(".github/workflows/release-preflight.yml@"))
' <<< "$run_json" > /dev/null; then
echo "preflight run did not successfully certify the requested release commit" >&2
exit 1
fi

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master
- name: Download reviewed preflight attestation
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
toolchain: 1.96.0

- name: Cache Cargo
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
name: reallyme-cose-crates-preflight-${{ inputs.version }}-${{ inputs.release_sha }}
path: release-attestation
github-token: ${{ github.token }}
repository: ${{ github.repository }}
run-id: ${{ inputs.preflight_run_id }}

- name: Inspect publishable crate tarballs
run: node scripts/publish_crates_in_order.mjs inspect
- name: Verify reviewed preflight inputs
id: verify-attestation
shell: bash
env:
PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }}
RELEASE_SHA: ${{ inputs.release_sha }}
RELEASE_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
attestation="release-attestation/crates-preflight.json"
if [[ ! -f "$attestation" ]]; then
echo "reviewed preflight attestation is missing" >&2
exit 1
fi
if ! jq --exit-status \
--arg repository "$GITHUB_REPOSITORY" \
--argjson run_id "$PREFLIGHT_RUN_ID" \
--arg release_sha "$RELEASE_SHA" \
--arg version "$RELEASE_VERSION" \
'
.schema == "reallyme.cose.crates_preflight.v1" and
.repository == $repository and
.workflow == "release-preflight.yml" and
.run_id == $run_id and
.run_attempt == 1 and
.release_sha == $release_sha and
.version == $version
' "$attestation" > /dev/null; then
echo "preflight inputs do not match the requested release" >&2
exit 1
fi
printf 'release_sha=%s\n' "$RELEASE_SHA" >> "$GITHUB_OUTPUT"
printf 'version=%s\n' "$RELEASE_VERSION" >> "$GITHUB_OUTPUT"

publish:
name: publish reallyme-cose
needs: dry-run
if: inputs.publish == true
runs-on: ubuntu-latest
name: publish reallyme-cose crates
needs: verify-preflight
runs-on: ubuntu-24.04
environment: crates-io-release
permissions:
contents: read
env:
RELEASE_SHA: ${{ needs.verify-preflight.outputs.release_sha }}
RELEASE_VERSION: ${{ needs.verify-preflight.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
- name: Checkout certified release commit
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ needs.verify-preflight.outputs.release_sha }}
fetch-depth: 0
persist-credentials: false

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master
with:
toolchain: 1.96.0

- name: Cache Cargo
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

- name: Install Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'

- name: Revalidate origin main before publishing
shell: bash
run: |
set -euo pipefail
if [[ "$GITHUB_SHA" != "$RELEASE_SHA" || "$(git rev-parse HEAD)" != "$RELEASE_SHA" ]]; then
echo "certified release commit no longer matches the workflow checkout" >&2
exit 1
fi
git fetch --force --no-tags origin main:refs/remotes/origin/main
if [[ "$(git rev-parse refs/remotes/origin/main)" != "$RELEASE_SHA" ]]; then
echo "origin/main moved after preflight review; refusing publication" >&2
exit 1
fi
cose_version="$(sed -nE 's/^version = "([^"]+)"$/\1/p' crates/cose/Cargo.toml)"
proto_version="$(sed -nE 's/^version = "([^"]+)"$/\1/p' crates/proto/Cargo.toml)"
if [[ "$cose_version" != "$RELEASE_VERSION" || "$proto_version" != "$RELEASE_VERSION" ]]; then
echo "certified version no longer matches both publishable crate manifests" >&2
exit 1
fi

- name: Publish reallyme-cose
- name: Publish crates in dependency order
run: node scripts/publish_crates_in_order.mjs publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

finalize:
name: tag and create GitHub release
needs: [verify-preflight, publish]
runs-on: ubuntu-24.04
permissions:
contents: write
env:
RELEASE_SHA: ${{ needs.verify-preflight.outputs.release_sha }}
RELEASE_VERSION: ${{ needs.verify-preflight.outputs.version }}
steps:
- name: Create release tag through the GitHub API
id: release-tag
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="reallyme-cose-v${RELEASE_VERSION}"
existing_commit="$(gh api "repos/$GITHUB_REPOSITORY/commits/$tag" --jq '.sha' 2>/dev/null || true)"
if [[ -n "$existing_commit" && "$existing_commit" != "$RELEASE_SHA" ]]; then
echo "$tag already exists at $existing_commit, not $RELEASE_SHA" >&2
exit 1
fi
if [[ -z "$existing_commit" ]]; then
gh api --method POST "repos/$GITHUB_REPOSITORY/git/refs" \
--field ref="refs/tags/$tag" \
--field sha="$RELEASE_SHA" > /dev/null
fi
printf 'tag=%s\n' "$tag" >> "$GITHUB_OUTPUT"

- name: Create GitHub release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ steps.release-tag.outputs.tag }}
run: |
set -euo pipefail
if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
exit 0
fi
gh release create "$RELEASE_TAG" \
--repo "$GITHUB_REPOSITORY" \
--verify-tag \
--title "$RELEASE_TAG" \
--generate-notes
50 changes: 37 additions & 13 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
- "docs/**"
- "LICENSE"
- "NOTICE"
- ".github/renovate.json"
- ".github/dependabot.yml"
push:
branches:
- main
Expand All @@ -20,35 +20,46 @@ on:
- "docs/**"
- "LICENSE"
- "NOTICE"
- ".github/renovate.json"
- ".github/dependabot.yml"
schedule:
- cron: "41 3 * * 1"
workflow_dispatch:

permissions:
contents: read

env:
CARGO_FUZZ_VERSION: 0.13.2
NIGHTLY_TOOLCHAIN: nightly-2026-07-01
FUZZ_MAX_TOTAL_TIME_SECONDS: 900

jobs:
build:
name: fuzz target build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Install Rust nightly
uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master
with:
toolchain: nightly
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}

- name: Cache Cargo
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

- name: Install cargo-fuzz
run: cargo install cargo-fuzz --locked
run: cargo install cargo-fuzz --version "$CARGO_FUZZ_VERSION" --locked

- name: Verify fuzz lockfile is current
run: cargo metadata --manifest-path fuzz/Cargo.toml --locked --no-deps

- name: Build fuzz targets
run: cargo +nightly fuzz build
run: cargo +nightly-2026-07-01 fuzz build

- name: Verify fuzz build did not rewrite the lockfile
run: git diff --exit-code -- fuzz/Cargo.lock

scheduled:
name: scheduled fuzz (${{ matrix.target }})
Expand All @@ -61,27 +72,40 @@ jobs:
- cose_sign1
- cose_key
- multikey_to_cose
- wire
- cose_encrypt
steps:
- name: Checkout
uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Install Rust nightly
uses: dtolnay/rust-toolchain@fa04a1451ff1842e2626ccb99004d0195b455a88 # master
with:
toolchain: nightly
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}

- name: Cache Cargo
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1

- name: Install cargo-fuzz
run: cargo install cargo-fuzz --locked
run: cargo install cargo-fuzz --version "$CARGO_FUZZ_VERSION" --locked

- name: Verify fuzz lockfile is current
run: cargo metadata --manifest-path fuzz/Cargo.toml --locked --no-deps

- name: Restore and persist fuzz corpus
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
with:
path: fuzz/corpus/${{ matrix.target }}
key: fuzz-corpus-v1-${{ runner.os }}-${{ matrix.target }}-${{ github.run_id }}
restore-keys: |
fuzz-corpus-v1-${{ runner.os }}-${{ matrix.target }}-

- name: Run time-boxed fuzz target
run: cargo +nightly fuzz run ${{ matrix.target }} -- -max_total_time=300 -rss_limit_mb=4096
run: cargo +nightly-2026-07-01 fuzz run ${{ matrix.target }} -- -max_total_time="$FUZZ_MAX_TOTAL_TIME_SECONDS" -rss_limit_mb=4096

- name: Upload crash artifacts
if: failure()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: fuzz-artifacts-${{ matrix.target }}
path: fuzz/artifacts/${{ matrix.target }}
Expand Down
Loading