Metadata-Version: 2.5
Name: truthlock
Version: 0.4.2
Summary: Official Python SDK for the Truthlocks verification platform
Project-URL: Homepage, https://truthlocks.com
Project-URL: Documentation, https://docs.truthlocks.com/sdk/python
Project-URL: Repository, https://github.com/truthlocks/sdk-python
Project-URL: Issues, https://github.com/truthlocks/sdk-python/issues
Author-email: Truthlocks <support@truthlocks.com>
License-Expression: MIT
License-File: LICENSE
Keywords: attestation,cryptographic-proof,trust,truthlock,verification
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1; extra == 'dev'
Description-Content-Type: text/markdown

# Truthlocks Python SDK

Issue signed records, verify an existing attestation, and retrieve its proof bundle
from Python applications and services.

**Runtime:** Python 3.9+ · **Package:** `truthlock` · **License:** MIT

[API reference](https://docs.truthlocks.com) · [Console](https://console.truthlocks.com) · [Source](https://github.com/truthlocks/sdk-python)

## Install

```sh
python -m pip install truthlock
```

## Verify a record

Set `TRUTHLOCKS_API_KEY` and `TRUTHLOCKS_ATTESTATION_ID` before running:

```python
import os
from truthlock import TruthlockClient, Verdict

with TruthlockClient(api_key=os.environ["TRUTHLOCKS_API_KEY"]) as client:
    result = client.verify.verify_online(os.environ["TRUTHLOCKS_ATTESTATION_ID"])
    print(result.verdict.value)
    if result.verdict == Verdict.VALID:
        print("Record verified")
```

A `tl_test_` key selects the sandbox host by default. Use `base_url` to override it;
production and sandbox credentials must match the selected host. The workspace is
derived from credentials, not a tenant header. A record-only verdict does not check
an unrelated local file: pass `payload_b64url` or `document_hash_hex` for content checks.

## Check the original signature

An ID-only check returns `Verdict.INCOMPLETE` when the signed payload was not supplied. It must not be accepted as a verified signature.

```python
import base64
from pathlib import Path

original = Path(os.environ["TRUTHLOCKS_PAYLOAD_FILE"]).read_bytes()
payload = base64.urlsafe_b64encode(original).rstrip(b"=").decode("ascii")
with TruthlockClient(api_key=os.environ["TRUTHLOCKS_API_KEY"]) as client:
    checked = client.verify.verify_online(
        os.environ["TRUTHLOCKS_ATTESTATION_ID"], payload_b64url=payload
    )
    print(checked.verdict.value, checked.signature_verified, checked.reasons)
    print(checked.expires_at_unix)  # None when no signed expiry is recorded
```

Use the exact signed bytes. Reformatting JSON changes its bytes. A matching document fingerprint alone does not check the envelope's signature. Inspect `details["log"]` when present for transparency proof results; neither a signature nor a fingerprint establishes the truth of every claim.

## Mint bytes

Use an existing issuer and signing key that is available for signing in your workspace:

```python
import base64
import os
import uuid
from truthlock import TruthlockClient

payload = base64.urlsafe_b64encode(b"Example record").rstrip(b"=").decode("ascii")
request_key = str(uuid.uuid4())  # retain alongside the body for a retry
with TruthlockClient(api_key=os.environ["TRUTHLOCKS_API_KEY"]) as client:
    record = client.attestations.mint(
        issuer_id=os.environ["TRUTHLOCKS_ISSUER_ID"],
        kid=os.environ["TRUTHLOCKS_KEY_ID"],
        alg=os.environ["TRUTHLOCKS_ALGORITHM"],
        payload_b64url=payload,
        idempotency_key=request_key,
    )
    print(record.attestation_id)
```

The algorithm must match the signing key. Registering a public key does not upload
its private key or enable service-side signing with that private key.

## Behavior

- Resources: issuers, keys, attestations, online verification and receipts.
- Current proof bundles retain their key, checkpoint, inclusion proof and audit fields.
  Downloading a bundle does not verify it offline.
- Verdict parsing accepts uppercase API verdicts while keeping the existing lowercase enum values.
- Writes are not automatically replayed unless their server route supports idempotency.
  Retain the same key and body when retrying one mint.
- HTTP errors and malformed successful JSON responses raise SDK errors.
- The main client is synchronous. Its separate registration helper is asynchronous;
  it does not make other resource methods awaitable.

Use a context manager or call `close()` when finished. Keep credentials out of source
control and use the API reference for environment and authorization requirements.

[Documentation](https://docs.truthlocks.com/sdk/python) · [Issues](https://github.com/truthlocks/sdk-python/issues)

## Retrieve a proof bundle

```python
import json
import os
from dataclasses import asdict
from pathlib import Path
from truthlock import TruthlockClient

with TruthlockClient(api_key=os.environ["TRUTHLOCKS_API_KEY"]) as client:
    bundle = client.attestations.proof_bundle(os.environ["TRUTHLOCKS_ATTESTATION_ID"])
    with Path("proof-bundle.json").open("x", encoding="utf-8") as output:
        json.dump(asdict(bundle), output, indent=2)
```

The file is created without overwriting an existing bundle. Use independently trusted
issuer and log keys with the offline CLI to validate downloaded evidence.

## Configuration

| Argument | Purpose |
| --- | --- |
| `api_key` | Workspace API key; never commit it to source control. |
| `bearer_token` | Authorized user token, used instead of an API key. |
| `base_url` | Explicit API origin; otherwise selected from key/environment. |
| `environment` | `sandbox` or `production`. |
| `timeout` | HTTP timeout in seconds; default 30. |
| `max_retries` | Retry count for supported safe requests; default 3. |

## Handle errors

```python
import os
from truthlock import TruthlockClient
from truthlock.errors import TruthlockError

try:
    with TruthlockClient(api_key=os.environ["TRUTHLOCKS_API_KEY"]) as client:
        record = client.attestations.get(os.environ["TRUTHLOCKS_ATTESTATION_ID"])
        print(record.attestation_id)
except TruthlockError as error:
    print(f"Truthlocks request failed: {error}")
```

For authentication errors, check the key and host. For mint failures, check that the
issuer is available and the selected key can sign with the requested algorithm. A
network timeout after a write can leave its outcome uncertain: reuse the same stored
idempotency key and identical payload only on supported idempotent operations.

## Migration from 0.3

Version 0.4 preserves current proof-bundle fields, handles uppercase wire verdicts,
selects sandbox for test keys, and avoids replaying unsupported writes. The client
remains synchronous; update examples that previously attempted to await its resource methods.
