Python client library for the Transparency Exchange API (TEA)
Project description
libtea
Python client library for the Transparency Exchange API (TEA) v0.3.0-beta.2.
TEA is an open standard for discovering and retrieving software transparency artifacts (SBOMs, VEX, build metadata) for any software product or component. A TEI identifier resolves via DNS to the right endpoint, similar to how email uses MX records — so consumers can fetch artifacts without knowing which server hosts them.
Specification: Ecma TC54-TG1 | OpenAPI spec
Status: Alpha — API is subject to change.
Features
- Auto-discovery via
.well-known/teaand TEI URNs - Products, components, releases, and versioned collections
- Search by PURL, CPE, or TEI identifier
- Artifact download with on-the-fly checksum verification (MD5 through BLAKE2b)
- Typed Pydantic v2 models with full camelCase/snake_case conversion
- Structured exception hierarchy with error context
- Bearer token isolation — tokens are never sent to artifact download hosts
Installation
pip install libtea
Quick start
from libtea import TeaClient
# Auto-discover from a domain's .well-known/tea
with TeaClient.from_well_known("example.com", token="your-bearer-token") as client:
# Browse a product
product = client.get_product("product-uuid")
print(product.name)
# Get a component release with its latest collection
cr = client.get_component_release("release-uuid")
for artifact in cr.latest_collection.artifacts:
print(artifact.name, artifact.type)
Or connect directly to a known endpoint:
client = TeaClient(
base_url="https://api.example.com/tea/v0.3.0-beta.2",
token="your-bearer-token",
timeout=30.0,
)
Using from_well_known, you can also override the spec version and timeout:
client = TeaClient.from_well_known(
"example.com",
token="your-bearer-token",
timeout=15.0,
version="0.3.0-beta.2", # default
)
Usage
Search
with TeaClient.from_well_known("example.com") as client:
# Search by PURL
results = client.search_products("PURL", "pkg:pypi/requests")
for product in results.results:
print(product.name, product.uuid)
# Search product releases (with pagination)
releases = client.search_product_releases(
"PURL", "pkg:pypi/requests@2.31.0",
page_offset=0, page_size=100,
)
print(releases.total_results)
Products and releases
with TeaClient.from_well_known("example.com") as client:
product = client.get_product("product-uuid")
print(product.name, product.identifiers)
releases = client.get_product_releases("product-uuid", page_size=25)
for release in releases.results:
print(release.version, release.created_date)
# Single product release
pr = client.get_product_release("release-uuid")
print(pr.version, pr.components)
# Product release collections
latest = client.get_product_release_collection_latest("release-uuid")
all_versions = client.get_product_release_collections("release-uuid")
specific = client.get_product_release_collection("release-uuid", 3)
Components
with TeaClient(base_url="https://api.example.com/tea/v0.3.0-beta.2") as client:
component = client.get_component("component-uuid")
releases = client.get_component_releases("component-uuid")
# Get a component release with its latest collection
cr = client.get_component_release("release-uuid")
print(cr.release.version, len(cr.latest_collection.artifacts))
Collections and artifacts
with TeaClient(base_url="https://api.example.com/tea/v0.3.0-beta.2") as client:
collection = client.get_component_release_collection_latest("release-uuid")
for artifact in collection.artifacts:
print(artifact.name, artifact.type)
# All collection versions for a component release
all_versions = client.get_component_release_collections("release-uuid")
# Specific collection version
collection_v3 = client.get_component_release_collection("release-uuid", 3)
Downloading artifacts with checksum verification
from pathlib import Path
with TeaClient(base_url="https://api.example.com/tea/v0.3.0-beta.2") as client:
artifact = client.get_artifact("artifact-uuid")
fmt = artifact.formats[0]
# Downloads and verifies checksums on-the-fly; returns the dest path
path = client.download_artifact(
fmt.url,
Path("sbom.json"),
verify_checksums=fmt.checksums,
)
Supported checksum algorithms: MD5, SHA-1, SHA-256, SHA-384, SHA-512, SHA3-256, SHA3-384, SHA3-512, BLAKE2b-256, BLAKE2b-384, BLAKE2b-512. BLAKE3 is recognized in the model but not verifiable (Python's hashlib has no BLAKE3 support — a clear error is raised).
Artifact downloads use a separate unauthenticated HTTP session so the bearer token is never leaked to third-party hosts (CDNs, Maven Central, etc.). On checksum mismatch, the downloaded file is automatically deleted.
Discovery
from libtea.discovery import parse_tei, fetch_well_known, select_endpoint
# Parse a TEI URN
tei_type, domain, identifier = parse_tei(
"urn:tei:purl:cyclonedx.org:pkg:pypi/cyclonedx-python-lib@8.4.0"
)
# Low-level: fetch and select an endpoint manually
well_known = fetch_well_known("example.com")
endpoint = select_endpoint(well_known, "0.3.0-beta.2")
print(endpoint.url, endpoint.priority)
# Discover product releases by TEI
with TeaClient(base_url="https://api.example.com/tea/v0.3.0-beta.2") as client:
results = client.discover("urn:tei:uuid:example.com:d4d9f54a-abcf-11ee-ac79-1a52914d44b")
for info in results:
print(info.product_release_uuid, info.servers)
Supported TEI types: uuid, purl, hash, swid, eanupc, gtin, asin, udi.
Error handling
All exceptions inherit from TeaError:
from libtea.exceptions import TeaError, TeaNotFoundError, TeaChecksumError
try:
product = client.get_product("unknown-uuid")
except TeaNotFoundError as exc:
print(exc.error_type) # "OBJECT_UNKNOWN" or "OBJECT_NOT_SHAREABLE"
except TeaError:
print("Something went wrong")
Exception hierarchy:
| Exception | When |
|---|---|
TeaConnectionError |
Network failure or timeout |
TeaAuthenticationError |
HTTP 401/403 |
TeaNotFoundError |
HTTP 404 (.error_type has the TEA error code) |
TeaRequestError |
Other HTTP 4xx |
TeaServerError |
HTTP 5xx |
TeaDiscoveryError |
Invalid TEI, .well-known failure, or no compatible endpoint |
TeaChecksumError |
Checksum mismatch (.algorithm, .expected, .actual) |
TeaValidationError |
Malformed server response |
TeaInsecureTransportWarning |
Warning emitted when using plaintext HTTP |
Using a bearer token over plaintext HTTP raises ValueError immediately — HTTPS is required for authenticated requests.
Requirements
Not yet supported
- Publisher API (spec is consumer-only in beta.2)
- Async client
- CLE (Common Lifecycle Enumeration) endpoints
- Mutual TLS (mTLS) authentication
- Endpoint failover with retry
Development
This project uses uv for dependency management.
uv sync # Install dependencies
uv run pytest # Run tests (with coverage)
uv run ruff check . # Lint
uv run ruff format --check . # Format check
uv build # Build wheel and sdist
License
Apache 2.0 — see LICENSE for details.
Project details
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 libtea-0.1.1.tar.gz.
File metadata
- Download URL: libtea-0.1.1.tar.gz
- Upload date:
- Size: 76.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5197e3992521a182d5f6af1bf75f851a619e5ba61d616bdebd438537beff202
|
|
| MD5 |
aae6c8eec0ffaf29952fd91d3a4d6072
|
|
| BLAKE2b-256 |
7a5ffbf7eae3d7589a7b9c27632a630f90eca5b455ddfebd98812ddf64935883
|
Provenance
The following attestation bundles were made for libtea-0.1.1.tar.gz:
Publisher:
pypi.yaml on sbomify/py-libtea
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
libtea-0.1.1.tar.gz -
Subject digest:
c5197e3992521a182d5f6af1bf75f851a619e5ba61d616bdebd438537beff202 - Sigstore transparency entry: 997204769
- Sigstore integration time:
-
Permalink:
sbomify/py-libtea@e07e4aad5213b613a93a1211b39a452a77927362 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/sbomify
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@e07e4aad5213b613a93a1211b39a452a77927362 -
Trigger Event:
release
-
Statement type:
File details
Details for the file libtea-0.1.1-py3-none-any.whl.
File metadata
- Download URL: libtea-0.1.1-py3-none-any.whl
- Upload date:
- Size: 22.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7b77cc606040152f2bd84f1a9aa59fa09490a7e8e988543850703f08c4caed7c
|
|
| MD5 |
9778033e93f8142fd5436415586643c2
|
|
| BLAKE2b-256 |
8ff39ac0c2f404275134f0bb3160e425d293ec01534fb4c12d11b6d0b30479e6
|
Provenance
The following attestation bundles were made for libtea-0.1.1-py3-none-any.whl:
Publisher:
pypi.yaml on sbomify/py-libtea
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
libtea-0.1.1-py3-none-any.whl -
Subject digest:
7b77cc606040152f2bd84f1a9aa59fa09490a7e8e988543850703f08c4caed7c - Sigstore transparency entry: 997204775
- Sigstore integration time:
-
Permalink:
sbomify/py-libtea@e07e4aad5213b613a93a1211b39a452a77927362 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/sbomify
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yaml@e07e4aad5213b613a93a1211b39a452a77927362 -
Trigger Event:
release
-
Statement type: