Package trust and provenance verification for PyPI consumers.
Project description
trustcheck
trustcheck is a Python package and CLI for evaluating the trust posture of PyPI releases before they are installed, promoted, or approved.
It combines PyPI metadata, vulnerability records, provenance availability, cryptographic attestation verification, Trusted Publisher identity hints, and repository matching into a single operator-friendly report.
What it checks
For a selected package version, trustcheck can:
- fetch project and release metadata from PyPI
- inspect declared repository URLs from project metadata
- retrieve provenance envelopes for each release artifact
- verify attestations against the downloaded artifact digest
- extract Trusted Publisher identity details such as repository and workflow
- compare expected repository input against declared and attested repository signals
- flag publisher repository and workflow drift against the previous release
- surface PyPI vulnerability records for the selected version
- emit a concise human-readable report or structured JSON
Installation
pip install trustcheck
Requirements:
- Python
>=3.10 - Network access to PyPI
CI runs a supported-version matrix and should stay aligned with the package's advertised Python support.
Quick start
Inspect the latest release:
trustcheck inspect requests
Inspect a specific version:
trustcheck inspect sampleproject --version 4.0.0
Require the release to match an expected source repository:
trustcheck inspect sampleproject \
--version 4.0.0 \
--expected-repo https://github.com/pypa/sampleproject
Show detailed per-file evidence:
trustcheck inspect sampleproject --version 4.0.0 --verbose
Emit JSON for another tool:
trustcheck inspect sampleproject --version 4.0.0 --format json
Fail CI when full verification is missing:
trustcheck inspect sampleproject --version 4.0.0 --strict
Supported Public API
trustcheck has a small supported Python API for programmatic use:
trustcheck.inspect_packagetrustcheck.TrustReporttrustcheck.TrustReport.to_dict()trustcheck.JSON_SCHEMA_VERSIONtrustcheck.JSON_SCHEMA_IDtrustcheck.get_json_schema()
Everything else under trustcheck.* should be treated as internal implementation detail and may change between minor releases.
Quick example:
from trustcheck import JSON_SCHEMA_VERSION, TrustReport, get_json_schema, inspect_package
report = inspect_package("sampleproject", version="4.0.0")
payload = report.to_dict()
assert payload["schema_version"] == JSON_SCHEMA_VERSION
schema = get_json_schema()
CLI reference
Primary command:
trustcheck inspect <project>
Supported flags:
--version: inspect a specific release instead of the latest project version--expected-repo: require repository evidence to match an expected GitHub or GitLab repository--format text|json: choose human-readable text or machine-readable JSON--verbose: include per-file provenance, digest, publisher, and note fields in text output--strict: apply the built-in strict policy--policy default|strict|internal-metadata: evaluate a built-in policy profile--policy-file PATH: load policy settings from a JSON file--config-file PATH: load network settings from a JSON config file--require-verified-provenance none|all: override provenance enforcement--allow-metadata-only/--disallow-metadata-only: override metadata-only handling--require-expected-repo-match: require expected repository evidence--fail-on-vulnerability ignore|any: override vulnerability blocking--fail-on-risk-severity none|medium|high: fail on advisory risk flags at or above a severity--timeout FLOAT: set request timeout in seconds--retries INT: set transient retry count--backoff FLOAT: set retry backoff factor--cache-dir PATH: persist cached PyPI responses for repeated runs--offline: use cached responses only--debug: emit structured debug logs and print tracebacks for operational failures--log-format text|json: choose debug log format for--debug
Output model
The default text output is optimized for operators. It starts with a concise summary and then expands into evidence and risk details.
It includes:
- recommendation tier
- package URL and package summary
- verification coverage summary
- publisher trust depth
- network/request diagnostics
- "why this result" explanations
- declared repository URLs
- ownership and vulnerability data when PyPI exposes them
- per-risk remediation guidance
With --verbose, the report also shows per-file provenance, digest, attestation, publisher, and error details.
Recommendation tiers:
verified: every discovered release artifact verified successfullymetadata-only: no cryptographically verified artifact set, but no risk flags elevated the resultreview-required: medium-severity issues require manual reviewhigh-risk: high-severity issues were detected
Exit codes
trustcheck is designed to fit into automation as well as interactive review.
0: success1: upstream PyPI/network failure2: command usage error3: invalid or unexpected response / processing failure4: strict policy failure triggered by--strict
--strict is intentionally conservative:
- if no release files are discovered, it fails
- if any discovered file is not fully verified, it fails
JSON contract
trustcheck inspect --format json is the stable machine-readable interface.
Top-level shape:
{
"schema_version": "1.2.0",
"report": {
"project": "demo",
"version": "1.2.3",
"summary": "Demo package",
"package_url": "https://pypi.org/project/demo/1.2.3/",
"diagnostics": {
"timeout": 10.0,
"max_retries": 2,
"backoff_factor": 0.25,
"offline": false,
"cache_dir": null,
"request_count": 3,
"retry_count": 1,
"cache_hit_count": 0,
"request_failures": [],
"artifact_failures": []
},
"policy": {
"profile": "default",
"passed": true,
"enforced": false,
"fail_on_severity": "none",
"require_verified_provenance": "none",
"require_expected_repository_match": false,
"allow_metadata_only": true,
"vulnerability_mode": "ignore",
"violations": []
},
"declared_repository_urls": ["https://github.com/example/demo"],
"repository_urls": ["https://github.com/example/demo"],
"expected_repository": "https://github.com/example/demo",
"ownership": {
"organization": "example-org",
"roles": []
},
"vulnerabilities": [],
"files": [],
"coverage": {
"total_files": 0,
"files_with_provenance": 0,
"verified_files": 0,
"status": "none"
},
"publisher_trust": {
"depth_score": 0,
"depth_label": "none",
"verified_publishers": [],
"unique_verified_repositories": [],
"unique_verified_workflows": []
},
"provenance_consistency": {
"has_sdist": false,
"has_wheel": false,
"sdist_wheel_consistent": null,
"consistent_repositories": [],
"consistent_workflows": []
},
"release_drift": {
"compared_to_version": null,
"publisher_repository_drift": null,
"publisher_workflow_drift": null,
"previous_repositories": [],
"previous_workflows": []
},
"risk_flags": [],
"recommendation": "verified"
}
}
Contract rules:
schema_versionis semantic and version-controls the JSON shapeJSON_SCHEMA_IDidentifies the exact JSON Schema document for a givenschema_version- patch releases keep the same JSON contract for a given schema version
- new fields may be added within expandable objects in a backward-compatible way
- breaking JSON changes require a new major
schema_version - text output is presentation-oriented and is not a compatibility contract
You can retrieve the published JSON Schema directly from Python:
from trustcheck import get_json_schema
schema = get_json_schema()
Compatibility Policy
trustcheck is intended for CI and policy automation, so compatibility is treated as a product feature.
Stable contract:
inspect_package(...)returning aTrustReportTrustReport.to_dict()returning the machine-readable report envelope- top-level JSON fields
schema_versionandreport - currently documented report field names
- the machine-readable
report.diagnosticsblock - the machine-readable
report.policyevaluation block - the meaning of
schema_version,JSON_SCHEMA_ID, andget_json_schema()
Best-effort fields:
- free-form text such as
summary,risk_flags[*].message,risk_flags[*].why, and remediation text - upstream-derived metadata such as ownership details, vulnerability summaries, and publisher
rawpayloads
Expandable areas:
- new fields may be added to the
reportobject in a backward-compatible release ownership, ownership roles, and publisherrawdata may gain extra keys without a schema-version bump- list contents may grow when PyPI or provenance sources expose more evidence
Breaking changes:
- removing or renaming stable fields
- changing the meaning or type of a stable field
- changing CLI JSON output so it no longer validates against the published schema for the same
schema_version
When a breaking JSON or Python API change is necessary, trustcheck will:
- increment the package major version
- publish a new schema major version
- record the change in
CHANGELOG.md
Policy Evaluation
inspect_package(...) collects evidence and produces the advisory report. CLI enforcement is then handled by a separate policy layer.
Built-in policies:
default: advisory only; never fails the command by itselfstrict: requires verified provenance for every artifact, disallows metadata-only outcomes, and blocks on known vulnerabilities or high-severity risk flagsinternal-metadata: permissive profile for internal review flows where metadata-only results are acceptable
Policy settings currently support:
- requiring verified provenance for all artifacts
- allowing or disallowing metadata-only results
- requiring an expected repository match
- blocking on any known vulnerability
- failing on advisory risk flags at
mediumorhigh
Example JSON policy file:
{
"profile": "team-policy",
"require_verified_provenance": "all",
"allow_metadata_only": false,
"require_expected_repository_match": true,
"vulnerability_mode": "any",
"fail_on_severity": "high"
}
Example usage:
trustcheck inspect sampleproject \
--expected-repo https://github.com/pypa/sampleproject \
--policy-file policy.json
Network Controls And Diagnostics
trustcheck distinguishes package risk from upstream instability. The report includes a machine-readable diagnostics block so automation can see whether a failure came from policy, verification, or PyPI/network behavior.
Diagnostics currently include:
- request failures encountered, with deterministic
codeandsubcode - retry counts and total request counts
- cache hit counts
- artifact-level provenance or verification failures
- effective network settings such as timeout, retry count, backoff, offline mode, and cache directory
Common upstream subcodes include:
http_not_foundhttp_transientnetwork_timeoutnetwork_dns_temporarynetwork_dns_failurenetwork_tlsnetwork_connection_refusedjson_malformedproject_shape_invalidprovenance_shape_invalidoffline_cache_miss
Network settings can come from three places:
- CLI flags such as
--timeout,--retries,--backoff,--cache-dir, and--offline - environment variables
TRUSTCHECK_TIMEOUT,TRUSTCHECK_RETRIES,TRUSTCHECK_BACKOFF,TRUSTCHECK_CACHE_DIR, andTRUSTCHECK_OFFLINE --config-file, using a JSON object with anetworksection
Example config file:
{
"network": {
"timeout": 5.0,
"retries": 4,
"backoff_factor": 0.5,
"cache_dir": ".cache/trustcheck",
"offline": false
}
}
Example repeated-CI usage with a persistent cache:
trustcheck inspect sampleproject \
--cache-dir .cache/trustcheck \
--format json
Example offline reuse of cached results:
trustcheck inspect sampleproject \
--cache-dir .cache/trustcheck \
--offline \
--format json
Repository matching rules
Repository matching is intentionally strict.
trustcheck currently normalizes and matches canonical repository roots for supported forges:
- GitHub
- GitLab
It accepts canonical repository URLs and equivalent git-style remotes, and rejects non-repository pages such as profile, organization, documentation, or archive URLs. Invalid --expected-repo values are reported explicitly as a risk condition rather than being matched loosely.
Trust and verification model
trustcheck does not treat project metadata alone as proof of origin.
The strongest result comes from verified provenance bound to the exact artifact digest that was downloaded. Repository URLs and publisher identity hints are useful context, but they are not equivalent to a cryptographically verified attestation.
That distinction is reflected in the report:
- metadata can support an explanation
- verified provenance can support a trust decision
- missing or unverifiable provenance drives risk flags and strict-policy failures
Common automation patterns
Fail a build if a pinned release is not fully verified:
trustcheck inspect sampleproject --version 4.0.0 --strict
Record JSON as a CI artifact:
trustcheck inspect sampleproject --version 4.0.0 --format json > trustcheck-report.json
Review a release against an expected repository during package admission:
trustcheck inspect sampleproject \
--version 4.0.0 \
--expected-repo https://github.com/pypa/sampleproject \
--strict
Quality and release process
The repository includes:
- CI for lint, type checks, cross-platform test matrices, coverage enforcement, and build smoke tests
- dependency auditing and secret scanning in CI
- CodeQL analysis for the Python codebase
- release publishing from immutable tagged commits
- annotated tag enforcement for releases
- GitHub Release creation with generated notes
- release artifact checksum generation
- SBOM generation for release artifacts
- PyPI Trusted Publishing with artifact attestations
- opt-in live integration tests against real PyPI packages
- contract snapshot tests that detect accidental JSON-schema drift
Live integration tests are excluded from the default test run and can be enabled with:
TRUSTCHECK_RUN_LIVE=1 python -m pytest -q tests/test_integration_live.py
Limitations
- PyPI metadata quality varies by project
- some projects do not publish provenance at all
- repository matching currently supports canonical GitHub and GitLab URLs only
- provenance verification may depend on local environment support required by underlying tooling
- text output is intentionally concise and may omit low-level detail unless
--verboseis used
Development
Run the local test suite:
python -m pytest -q tests
Run tests with coverage:
python -m pytest --cov=trustcheck --cov-report=term-missing tests
Run lint:
ruff check src tests
Run type checks:
mypy src
License
Trustcheck Personal Use License
Documentation
Documentation: https://Halfblood-Prince.github.io/trustcheck/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file trustcheck-1.4.0.tar.gz.
File metadata
- Download URL: trustcheck-1.4.0.tar.gz
- Upload date:
- Size: 628.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fc924c3a85dea2262867f656b1ba14f87578c74836531ffc98c05d178251942
|
|
| MD5 |
15f754549a2a088d8b5314d92949cafe
|
|
| BLAKE2b-256 |
5e6a995bf1f621640da08608c092d8958b557de9e7da733058601c061677e54b
|
Provenance
The following attestation bundles were made for trustcheck-1.4.0.tar.gz:
Publisher:
publish.yml on Halfblood-Prince/trustcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
trustcheck-1.4.0.tar.gz -
Subject digest:
6fc924c3a85dea2262867f656b1ba14f87578c74836531ffc98c05d178251942 - Sigstore transparency entry: 1281015163
- Sigstore integration time:
-
Permalink:
Halfblood-Prince/trustcheck@671a32b18f38cf231051db7d3ba8e0ac6229cf09 -
Branch / Tag:
refs/tags/v1.4.0 - Owner: https://github.com/Halfblood-Prince
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@671a32b18f38cf231051db7d3ba8e0ac6229cf09 -
Trigger Event:
push
-
Statement type:
File details
Details for the file trustcheck-1.4.0-py3-none-any.whl.
File metadata
- Download URL: trustcheck-1.4.0-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2d7971db61b24edf1aa58fa3e9cad41f81f225564702c75619ab6a2e0c40f24
|
|
| MD5 |
80779247553b7e4c0ae16cd16a44bd07
|
|
| BLAKE2b-256 |
b5b26f56974ae9188782f3cd0125881739d32a1ab693c85e98fb5ed30840064b
|
Provenance
The following attestation bundles were made for trustcheck-1.4.0-py3-none-any.whl:
Publisher:
publish.yml on Halfblood-Prince/trustcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
trustcheck-1.4.0-py3-none-any.whl -
Subject digest:
e2d7971db61b24edf1aa58fa3e9cad41f81f225564702c75619ab6a2e0c40f24 - Sigstore transparency entry: 1281015181
- Sigstore integration time:
-
Permalink:
Halfblood-Prince/trustcheck@671a32b18f38cf231051db7d3ba8e0ac6229cf09 -
Branch / Tag:
refs/tags/v1.4.0 - Owner: https://github.com/Halfblood-Prince
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@671a32b18f38cf231051db7d3ba8e0ac6229cf09 -
Trigger Event:
push
-
Statement type: