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
4 changes: 0 additions & 4 deletions .auths/allowed_signers

This file was deleted.

23 changes: 23 additions & 0 deletions .auths/ci-bundle.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"identity_did": "did:keri:ECHoDk6bcHtZm3rngCXNpANJNh-U-3Bd5bSO1YVx6Fac",
"public_key_hex": "025587d93658ed5be7be14027e3dae1f7312c968011faa45302abdf677a0c74b2a",
"curve": "p256",
"attestation_chain": [
{
"version": 1,
"rid": ".auths",
"issuer": "did:keri:ECHoDk6bcHtZm3rngCXNpANJNh-U-3Bd5bSO1YVx6Fac",
"subject": "did:key:zDnaeWBqqznf8xyJhTAfc1RU4VFzUoRmw85G3hVdVS9mpLemP",
"device_public_key": {
"curve": "p256",
"key": "025587d93658ed5be7be14027e3dae1f7312c968011faa45302abdf677a0c74b2a"
},
"identity_signature": "d6776a5182df46bb646938d8416ed7ce3a2a9f55a841900c360d1e7190d3af7a5a5d8832dfad1bd43540cfeaba43df3f96c7a8f95ff8b970f6bdf71c878989c9",
"device_signature": "49d8c6dc15ad8d79f91bce2b9cb3533dd8cf4d3e12305641f0765f5624655812c549dcac07ad6a52f07e9fc41a3827db55f68069f9bd1bcc74feeb85197665c1",
"timestamp": "2026-06-03T17:32:34.460661Z",
"note": "Linked by auths-sdk setup"
}
],
"bundle_timestamp": "2026-06-09T19:49:11.207253Z",
"max_valid_for_secs": 31536000
}
32 changes: 32 additions & 0 deletions .github-template/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Starter workflow — verify that every commit in a PR is cryptographically
# signed with Auths. Copy this file to `.github/workflows/verify.yml` in your
# repository.
#
# Docs: https://github.com/auths-dev/verify
name: Verify Commits

on:
pull_request:

# Least privilege: verification only needs to read the repository.
permissions:
contents: read

jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history is required for commit verification

- uses: auths-dev/verify@v1 # or pin to a full commit SHA for stricter supply-chain hardening
with:
# Pin the CLI to a release that publishes a .sha256 — the action never
# resolves `latest` and fails closed if the binary can't be verified.
auths-version: "0.0.1-rc.12"
fail-on-unsigned: true

# Stateless CI (no ~/.auths on the runner)? Commit an identity bundle
# (public data) and pass it here:
# identity-bundle: ${{ secrets.AUTHS_IDENTITY_BUNDLE }}
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
with:
files: 'dist/index.js'
note: 'GitHub Actions release — ${{ github.ref_name }}'
auths-version: '0.0.1-rc.12'

- name: Generate SHA256 checksums
run: |
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/sign-commits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ jobs:
- uses: auths-dev/sign@v1
with:
commits: 'HEAD~1..HEAD'
auths-version: '0.0.1-rc.12'
2 changes: 2 additions & 0 deletions .github/workflows/verify-commits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ jobs:

- uses: ./
with:
auths-version: '0.0.1-rc.12'
identity-bundle: .auths/ci-bundle.json
fail-on-unsigned: true
post-pr-comment: 'true'
github-token: ${{ secrets.GITHUB_TOKEN }}
48 changes: 48 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.4.0] - 2026-06-09

### Changed

- **BREAKING:** Renamed the `token` action input to `identity-bundle`. It carries
*public* identity-bundle data (not a secret), so the old name was misleading and
collided with the `GITHUB_TOKEN` mental model. Update workflows by changing the
`with:` key `token:` → `identity-bundle:`. The separate `github-token` input
(used for posting PR comments) is unchanged.
- Commit verification is now **KEL-native**: the signer is read from each commit's
`Auths-Id`/`Auths-Device` trailers and checked against its key history (KEL),
replacing the static `.auths/allowed_signers` probe.

### Added

- **Supply-chain hardening of the `auths` CLI download.** `auths-version` must be
pinned to a released version — the action never resolves `releases/latest`, which
could let an upstream release silently change the binary a verification action
runs. The downloaded binary's SHA256 checksum is verified **fail-closed**: a
release without a fetchable `.sha256` is refused rather than run unverified. A
pre-installed `auths` on `PATH` is exempt. Every README example now pins
`auths-version` so it runs on a clean runner.

### Fixed

- Corrected the package license metadata to `Apache-2.0` (matching `LICENSE` and the
README); `package.json` previously declared `MIT`.

## Released

Releases through **v1.3.0** predate this changelog. See the
[GitHub releases](https://github.com/auths-dev/verify/releases) and
[commit history](https://github.com/auths-dev/verify/commits) for details —
highlights include KEL-native commit verification, artifact attestation
verification, identity-bundle support for stateless CI, optional PR-comment
results with fix instructions, and zero-config Marketplace metadata.

[Unreleased]: https://github.com/auths-dev/verify/compare/v1.4.0...HEAD
[1.4.0]: https://github.com/auths-dev/verify/compare/v1.3.0...v1.4.0
52 changes: 37 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Auths Verify Action

[![Verified with Auths](https://img.shields.io/badge/Verified%20with-Auths-4B9CD3?logo=github&logoColor=white)](https://github.com/auths-dev/verify)
[![Verify Commits](https://github.com/auths-dev/verify/actions/workflows/verify-commits.yml/badge.svg)](https://github.com/auths-dev/verify/actions/workflows/verify-commits.yml?query=branch%3Amain+event%3Apush)
[![Sign Commits](https://github.com/auths-dev/verify/actions/workflows/sign-commits.yml/badge.svg)](https://github.com/auths-dev/verify/actions/workflows/sign-commits.yml?query=branch%3Amain)

Expand All @@ -9,13 +8,18 @@ Verify commit signatures using [Auths](https://github.com/auths-dev/auths) ident
## Quickstart

```yaml
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: auths-dev/verify@v1
permissions:
contents: read # verification needs nothing more
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: auths-dev/verify@v1
with:
auths-version: "0.0.1-rc.12" # pin the CLI — the action never resolves `latest`
```

That's it. The action auto-detects the commit range from the GitHub event (PR or push), downloads the `auths` CLI, and verifies each commit with `auths verify`. Verification is **KEL-native**: the signer is read from each commit's `Auths-Id`/`Auths-Device` trailers and checked against its key history (KEL). For stateless CI, pass an identity bundle via the `token` input.
That's it. The action auto-detects the commit range from the GitHub event (PR or push), downloads the **pinned** `auths` CLI (SHA256-checksum verified — it **fails closed** if the release has no checksum), and verifies each commit with `auths verify`. Verification is **KEL-native**: the signer is read from each commit's `Auths-Id`/`Auths-Device` trailers and checked against its key history (KEL). For stateless CI, pass an identity bundle via the `identity-bundle` input.

## One-Liner Install

Expand All @@ -25,6 +29,8 @@ Add this file to your repo to start enforcing signed commits on every PR:
# .github/workflows/verify.yml
name: Verify Commits
on: [pull_request]
permissions:
contents: read # least privilege — no id-token, no write
jobs:
verify:
runs-on: ubuntu-latest
Expand All @@ -34,10 +40,13 @@ jobs:
fetch-depth: 0
- uses: auths-dev/verify@v1
with:
auths-version: "0.0.1-rc.12" # pin the CLI version (required)
fail-on-unsigned: true
```

That's it for verifying against the local identity store. For stateless CI (no `~/.auths` on the runner), commit an identity bundle and point the `token` input at it — see [Identity Bundle](#identity-bundle-stateless-ci) below.
> **Pin the CLI.** `auths-version` must be set to a released version that publishes a `.sha256` (e.g. `0.0.1-rc.12`). The action refuses to resolve `latest` and fails closed if the binary cannot be checksum-verified — supply-chain hardening for a tool whose entire job is trust. (If `auths` is already on `PATH`, the version is not needed.)

That's it for verifying against the local identity store. For stateless CI (no `~/.auths` on the runner), commit an identity bundle and point the `identity-bundle` input at it — see [Identity Bundle](#identity-bundle-stateless-ci) below.

## Features

Expand All @@ -56,9 +65,9 @@ That's it for verifying against the local identity store. For stateless CI (no `

| Input | Description | Required | Default |
|-------|-------------|----------|---------|
| `token` | Identity bundle for stateless verification. Accepts: CI token JSON, identity bundle JSON, or a file path to a bundle. Empty → KEL-native verification against the local identity store | No | `''` (KEL-native) |
| `identity-bundle` | Identity bundle for stateless verification. Accepts: CI token JSON, identity bundle JSON, or a file path to a bundle. Empty → KEL-native verification against the local identity store | No | `''` (KEL-native) |
| `commits` | Git commit range to verify (e.g. `HEAD~5..HEAD`) | No | Auto-detected from event |
| `auths-version` | Auths CLI version to use (e.g. `0.5.0`) | No | `''` (latest) |
| `auths-version` | Auths CLI version to **pin** (e.g. `0.0.1-rc.12`). Required unless `auths` is on `PATH`; the action never resolves `latest` and fails closed without a verifiable `.sha256` | Yes (unless on PATH) | `''` |
| `fail-on-unsigned` | Whether to fail the action if unsigned commits are found | No | `true` |
| `skip-merge-commits` | Whether to skip merge commits during verification | No | `true` |
| `post-pr-comment` | Post a PR comment with results and fix instructions (requires `pull-requests: write`) | No | `false` |
Expand All @@ -67,7 +76,7 @@ That's it for verifying against the local identity store. For stateless CI (no `
| `artifact-attestation-dir` | Directory containing `.auths.json` attestation files | No | `''` |
| `fail-on-unattested` | Fail the action if any artifact lacks a valid attestation | No | `true` |

The `token` input auto-detects the format (bundle JSON, CI token JSON, or a file path to a bundle). When empty, verification is KEL-native against the local identity store. When only `files` is set with an identity bundle, commit verification is skipped automatically.
The `identity-bundle` input auto-detects the format (bundle JSON, CI token JSON, or a file path to a bundle). When empty, verification is KEL-native against the local identity store. When only `files` is set with an identity bundle, commit verification is skipped automatically.

## Outputs

Expand All @@ -78,15 +87,19 @@ The `token` input auto-detects the format (bundle JSON, CI token JSON, or a file
| `total` | Total number of commits checked |
| `passed` | Number of commits that passed verification |
| `failed` | Number of commits that failed verification |
| `artifacts-verified` | Whether all artifacts were verified (`true`/`false`) |
| `artifact-results` | JSON array of per-artifact verification results |

## Verification Modes

### KEL-native (default)

With an empty `token`, the action runs `auths verify` against the local identity store. Each commit's `Auths-Id`/`Auths-Device` trailers identify the signer, and the signature is checked against the signer's key history (KEL). This works on a developer machine or any runner that has `~/.auths`:
With an empty `identity-bundle`, the action runs `auths verify` against the local identity store. Each commit's `Auths-Id`/`Auths-Device` trailers identify the signer, and the signature is checked against the signer's key history (KEL). This works on a developer machine or any runner that has `~/.auths`:

```yaml
- uses: auths-dev/verify@v1
with:
auths-version: "0.0.1-rc.12" # pin the CLI (required on clean runners)
```

### Identity Bundle (stateless CI)
Expand All @@ -102,7 +115,8 @@ Commit the bundle (it contains only public data) and reference the file:
```yaml
- uses: auths-dev/verify@v1
with:
token: '.auths/ci-bundle.json'
auths-version: "0.0.1-rc.12"
identity-bundle: '.auths/ci-bundle.json'
```

Or store it as a GitHub secret and pass it inline — the action detects the JSON format automatically:
Expand All @@ -114,7 +128,8 @@ gh secret set AUTHS_IDENTITY_BUNDLE < .auths/ci-bundle.json
```yaml
- uses: auths-dev/verify@v1
with:
token: ${{ secrets.AUTHS_IDENTITY_BUNDLE }}
auths-version: "0.0.1-rc.12"
identity-bundle: ${{ secrets.AUTHS_IDENTITY_BUNDLE }}
```

Bundles carry a freshness TTL (`--max-age-secs`); the action fails if a bundle is older than its TTL, so refresh it when it lapses or when keys rotate.
Expand All @@ -139,6 +154,8 @@ jobs:
fetch-depth: 0

- uses: auths-dev/verify@v1
with:
auths-version: "0.0.1-rc.12"
```

### Identity Bundle with Secret
Expand All @@ -157,14 +174,16 @@ jobs:

- uses: auths-dev/verify@v1
with:
token: ${{ secrets.AUTHS_IDENTITY_BUNDLE }}
auths-version: "0.0.1-rc.12"
identity-bundle: ${{ secrets.AUTHS_IDENTITY_BUNDLE }}
```

### Non-blocking (Warn Only)

```yaml
- uses: auths-dev/verify@v1
with:
auths-version: "0.0.1-rc.12"
fail-on-unsigned: 'false'
```

Expand All @@ -186,6 +205,7 @@ jobs:

- uses: auths-dev/verify@v1
with:
auths-version: "0.0.1-rc.12"
post-pr-comment: 'true'
github-token: ${{ secrets.GITHUB_TOKEN }}
```
Expand All @@ -197,6 +217,7 @@ jobs:
id: verify
uses: auths-dev/verify@v1
with:
auths-version: "0.0.1-rc.12"
fail-on-unsigned: 'false'

- name: Gate a downstream step on verification
Expand Down Expand Up @@ -231,7 +252,8 @@ jobs:

- uses: auths-dev/verify@v1
with:
token: ${{ secrets.AUTHS_IDENTITY_BUNDLE }}
auths-version: "0.0.1-rc.12"
identity-bundle: ${{ secrets.AUTHS_IDENTITY_BUNDLE }}
fail-on-unsigned: ${{ inputs.mode == 'enforce' && 'true' || 'false' }}
```

Expand Down
4 changes: 2 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: 'Verify cryptographic commit and artifact signatures on every PR us
author: 'auths-dev'

inputs:
token:
identity-bundle:
description: 'Identity bundle for stateless verification. Accepts: identity bundle JSON, CI token JSON, or a file path to a bundle (e.g. .auths/ci-bundle.json). Leave empty for KEL-native verification against the local identity store. Generate one with `auths id export-bundle`.'
required: false
default: ''
Expand All @@ -12,7 +12,7 @@ inputs:
required: false
default: ''
auths-version:
description: 'Auths CLI version to use (leave empty for latest)'
description: 'Auths CLI version to pin (e.g. "0.0.1-rc.12"). Required unless `auths` is already on PATH — a verification action must not resolve `latest` (supply-chain hardening). The pinned release must publish a `.sha256` checksum.'
required: false
default: ''
fail-on-unsigned:
Expand Down
Loading
Loading