Anthropic Integration
The Glacis Anthropic integration wraps the official Anthropic client to automatically create cryptographic attestations for every message. Your data is hashed locally and never leaves your environment — only hashes and metadata are sent to the Glacis transparency log.
Installation
Section titled “Installation”pip install glacis[anthropic]Quick Start
Section titled “Quick Start”from glacis.integrations.anthropic import attested_anthropic, get_last_receipt
client = attested_anthropic( glacis_api_key="glsk_live_...", anthropic_api_key="sk-ant-...")
# Make a normal Anthropic call -- attestation happens automaticallyresponse = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[{"role": "user", "content": "Hello, Claude!"}])
print(response.content[0].text)
# Get the attestation receiptreceipt = get_last_receipt()print(f"Attested: {receipt.id}")print(f"Status: {receipt.witness_status}")What Gets Attested
Section titled “What Gets Attested”For each message, Glacis captures:
| Field | Treatment | Details |
|---|---|---|
| Request messages | Hashed | SHA-256, never sent to Glacis |
| Response content | Hashed | SHA-256, never sent to Glacis |
| System prompt | Hashed | SHA-256 hash included in control plane record |
| Model name | Metadata | Sent as-is |
| Temperature | Metadata | Included in control plane record |
| Stop reason | Metadata | Included in output evidence |
| Token counts | Metadata | input and output tokens |
| Provider | Metadata | Always "anthropic" |
Environment Variables
Section titled “Environment Variables”export ANTHROPIC_API_KEY=sk-ant-...from glacis.integrations.anthropic import attested_anthropic
# Anthropic key read from ANTHROPIC_API_KEY env var automatically# Glacis API key must be passed explicitlyclient = attested_anthropic(glacis_api_key="glsk_live_...")export ANTHROPIC_API_KEY=sk-ant-...import osfrom glacis.integrations.anthropic import attested_anthropic
# No Glacis API key needed for offline modeclient = attested_anthropic( offline=True, signing_seed=os.urandom(32),)from glacis.integrations.anthropic import attested_anthropic
client = attested_anthropic( glacis_api_key="glsk_live_...", anthropic_api_key="sk-ant-...")Accessing Receipts
Section titled “Accessing Receipts”Use get_last_receipt() to retrieve the attestation from the most recent API call. Receipts are stored in thread-local storage, so each thread maintains its own last receipt independently.
from glacis.integrations.anthropic import get_last_receipt
receipt = get_last_receipt()if receipt: print(f"ID: {receipt.id}") print(f"Evidence hash: {receipt.evidence_hash}") print(f"Status: {receipt.witness_status}") # "WITNESSED" or "UNVERIFIED" print(f"Service: {receipt.service_id}")Offline Mode
Section titled “Offline Mode”Offline mode creates locally-signed attestations without connecting to the Glacis server. This is useful for development, air-gapped environments, or when you want to defer attestation submission.
Offline mode requires a signing_seed — a 32-byte Ed25519 seed used for local signing:
import osfrom glacis.integrations.anthropic import attested_anthropic, get_last_receipt
client = attested_anthropic( offline=True, signing_seed=os.urandom(32), anthropic_api_key="sk-ant-...")
response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}])
receipt = get_last_receipt()print(f"Status: {receipt.witness_status}") # "UNVERIFIED"System Prompts
Section titled “System Prompts”System prompts are hashed but never transmitted to Glacis. The hash is included in the control plane record for integrity verification:
response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, system="You are a helpful AI assistant specialized in compliance.", messages=[{"role": "user", "content": "What is GDPR?"}])
# System prompt hash is included in the attestationreceipt = get_last_receipt()Multi-Turn Conversations
Section titled “Multi-Turn Conversations”Each API call creates a separate attestation. The full message history is included in the evidence hash for each turn:
messages = []
# First turnmessages.append({"role": "user", "content": "Hello!"})response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=messages)receipt1 = get_last_receipt()messages.append({"role": "assistant", "content": response.content[0].text})
# Second turnmessages.append({"role": "user", "content": "How are you?"})response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=messages)receipt2 = get_last_receipt()
# Each turn has its own attestationprint(f"Turn 1: {receipt1.id}")print(f"Turn 2: {receipt2.id}")Using Controls
Section titled “Using Controls”Controls let you scan inputs and outputs for PII, jailbreak attempts, banned words, and more. Configure them via a glacis.yaml file:
from glacis.integrations.anthropic import attested_anthropic, GlacisBlockedError
client = attested_anthropic( config="glacis.yaml", anthropic_api_key="sk-ant-...")
try: response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) print(response.content[0].text)except GlacisBlockedError as e: print(f"Blocked by {e.control_type} (score={e.score})")The Anthropic integration supports multi-block content in user messages — controls will scan all text blocks within a message.
You can also pass custom controls programmatically via the input_controls and output_controls parameters. See the BaseControl interface in glacis.controls.base for details on implementing custom controls.
Retrieving Evidence
Section titled “Retrieving Evidence”Evidence includes the full input, output, and control plane results that were attested. Evidence is stored locally and never sent to Glacis servers.
from glacis.integrations.anthropic import get_last_receipt, get_evidence
receipt = get_last_receipt()if receipt: evidence = get_evidence(receipt.id) if evidence: print(evidence["input"]) # Original request (model, messages, system) print(evidence["output"]) # Full response (content, stop_reason, usage)get_evidence() accepts optional storage_backend and storage_path parameters to override the default storage location:
evidence = get_evidence( receipt.id, storage_backend="sqlite", storage_path="/path/to/evidence.db")attested_anthropic() Reference
Section titled “attested_anthropic() Reference”| Parameter | Type | Default | Description |
|---|---|---|---|
glacis_api_key | Optional[str] | None | Glacis API key. Required for online mode. Must be passed explicitly (no env var fallback). |
anthropic_api_key | Optional[str] | None | Anthropic API key. Falls back to ANTHROPIC_API_KEY env var. |
glacis_base_url | str | "https://api.glacis.io" | Glacis API base URL. |
service_id | str | "anthropic" | Service identifier for attestations. |
debug | bool | False | Enable debug logging. |
offline | Optional[bool] | None | Enable offline mode. If None, inferred from config or presence of glacis_api_key. |
signing_seed | Optional[bytes] | None | 32-byte Ed25519 signing seed. Required when offline=True. |
policy_key | Optional[bytes] | None | 32-byte HMAC key for sampling decisions. Falls back to signing_seed if not provided. |
config | Optional[str] | None | Path to glacis.yaml config file for controls, sampling, and policy settings. |
input_controls | Optional[list[BaseControl]] | None | Custom controls to run on input text before the LLM call. |
output_controls | Optional[list[BaseControl]] | None | Custom controls to run on output text after the LLM call. |
**anthropic_kwargs | Any | — | Additional keyword arguments passed directly to the Anthropic() client constructor. |
Returns: A wrapped Anthropic client. The client.messages.create() method is intercepted to perform attestation automatically.
Raises: GlacisBlockedError if a control blocks the request.
Full Example
Section titled “Full Example”#!/usr/bin/env python3"""Complete example: Anthropic Claude with Glacis attestation."""import osfrom glacis.integrations.anthropic import attested_anthropic, get_last_receipt, get_evidence
def main(): # Create attested client (online mode — requires GLACIS_API_KEY) client = attested_anthropic( glacis_api_key=os.environ["GLACIS_API_KEY"], )
# Make a request with a system prompt response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, system="You are a compliance expert.", messages=[ {"role": "user", "content": "What is ISO 42001?"} ] )
print("Response:", response.content[0].text) print()
# Get attestation receipt receipt = get_last_receipt() if receipt: print("Attestation Details:") print(f" ID: {receipt.id}") print(f" Evidence hash: {receipt.evidence_hash}") print(f" Status: {receipt.witness_status}") print(f" Service: {receipt.service_id}") print()
# Retrieve full evidence evidence = get_evidence(receipt.id) if evidence: print("Evidence stored locally:") print(f" Input model: {evidence['input']['model']}") print(f" Output tokens: {(evidence['output'].get('usage') or {}).get('output_tokens')}")
if __name__ == "__main__": main()