Skip to content
Open
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
20 changes: 12 additions & 8 deletions docs/install/pypi.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,19 @@ The `--extra-index-url` is required so dependencies of darnit that exist only on

Every release attaches a [PEP 740](https://peps.python.org/pep-0740/) Sigstore attestation. The signing identity is the GitHub Actions workflow that produced the wheel; you can verify the chain back to the canonical repository without trusting anything in between.

### One-step verification with pip
### Verification with `pypi-attestations`

If your pip is 25.0 or newer, `--verify-attestations` does the whole thing automatically:
pip does not have a `--verify-attestations` flag and does not check PEP 740 attestations at install time. Use the [`pypi-attestations`](https://pypi.org/project/pypi-attestations/) tool against a downloaded wheel:

```bash
pip install --verify-attestations darnit-mcp==0.1.0
pip install pypi-attestations
pip download --no-deps darnit-mcp==0.1.0
pypi-attestations verify pypi \
--repository https://github.com/darnitdevorg/darnit \
darnit_mcp-0.1.0-py3-none-any.whl
```

pip refuses to install if the attestation is missing or fails to verify against PyPI's public certs.
The `--repository` value must match the repository recorded in the attestation (the same identity used by the `sigstore` commands below).

### Manual verification with `sigstore`

Expand All @@ -70,18 +74,18 @@ with open('attestation.sigstore.json', 'w') as out:
json.dump(data['attestation_bundles'][0]['attestations'][0], out)
"

# Verify against the canonical kusari-oss/darnit identity
# Verify against the canonical darnitdevorg/darnit identity
python -m sigstore verify identity \
--bundle attestation.sigstore.json \
--cert-identity-regexp '^https://github\.com/kusari-oss/darnit/\.github/workflows/release\.yml@' \
--cert-identity-regexp '^https://github\.com/darnitdevorg/darnit/\.github/workflows/release\.yml@' \
--cert-oidc-issuer https://token.actions.githubusercontent.com \
darnit_mcp-0.1.0-py3-none-any.whl
```

A passing verification proves:

- The wheel bytes match exactly what was signed.
- The signer was the `release.yml` workflow in `kusari-oss/darnit`.
- The signer was the `release.yml` workflow in `darnitdevorg/darnit`.
- The OIDC issuer was GitHub Actions (not some other identity provider).

For TestPyPI pre-releases, substitute `test.pypi.org` for `pypi.org` in the provenance URL.
Expand Down Expand Up @@ -112,4 +116,4 @@ For most users, `pip install darnit-mcp` is the right command. The other package
| `ERROR: Package requires a different Python` | Host Python is older than 3.11. Install Python 3.11+ or use [pipx](https://pipx.pypa.io/) with an explicit `--python` flag. |
| `Could not find a version that satisfies the requirement` (for a pre-release) | Missing `--pre` flag or wrong `--index-url`. |
| Sigstore verification fails with "no attestation bundles" | The release was published before PEP 740 attestations existed, or the attestation hasn't propagated yet (rare; retry in a few minutes). |
| Sigstore verification fails with "identity mismatch" | The wheel was not signed by `kusari-oss/darnit`'s release workflow. **Do not trust this artifact.** Report it via the project's security policy. |
| Sigstore verification fails with "identity mismatch" | The wheel was not signed by `darnitdevorg/darnit`'s release workflow. **Do not trust this artifact.** Report it via the project's security policy. |
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pip install --index-url $INDEX --pre $PKG==$VERSION
darnit --version # for darnit-mcp; "$PKG --help" or import smoke for others
```

`pip install --verify-attestations $PKG==$VERSION` runs additionally for stable releases on `pypi.org`, where the attestation API is available.
For stable releases on `pypi.org`, additionally download the wheel and run `pypi-attestations verify pypi --repository https://github.com/darnitdevorg/darnit $WHEEL` (pip has no `--verify-attestations` flag).

## Pre-flight assertions (per package)

Expand Down
2 changes: 1 addition & 1 deletion specs/012-packaging-distribution/research.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ A future hardened-image variant (Chainguard- or distroless-based) is tracked as
**Rationale**:
- All three mechanisms share the same OIDC identity (the GitHub Actions workflow's identity), so one signing identity covers every artifact across every channel.
- No long-lived publishing tokens or signing keys live anywhere — satisfies spec FR-007.
- Sigstore attestations on PyPI are now the default expectation for serious open-source Python projects and integrate with `pip install --verify-attestations` (PEP 740).
- Sigstore attestations on PyPI are now the default expectation for serious open-source Python projects (PEP 740). pip does not verify PEP 740 attestations at install time; verify a downloaded wheel with `pypi-attestations verify pypi`.
- cosign + GHCR provides verifiable image signatures consumable by every major policy engine (Kyverno, Connaisseur, Sigstore Policy Controller).
- The same cosign workflow signs detached blobs for binary downloads. Users verify with `cosign verify-blob`.

Expand Down