ci: add Auths cryptographic commit verification (warn-only)#25124
ci: add Auths cryptographic commit verification (warn-only)#25124bordumb wants to merge 6 commits intoBerriAI:mainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR adds a warn-only Auths commit-signature verification layer to LiteLLM's CI, motivated by the March 24, 2026 supply-chain incident where a stolen Key changes:
Open items:
Confidence Score: 5/5Safe to merge — warn-only, no modifications to existing files, all prior P0/P1 concerns resolved. The two previously critical findings (unpinned third-party action, resource leak) have been fully addressed: the action is pinned to a full commit SHA and the file-handle issue no longer exists in the current version. The only remaining findings are P2 style suggestions (inline imports in a cookbook script). The workflow is strictly non-blocking and additive. .auths/allowed_signers — needs at least one LiteLLM core maintainer key for the workflow to provide meaningful enforcement signal before fail-on-unsigned is ever flipped to true.
|
| Filename | Overview |
|---|---|
| .github/workflows/auths-verify-commits.yml | Warn-only Auths commit verification workflow; action now pinned to full commit SHA (57e304ef…) after prior review. Permissions correctly scoped at job level. Non-blocking to existing CI. |
| .auths/allowed_signers | Contains a single Ed25519 key belonging to the PR author (external contributor). Per prior discussion, at least two LiteLLM maintainers should add their keys before the file provides meaningful enforcement signal. |
| cookbook/security/auths_attack_simulation.py | Educational simulation of the March 24 supply-chain attack. Logic is sound and handles missing-SDK cases gracefully. Minor style issue: optional-dependency imports placed inside main() rather than at module level, against project conventions. |
| cookbook/security/README.md | Clear documentation accurately describing the March 24 attack vector, what Auths does and does not protect against, and how to run the simulation. |
Sequence Diagram
sequenceDiagram
participant Dev as Developer
participant GH as GitHub Actions
participant Auths as Auths CLI<br/>(auths-verify-github-action)
participant Signers as .auths/allowed_signers
participant PR as PR Comment
Dev->>GH: Push commit / open PR
GH->>GH: Checkout repo (fetch-depth: 0)
GH->>Auths: Run auths-verify-github-action
Auths->>Signers: Load authorized Ed25519 public keys
loop Each non-merge commit
Auths->>Auths: Verify Ed25519 signature
alt Signed by key in allowed_signers
Auths-->>GH: Verified
else Unsigned or unknown signer
Auths-->>GH: Unverified (warn-only, fail-on-unsigned=false)
end
end
Auths->>PR: Post verification summary comment
GH-->>Dev: Workflow complete (never blocks merge)
Reviews (3): Last reviewed commit: "fix: clarify that simulation shows commi..." | Re-trigger Greptile
| # | ||
| # Documentation: https://github.com/auths-dev/auths/blob/main/docs/guides/platforms/ci-cd.md | ||
| # | ||
| # Maintainers — add your keys below this line: |
There was a problem hiding this comment.
Empty
allowed_signers means zero cryptographic enforcement at merge time
The file contains no public keys. Until at least one maintainer adds their key, the Auths verification step has no authorized identities to validate commits against. Depending on the Auths CLI implementation, this will either:
- Report every commit as unsigned/unverified (warn-only, so nothing is blocked), or
- Vacuously pass verification (nothing to check against = no violations).
Either outcome means the "cryptographic chain of custody" described in the PR description doesn't exist the moment this merges. The workflow will run but provide no actual signal until keys are populated.
Consider making this a follow-up requirement: the workflow should only be merged once at least one maintainer key is present, otherwise it creates the impression of coverage without providing any.
| run(["git", "config", "user.signingkey", key_path], cwd=repo) | ||
|
|
||
| # Create allowed_signers file | ||
| pub_key_content = open(f"{key_path}.pub").read().strip() |
There was a problem hiding this comment.
File opened without a context manager — resource leak
open() used directly without with leaves the file handle open until garbage collection. Use a context manager:
| pub_key_content = open(f"{key_path}.pub").read().strip() | |
| with open(f"{key_path}.pub") as fh: | |
| pub_key_content = fh.read().strip() |
| # Documentation: https://github.com/auths-dev/auths/blob/main/docs/guides/platforms/ci-cd.md | ||
| # | ||
| # Maintainers — add your keys below this line: | ||
| z6MkhPJCPXd5A9VN4wScJkxTtz6de7egZQx78vsiAT1vg3PZ@auths.local namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICuPK6OfYp7ngZp40Q+Dsrahhks472v6gPIMD0upCRnM |
There was a problem hiding this comment.
External contributor key is the sole authorized signer
The only entry in .auths/allowed_signers is a key whose DID (z6MkhPJCPXd5A9VN4wScJkxTtz6de7egZQx78vsiAT1vg3PZ) belongs to the PR author — an external contributor, not a LiteLLM core maintainer.
With this configuration, the Auths CI will:
- Mark every commit from LiteLLM maintainers as unverified (flagged in the warn output)
- Mark commits from this external contributor as verified
This inverts the trust model the file is meant to establish. While fail-on-unsigned: false keeps it non-blocking today, the verification output will consistently produce false-positive warnings on all legitimate maintainer commits, and will create the appearance that the external contributor's key is the canonical signing authority for the repo.
The key should belong to at least one LiteLLM core maintainer before this file provides meaningful signal. The original PR description's "To activate enforcement" section (step 3) describes the correct flow for maintainers to export their own keys.
There was a problem hiding this comment.
This is expected
This PR should not be merged unless maintainers are interested in adopting Auths for an added layer of security that is device-attested (i.e. attackers can steal your identity/credentials, but will ultimately fail when pushing from a device that isn't tied to maintainer's identity)
Next steps:
Before merging, at least 2 maintainers should try adding their Auths identity to .auths/approved_signers
Relevant issues
Motivated by the March 24, 2026 supply chain incident where compromised CI credentials were used to publish malicious LiteLLM versions (v1.82.7, v1.82.8) to PyPI. LiteLLM's own SECURITY.md classifies supply chain attacks as P0 (Critical).
Pre-Submission checklist
cookbook/security/auths_attack_simulation.pysimulation scriptfail-on-unsigned: falseensures existing CI is unaffectedChanges
What this adds
Auths provides Ed25519 signatures bound to KERI-based decentralized identifiers (DIDs), creating a cryptographic chain of custody from commit to published artifact.
.github/workflows/auths-verify-commits.yml— GitHub Actions workflow that runs Auths commit signature verification on PRs and pushes to main. Configured in warn-only mode (fail-on-unsigned: false) so it reports unsigned commits without blocking merges..auths/allowed_signers— Configuration file listing authorized maintainer public keys. Currently contains placeholder entries with instructions for maintainers to add their keys.cookbook/security/auths_attack_simulation.py— Demonstrates how the March 24 attack vector (unauthorized PyPI publish via compromised CI credentials) would be detected by Auths verification. Creates a simulated repo with signed and unsigned commits, showing that the unsigned "attacker" commit is flagged.Why this matters
The March 24 incident succeeded because there was no cryptographic binding between the published PyPI package and a verified maintainer identity. The attacker used a stolen
PYPI_PUBLISHtoken (exfiltrated via a compromised Trivy GitHub Action) to publish v1.82.7 and v1.82.8 directly to PyPI. The source code on GitHub was never modified.Auths addresses this gap: even if registry credentials are stolen, an attacker cannot produce a valid Ed25519 signature for commits they did not author. The signing key is bound to the maintainer's device keychain and never leaves it.
This is complementary to existing security measures (OIDC Trusted Publishing, cosign for Docker images, CodeQL, OSSF Scorecard, zizmor) — it adds the missing layer of per-commit identity verification.
To activate enforcement
brew tap auths-dev/auths-cli && brew install auths(orcargo install auths_cli)auths initauths signers sync --output .auths/allowed_signersfail-on-unsigned: truein the workflowDeployment notes