Skip to content

Diogenes Developer SDK Reference

The Diogenes Developer SDK provides a high-level Python API for document signing, verification, trust evaluation, and webhook management. It wraps the core Diogenes modules behind a clean facade so you can integrate cryptographic provenance into your application with minimal boilerplate.

Installation

Internal teams and partners install a pinned, versioned wheel attached to a GitHub Release on the (private) ubiquitousthey/diogenes repository. See Installing the Diogenes SDK for the full guide, including CI usage, latest-release lookup, and pyproject.toml pinning.

Quick form, for tag v0.0.2:

gh release download v0.0.2 \
  --repo ubiquitousthey/diogenes \
  --pattern '*.whl' \
  --dir /tmp
pip install /tmp/diogenes_sdk-0.0.2-py3-none-any.whl

gh must be authenticated with a PAT (repo scope) — the repo is private, so the raw release-asset URL is not directly pip-installable.

From a local checkout (development)

If you're working in the monorepo itself, install editable with dev dependencies:

pip install -e ".[dev]"

Or just the runtime package:

pip install -e .

The SDK is included in the diogenes package at diogenes.sdk.

Quickstart

from diogenes.sdk import DiogenesSDK, generate_key_pair, compute_fingerprint
from diogenes.core.transparency_log import TransparencyLogService
from diogenes.core.schemas import EventType
from diogenes.core.keys import _serialize_public_key, compute_fingerprint as core_fp

# 1. Set up a transparency log
log = TransparencyLogService()

# 2. Generate and register a key pair
private_key, public_key = generate_key_pair("ed25519")
fp = core_fp(public_key)
log.append_entry(EventType.KEY_REGISTRATION, {
    "fingerprint": fp,
    "algorithm": "ed25519",
    "public_key": _serialize_public_key(public_key),
})

# 3. Create the SDK client
sdk = DiogenesSDK(transparency_log=log)

# 4. Sign a document
receipt = sdk.sign("Hello, Diogenes!", private_key)
manifest, attestation = receipt.manifest, receipt.attestation
# receipt.leaf_index → pass to GET /api/v1/log/proof/{leaf_index}
# receipt.log_entry_id → autoincrement PK on the log
# Legacy 2-tuple unpack still works: ``manifest, attestation = sdk.sign(...)``

# 5. Verify the document
result = sdk.verify(manifest, source_bytes="Hello, Diogenes!")
print(result.overall.value)  # "valid"

See examples/sdk_quickstart.py for a complete runnable example including trust evaluation and webhooks.


Core Classes

DiogenesSDK

The primary entry point. Wraps signing, verification, and trust evaluation.

from diogenes.sdk import DiogenesSDK

Constructor

DiogenesSDK(
    transparency_log: TransparencyLogService,
    endorsement_service: EndorsementService | None = None,
    trust_config: TrustConfiguration | None = None,
)
Parameter Required Description
transparency_log Yes The transparency log service for key registration and attestation recording.
endorsement_service No Required for evaluate_trust() and explain_trust().
trust_config No Required for evaluate_trust() and explain_trust().

Methods

sign(content, private_key, attestation_type="authorship", predecessors=None, intent_statement=None)

Sign content and produce a manifest with an attestation.

Parameter Type Default Description
content bytes \| str -- The document content to sign.
private_key PrivateKey -- The signer's private key (must be registered).
attestation_type str "authorship" One of: authorship, editorial, review, publication, custodial_transcription, translation, compilation.
predecessors list[str] \| None None Predecessor attestation IDs for DAG chaining.
intent_statement str \| None None Human-readable intent statement.

Returns: SignReceipt — a frozen dataclass exposing manifest, attestation, log_entry_id, leaf_index, fingerprint, and pseudonym. Iterates as (manifest, attestation) so legacy callers can still unpack with manifest, attestation = sdk.sign(...). leaf_index is the 0-based Merkle position — pass it to GET /api/v1/log/proof/{leaf_index} to fetch an inclusion proof for offline-verification bundles (issue #375).

Raises: SigningError if signing fails or the server response is missing required receipt fields.

receipt = sdk.sign("My document", private_key)
print(receipt.attestation.id, receipt.attestation.signature)
print(receipt.log_entry_id, receipt.leaf_index)

# Legacy unpack still works:
manifest, attestation = sdk.sign("My document", private_key)
verify(manifest, source_bytes=None)

Verify a signed manifest.

Parameter Type Default Description
manifest Manifest -- The signed manifest to verify.
source_bytes bytes \| str \| None None Original content for hash verification. If None, hash-only verification is used.

Returns: VerificationResult with overall, layer1 (crypto), layer2 (key status), and optionally layer3 (trust) results.

Raises: VerificationError if verification encounters an error.

result = sdk.verify(manifest, source_bytes="My document")
assert result.overall.value == "valid"
evaluate_trust(fingerprint)

Evaluate trust for a key fingerprint using the configured trust anchors and endorsement graph.

Parameter Type Description
fingerprint str SHA-256 fingerprint of the public key (with sha256: prefix).

Returns: TrustPathReport with aggregate_score, threshold_met, and contributing_paths.

Raises: TrustEvaluationError if trust evaluation is not configured or fails.

report = sdk.evaluate_trust("sha256:abc123...")
print(report.threshold_met, report.aggregate_score)
explain_trust(fingerprint)

Get a human-readable trust explanation for a key.

Parameter Type Description
fingerprint str SHA-256 fingerprint of the public key.

Returns: TrustExplanation with trusted, score, summary, contributing_paths, and recommendations.

Raises: TrustEvaluationError if trust evaluation is not configured or fails.

explanation = sdk.explain_trust("sha256:abc123...")
print(explanation.summary)
# "Yes, you should trust this key. It scores 0.90 against ..."

Key Management

generate_key_pair(algorithm="ed25519")

Generate a cryptographic key pair.

Parameter Type Default Description
algorithm str "ed25519" One of "ed25519", "ecdsa-p256", "rsa-2048".

Returns: tuple[PrivateKey, PublicKey]

from diogenes.sdk import generate_key_pair
private_key, public_key = generate_key_pair("ed25519")

compute_fingerprint(public_key)

Compute the SHA-256 fingerprint of a public key.

Returns: Hex-encoded string (64 characters).

from diogenes.sdk import compute_fingerprint
fp = compute_fingerprint(public_key)
# "a1b2c3d4e5f6..."

serialize_private_key(key, password=None)

Serialize a private key to PEM format, optionally encrypted.

from diogenes.sdk import serialize_private_key
pem = serialize_private_key(private_key, password=b"secret")

load_private_key(data, password=None)

Load a private key from PEM-encoded bytes.

from diogenes.sdk import load_private_key
key = load_private_key(pem_bytes, password=b"secret")

Trust Configuration

load_trust_config(source)

Load a TrustConfiguration from a JSON string.

from diogenes.sdk import load_trust_config
config = load_trust_config('{"direct_signer_anchors": [{"fingerprint": "sha256:abc", "weight": 0.9}], "minimum_threshold": 0.5}')

Raises: TrustConfigError on invalid JSON or validation failure.

load_trust_config_file(path)

Load a TrustConfiguration from a JSON file on disk.

from diogenes.sdk import load_trust_config_file
config = load_trust_config_file("trust-policy.json")

Raises: TrustConfigError on file read or validation failure.


Encrypted Claims

The Diogenes server records opaque metadata claims on the public transparency log via the encrypted-claims primitive (ECIES P-256 + AES-GCM, with an SHA-256 commitment for auditor-verifiable reveal). The SDK exposes a high-level wrapper so consumers do not need to re-implement the cryptography against the raw /api/v1/claims/* endpoints.

When to use it. Use cases that need opaque-on-the-log metadata — sealed contracts pre-disclosure, medical-records claims, embargoed-journalism attestations, and the Diogenes Vault sealed-manifest mode — are the natural consumers. The encrypted payload sits on the public log indefinitely; only holders of the recipient's P-256 private key can decrypt it, and auditors can verify a revealed plaintext against the on-chain commitment without ever needing decryption capability.

Auth. post_encrypted_claim requires a JWT (call sdk.login(...) first) and the signing key must satisfy the server's hardware-signer policy (require_hardware_signer). The fetch_* and reveal_* endpoints are unauthenticated — possession of the lookup_key is the access-control token.

EncryptedClaimReceipt, EncryptedClaimEntry, RevealResult

from diogenes.sdk import (
    EncryptedClaimEntry,
    EncryptedClaimReceipt,
    RevealResult,
)
Type Fields
EncryptedClaimReceipt entry_id: int, commitment: str (SHA-256 hex), lookup_key: str (hex token)
EncryptedClaimEntry id, timestamp, event_type, payload, previous_hashes, entry_hashes
RevealResult valid: bool, stored_commitment: str, recomputed_commitment: str

DiogenesSDK.post_encrypted_claim(plaintext, *, recipient_p256_public_key, lookup_key=None)

Encrypts the plaintext against the recipient's P-256 public key and posts the sealed payload to the transparency log. Returns an EncryptedClaimReceipt.

  • plaintext may be a dict (canonicalised via RFC 8785 before encryption) or bytes (auto-wrapped as {"data": "<base64>"} so the commitment shape is well-defined).
  • recipient_p256_public_key may be either a PEM string or a loaded cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey. Non-PEM strings or non-SECP256R1 keys raise KeyOperationError.
  • lookup_key is optional; when omitted the SDK uses the cryptographically random token generated by the core encryption helper. Supply a value to group multiple posts under one retrieval token.

Raises: KeyOperationError (malformed key / wrong curve), AuthError (no JWT or hardware-signer-policy failure), ServerError for any other 4xx/5xx response.

DiogenesSDK.fetch_encrypted_claims(lookup_key)

Unauthenticated GET. Returns a list[EncryptedClaimEntry] in ascending id order. Empty list when no entries match.

DiogenesSDK.decrypt_claim(entry, *, recipient_private_key)

Decrypts an EncryptedClaimEntry entirely in-process — no HTTP call is made and the private key never crosses the wire. recipient_private_key must be a loaded EllipticCurvePrivateKey object (callers handle PEM loading and passwords themselves). Returns the original plaintext dict.

DiogenesSDK.reveal_encrypted_claim(entry_id, plaintext)

Unauthenticated POST to /api/v1/claims/{id}/reveal. The server canonicalises the submitted plaintext, recomputes SHA-256, and compares against the entry's stored commitment. The SDK also performs the same recomputation locally and bundles both commitments into a RevealResult so callers can diff them when valid=False without parsing the server response.

Worked example: post → fetch → decrypt → reveal

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec

from diogenes.sdk import DiogenesSDK, LocalKeystoreSigner

# 1. Generate a recipient P-256 key pair locally (kept by the recipient).
recipient_priv = ec.generate_private_key(ec.SECP256R1())
recipient_pem = (
    recipient_priv.public_key()
    .public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo,
    )
    .decode()
)

# 2. Log in with a hardware-backed credential (required for POST).
sdk = DiogenesSDK(server_url="https://trustdiogenes.com")
sdk.login(fingerprint=my_fp, password="hunter2")

# 3. Post the encrypted claim.
plaintext = {
    "claim_type": "training_completion",
    "trainee_id": "alice",
    "completion_date": "2026-03-01",
}
receipt = sdk.post_encrypted_claim(
    plaintext,
    recipient_p256_public_key=recipient_pem,
)
print(receipt.entry_id, receipt.commitment, receipt.lookup_key)

# 4. (Recipient side) Fetch by lookup_key (no auth required).
public_sdk = DiogenesSDK(server_url="https://trustdiogenes.com")
entries = public_sdk.fetch_encrypted_claims(receipt.lookup_key)
assert len(entries) == 1

# 5. Decrypt locally — the private key never leaves this process.
revealed = public_sdk.decrypt_claim(
    entries[0], recipient_private_key=recipient_priv
)
assert revealed == plaintext

# 6. (Auditor side) Verify the revealed plaintext against the commitment.
result = public_sdk.reveal_encrypted_claim(receipt.entry_id, plaintext)
assert result.valid is True
assert result.stored_commitment == result.recomputed_commitment == receipt.commitment

Posting raw bytes

# Raw bytes are wrapped automatically; decrypt returns the wrapper dict
# and the caller base64-decodes the "data" key to recover the bytes.
import base64

receipt = sdk.post_encrypted_claim(
    b"\x00\x01\x02 binary blob",
    recipient_p256_public_key=recipient_pem,
)
entry = public_sdk.fetch_encrypted_claims(receipt.lookup_key)[0]
decrypted = public_sdk.decrypt_claim(entry, recipient_private_key=recipient_priv)
original_bytes = base64.b64decode(decrypted["data"])

Webhook Management

WebhookClient

Client for managing webhook subscriptions against the Diogenes REST API.

from diogenes.sdk import WebhookClient

Constructor

WebhookClient(
    base_url: str,
    api_key: str | None = None,
    http_client: httpx.Client | None = None,
)
Parameter Type Default Description
base_url str -- Base URL of the Diogenes server (e.g. http://localhost:8000).
api_key str \| None None Optional API key for authentication.
http_client httpx.Client \| None None Optional pre-configured HTTP client.

Supports context manager usage:

with WebhookClient("http://localhost:8000") as client:
    subs = client.list_webhooks()

create_webhook(url, events=None, secret=None, key_fingerprint=None, document_hash=None)

Create a new webhook subscription.

Parameter Type Default Description
url str -- Callback URL to receive events.
events list[str] \| None None Event types to subscribe to. Empty list = all events.
secret str \| None None Shared secret for HMAC-SHA256 payload signatures.
key_fingerprint str \| None None Filter events by key fingerprint.
document_hash str \| None None Filter events by document hash.

Returns: WebhookSubscription

Raises: WebhookError on failure.

sub = client.create_webhook(
    url="https://example.com/hook",
    events=["attestation.created", "key.registered"],
    secret="my-shared-secret",
)
print(sub.id)

list_webhooks()

List all active webhook subscriptions.

Returns: list[WebhookSubscription]

subs = client.list_webhooks()
for sub in subs:
    print(sub.id, sub.url, sub.events)

get_webhook(subscription_id)

Get a single webhook subscription by ID.

Returns: WebhookSubscription

Raises: WebhookError if not found.

sub = client.get_webhook(42)
print(sub.active, sub.healthy)

delete_webhook(subscription_id)

Delete (deactivate) a webhook subscription.

Raises: WebhookError if not found.

client.delete_webhook(42)

WebhookSubscription

Data class representing a webhook subscription.

Field Type Description
id int Unique identifier.
url str Callback URL.
events list[str] Subscribed event types.
key_fingerprint str \| None Key fingerprint filter.
document_hash str \| None Document hash filter.
active bool Whether the subscription is active.
healthy bool Whether the endpoint is healthy.
retry_count int Consecutive delivery failures.
created_at str \| None ISO-8601 creation timestamp.

Exceptions

All SDK exceptions inherit from DiogenesSDKError.

Exception When raised
DiogenesSDKError Base class for all SDK errors.
SigningError Signing fails (e.g. unregistered or revoked key).
VerificationError Verification encounters an error.
TrustConfigError Trust configuration loading or validation fails.
TrustEvaluationError Trust evaluation not configured or fails.
KeyOperationError Key operation fails.
WebhookError Webhook API call fails.
AuthError Authentication against the server fails (raised by post_encrypted_claim when no JWT is set or the signing key fails the hardware-signer policy).
ServerError Server returned an unexpected error response. Exposes status_code and detail attributes.
from diogenes.sdk import DiogenesSDKError, SigningError, WebhookError

try:
    sdk.sign("doc", unregistered_key)
except SigningError as e:
    print(f"Signing failed: {e}")

Supported Event Types

The following event types can be used when creating webhook subscriptions:

Event Description
attestation.created A new attestation was signed and recorded.
key.registered A new key was registered on the transparency log.
key.revoked A key was revoked.
endorsement.created A new endorsement was accepted.
endorsement.revoked An endorsement was revoked.

Architecture Notes

  • The SDK is a facade over the core Diogenes modules (diogenes.core.*). Advanced users can import core modules directly for lower-level access.
  • Signing and verification are synchronous operations.
  • The WebhookClient uses httpx for HTTP and is safe for use in both sync and async contexts (via httpx.Client).
  • Trust evaluation uses a web-of-trust model with configurable anchors and thresholds, not a certificate authority.