Official Python SDK for the CertiSigma attestation & verification API
Project description
CertiSigma Python SDK
Official Python client for the CertiSigma cryptographic attestation API.
Installation
pip install certisigma
Or from source:
cd sdk/python
pip install -e .
Quick Start
import os
from certisigma import CertiSigmaClient, hash_file, hash_bytes, hash_string
client = CertiSigmaClient(api_key=os.environ["CERTISIGMA_API_KEY"])
# 1. Compute the SHA-256 hash of your file
file_hash = hash_file("contract.pdf")
# 2. Attest — creates a timestamped, signed proof of existence
result = client.attest(file_hash, source="my-app")
print(f"Attestation: {result.id} at {result.timestamp}")
print(f"ECDSA signature: {result.signature}")
# 3. Verify — confirm the hash was attested
check = client.verify(file_hash)
print(f"Exists: {check.exists}, Level: {check.level}")
# Or hash raw bytes
data_hash = hash_bytes(b"any raw content")
result = client.attest(data_hash)
Public Verification (No API Key)
Verification endpoints are public. You can verify attestations without any API key:
from certisigma import CertiSigmaClient, hash_file
# No api_key needed — works out of the box
client = CertiSigmaClient()
file_hash = hash_file("contract.pdf")
check = client.verify(file_hash)
print(f"Exists: {check.exists}, Level: {check.level}")
# Batch verify also works without a key
results = client.batch_verify([file_hash])
print(f"Found: {results.found}/{results.count}")
Hashing Utilities
Standalone SHA-256 hash functions -- compute hashes without attestation:
from certisigma import hash_file, hash_bytes, hash_string
# Hash a file (streamed, constant memory)
file_hash = hash_file("/path/to/document.pdf")
# Hash raw bytes
data_hash = hash_bytes(b"raw content")
# Hash a string (UTF-8, no file needed)
str_hash = hash_string("SN-2026-001234")
Or hash + attest in one step:
result = client.attest_file("/path/to/document.pdf")
print(f"Attested: {result.hash_hex}")
String Attestation
Attest identifiers, serial numbers, or any non-file content without temporary files. The string is hashed client-side (UTF-8); only the hash reaches the API:
# One-step: hash + attest
result = client.attest_string("SN-2026-001234", source="serial-registry")
# Verify later
check = client.verify_string("SN-2026-001234")
assert check.exists
# Also works for files
check = client.verify_file("/path/to/document.pdf")
Canonical form: UTF-8, no BOM, no trailing newline, no normalization.
hash_string("SN-001")andhash_string("SN-001\n")produce different hashes.Note:
sourceis never auto-populated from the string content. Pass it explicitly if needed — otherwise the string would leak into server-visible metadata.
Async Support
import asyncio, os
from certisigma import AsyncCertiSigmaClient
async def main():
async with AsyncCertiSigmaClient(api_key=os.environ["CERTISIGMA_API_KEY"]) as client:
result = await client.attest("abcdef..." * 4 + "0" * 16)
print(result.id)
asyncio.run(main())
Batch Operations
# Attest up to 100 hashes in one call — returns full claim metadata per item
batch = client.batch_attest(
["aabb..." * 4, "ccdd..." * 4],
source="monthly-invoices"
)
print(f"Created: {batch.created}, Existing: {batch.existing}")
for att in batch.attestations:
print(f" {att['id']} claim={att['claim_id']} src={att['source']}")
# Verify batch (public, no key needed)
results = client.batch_verify(["aabb..." * 4, "ccdd..." * 4])
print(f"Found: {results.found}/{results.count}")
# Detailed mode — certification level + claim metadata (requires api_key)
results = client.batch_verify(["aabb..." * 4], detailed=True)
for r in results.results:
if r["exists"]:
print(f" {r['id']} level={r['level']} src={r['source']}")
Attestation Status
Check the current trust tier of an attestation (public, no API key needed):
status = client.status("att_1234")
print(f"Level: {status.level}") # "T0", "T1", or "T2"
print(f"Signature: {status.signature_available}")
print(f"Merkle: {status.merkle_proof_available}")
print(f"OTS: {status.ots_available}")
| Method | Input | Auth | Best for |
|---|---|---|---|
verify(hash) |
SHA-256 hash | Optional | Compliance checks, "does this hash exist?" |
status(att_id) |
Attestation ID | No | Dashboards, progress tracking, polling |
get_evidence(att_id) |
Attestation ID | No | Independent verification, long-term archival |
Metadata Management
# Update claim metadata
result = client.update_metadata("att_1234", source="pipeline-v2", extra_data={"project": "alpha"})
# Soft-delete claim
client.delete_metadata("att_1234")
# Get evidence
evidence = client.get_evidence("att_1234")
print(evidence.level, evidence.t0)
Evidence & OTS Verification
import os
from certisigma import CertiSigmaClient, get_blockchain_url, save_ots_proof
client = CertiSigmaClient(api_key=os.environ["CERTISIGMA_API_KEY"])
# Get full cryptographic evidence (T0 + T1 + T2)
evidence = client.get_evidence("att_1234")
if evidence.level == "T2":
# Bitcoin block explorer link
print(get_blockchain_url(evidence)) # mempool.space/block/...
print(get_blockchain_url(evidence, "tx")) # mempool.space/tx/...
# Save the raw .ots proof for independent verification
save_ots_proof(evidence, "contract.pdf.ots")
# Then verify with: ots verify contract.pdf.ots
Client-Side Encryption (Zero Knowledge)
Requires: pip install certisigma[crypto]
from certisigma.crypto import generate_key, encrypt_metadata, decrypt_metadata
# Generate a key (store securely — server never sees it)
key = generate_key()
# Encrypt before sending
encrypted = encrypt_metadata({"secret": "classified"}, key)
result = client.attest(hash_hex, extra_data=encrypted, client_encrypted=True)
# Decrypt after retrieving
plaintext = decrypt_metadata(result.extra_data, key)
Error Handling
from certisigma import (
CertiSigmaError,
AuthenticationError,
RateLimitError,
QuotaExceededError,
)
try:
client.attest(hash_hex)
except AuthenticationError:
print("Invalid API key")
except RateLimitError as e:
print(f"Rate limited, retry after {e.retry_after}s")
except QuotaExceededError:
print("Monthly quota reached")
except CertiSigmaError as e:
print(f"API error {e.status_code}: {e}")
Configuration
| Parameter | Default | Description |
|---|---|---|
api_key |
None |
Bearer token (cs_live_...). Optional for verify/health. |
base_url |
https://api.certisigma.ch |
API endpoint |
timeout |
30.0 |
Request timeout in seconds |
Proxy: httpx respects HTTP_PROXY / HTTPS_PROXY / NO_PROXY environment variables.
Custom TLS CA: Configure httpx with verify="/path/to/ca-bundle.pem" for corporate proxies.
Retry: Not built-in. Implement at caller level — retry only on 429 and 5xx, fail fast on other 4xx.
Share Tokens (Forensic Metadata Sharing)
Create time-limited, auditable tokens for sharing attestation metadata with forensic analysts:
token = client.create_share_token(
attestation_ids=[42, 43, 44],
expires_in=86400,
recipient_label="forensic-analyst",
max_uses=10,
)
print(f"Share token: {token.share_token}") # shown once, save it
tokens = client.list_share_tokens()
info = client.get_share_token_info(token.id)
client.revoke_share_token(token.id)
Structured Tagging
Multi-dimensional classification of attestations with server-side querying:
# Upsert tags (max 50 per attestation per key)
client.put_tags("att_42", tags=[
{"key": "department", "value": "hr"},
{"key": "classification", "value": "confidential"},
])
tags = client.get_tags("att_42")
# Query by tags — AND semantics, max 10 conditions per query
results = client.query_tags(
filter={"and": [
{"key": "department", "value": "hr"},
{"key": "classification", "value": "confidential"},
]},
limit=100,
)
print(f"Found {results.count} matching attestations")
client.delete_tag("att_42", "classification")
Tag key format: ^[a-z][a-z0-9_-]{0,62}$ (lowercase, no _ prefix — reserved). Client-encrypted tags (value_enc + value_nonce hex) are supported but excluded from server-side query.
Census — Derived Lists
Create opaque HMAC-SHA256 derived lists for third-party hash verification without revealing your inventory. Requires census scope and org_id.
import hashlib, hmac
# Create a derived list
dl = client.create_derived_list(
hashes=["a665a459...", "b4c9a289..."],
label="Partner audit Q1",
expires_in_hours=720,
)
print(f"List key (save now!): {dl.list_key}")
# Third party: match files (no API key needed)
public = CertiSigmaClient()
file_hash = "a665a459..."
derived = hmac.new(
bytes.fromhex(dl.list_key), file_hash.encode(), hashlib.sha256,
).hexdigest()
result = public.match_derived_list(dl.id, dl.list_key, [derived])
print(f"Matched: {result.matched}/{result.total}")
# Owner operations
lists = client.list_derived_lists()
detail = client.get_derived_list(dl.id)
sig = client.get_derived_list_signature(dl.id)
log = client.get_derived_list_access_log(dl.id)
client.revoke_derived_list(dl.id)
Webhooks (T1 / T2 lifecycle)
Requires an API key with the webhook scope. Events: t1_complete, t2_complete. The backend validates URLs (HTTPS in production; SSRF protections apply server-side).
import os
from certisigma import CertiSigmaClient, verify_webhook_signature
client = CertiSigmaClient(api_key=os.environ["CERTISIGMA_API_KEY"])
# Register — signing_secret is shown once; store it securely (env/secret manager)
wh = client.register_webhook(
url="https://your-server.example.com/certisigma-hook",
events=["t1_complete", "t2_complete"],
label="census-prod", # optional
)
print(wh.id, wh.signing_secret) # never log signing_secret
# List / delete / delivery history
all_hooks = client.list_webhooks()
client.delete_webhook(wh.id)
history = client.list_webhook_deliveries(wh.id)
# In your HTTP handler: verify raw body bytes against X-CertiSigma-Signature
# is_ok = verify_webhook_signature(request.body(), signature_header, signing_secret)
verify_webhook_signature uses hmac.compare_digest (constant-time). Pass raw request body as bytes, not a parsed JSON string, or verification will fail.
The public API does not accept a custom signing secret on register_webhook (omit secret or use null in raw JSON). The server always returns a random signing_secret once per registration.
AsyncCertiSigmaClient exposes the same four methods (register_webhook, list_webhooks, delete_webhook, list_webhook_deliveries).
Read Metadata
Explicit metadata read without re-verifying:
meta = client.get_metadata("att_42")
print(f"Source: {meta.source}, Extra: {meta.extra_data}")
Security & Privacy
CertiSigma is a public attestation platform. Cryptographic proofs (hash, signature, Merkle, OTS) are intentionally public and verifiable by any party. Organizational metadata (source, extra_data, tags) is never exposed on public endpoints — only the authenticated API key owner can read their own claim data. Tags are scoped per API key with full tenant isolation. Share tokens provide time-limited, auditable, revocable read-only access to specific attestations — all accesses are logged. For sensitive metadata, use client-side encryption (encrypt_metadata()); for sensitive tag values, use client-side encrypted tags (value_enc + value_nonce).
See the Security & Privacy Model in the full documentation for the complete threat model and data boundary.
Test Vectors
Canonical test vectors with real production data are available for independent T0/T1/T2 verification:
Compatibility
- Follows Semantic Versioning 2.0.0.
- SDK v1.x targets API v1 (
/v1/prefix). Breaking API changes get a new prefix with 12-month deprecation window. - See full SDK documentation for error codes, webhook semantics, T0 signature format, and compatibility policy.
Requirements
- Python 3.10+
httpx>= 0.25.0cryptography>= 44.0.0 (optional, forcertisigma.crypto)
License
MIT — Ten Sigma Sagl, Lugano, Switzerland
Project details
Release history Release notifications | RSS feed
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 certisigma-1.10.0.tar.gz.
File metadata
- Download URL: certisigma-1.10.0.tar.gz
- Upload date:
- Size: 28.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e21766f76a6f67d3b672ef5c02749f68b45e6da8262c5bd905b0f5d4ae4d015
|
|
| MD5 |
c634eb6945b69e44c853512a0971690b
|
|
| BLAKE2b-256 |
11373d14ef5ca84e7d56da606b55ba8d1192a71d396d653dfbe2758cf202b249
|
File details
Details for the file certisigma-1.10.0-py3-none-any.whl.
File metadata
- Download URL: certisigma-1.10.0-py3-none-any.whl
- Upload date:
- Size: 19.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3393a4d971d2c9a4a3b46ed7820a20ca333c0e497099b71ee8d7e5b4036183fa
|
|
| MD5 |
503d9532dd18f5355059dfddda8cb47e
|
|
| BLAKE2b-256 |
b88fa4d5ebf34bccca0f75f42d0250cca5359b7cb998267f293651794d9aadda
|