Signatrust — Python SDK
A verifiable, tamper‑evident receipt for every AI decision. Standard library only — no dependencies.
from signatrust import Signatrust
client = Signatrust(api_key=os.environ["SIGNATRUST_API_KEY"])
result = client.sign(
model={"provider": "openai", "name": "gpt-4o", "version": "2026.4"},
decision={
"type": "loan_rejection",
"input": user_input, # hashed locally — raw data never leaves
"output": agent_output, # hashed locally
"risk_level": "high",
"human_review": True,
"policies": ["eu-ai-act-high-risk"],
},
)
receipt = result["receipt"]
assert client.verify(receipt["id"])["valid"]
Zero-Data-Access by default
Signatrust is designed so that the raw content of a decision, the subject's identity, and the reviewer's identity stay inside your environment. The SDK only ships cryptographic commitments (SHA-256) to our servers.
| Field | Sent to Signatrust | Stays local |
|---|---|---|
input / output raw text |
✗ | ✓ (via hash_locally=True, default) |
input_hash / output_hash |
✓ | ✓ |
| Model id, risk level, policies, business event | ✓ | ✓ |
| Subject identity (patient MRN, applicant id, claim id) | ✗ (never — you don't send it) | ✓ |
| Reviewer identity, role, specialty | ✗ (never — see Human review) | ✓ |
human_review_attestation_hash (SHA-256 of reviewer info) |
✓ | ✓ |
| Receipt id, receipt hash, signature | ✓ | ✓ (returned to you) |
Constructor
Signatrust(api_key, base_url="https://signatrust.net", hash_locally=True, timeout=15.0)
Methods
sign(decision, model=None, metadata=None, trace=None, scope_declaration=None, human_reviewer=None)→ seal a decision.issue_receipt(...)→ alias forsign(identical behaviour).verify(receipt_id)→ verify a stored receipt.verify_receipt(receipt)→ verify a receipt object you hold.get_receipt(receipt_id)→ fetch a sealed receipt.trust_score(agent_id)→ fetch an agent's Trust Score.trace(trace_id=None, mode="every_tool_call")→ open a trace scope for tool-call-level sealing.
Utilities also exported: fingerprint(content), new_trace_id(), human_reviewer_attestation(reviewer), and the ReceiptReferenceStore class.
Human review attestation — WHO reviewed and what is their specialty
Signatrust records whether a human reviewed a decision (human_review: True|False) and optionally a cryptographic commitment to the reviewer's identity and specialty (human_review_attestation_hash). The reviewer's actual name, role, and specialty stay on your side — they never enter our systems.
result = client.sign(
decision={
"type": "triage_recommendation",
"input": raw_prompt, # hashed locally
"output": raw_output, # hashed locally
"risk_level": "high",
"policies": ["triage-protocol-v4", "stemi-fast-track"],
},
human_reviewer={
"id": "STAFF-7734", # your internal staff/licence id
"role": "attending_physician", # free-form role label
"specialty": "cardiology", # discipline
"reviewed_at": "2027-06-01T09:20:00Z", # ISO-8601, defaults to now
"note": "STEMI protocol confirmed",
},
)
# result["receipt"]["decision"]["human_review"] == True
# result["receipt"]["decision"]["human_review_attestation_hash"] == "sha256:<64 hex>"
# result["human_reviewer"] echoes the reviewer fields for your local storage.
What each side holds
Customer supplies to the SDK: Sent to Signatrust (enters signed receipt):
────────────────────────────── ──────────────────────────────
{ {
"id": "STAFF-7734", "human_review": true,
"role": "attending_physician", "human_review_attestation_hash":
"specialty": "cardiology", "sha256:<64 hex>"
"reviewed_at": "2027-06-01T09:20:00Z", }
"note": "STEMI protocol confirmed" (no reviewer name / role /
} specialty / note travels)
Years later, given the reviewer's original details (held by you or your customer's record system), anyone can recompute the same commitment via human_reviewer_attestation({...})["hash"] and match it against the historical value inside the sealed receipt — proving which reviewer signed off, without Signatrust ever having held that information.
ReceiptReferenceStore — linking receipts to your own records
Signatrust intentionally does not hold a subject → receipt_id map. The subject (patient, applicant, employee, claim) belongs to your domain; the receipt belongs to ours. The bridge lives on your side.
The SDK ships a minimal, dependency-free ReceiptReferenceStore that persists these bridges as append-only JSONL with mode 0o600 — a reference implementation you can adopt, adapt, or replace with your own database.
from signatrust import Signatrust, ReceiptReferenceStore
client = Signatrust(api_key=os.environ["SIGNATRUST_API_KEY"])
store = ReceiptReferenceStore(file_path="/var/lib/hospital/signatrust-refs.jsonl")
result = client.sign(
decision={...},
human_reviewer={
"id": "STAFF-7734", "role": "attending_physician", "specialty": "cardiology",
},
)
# Attach the Receipt ID to your own internal record. Signatrust never sees this row.
store.attach(
result,
subject_ref="PT-84721", # your internal patient/customer id
case_ref="E-2027-551", # your internal encounter/case id
label="triage_recommendation",
actor="TriageBot v4",
action="STEMI fast-track routing",
method_note="protocol=stemi-fast-track; prompt_id=triage-2027-Q3",
)
# Five years later, retrieve by any local identifier:
by_patient = store.find_by_subject("PT-84721")
by_case = store.find_by_case("E-2027-551")
by_receipt = store.find_by_receipt_id("STR-A1B2C3...")
What each row contains (all local, never sent to Signatrust)
| Field | Purpose |
|---|---|
receipt_id, receipt_hash |
The bridge to the Signatrust receipt. |
subject_ref |
Your internal subject id (patient MRN, applicant id, claim id, employee id). |
case_ref |
Optional secondary id (encounter, transaction, work-order). |
label, actor, action |
Human-searchable descriptors in your vocabulary. |
method_note |
Free-text: protocol id, prompt id, chain-of-custody id. |
input_hash, output_hash |
Copies of the receipt commitments (convenience). |
human_reviewer |
Full reviewer object — id, role, specialty, reviewed_at, note. |
human_review_attestation_hash |
Copy of the commitment that entered the signed receipt. |
tags |
Any non-sensitive tags for filtering. |
recorded_at |
When your system wrote the row. |
Deeper walkthrough: https://signatrust.net/receipt-reference.
Chain of custody — tool-call level sealing
By default, sign() seals the final decision only. For agentic workflows
where intermediate tool calls influence the outcome, open a trace and seal
every step — the full chain (search → db lookup → LLM decision → final response)
becomes a verifiable, tamper-evident timeline sharing one trace_id.
trace = client.trace(mode="every_tool_call")
trace.step(
step_type="tool_call",
tool_name="search",
decision={"type": "tool_result", "output": raw_search_json},
)
trace.step(
step_type="tool_call",
tool_name="db_lookup",
decision={"type": "tool_result", "output": rows_json},
)
trace.step(
step_type="final_response",
decision={
"type": "loan_decision",
"input": user_application,
"output": final_decision_json,
"risk_level": "high",
},
)
print(trace.trace_id, len(trace.receipts)) # 3
Receipt Mode
| Mode | Seals |
|---|---|
final_decision_only (default of sign()) |
Only the final agent output. |
every_tool_call |
One receipt per tool invocation. |
every_agent_step |
One receipt per agent reasoning step. |
custom |
You pick step_type per call. |
Decision Boundary Disclosure (DBD)
Declare what your system evaluated and what it excluded — backed by versioned sector schemas. Silent omission is structurally impossible: every domain in the schema must be classified.
result = client.sign(
decision={"type": "collision_assessment", "input": raw_in, "output": raw_out, "risk_level": "high"},
scope_declaration={
"sector_schema_id": "automotive_collision.v1",
"domains_evaluated": [
{"domain_id": "physics_impact_force", "status": "computed"},
{"domain_id": "vehicle_structural_integrity", "status": "computed"},
],
"domains_excluded": [
{"domain_id": "occupant_biological_impact",
"reason": "no_data_source_available", "excluded_by": "data_gap"},
{"domain_id": "pedestrian_third_party_impact",
"reason": "not_in_scope_of_this_module", "excluded_by": "design"},
# ...all remaining domains
],
},
)
DBD requires that every domain defined by the referenced sector schema appears in either domains_evaluated or domains_excluded — silent omission is rejected by the server.
Upgrading from earlier versions
Every field introduced in the SDK is additive and optional. Upgrading does not change the shape of receipts you have already sealed and it does not change any existing method signature.
human_reviewer,human_reviewer_attestation,ReceiptReferenceStore— new, opt-in.decision["human_review_attestation_hash"]— optional field on the receipt body.None/ missing values are dropped by canonicalisation, so a receipt sealed before this SDK version produces the exact same canonical bytes today as it did then. All historical receipts continue to verify unchanged.traceand DBD (scope_declaration) remain unchanged; existing workflows keep working.
If a call site does not pass human_reviewer or scope_declaration, the SDK behaves exactly as in previous versions.
Missing something? Ask us to build it
If your stack, framework, or workflow needs an integration that isn't shipped yet — a specific SDK, a hosted connector, a native adapter for your platform, or a custom field mapping — send the details to partners@signatrust.net. We'll ship what you need.
Please include:
- The runtime or framework (Node/Python/Go/Rust/Java/.NET, LangChain, LlamaIndex, CrewAI, n8n, Zapier, Make, …).
- The subject records you'd want to bind receipts to (patient, applicant, claim, transaction, work-order, …).
- Any human-review workflow (who signs off, what specialty, whether attestation must be verifiable later).
- Whether you need on-prem, hybrid, or self-hosted verification tooling.
License
Apache-2.0
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 signatrust-1.3.1.tar.gz.
File metadata
- Download URL: signatrust-1.3.1.tar.gz
- Upload date:
- Size: 12.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6eb06b892f60df18ace5c3fb2c56a9620acb173d5bcee8de22f6e53803e9a2b5
|
|
| MD5 |
4572320fc8bab7d42f20bfe43c833853
|
|
| BLAKE2b-256 |
12ea41e178cdbdc839156c8dea2f2433d653a5103b0c7633394a9791a1fefd31
|
File details
Details for the file signatrust-1.3.1-py3-none-any.whl.
File metadata
- Download URL: signatrust-1.3.1-py3-none-any.whl
- Upload date:
- Size: 11.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7354870ec16f83fd909de98402182d3d39d8dec49dc999a91c7d9e2cf495b9a
|
|
| MD5 |
1401d2189dfa60f05ab403c1038c12a6
|
|
| BLAKE2b-256 |
9cb93f88223b19bcc7dc8a5bc818a828585c7c0796b87db7c1c5ceae65942e55
|