Skip to content
Conduit

mcp server

sanna

Trust infrastructure for AI agents — constitution enforcement and cryptographic receipts. Python SDK.

sanna-ai/sannacommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "sanna": {
      "command": "uvx",
      "args": [
        "sanna"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 17, 2026

Sanna — Trust Infrastructure for AI Agents

PyPI version OpenSSF Best Practices

Sanna generates portable cryptographic receipts attesting to governance outcomes. The @sanna_observe decorator is post-execution detection and attestation -- the wrapped function executes before output checks run, so its side effects are not prevented; the gateway and interceptor surfaces enforce governance pre-execution and block disallowed actions before they occur. An opt-in pre-execution reasoning gate in @sanna_observe activates when a constitution defines reasoning checks and _justification is supplied. Constitution-as-code: your governance rules live in version-controlled YAML, not in a vendor dashboard.

What's New in v1.5.0

  • enforcement_surface field — Receipts now record which SDK component generated them (middleware, gateway, cli_interceptor, http_interceptor).
  • invariants_scope field — Receipts record which invariants were evaluated (full, authority_only, limited, none).
  • 21-field fingerprint — CHECKS_VERSION bumped to "10", SPEC_VERSION to "1.5". Fields 15-16 are enforcement_surface_hash and invariants_scope_hash; fields 17-20 are tool_name, agent_model, agent_model_provider, and agent_model_version; field 21 is agent_identity_hash.
  • Schema cross-field consistency — allOf rules enforce halted → FAIL, warned → WARN, allowed → PASS.
  • skip_default_checks parameter — Interceptor receipts derive status from enforcement.action without running C1-C5 checks (SAN-216).

Previous: v1.1.0

  • Subprocess interceptor hardening — Shell metacharacter bypass fix (SAN-35), os.exec*/spawn*/popen patching (SAN-42), TOCTOU race mitigation on binary path resolution (SAN-44), wrapper script bypass detection (SAN-45), thread-safe restore (SAN-46), env var manipulation bypass prevention (SAN-47).
  • Fingerprint edge-case alignment — Python edge cases aligned with TypeScript SDK and spec (SAN-27).
  • Narrowed exception handling — Broad except Exception replaced with specific exception types across the codebase (SAN-1).

Quick Start — Library Mode

pip install sanna

Set up governance (one-time):

sanna init         # Choose template, set agent name, enforcement level
sanna keygen       # Generate Ed25519 keypair (~/.sanna/keys/)
# Output:
#   Generated Ed25519 keypair (a1b2c3d4e5f6...)
#   Private key: /Users/you/.sanna/keys/a1b2c3d4e5f6...7890.key
#   Public key:  /Users/you/.sanna/keys/a1b2c3d4e5f6...7890.pub
sanna sign constitution.yaml --private-key ~/.sanna/keys/<key-id>.key

Now wrap the functions you want to govern. @sanna_observe decorates the functions you choose — internal reasoning, prompt construction, and non-governed function calls produce no receipts.

from sanna import sanna_observe, SannaHaltError

@sanna_observe(
    constitution_path="constitution.yaml",
    constitution_public_key_path="~/.sanna/keys/<key-id>.pub",  # from sanna keygen above
)
def my_agent(query: str, context: str) -> str:
    return "Based on the data, revenue grew 12% year-over-year."

# @sanna_observe wraps the return value in a SannaResult with .output and .receipt.
# The original str return is available as result.output.
try:
    result = my_agent(
        query="What was revenue growth?",
        context="Annual report: revenue increased 12% YoY to $4.2B."
    )
    print(result.output)   # The original str return value
    print(result.receipt)  # Cryptographic governance receipt (dict)
    # To persist receipts, use ReceiptStore separately:
    #   from sanna import ReceiptStore
    #   store = ReceiptStore(".sanna/receipts.db")
    #   store.store(result.receipt)
except SannaHaltError as e:
    print(f"HALTED: {e}")  # Constitution violation detected

Quick Start — Gateway Mode

No code changes to your agent. The gateway sits between your MCP client and downstream servers.

pip install sanna[mcp]

sanna init         # Creates constitution.yaml + gateway.yaml
sanna keygen --label gateway
sanna sign constitution.yaml --private-key ~/.sanna/keys/<key-id>.key
sanna gateway --config gateway.yaml

Minimum gateway.yaml:

gateway:
  constitution: ./constitution.yaml
  signing_key: ~/.sanna/keys/<gateway-key-id>.key        # Key generated by sanna keygen
  constitution_public_key: ~/.sanna/keys/<author-key-id>.pub  # Public key of constitution signer
  receipt_store: .sanna/receipts/

downstream:
  - name: notion
    command: npx
    args: ["-y", "@notionhq/notion-mcp-server"]
    env:
      OPENAPI_MCP_HEADERS: "${OPENAPI_MCP_HEADERS}"
    default_policy: can_execute

Point your MCP client (Claude Desktop, Claude Code, Cursor) at the gateway instead of directly at your downstream servers. Every tool call is now governed. The gateway governs tool calls that pass through it — only actions that cross the governance boundary produce receipts. Reasoning is captured via the explicit _justification parameter in tool calls, not from internal model reasoning. The gateway cannot observe LLM chain-of-thought.

MCP Client (Claude Desktop / Claude Code / Cursor)
        |
        v  (MCP stdio)
sanna-gateway
        |  1. Receive tool call
        |  2. Evaluate against constitution
        |  3. Enforce policy (allow / escalate / deny)
        |  4. Generate signed receipt
        |  5. Forward to downstream (if allowed)
        v  (MCP stdio)
Downstream MCP Servers (Notion, GitHub, filesystem, etc.)

Demo

Run a self-contained governance demo — no external dependencies:

sanna demo

This generates keys, creates a constitution, simulates a governed tool call, generates a receipt, and verifies it.

Core Concepts

Constitution — YAML document defining what the agent can, cannot, and must escalate. Ed25519-signed. Modification after signing is detected on load. Constitution signing (via sanna sign) is required for enforcement. Constitution approval is an optional additional governance step for multi-party review workflows.

Receipt — JSON artifact binding inputs, reasoning, action, and check results into a cryptographically signed, schema-validated, deterministically fingerprinted record. Receipts are generated per governed action — when an agent calls a tool or executes a decorated function — not per conversational turn. An agent that reasons for twenty turns and executes one action produces one receipt.

Coherence Checks (C1-C5) — Five built-in deterministic heuristics. No API calls or external dependencies.

CheckInvariantWhat it catches
C1INV_NO_FABRICATIONOutput contradicts provided context
C2INV_MARK_INFERENCEDefinitive claims without hedging
C3INV_NO_FALSE_CERTAINTYConfidence exceeding evidence strength
C4INV_PRESERVE_TENSIONConflicting information collapsed
C5INV_NO_PREMATURE_COMPRESSIONComplex input reduced to single sentence

Authority Boundaries — cannot_execute (deny, checked first, tool names only), must_escalate (prompt user, checked second, matches full action context including parameters), can_execute (allow, checked third, tool names only). A tool in can_execute is still subject to must_escalate conditions. Policy cascade: per-tool override > server default > constitution.

Key Management — Public keys are stored in ~/.sanna/keys/ and referenced by their key ID (SHA-256 fingerprint of the public key). For verification, pass the public key path explicitly via --public-key on the CLI or constitution_public_key_path in code. See docs/key-management.md for key roles and rotation.

Receipt Format

Every governed action produces a reasoning receipt — a JSON artifact that cryptographically binds inputs, outputs, check results, and constitution provenance. See spec/sanna-specification-v1.5.md for the full specification.

Identification

FieldTypeDescription
spec_versionstringSchema version, "1.5"
tool_versionstringPackage version, e.g. "1.5.0"
checks_versionstringCheck algorithm version, e.g. "10"
receipt_idstringUUID v4 unique identifier
correlation_idstringPath-prefixed identifier for grouping related receipts

Integrity

FieldTypeDescription
receipt_fingerprintstring16-hex SHA-256 truncation for compact display
full_fingerprintstring64-hex SHA-256 of all fingerprinted fields
context_hashstring64-hex SHA-256 of canonical inputs
output_hashstring64-hex SHA-256 of canonical outputs

Content

FieldTypeDescription
timestampstringISO 8601 timestamp
inputsobjectDictionary of function arguments passed to the decorated function (e.g., query, context)
outputsobjectContains response

Governance

FieldTypeDescription
checksarrayList of CheckResult objects with check_id, passed, severity, evidence
checks_passedintegerCount of checks that passed
checks_failedintegerCount of checks that failed
statusstring"PASS" / "WARN" / "FAIL" / "PARTIAL"
constitution_refobjectContains document_id, policy_hash, version, source, signature_verified, constitution_approval
enforcementobject or nullContains action, reason, failed_checks, enforcement_mode, timestamp when enforcement triggered
evaluation_coverageobjectContains total_invariants, evaluated, not_checked, coverage_basis_points

Receipt Triad (Gateway)

FieldTypeDescription
input_hashstring64-hex SHA-256, present in gateway receipts
reasoning_hashstring64-hex SHA-256 of reasoning content
action_hashstring64-hex SHA-256 of action content
assurancestring"full" or "partial"

Identity and Signature

FieldTypeDescription
receipt_signatureobjectContains value, key_id, signed_by, signed_at, scheme
identity_verificationobject or nullVerification results for identity claims, when present

Extensions

FieldTypeDescription
extensionsobjectReverse-domain namespaced metadata (com.sanna.gateway, com.sanna.middleware)

Multi-Step Workflows (v1.0.0)

FieldTypeDescription
parent_receiptsarray or nullList of parent receipt IDs for multi-step workflow chaining
workflow_idstring or nullShared identifier grouping receipts in a workflow
content_modestring or null"full" or "hash_only" — whether inputs/outputs are stored or only hashed
content_mode_sourcestring or nullWhere the content_mode setting originated

This section provides a high-level overview. For a complete field reference and normative format details, see spec/sanna-specification-v1.5.md.

Minimal example receipt (abbreviated -- production receipts typically contain 3-7 checks):

{
  "spec_version": "1.5",
  "tool_version": "1.5.0",
  "checks_version": "10",
  "receipt_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
  "receipt_fingerprint": "7b4d06e836514eef",
  "full_fingerprint": "7b4d06e836514eef26ab96f5c62b193d036c92b45d966ef7025d75539ff93aca",
  "correlation_id": "sanna-my-agent-1708128000",
  "timestamp": "2026-02-17T00:00:00+00:00",
  "inputs": {"query": "refund policy", "context": "All sales are final."},
  "outputs": {"response": "Unfortunately, all sales are final per our policy."},
  "context_hash": "...(64 hex)...",
  "output_hash": "...(64 hex)...",
  "checks": [
    {"check_id": "C1", "name": "Context Contradiction", "passed": true, "severity": "info"}
  ],
  "checks_passed": 1,
  "checks_failed": 0,
  "status": "PASS",
  "constitution_ref": {"document_id": "support-agent/1.0", "policy_hash": "...", "signature_verified": true},
  "enforcement": null,
  "parent_receipts": null,
  "workflow_id": null
}

Constitution Format

Constitutions are YAML documents that define an agent's governance boundaries. They are version-controlled, cryptographically signed (and optionally approved) before enforcement.

sanna_constitution: "1.1"

identity:
  agent_name: support-agent
  domain: customer-support
  description: Handles refund and billing inquiries

provenance:
  authored_by: governance-team
  approved_by: vp-risk
  approval_date: "2026-01-15"

boundaries:
  - id: B1
    description: Only answer questions about products in the catalog
    category: scope
    severity: critical
  - id: B2
    description: Never promise refunds outside the 30-day window
    category: policy
    severity: critical

invariants:
  - id: INV_NO_FABRICATION
    rule: Never state facts not grounded in provided context
    enforcement: critical
  - id: INV_MARK_INFERENCE
    rule: Clearly mark any inference or assumption
    enforcement: warning
  - id: INV_NO_FALSE_CERTAINTY
    rule: Do not express certainty beyond what evidence supports
    enforcement: warning
  - id: INV_PRESERVE_TENSION
    rule: When context contains conflicting rules, surface both
    enforcement: warning
  - id: INV_NO_PREMATURE_COMPRESSION
    rule: Do not over-summarize multi-faceted context
    enforcement: warning

authority_boundaries:
  cannot_execute:    # checked FIRST — tool names only
    - Delete customer accounts
    - Access payment credentials
  must_escalate:     # checked SECOND — matches tool name + parameters
    - Issue refund over $500
    - Override account restrictions
  can_execute:       # checked THIRD — tool names only
    - Look up order status
    - Search knowledge base

escalation_targets:
  - condition: "refund over limit"
    target:
      type: webhook
      url: https://ops.example.com/escalate

reasoning:
  require_justification: true
  assurance_level: full

Custom Evaluators

Register domain-specific invariant evaluators alongside the built-in C1-C5 checks:

from sanna.evaluators import register_invariant_evaluator
from sanna.receipt import CheckResult

@register_invariant_evaluator("INV_PII_CHECK")
def pii_check(context, output, constitution, check_config):
    """Flag outputs containing email addresses."""
    import re
    has_pii = bool(re.search(r'\b[\w.+-]+@[\w-]+\.[\w.]+\b', output))
    return CheckResult(
        check_id="INV_PII_CHECK",
        name="PII Detection",
        passed=not has_pii,
        severity="high",
        evidence="Email address detected in output" if has_pii else "",
    )

Add the invariant to your constitution and it runs alongside C1-C5 automatically.

Receipt Querying

from sanna import ReceiptStore

store = ReceiptStore(".sanna/receipts.db")

# Query with filters
receipts = store.query(agent_id="support-agent", status="FAIL", limit=10)

# Drift analysis
from sanna import DriftAnalyzer
analyzer = DriftAnalyzer(store)
report = analyzer.analyze(window_days=30, threshold=0.15)

Or via CLI:

sanna drift-report --db .sanna/receipts.db --window 30 --json

Receipt Sinks

Pluggable receipt persistence with the ReceiptSink interface:

from sanna import LocalSQLiteSink, CloudHTTPSink, CompositeSink

# Local SQLite persistence
local = LocalSQLiteSink(".sanna/receipts.db")

# Remote HTTP endpoint with retry
cloud = CloudHTTPSink("https://governance.example.com/receipts", api_key="...")

# Fan-out to both
sink = CompositeSink([local, cloud])

Configure in gateway YAML:

gateway:
  receipt_sink:
    type: local_sqlite
    path: .sanna/receipts.db

Available sink types: null (no-op), local_sqlite, cloud_http, composite. The FailurePolicy enum controls behavior on sink errors: LOG_AND_CONTINUE (default -- log and proceed), RAISE (propagate the error), BUFFER_AND_RETRY (buffer to disk and retry later).

Constitution Templates

sanna init offers three interactive templates plus blank:

TemplateUse Case
Enterprise ITStrict enforcement, ServiceNow-style compliance
Customer-FacingStandard enforcement, Salesforce-style support agents
General PurposeAdvisory enforcement, starter template
BlankEmpty constitution for custom configuration

Five additional gateway-oriented templates are available in examples/constitutions/. Each includes inline documentation explaining the authority boundary evaluation order and common mistakes:

TemplateUse Case
openclaw-personalIndividual agents on personal machines
openclaw-developerSkill builders for marketplace distribution
cowork-personalKnowledge workers with Claude Desktop
cowork-teamSmall teams sharing governance via Git (each dev runs own gateway)
claude-code-standardDevelopers with Claude Code + MCP connectors

CLI Reference

All commands are available as sanna <command> or sanna-<command>:

CommandDescription
sanna initInteractive constitution generator with template selection
sanna keygenGenerate Ed25519 keypair (--label for human-readable name)
sanna signSign a constitution with Ed25519
sanna verifyVerify receipt integrity, signature, and provenance chain
sanna verify-constitutionVerify constitution signature
sanna approveApprove a signed constitution
sanna demoRun self-contained governance demo
sanna inspectPretty-print receipt contents
sanna check-configValidate gateway config (dry-run)
sanna gatewayStart MCP enforcement proxy
sanna mcpStart MCP server (7 tools, stdio transport)
sanna diffDiff two constitutions (text/JSON/markdown)
sanna drift-reportFleet governance drift report
sanna bundle-createCreate evidence bundle zip
sanna bundle-verifyVerify evidence bundle (7-step)
sanna generateGenerate receipt from trace-data JSON

Documentation

  • Python SDK Reference — full API surface: signatures, parameters, examples for every domain.
  • Cookbook — recipes for interceptors, custom evaluators, drift reports, and evidence bundles.

API Reference

The top-level sanna package exports 23 names:

from sanna import (
    __version__,          # Package version string
    sanna_observe,        # Decorator: governance wrapper for agent functions
    SannaResult,          # Return type from @sanna_observe-wrapped functions
    SannaHaltError,       # Raised when a halt-enforcement invariant fails
    generate_receipt,     # Generate a receipt from trace data
    receipt_to_dict,      # Serialize SannaReceipt dataclass to plain dict
    SannaReceipt,         # Receipt dataclass
    verify_receipt,       # Offline receipt verification
    VerificationResult,   # Verification result dataclass
    ReceiptStore,         # SQLite-backed receipt persistence
    DriftAnalyzer,        # Per-agent failure-rate trending
    # Receipt sinks (v1.0.0)
    ReceiptSink,          # Abstract base class for receipt persistence
    NullSink,             # No-op sink (default)
    LocalSQLiteSink,      # SQLite-backed local persistence
    CloudHTTPSink,        # HTTP endpoint with retry and buffer-on-failure
    CompositeSink,        # Fan-out to multiple sinks
    SinkResult,           # Result from a sink.store() call
    FailurePolicy,        # Enum: LOG_AND_CONTINUE, RAISE, BUFFER_AND_RETRY
    # Interceptors
    patch_subprocess,     # Patch subprocess module for governance enforcement
    unpatch_subprocess,   # Restore original subprocess functions
    patch_http,           # Patch HTTP libraries for governance enforcement
    unpatch_http,         # Restore original HTTP functions
    # Redaction
    RedactionConfig,      # PII redaction controls for receipt content
)

Everything else imports from submodules: sanna.constitution, sanna.crypto, sanna.enforcement, sanna.evaluators, sanna.verify, sanna.bundle, sanna.hashing, sanna.drift, sanna.sinks, sanna.constitution_diff.

Verification

Verification proves four properties:

  • Schema validation: Receipt structure matches the expected format.
  • Hash verification: Content hashes match the actual inputs and outputs (tamper detection).
  • Signature verification: Receipt was signed by a known key (authenticity).
  • Chain verification: Constitution was signed, and any approvals are cryptographically bound.
# Verify receipt integrity
sanna verify receipt.json

# Verify with signature check
sanna verify receipt.json --public-key <key-id>.pub

# Full chain: receipt + constitution + approval
sanna verify receipt.json \
  --constitution constitution.yaml \
  --constitution-public-key <key-id>.pub

# Evidence bundle (self-contained zip)
sanna bundle-create \
  --receipt receipt.json \
  --constitution constitution.yaml \
  --public-key <key-id>.pub \
  --output evidence.zip

sanna bundle-verify evidence.zip

No network. No API keys. No vendor dependency.

Enterprise Features

  • DMARC-style adoption: Start with log enforcement (observe), move to warn (escalate), then halt (enforce).
  • Ed25519 cryptographic signatures: Constitutions, receipts, and approval records are independently signed and verifiable.
  • Offline verification: No platform dependency. Verify receipts with a public key and the CLI.
  • Evidence bundles: Self-contained zip archives with receipt, constitution, and public keys for auditors.
  • Drift analytics: Per-agent failure-rate trending with linear regression and breach projection. See docs/drift-reports.md.
  • Receipt Triad: Cryptographic binding of input, reasoning, and action for auditability. See docs/reasoning-receipts.md.
  • Receipt queries: SQL recipes, MCP query tool. See docs/receipt-queries.md.
  • Key management: SHA-256 key fingerprints, labeled keypairs. See docs/key-management.md.
  • Production deployment: Docker, logging, retention, failure modes. See docs/production.md.
  • Gateway configuration: Full config reference. See docs/gateway-config.md.

Security

  • Ed25519 cryptographic signatures: Constitutions, receipts, and approval records are independently signed and verifiable offline.
  • Prompt injection isolation: Evaluator prompts use trust separation -- trusted policy rules are isolated from untrusted agent content to mitigate prompt injection risks through trust separation and input escaping. Untrusted content is wrapped in <audit> tags with XML entity escaping.
  • Atomic file writes: All file operations use symlink-protected atomic writes (O_NOFOLLOW, O_EXCL, fsync, os.replace()).
  • SQLite hardening: Receipt stores validate file ownership, enforce 0o600 permissions, and reject symlinks.
  • Signature structure validation: Enforcement points validate Ed25519 base64 encoding and 64-byte signature length, rejecting whitespace, junk, and placeholder strings.

Cryptographic Design

  • Signing: Ed25519 over canonical JSON (RFC 8785-style deterministic serialization)
  • Hashing: SHA-256 for all content hashes, fingerprints, and key IDs
  • Canonicalization: Sorted keys, NFC Unicode normalization, integer-only numerics (no floats in signed content)
  • Fingerprinting: 14 pipe-delimited fields hashed with SHA-256; 16-hex truncation for display, 64-hex for full fingerprint

See the specification for full cryptographic construction details.

Threat Model

Defends against:

  • Tampering with stored receipts (detected via fingerprint and signature verification)
  • Unverifiable governance claims (receipts are cryptographically signed attestations)
  • Substitution of receipts across contexts (receipts are cryptographically bound to specific inputs, outputs, and correlation IDs; verifiers should enforce timestamp and correlation expectations)
  • Unauthorized tool execution (constitution enforcement blocks or escalates disallowed actions)

Does not defend against:

  • Compromised runtime environment (if the host is compromised, all bets are off)
  • Stolen signing keys (key compromise requires re-keying and re-signing)
  • Bypassing Sanna entirely (governance only applies to functions decorated with @sanna_observe or tool calls routed through the gateway)
  • Malicious constitutions (Sanna enforces the constitution as written; it does not validate whether the constitution itself is correct or sufficient)

Limitations

Receipts are attestations of process, not guarantees of outcome.

  • Receipts do not prove internal reasoning was truthful -- they prove that checks were run against the output
  • Receipts do not prove upstream input was complete or accurate
  • Receipts do not protect against a compromised host or stolen signing keys
  • Receipts do not prove the constitution itself was correct or sufficient for the use case
  • Heuristic checks (C1-C5) are deterministic but not exhaustive -- they catch common failure modes, not all possible failures

Observability (OpenTelemetry)

Sanna can emit OpenTelemetry signals to correlate governed actions with receipts on disk. Receipts are the canonical audit artifact — telemetry is optional and intended for dashboards, alerts, and correlation.

pip install "sanna[otel]"

See docs/otel-integration.md for configuration and signal reference.

Install

pip install sanna                # Core library (Python 3.10+)
pip install sanna[mcp]           # MCP server + gateway
pip install sanna[otel]          # OpenTelemetry bridge

Development

git clone https://github.com/sanna-ai/sanna.git
cd sanna
pip install -e ".[dev]"
python -m pytest tests/ -q

License

AGPL-3.0

↑↓ navigate openesc close