Skip to content
Merged
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
92 changes: 92 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: CI

# This repository had no automated checks. Two releases' worth of a credential
# library - including `authority::verify_chain`, which is the part that decides
# whether a holder acquired authority they were never granted - merged on local
# runs alone. That is the gap this closes.

on:
push:
branches: [main]
pull_request:

env:
CARGO_TERM_COLOR: always

jobs:
fmt:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all --check

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
# affinidi-tdk (a dev-dependency) reaches the OS keyring, which links
# dbus and pcsclite. Only the jobs that build dev-dependencies need these
# — `cargo check` and `cargo package` do not, and stay lean.
- name: System dependencies
run: sudo apt-get update && sudo apt-get install -y libpcsclite-dev libdbus-1-dev
- run: cargo clippy --all-targets --all-features -- -D warnings

test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# affinidi-tdk (a dev-dependency) reaches the OS keyring, which links
# dbus and pcsclite. Only the jobs that build dev-dependencies need these
# — `cargo check` and `cargo package` do not, and stay lean.
- name: System dependencies
run: sudo apt-get update && sudo apt-get install -y libpcsclite-dev libdbus-1-dev
- run: cargo test --all-features

# The signing backend is optional and `default = ["affinidi-signing"]`, so the
# default build never exercises the feature-off path. A consumer who disables
# default features is the one who finds out.
no-default-features:
name: No default features
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# affinidi-tdk (a dev-dependency) reaches the OS keyring, which links
# dbus and pcsclite. Only the jobs that build dev-dependencies need these
# — `cargo check` and `cargo package` do not, and stay lean.
- name: System dependencies
run: sudo apt-get update && sudo apt-get install -y libpcsclite-dev libdbus-1-dev
- run: cargo test --no-default-features

msrv:
name: Minimum Supported Rust Version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@1.95.0
- run: cargo check --all-features

# A crate that cannot be packaged cannot be released, and finding that out at
# tag time means the tag is already wrong. `--locked` is deliberate: it is what
# the publish job uses, so a lockfile that has drifted fails here first.
package:
name: Package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo package --locked
78 changes: 78 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: Publish

# Publish `dtg-credentials` to crates.io on a `vX.Y.Z` tag push.
#
# This repository had no release path at all: every version on crates.io got
# there by someone running `cargo publish` from a laptop. That is why 0.6.0 -
# the release carrying the VAC and VDC, which the data-rooms work in
# `verifiable-trust-infrastructure` depends on - sat merged and unpublished
# while the consumer could not compile against it. A tag is now the release.
#
# The trigger is the tag push rather than `release: published` on purpose: a
# Release created with the default GITHUB_TOKEN does not cascade-trigger other
# workflows, so a `release:` handler would silently never fire.
#
# Auth is crates.io Trusted Publishing (OIDC), so there is no long-lived token
# in this repository. ONE-TIME SETUP, on crates.io under the crate's Settings ->
# Trusted Publishing: add owner `OpenVTC`, repository `dtg-credentials`,
# workflow `publish.yml`. Until that exists the job fails at the auth step with
# a message naming exactly this, which is the right failure - better than a
# token sitting in the repo for the one day a year it is used.
#
# The run is idempotent: a tag re-pushed after a failed release job finds the
# crate already on crates.io at that version and skips rather than dying on
# "crate version already uploaded", which would otherwise force a version bump
# nothing needed.
on:
push:
tags: ["v*.*.*"]
workflow_dispatch:

permissions:
id-token: write # OIDC token for crates.io Trusted Publishing
contents: read

jobs:
publish:
name: Publish to crates.io
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: dtolnay/rust-toolchain@stable

# The tag says one version and Cargo.toml says another exactly once - the
# time someone tags before the bump lands - and the result is a release
# whose number means nothing. Cheap to check, unrecoverable to fix.
- name: Tag must match the crate version
if: startsWith(github.ref, 'refs/tags/v')
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME#v}"
crate=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')
if [ "$tag" != "$crate" ]; then
echo "::error::tag v${tag} does not match Cargo.toml version ${crate}"
exit 1
fi
echo "publishing dtg-credentials ${crate}"

- name: Authenticate to crates.io
uses: rust-lang/crates-io-auth-action@v1
id: auth

- name: Publish
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: |
set -euo pipefail
version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')

# The sparse index is newline-delimited JSON, one object per version.
# A 404 (name never published) is a clean "not there".
if curl -sSf "https://index.crates.io/dt/g-/dtg-credentials" 2>/dev/null \
| jq -se --arg v "$version" 'any(.[]; .vers == $v)' >/dev/null; then
echo "::notice::dtg-credentials ${version} is already on crates.io — nothing to do"
exit 0
fi

cargo publish --locked
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- Both examples now declare `required-features = ["affinidi-signing"]`. A
`--no-default-features` build previously failed on them while the library
itself compiled fine, and the error named `.sign()` rather than the disabled
backend

## [0.6.0] - 2026-09-02

Adds the two credentials that confer rather than assert: the **VAC** (verifiable authority
Expand Down
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,15 @@ tokio = "1"
# the sake of an example would be the wrong trade.
chacha20poly1305 = "0.10"
rand = "0.8"

# Both examples sign, and signing lives behind `affinidi-signing`. Without this
# a `--no-default-features` build fails on the examples while the library it is
# meant to be testing compiles perfectly - the failure names `.sign()`, not the
# feature, so it reads as a missing method rather than a disabled backend.
[[example]]
name = "sign_and_verify"
required-features = ["affinidi-signing"]

[[example]]
name = "data_room"
required-features = ["affinidi-signing"]