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
27 changes: 7 additions & 20 deletions crates/auths-cli/src/commands/init/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,10 @@ fn set_git_config(key: &str, value: &str, scope: &str) -> Result<()> {

// --- GitHub Action Scaffolding ---

const GITHUB_ACTION_WORKFLOW_TEMPLATE: &str = r#"# Auths release workflow — signs artifacts and commits attestations to the repo.
const GITHUB_ACTION_WORKFLOW_TEMPLATE: &str = r#"# Auths release workflow — signs artifacts and verifies them.
# Generated by: auths init --github-action
#
# Required secrets (generate with `just ci-setup`):
# AUTHS_CI_PASSPHRASE — passphrase for the CI keychain
# AUTHS_CI_KEYCHAIN — base64-encoded keychain file
# AUTHS_CI_IDENTITY_BUNDLE — base64-encoded identity bundle
# Required: run `auths ci setup` to set the AUTHS_CI_TOKEN secret.

name: Auths Release

Expand All @@ -157,22 +154,12 @@ jobs:
# Replace this with your build step
echo "Build your artifacts here"

- name: Sign artifacts and commit attestations
uses: auths-dev/attest-action@v1
env:
# These secrets must be set in your repository settings
AUTHS_PASSPHRASE: ${{ secrets.AUTHS_CI_PASSPHRASE }}
AUTHS_CI_KEYCHAIN_B64: ${{ secrets.AUTHS_CI_KEYCHAIN }}
AUTHS_CI_IDENTITY_BUNDLE_B64: ${{ secrets.AUTHS_CI_IDENTITY_BUNDLE }}
- name: Sign and verify artifacts
uses: auths-dev/sign@v1
with:
# Glob pattern matching your release artifacts
artifacts: 'dist/*.tar.gz'

# Directory in your repo where attestation files are committed
attestation-path: '.auths/releases'

# "direct" pushes to the current branch; "pr" opens a pull request
commit-strategy: 'direct'
token: ${{ secrets.AUTHS_CI_TOKEN }}
files: 'dist/*.tar.gz'
verify: true
"#;

/// Scaffolds a GitHub Actions workflow for attestation signing.
Expand Down
8 changes: 4 additions & 4 deletions crates/auths-cli/tests/cases/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ fn test_init_github_action_scaffold() {

let content = std::fs::read_to_string(&workflow).unwrap();
assert!(
content.contains("auths-dev/attest-action@v1"),
"workflow should reference attest-action"
content.contains("auths-dev/sign@v1"),
"workflow should reference sign action"
);
assert!(
content.contains("AUTHS_CI_PASSPHRASE"),
"workflow should reference secrets"
content.contains("AUTHS_CI_TOKEN"),
"workflow should reference AUTHS_CI_TOKEN secret"
);

// .auths/.gitkeep should exist
Expand Down
7 changes: 7 additions & 0 deletions crates/xtask/src/ci_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ use walkdir::WalkDir;
use crate::shell::{run_capture, run_capture_env, run_with_stdin};

pub fn run() -> Result<()> {
eprintln!(
"\x1b[1;33m⚠ `cargo xt ci-setup` is deprecated. Use `auths ci setup` instead.\x1b[0m"
);
eprintln!(" The new command bundles all secrets into a single AUTHS_CI_TOKEN.");
eprintln!(" Run `auths ci setup --help` for details.");
eprintln!();

println!();
println!("\x1b[0;36m╔════════════════════════════════════════════════════════════╗\x1b[0m");
println!("\x1b[0;36m║\x1b[0m\x1b[1m CI Release Signing Setup (One-Time) \x1b[0m\x1b[0;36m║\x1b[0m");
Expand Down
8 changes: 4 additions & 4 deletions docs/contributing/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ This makes similar checks as the github release. It also includes properly order

### One-time CI signing setup

Before the first release, GitHub Actions needs a device key and identity bundle for artifact signing:
Before the first release, GitHub Actions needs a device key and identity token for artifact signing:

```bash
just ci-setup
auths ci setup
```

This creates a limited-capability CI device key, exports it as an encrypted keychain, and sets three GitHub secrets: `AUTHS_CI_PASSPHRASE`, `AUTHS_CI_KEYCHAIN`, and `AUTHS_CI_IDENTITY_BUNDLE`. Artifact signing is skipped gracefully if these secrets are missing.
This creates a limited-capability CI device key and sets a single `AUTHS_CI_TOKEN` secret on GitHub containing everything CI needs. Artifact signing is skipped gracefully if the secret is missing.

Re-run `ci-setup` only if the CI device key is revoked or the identity repo changes significantly.
To refresh the token without regenerating the device key, run `auths ci rotate`.

### Manual steps (if needed)

Expand Down
71 changes: 26 additions & 45 deletions docs/guides/platforms/ci-cd.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,38 @@ CI signing in Auths works through device delegation:
4. In CI, the runner restores the identity bundle and signs artifacts using the CI device key.
5. You can **revoke** the CI device at any time without affecting your root identity.

## One-time setup with `cargo xt ci-setup`
## One-time setup with `auths ci setup`

The `ci-setup` xtask automates the entire provisioning flow. Run it from the project root:
Run this from any repo with a git remote:

```bash
cargo xt ci-setup
auths ci setup
```

This command will:

1. **Verify your identity exists** by running `auths status`.
2. **Read your identity DID** from `auths id show` and your key alias from `auths key list`.
3. **Generate a CI device key** (Ed25519, 32-byte seed) and import it into your platform keychain under the alias `ci-release-device`.
4. **Prompt for a passphrase** that will protect the CI device key. This passphrase will be stored as a GitHub Secret.
5. **Create an encrypted file keychain** by copying the key to a file-backed keychain using `auths key copy-backend --alias ci-release-device --dst-backend file`.
6. **Derive the device DID** using `auths key export --alias ci-release-device --format pub` and `auths debug util pubkey-to-did`.
7. **Link the CI device** to your identity with `auths device link --capabilities sign_release --note "GitHub Actions release signer"`.
8. **Package your `~/.auths` repository** as a base64-encoded tarball (excluding `.sock` files).
9. **Set three GitHub Secrets** via the `gh` CLI:
- `AUTHS_CI_PASSPHRASE` -- The passphrase for the CI device key.
- `AUTHS_CI_KEYCHAIN` -- The encrypted file keychain (base64).
- `AUTHS_CI_IDENTITY_BUNDLE` -- The `~/.auths` repository snapshot (base64 tarball).

If the `gh` CLI is not authenticated, the command prints the secret values for you to add manually via **Repository > Settings > Secrets > Actions > New secret**.
1. **Verify your identity exists** and read your identity DID and key alias.
2. **Generate a CI device key** (Ed25519) and link it to your identity with `sign_release` capability.
3. **Package everything** into a single `AUTHS_CI_TOKEN` JSON secret containing the passphrase, encrypted keychain, identity repo snapshot, and verification bundle.
4. **Set the secret** on your forge automatically via the `gh` CLI (GitHub) or print the token for manual setup (other forges).

If the `gh` CLI is not authenticated, the command prints the token value for you to add manually via **Repository > Settings > Secrets > Actions > New secret**.

### Rotating the token

To refresh the token (new TTL, updated identity repo) without regenerating the device key:

```bash
auths ci rotate
```

### Re-running setup

If you already have a `ci-release-device` key, `cargo xt ci-setup` detects it and reuses the existing key while regenerating the file keychain and secrets.
If you already have a `ci-release-device` key, `auths ci setup` detects it and reuses the existing key while regenerating the token.

## Signing artifacts in GitHub Actions

Once the secrets are set, add a signing step to your release workflow:
Once `AUTHS_CI_TOKEN` is set, add a signing step to your release workflow:

```yaml
name: Release
Expand All @@ -60,34 +60,15 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Install Auths
run: cargo install auths-cli

- name: Restore Auths identity
env:
AUTHS_CI_IDENTITY_BUNDLE: ${{ secrets.AUTHS_CI_IDENTITY_BUNDLE }}
AUTHS_CI_KEYCHAIN: ${{ secrets.AUTHS_CI_KEYCHAIN }}
AUTHS_CI_PASSPHRASE: ${{ secrets.AUTHS_CI_PASSPHRASE }}
run: |
# Restore the ~/.auths identity repository
mkdir -p ~/.auths
echo "$AUTHS_CI_IDENTITY_BUNDLE" | base64 -d | tar xz -C ~/.auths

# Restore the file keychain
echo "$AUTHS_CI_KEYCHAIN" | base64 -d > /tmp/ci-keychain.enc

# Set environment for file-backend keychain
echo "AUTHS_KEYCHAIN_BACKEND=file" >> $GITHUB_ENV
echo "AUTHS_KEYCHAIN_FILE=/tmp/ci-keychain.enc" >> $GITHUB_ENV
echo "AUTHS_PASSPHRASE=$AUTHS_CI_PASSPHRASE" >> $GITHUB_ENV

- name: Build release artifact
run: cargo build --release && tar czf myproject.tar.gz -C target/release myproject

- name: Sign release artifact
run: |
auths sign myproject.tar.gz \
--device-key ci-release-device
- name: Sign and verify artifact
uses: auths-dev/sign@v1
with:
token: ${{ secrets.AUTHS_CI_TOKEN }}
files: 'myproject.tar.gz'
verify: true

- name: Upload release
uses: softprops/action-gh-release@v2
Expand Down Expand Up @@ -227,4 +208,4 @@ auths device revoke \
--key <your-key>
```

The device DID and identity key alias are printed by `cargo xt ci-setup` when the device is created. After revocation, the CI device key can no longer produce valid attestations, even if the secrets remain in GitHub.
The device DID and identity key alias are printed by `auths ci setup` when the device is created. After revocation, the CI device key can no longer produce valid attestations, even if the secrets remain in GitHub.
7 changes: 3 additions & 4 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ release-github:
release-crates:
python scripts/releases/2_crates.py --publish

# One-time setup: create a CI release-signing device and export secrets for GitHub
# Run this once locally, then add the printed values as GitHub secrets
# Delegates to the xtask crate for cross-platform correctness.
# One-time setup: create a CI release-signing device and set AUTHS_CI_TOKEN secret.
# Run this once locally — detects forge from git remote and sets the secret automatically.
ci-setup:
cargo xt ci-setup
auths ci setup
5 changes: 1 addition & 4 deletions scripts/auths_workflows/artifact_signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,7 @@ def main() -> None:
print(f"{BOLD}{GREEN} Artifact signing workflow completed successfully!{RESET}")
print(f"{BOLD}{GREEN}{'='*60}{RESET}")
print(f"\n This confirms the release.yml signing step will work in CI.")
print(f" Make sure these GitHub secrets are set (via 'just ci-setup'):")
print(f" • AUTHS_CI_PASSPHRASE")
print(f" • AUTHS_CI_KEYCHAIN")
print(f" • AUTHS_CI_IDENTITY_BUNDLE\n")
print(f" Make sure AUTHS_CI_TOKEN is set (via 'auths ci setup').\n")


if __name__ == "__main__":
Expand Down
Loading