Skip to main content

The Thessor SDK — cryptographic AI decision accountability

Project description

Thessor Python SDK

Cryptographic accountability for AI decisions. Seals decisions into tamper-evident, Ed25519-signed envelopes and verifies them — without ever requiring you to send PHI to Thessor.

v0.2.0 · Python ≥ 3.11 · zero third-party dependencies

Install

pip install thessor

Quick start

from thessor import Client

client = Client(api_key="thsr_...")

result = client.seal(
    decision_id="dec-001",
    model_id="cnds-classifier-v3",
    model_version="3.2.1",
    timestamp="2026-06-19T10:00:00Z",
    input_hash="sha256:...",
    output={"label": "approved"},
    confidence_score=0.97,
    policy_version="policy-2026-Q2",
    payload={"finding": "..."},
)
print(result.envelope_id, result.verify_url)

verification = client.verify(result.envelope_id)
assert verification.valid

PHI never has to leave your environment

Use hash_patient_id() to turn a patient ID into a one-way SHA-256 hash before it ever leaves your systems, and pass the hash as patient_id_hash in attestations:

from thessor import hash_patient_id

result = client.seal(
    ...,
    attestations={"patient_id_hash": hash_patient_id(patient.mrn)},
)

Thessor stores and seals only the hash. canonicalize() and sha256_hex() are also exposed for fully offline, trustless verification with no API call (client.verify_direct supports this flow).


API reference

Sealing decisions

client.seal(...) → SealResult

Seal a decision into a tamper-evident, Ed25519-signed envelope.

result = client.seal(
    decision_id="dec-001",         # caller-supplied stable ID
    model_id="cnds-classifier-v3",
    model_version="3.2.1",
    timestamp="2026-06-19T10:00:00Z",
    input_hash="sha256:abc123",    # SHA-256 of the canonical input
    output={"label": "approved"},
    confidence_score=0.97,
    policy_version="policy-2026-Q2",
    payload={"finding": "..."},    # extra context (not sealed, not sent)
    attestations={                 # optional: sealed regulatory metadata
        "regulatory_frameworks": ["fda_pccp", "eu_ai_act_high_risk"],
        "clinician_npi": "1234567890",
        "patient_id_hash": hash_patient_id(patient.mrn),
    },
    sync=True,   # default: block until envelope is written
)
# result.envelope_id, result.canonical_hash, result.signature, result.verify_url

sync=False queues the seal and returns immediately (status "pending"). Poll with get_seal_status(seal_id) or use seal_and_wait().

client.seal_with_rationale(...) → SealWithRationaleResult

Seal a decision and immediately seal its rationale record in one call.

result = client.seal_with_rationale(
    # all seal() fields:
    decision_id="dec-002",
    model_id="cnds-classifier-v3",
    model_version="3.2.1",
    timestamp="2026-06-19T10:00:00Z",
    input_hash="sha256:abc123",
    output={"label": "approved"},
    confidence_score=0.97,
    policy_version="policy-2026-Q2",
    payload={},
    # rationale fields:
    confidence_distribution={"approved": 0.97, "denied": 0.03},
    salient_inputs={"age": 0.42, "risk_score": 0.81},
    policy_logic_applied={"rule": "threshold_0.85", "version": "v3"},
    human_oversight_action="clinician_confirmed",
    human_operator_id_hash=sha256_hex(b"npi:1234567890"),
)
print(result.envelope_id)       # shortcut to result.seal.envelope_id
print(result.rationale_id)
print(result.rationale_hash)

client.seal_agent_decision(...) → dict

Seal an agent tool call without setting up ThessorMCPMiddleware directly. Works with any agent framework (LangChain, AutoGen, custom orchestrators). Raw inputs and outputs are never sent — only their SHA-256 hashes.

envelope = client.seal_agent_decision(
    tool_name="search_formulary",
    tool_input={"drug_name": "metformin", "plan_id": "BCBS-HMO"},
    tool_output={"formulary_match": True, "tier": 2},
    model_id="gpt-4o",
    model_version="2024-11-20",
    agent_id_hash=sha256_hex(b"agent:workflow-v1"),
    confidence=0.95,
    policy_version="formulary-policy-v2",
    parent_decision_id=prior_envelope_id,   # chains to parent decision
)
print(envelope["envelope_id"])

client.seal_and_wait(...) → SealResult

Queue an async seal and block until it completes or times out.

result = client.seal_and_wait(
    poll_interval=0.5,
    timeout=30.0,
    decision_id="dec-003",
    ...   # same kwargs as seal()
)

client.get_seal_status(seal_id) → SealResult

Poll the status of an async seal (seal(sync=False)).


Verification

client.verify(envelope_id) → VerifyResult

Fetch verification metadata for a sealed envelope.

v = client.verify(result.envelope_id)
print(v.valid, v.canonical_hash, v.regulatory_context)

client.verify_with_payload(envelope_id, payload) → VerifyResult

Verify that a payload matches the sealed envelope's hash and signature.

client.verify_direct(payload, signature_hex, public_key_hex) → VerifyResult

Fully trustless, offline verification — no envelope lookup, no DB. Useful for auditors who only have the envelope contents and the published Thessor public key.


Outcomes and consequence chains

client.seal_outcome(outcome_id, model_id, outcome_type, payload) → SealResult

Seal an outcome envelope to later link to a decision.

client.link(decision_envelope_id, outcome_envelope_id, causal_claim) → dict

Causally link a decision envelope to an outcome envelope.

client.get_chain(decision_envelope_id) → ChainResult

Fetch the full consequence chain (decision + linked outcomes) for a decision envelope.


Population attestation

client.attest_population(...) → dict

Post a sealed aggregate performance attestation for a model version over a defined time window. Required for GMLP, EU AI Act post-market surveillance, ISO 42001, and FDA PCCP total-lifecycle monitoring.

attestation = client.attest_population(
    model_version="ct-lung-v4.1.0",
    period_start="2026-01-01T00:00:00Z",
    period_end="2026-03-31T23:59:59Z",
    site_identifier_hash=sha256_hex(b"site:hospital-a"),
    decision_count=1000,
    confirmed_count=400,
    true_positive_count=180,
    false_positive_count=20,
    true_negative_count=190,
    false_negative_count=10,
    drift_flag=False,
    regulatory_frameworks=["fda_pccp", "gmlp_principle_7"],
)
print(attestation["attestation_id"])
print(attestation["sensitivity"])    # 0.947
print(attestation["specificity"])    # 0.905

Offline audit export

client.export_packet(decision_id) → dict

Fetch the signed, self-verifying offline audit packet for a decision — a single JSON bundle containing the decision envelope, replay record, rationale, consequence chain, population context, and Merkle checkpoint. Every nested record carries its own signature; the export_signature seals the entire bundle. A regulator can verify it offline without Thessor infrastructure.

packet = client.export_packet(envelope_id)
# packet["export_signature"] seals every field

client.export_to_file(decision_id, filepath) → dict

Fetch the audit packet and write it to filepath as pretty-printed JSON. Returns the packet dict as well.

packet = client.export_to_file(envelope_id, "/tmp/audit-dec-001.json")

MCP middleware

For agent frameworks using the Model Context Protocol, use ThessorMCPMiddleware to seal every tool call automatically:

from thessor import ThessorMCPMiddleware, sha256_hex

middleware = ThessorMCPMiddleware(
    thessor_client=client,
    model_id="claude-sonnet-4-6",
    model_version="20250620",
    agent_id_hash=sha256_hex(b"agent:my-workflow-v1"),
)

# Seal a tool call manually:
envelope = middleware.seal_tool_call(
    "lookup_icd10",
    tool_input={"description": "type 2 diabetes"},
    tool_output={"code": "E11", "description": "Type 2 diabetes mellitus"},
    confidence=0.99,
    parent_decision_id=prior_envelope_id,
)

# Or use the decorator to seal every call automatically:
@middleware.seal_mcp_tool
def search_formulary(tool_input: dict) -> dict:
    return {"tier": 2, "covered": True}

result = search_formulary({"drug_name": "metformin"})
# → tool runs, output sealed, result returned unchanged

seal_tool_call_async() is also available for async contexts.


Error handling

All API errors raise a subclass of ThessorError:

from thessor import AuthError, ValidationError, RateLimitError, NotFoundError

try:
    client.seal(...)
except ValidationError as exc:
    print(exc.field_errors)   # structured per-field validation errors
except AuthError:
    print("check your API key")
except RateLimitError:
    print("back off and retry later")
except NotFoundError:
    print("envelope not found")

429 and 5xx responses are retried automatically with exponential backoff (0.5 s, 1 s, 2 s). 4xx errors are raised immediately.


Changelog

0.2.0

  • Client.seal_with_rationale() — seal + rationale in one call
  • Client.seal_agent_decision() — agent sealing without MCP setup
  • Client.attest_population() — population performance attestation
  • ThessorMCPMiddleware — MCP-native agent decision sealing
  • Model classes (SealResult, SealWithRationaleResult, VerifyResult, ChainResult) now exported from the top-level package
  • Version bump to 0.2.0

0.1.0

  • Initial release: Client, FHIRConnector, DICOMSRConnector, ATNAConnector, HL7Connector, PHI-safe hashing utilities, export_packet, export_to_file

Releasing

To publish a new version to PyPI:

  1. Bump version in sdk/pyproject.toml, sdk/thessor/__init__.py, and sdk/VERSION
  2. git add sdk/
  3. git commit -m "release: SDK v0.x.x"
  4. git tag sdk-v0.x.x
  5. git push origin main --tags

GitHub Actions will build and push to PyPI automatically.

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

thessor-0.2.0.tar.gz (36.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

thessor-0.2.0-py3-none-any.whl (32.0 kB view details)

Uploaded Python 3

File details

Details for the file thessor-0.2.0.tar.gz.

File metadata

  • Download URL: thessor-0.2.0.tar.gz
  • Upload date:
  • Size: 36.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for thessor-0.2.0.tar.gz
Algorithm Hash digest
SHA256 5d50afb81fdd79e7cd3c50e8f85a04461d6bb16a629bab273bb21a3193660117
MD5 6504e23ccae8fa8328a689cbc2d3a227
BLAKE2b-256 d0267d1072c1364492713d5a89742c8315bd3cfd8abc614b219083ffff1bce71

See more details on using hashes here.

Provenance

The following attestation bundles were made for thessor-0.2.0.tar.gz:

Publisher: publish-sdk.yml on SOLOMONTHESSOR/thessor-api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file thessor-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: thessor-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 32.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for thessor-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4c4ad0e04853f02f02c4cd0c55423c0a34c0ec180d2c3e02d2b8944cb71f2597
MD5 6a07b8f494932810fec472c83ce59b6b
BLAKE2b-256 d196d3fe72a8c949100a55b329e4c960fd6370f3ab1136156d78f139065006a3

See more details on using hashes here.

Provenance

The following attestation bundles were made for thessor-0.2.0-py3-none-any.whl:

Publisher: publish-sdk.yml on SOLOMONTHESSOR/thessor-api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page