reference

Python SDK Reference

The attestd package provides sync and async clients, typed response models, named exceptions, and testing utilities. Requires Python 3.10+.

bash
pip install attestd
client

attestd.Client

Synchronous client. Thread-safe; a single instance can be shared across threads.

ParameterDescription
api_keyRequired. Your Attestd API key.
base_urlBase URL override. Defaults to "https://api.attestd.io".
timeoutRequest timeout in seconds. Default: 10.0.
max_retriesRetry attempts on 5xx errors. Default: 2. Set to 0 to disable.
transportOptional httpx transport override — used in tests (see attestd.testing).
check.py
import attestd

client = attestd.Client(api_key="YOUR_API_KEY")

result = client.check("nginx", "1.24.0")
print(result.risk_state)         # "high"
print(result.actively_exploited) # False
print(result.cve_ids)            # ["CVE-2021-23017"]

client.close()
gate.py
with attestd.Client(api_key="YOUR_API_KEY") as client:
    result = client.check("log4j", "2.14.1")
    if result.risk_state == "critical":
        raise SystemExit("Deployment blocked")

attestd.AsyncClient

Async client with identical parameters to Client. Use in FastAPI, async agents, or any asyncio environment. Call await client.check().

async_check.py
import attestd
import asyncio

async def check_deps():
    async with attestd.AsyncClient(api_key="YOUR_API_KEY") as client:
        result = await client.check("redis", "6.0.9")
        return result

result = asyncio.run(check_deps())
models

attestd.RiskResult

Frozen dataclass returned by client.check(). All fields are read-only. See the Response Field Reference for field semantics.

python
@dataclass(frozen=True, slots=True)
class RiskResult:
    product: str
    version: str
    risk_state: RiskState        # "critical" | "high" | "elevated" | "low" | "none"
    risk_factors: list[str]
    actively_exploited: bool
    remote_exploitable: bool
    authentication_required: bool
    patch_available: bool
    fixed_version: str | None
    confidence: float
    cve_ids: list[str]
    last_updated: datetime
errors

Error types

ExceptionWhen raised
AttestdAuthErrorHTTP 401 — invalid or missing API key
AttestdRateLimitErrorHTTP 429 — rate limit exceeded. Has retry_after attribute (seconds).
AttestdUnsupportedProductErrorsupported: false — product is outside coverage
AttestdAPIErrorHTTP 5xx, connection errors, or unexpected responses after all retries
AttestdErrorBase class for all above — catch this for a single broad handler

Note on retry behaviour: 401 and 429 errors are never retried. Only transient 5xx errors and connection failures are retried (up to max_retries).

errors.py
import attestd

client = attestd.Client(api_key="YOUR_API_KEY")

try:
    result = client.check("nginx", "1.24.0")
except attestd.AttestdAuthError:
    print("Invalid API key")
except attestd.AttestdRateLimitError as e:
    print(f"Rate limited — retry in {e.retry_after}s")
except attestd.AttestdUnsupportedProductError as e:
    # Product is outside coverage — not a safety clearance.
    # Make an explicit policy decision: block, warn, or skip.
    print(f"{e.product} is outside attestd coverage")
    raise  # or handle according to your policy
except attestd.AttestdAPIError as e:
    print(f"API error: {e}")
testing

attestd.testing

The attestd.testing module provides httpx transport classes for injecting mock API responses without making real network calls. Use these to test your security branching logic against controlled, reproducible responses.

MockTransport / MockAsyncTransport

Returns the same response for every request.

test_gate.py
import attestd
from attestd.testing import MockTransport, NGINX_VULNERABLE, NGINX_SAFE

def test_deployment_blocked_on_high_risk():
    transport = MockTransport(200, NGINX_VULNERABLE)
    client = attestd.Client(api_key="test", transport=transport)

    result = client.check("nginx", "1.20.0")
    assert result.risk_state == "high"

def test_deployment_allowed_when_safe():
    transport = MockTransport(200, NGINX_SAFE)
    client = attestd.Client(api_key="test", transport=transport)

    result = client.check("nginx", "1.27.4")
    assert result.risk_state == "none"

SequentialMockTransport / SequentialMockAsyncTransport

Returns responses from a pre-defined sequence. Use for testing retry logic where the first one or two requests fail before a success. This pattern is especially useful for testing AI agent tools that must handle transient failures gracefully.

test_retry.py
from attestd.testing import SequentialMockTransport, NGINX_SAFE

def test_retries_on_503():
    transport = SequentialMockTransport([
        (503, {}),         # first attempt fails
        (503, {}),         # second attempt fails
        (200, NGINX_SAFE), # third attempt succeeds
    ])
    client = attestd.Client(api_key="test", transport=transport, max_retries=2)
    result = client.check("nginx", "1.27.4")
    assert result.risk_state == "none"
    assert transport.call_count == 3

Canned response bodies

Ready-made response dicts for common test scenarios. All are plain dicts and can be merged with | to override individual fields.

test_coverage.py
from attestd.testing import (
    NGINX_SAFE,       # risk_state: "none"
    NGINX_VULNERABLE, # risk_state: "high"
    LOG4J_CRITICAL,   # risk_state: "critical", actively_exploited: true
    UNSUPPORTED,      # supported: false
)

# Override individual fields
transport = MockTransport(200, NGINX_VULNERABLE | {"risk_state": "critical"})