diff --git a/docs/install/pypi.md b/docs/install/pypi.md index 8d861dd9..cbdc2840 100644 --- a/docs/install/pypi.md +++ b/docs/install/pypi.md @@ -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` @@ -70,10 +74,10 @@ 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 ``` @@ -81,7 +85,7 @@ python -m sigstore verify identity \ 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. @@ -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. | diff --git a/specs/012-packaging-distribution/contracts/pypi-publish-contract.md b/specs/012-packaging-distribution/contracts/pypi-publish-contract.md index 16b84b2c..e9ecac63 100644 --- a/specs/012-packaging-distribution/contracts/pypi-publish-contract.md +++ b/specs/012-packaging-distribution/contracts/pypi-publish-contract.md @@ -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) diff --git a/specs/012-packaging-distribution/research.md b/specs/012-packaging-distribution/research.md index 913d433c..87ccdc73 100644 --- a/specs/012-packaging-distribution/research.md +++ b/specs/012-packaging-distribution/research.md @@ -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`.