OIDC-based Trusted Publishing for crates.io — eliminate long-lived API tokens
From gh-guardnpx claudepluginhub anthropics/claude-plugins-community --plugin gh-guardThis skill is limited to using the following tools:
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Trusted Publishing uses OIDC (OpenID Connect) to authenticate GitHub Actions workflows to crates.io without long-lived API tokens. This eliminates the risk of token theft and credential leaks.
| Attack Vector | API Token | Trusted Publishing |
|---|---|---|
| Token leaked in logs | Vulnerable | N/A (no token) |
| Token stolen from secrets | Vulnerable | N/A (no token) |
| Compromised maintainer account | Token can be exfiltrated | OIDC scoped to repo + workflow |
| Supply chain attack on CI | Token available in env | Token scoped to specific environment |
| Token rotation burden | Manual rotation needed | Automatic, ephemeral tokens |
cargo publish with a traditional token. Trusted Publishing cannot be used for the initial publish.crates-io environment in repo Settings > Environments.crates-io (must match the environment: in your workflow)GOTCHA: This is configured at crates.io, not in your repository.
https://crates.io/crates/YOUR_CRATE/settingspublish.yml (or whatever you named your publish workflow)crates-ioThe workflow needs two key pieces:
id-token: write permission on the publish job:permissions:
contents: read
id-token: write
crates-io-auth-action to exchange the OIDC token:- name: Authenticate to crates.io
uses: rust-lang/crates-io-auth-action@b7e9a28eded4986ec6b1fa40eeee8f8f165559ec # v1
id: auth
- name: Publish crate
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
run: cargo publish --locked
Once Trusted Publishing is working:
CARGO_REGISTRY_TOKEN secret from repo Settings > SecretsSee templates/workflows/publish.yml for a complete three-job pipeline (publish → provenance → release) with Trusted Publishing.
| Problem | Cause | Fix |
|---|---|---|
| "OIDC token exchange failed" | Environment name mismatch | Ensure workflow environment: matches crates.io config |
| "Not authorized" | Workflow filename mismatch | Check exact filename at crates.io matches workflow file |
| "No matching publisher" | Repo owner/name mismatch | Verify repo owner and name at crates.io settings |
| "Token expired" | OIDC token has short TTL | Ensure publish step runs promptly after auth step |
| First publish fails | Crate not yet registered | Do initial publish with cargo publish and a traditional token |
For workspaces publishing multiple crates:
cargo publish -p <crate> for eachThe same OIDC pattern used for crates.io Trusted Publishing works for other services:
| Service | OIDC Provider | Use Case |
|---|---|---|
| AWS | aws-actions/configure-aws-credentials | Deploy to S3, ECR, Lambda |
| GCP | google-github-actions/auth | Deploy to Cloud Run, GCS, Artifact Registry |
| Azure | azure/login | Deploy to Azure Container Apps, Blob Storage |
| PyPI | Native Trusted Publishing | If your project includes Python bindings (PyO3) |
| Private registries | Varies | Custom OIDC integration or short-lived tokens |
All use the same principle: request a short-lived OIDC token from GitHub, exchange it with the target service, and use the resulting credential for the operation:
permissions:
id-token: write # Required for OIDC token
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@SHA
with:
role-to-arn: arn:aws:iam::ACCOUNT:role/deploy-role
aws-region: us-east-1
The key requirement is id-token: write permission on the job. The service-specific action handles the token exchange.