EXPERIMENTAL: Still playing around, do not depend on its security!
Releases a Cargo workspace from a hand-written CHANGELOG.md, and lets CI publish to crates.io only after you approve with a FIDO2 security key, by touch and PIN. Nothing holding your GitHub token, such as a coding agent, can publish on its own.
- Run the
prepare-releaseworkflow with a version. It dates## Unreleased, sets the version and opens a pull request. - Once its checks pass, check out the branch and run
touchgate approve. It signs an emptyApprove release 0.4.0.commit with the key, which asks for its PIN and a touch, and pushes it. Your git signing settings stay as they are. - Within the hour, merge with a merge commit. Once every check on the merge has passed, the release publishes to crates.io, then tags and makes the GitHub release.
The merge's second parent must be the approval: same tree as the merge, at most an hour old, signed by the one key in release.yml, with the signature's flags showing a touch and the PIN, and naming the publish commit the release runs from, so a change to publish after you approve stops the release. git and ssh-keygen accept FIDO2 signatures made without either, so touchgate reads those flags itself. It runs no code from the commit it checks.
- Your everyday account has Maintain, never Admin, on the repository and on touchgate. A separate admin account, used only in the browser, makes the settings below.
release.ymlruns from apublishbranch that a ruleset lets only the admin change, in areleaseenvironment limited to exactly that branch.- crates.io trusts only
release.ymlin thereleaseenvironment, with Trusted Publishing only on and no API tokens left. - Immutable releases are on.
Make a resident key on the security key, where touchgate approve looks for it, and install touchgate:
ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:signing -f ~/.ssh/id_ed25519_sk_signing
cargo install --locked --git https://github.com/borink-org/touchgateAdd two workflows that call touchgate's, pinned to a touchgate commit. .github/workflows/touchgate.yml, with your default branch:
name: touchgate
on:
workflow_dispatch:
inputs:
version:
description: The version to release, such as 0.4.0
required: true
type: string
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened, labeled, unlabeled]
permissions: {}
jobs:
touchgate:
uses: borink-org/touchgate/.github/workflows/touchgate.yml@<commit>
permissions:
contents: write
pull-requests: write
actions: write
with:
version: ${{ inputs.version }}.github/workflows/release.yml, with the contents of your .pub file, committed to both the default branch and publish:
name: release
on:
workflow_dispatch:
inputs:
commit:
required: true
type: string
permissions: {}
jobs:
release:
uses: borink-org/touchgate/.github/workflows/release.yml@<commit>
permissions:
contents: write
id-token: write
checks: read
with:
commit: ${{ inputs.commit }}
key: sk-ssh-ed25519@openssh.com AAAA...Allow GitHub Actions to create pull requests, and add a no-changelog label for pull requests with nothing to say in the changelog.