Skip to main content

Sasana

Your AI agent logs are not evidence. Sasana makes them defensible.

Every major observability tool — LangSmith, Arize, Langfuse — stores logs in mutable, operator-controlled storage. An administrator with file-system or database access can modify or delete a record with no detectable trace. Those logs satisfy a reporting requirement. They do not prove what happened.

Sasana records a SHA-256 hash-chained audit trail: editing, inserting, deleting or reordering any event breaks the chain and sasana verify reports it. Raw content never leaves your machine: only hashes are stored.

What the chain alone does not do. Someone with write access to the file can edit an event and recompute every hash after it; the result is a different, internally consistent chain. Detecting that needs an independent seal from Archeion verified against a public key you pinned with --trust-key, from an Archeion the operator cannot use to seal a rewrite (see Against the operator). Without the pin, a seal only proves the file is self-consistent — anyone can generate a key and seal a rewritten log. Read the evidence class, not just the word INTACT.

$ sasana verify session.jsonl --trust-key <archeion-pubkey> --strict

Sasana Verifier v2.0.0
File: session.jsonl
Session: 3f8a2c1d-…
Events: 5  Evidence: AUTHORITATIVE_EVIDENCE
Trusted key: pinned (--trust-key)

[1/5] Structural validity  ... PASS
[2/5] Sequence integrity   ... PASS
[3/5] Hash chain integrity ... PASS
[4/5] Session completeness ... PASS
[5/5] Seal signature       ... PASS  (signed by the pinned key)

Result: INTACT ✅

Who this is for

Teams deploying AI agents in regulated environments — fintech, healthtech, HR tech. Specifically:

  • Compliance engineers implementing EU AI Act Article 12 (automatic event logging for high-risk AI systems) who want integrity evidence on top of the log
  • Security teams who need a cryptographically verifiable audit trail for incident response
  • DevSecOps engineers who need to detect whether a session log was modified after it ended (a complete rewrite is detectable only with a pinned Archeion seal)

If you are building an AI agent for a use case that falls under GDPR, SOC 2, HIPAA, or EU AI Act audit requirements, Sasana is the audit layer.


What it produces

A completed session produces a JSONL file. The verifier checks the structure, sequence, hash chain, session bookends and (if present) the seal, and returns one of four results:

Result Meaning
INTACT All checks pass: the file is internally consistent. How much that proves is given by the evidence class below.
PARTIAL Hash chain intact but events were dropped during the session (LOG_DROP).
COMPROMISED An integrity check failed (broken chain, bad or untrusted seal, missing required seal, …).
ERROR The input could not be assessed (empty file, not JSONL, wrongly-typed fields). Never a pass.

Exit codes — parse the JSON status and evidence_class rather than relying on the exit code alone, because 0 also covers INTACT with a weak evidence class:

Code Meaning
0 INTACT
1 COMPROMISED
2 PARTIAL
3 ERROR, or bad usage (including an unrecognised flag)
4 --strict only: INTACT but not AUTHORITATIVE_EVIDENCE

--json prints the full result: status, evidence class, per-check results, errors, warnings, whether a key was pinned, whether a seal is present and trusted, and the timestamp check.

The evidence class tells you how strong the guarantee is (strongest first):

Class Meaning
AUTHORITATIVE_EVIDENCE The log carries a CHAIN_SEAL that verified against the Archeion public key you pinned with --trust-key. Only then does the sealing authority, rather than whoever holds the file, vouch for the chain head.
SELF_SEALED_EVIDENCE A well-formed CHAIN_SEAL that verifies only against the key embedded in the seal itself. Anyone can produce this; it does not show who sealed the log. The CLI prints a warning.
SIGNED_NON_AUTHORITATIVE_EVIDENCE Per-event Ed25519 signatures verify against a session key embedded in the log. They show consistency, not identity: a complete rewrite under a fresh key verifies equally well.
NON_AUTHORITATIVE_EVIDENCE Hash chain intact, no seal. Detects edits to individual events; cannot detect a complete rewrite of the file.
PARTIAL_EVIDENCE The session recorded dropped events.
NO_EVIDENCE COMPROMISED or ERROR.

Install

pip install sasana

Install via OpenClaw

# Via GitHub URL
openclaw skill install https://github.com/sahiee-dev/Sasana

# Via skills.sh shorthand (when listed in the registry)
openclaw skill install sahiee-dev/Sasana/sasana

Every session automatically produces ~/.openclaw/sasana/<session_id>.jsonl. No configuration required.


Quick start

from sasana.sqlite_ledger import SqliteLedger
import hashlib

def sha256(text: str) -> str:
    return hashlib.sha256(text.encode()).hexdigest()

ledger = SqliteLedger(db_path="session.db")
ledger.connect()
ledger.open_session(session_id="my-session", agent_id="my-agent")
ledger.record("LLM_CALL",     {"prompt_hash":   sha256(prompt)})
ledger.record("LLM_RESPONSE", {"response_hash": sha256(response)})
ledger.close_session(status="success")
ledger.export_jsonl("session.jsonl")
ledger.close()

Verify:

sasana verify session.jsonl

This checks internal consistency only. For a verdict you can rely on against someone who can write to the file, seal the session with Archeion and verify with --trust-key (see Authority sealing).

Passive observer (zero code changes required):

pip install sasana[observer]
sasana observe  # auto-detects OpenClaw WebSocket port

Already using OpenTelemetry? Keep your existing tracing (LangSmith, Langfuse, Datadog, OTLP) and get a hash-chained evidence ledger underneath — one session per trace:

pip install sasana[otel]
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from sasana.integrations.otel import SasanaSpanExporter

provider.add_span_processor(BatchSpanProcessor(SasanaSpanExporter()))

Evidence packs

Turn any verified session into the artifact an auditor actually reads — a self-contained report plus a machine-readable manifest anyone can re-verify:

sasana evidence-pack session.jsonl --standard eu_ai_act

Writes <session>_eu_ai_act_art12.html (the report) and .json (re-verifiable independently with sasana verify). Standards: eu_ai_act, soc2, hipaa, siem. Exit codes 0 (compliant) / 2 (gaps) / 3 (error) for CI.

Pass --trust-key <archeion-pubkey> so the report can show the session as sealed by your Archeion; without it a seal is reported as self-sealed and the evidence class is not AUTHORITATIVE_EVIDENCE. "Compliant" here means the report's checklist passed. It is not a legal determination.

Try it with no install at sasana.site/verify — paste a session and the hash chain + Article 12 checks run live in your browser.


Authority sealing

NON_AUTHORITATIVE_EVIDENCE means the operator is attesting their own logs, and anyone who can write the file can rebuild the chain. That is weak when the operator is a party to a dispute.

Archeion is a sealing server you run inside your security perimeter, structurally separate from the agent process. It appends a signed CHAIN_SEAL over the chain head. The agent never holds Archeion's private key, so it cannot produce a seal that verifies against Archeion's public key.

The seal is only as trustworthy as the key you check it against. The public key is embedded in the seal, so a verifier that does not already know Archeion's key cannot tell Archeion from an attacker who generated a fresh key and re-sealed a rewritten log. Record Archeion's public key out-of-band when you deploy it (GET /pubkey, over a channel you trust) and pin it:

# Start Archeion (self-hosted, inside your perimeter)
docker compose up -d

# Seal a completed session
sasana seal session.jsonl --server http://localhost:8747

# Verify. --trust-key requires a seal signed by exactly this key, and is the only way to
# get AUTHORITATIVE_EVIDENCE. --strict makes anything weaker exit 4.
sasana verify session.jsonl --trust-key <archeion-pubkey> --strict
How you verify Rewrite + re-seal with an attacker key Seal stripped
--trust-key K COMPROMISED (untrusted key) COMPROMISED (pinned key but no seal)
--require-seal (no key) INTACT, SELF_SEALED_EVIDENCE — not detected COMPROMISED (seal required)
neither INTACT, SELF_SEALED_EVIDENCE — not detected INTACT, NON_AUTHORITATIVE_EVIDENCE — not detected

For regulatory submissions and legal proceedings, verify with --trust-key (and --strict in automation). Use --require-seal on its own only when you cannot pin a key, knowing the best result is SELF_SEALED_EVIDENCE.

Against the operator

A seal shows only that a chain head was presented to Archeion, at the time recorded in the seal. It does not show that this head is the original. Archeion authenticates no callers and keeps no record of what it has sealed, so anyone who can reach POST /seal can get a rewritten copy of a session sealed with the genuine key, and with that key pinned it verifies as AUTHORITATIVE_EVIDENCE (threat A8; reproduced against the real server).

Against the operator, Sasana 2.x provides no rewrite protection unless access to /seal is controlled by a party independent of the operator. Against everyone who cannot reach Archeion, the guarantees in the table above hold. A seal also says nothing about whether the recorded events are true. Catching an operator's rewrite needs an external witness the operator does not control; that is design work, not a feature (docs/design/witness-anchoring.md). Full list of what is and is not defended: docs/THREAT_MODEL.md.

Version 1.0.0 and 1.1.0 treated any valid seal as AUTHORITATIVE_EVIDENCE and accepted a log whose seal had been removed even when a key was pinned; see the changelog.

See docs/DEPLOYMENT.md for the full deployment guide — key lifecycle, network isolation, and what to tell a security reviewer.


Timestamps

When a session opens, Sasana sends the SHA-256 hash of SESSION_START — never content — to an RFC 3161 time-stamp authority (FreeTSA by default; set SASANA_TSA_URL to change it) and embeds the returned token. This is fail-open: if the TSA is unreachable the session is recorded without a token.

sasana verify checks only that the token's MessageImprint matches the recomputed SESSION_START hash, and reports IMPRINT_MATCH. It does not verify the TSA's signature, the certificate chain, or that the time in the token is genuine, so a forged or backdated token passes. Treat the reported time as a claim, not as proof of when the session happened. A strict verifier exists as a library function (sasana.rfc3161.verify_timestamp_strict, used by sasana verify-commitment) but is not part of sasana verify.


How it works

Each event is stored with:

  • SHA-256 hash over canonical JSON (RFC 8785 for sessions written by 2.0.0 and later; see Canonicalization) — any mutation changes the hash
  • prev_hash — each event commits to all prior events, forming a chain
  • Ed25519 signature — optional per-session keypair; or Archeion's independent key

Raw content is never stored — only hashes. You cannot reconstruct what the agent said from a Sasana log.

A Rust binary (sasana-rs/) verifies sessions without a Python dependency — for forensic environments where Python is not present or trusted. It follows the same trust model (pinned key, --require-seal, --strict, SELF_SEALED_EVIDENCE) as the Python verifier v2.0.0 and is tested against the same tamper cases. It also implements both canonicalizations (below) and per-event signatures, and is tested against the same golden fixtures. It does not check RFC 3161 tokens, and its --json output has no per-check breakdown.


Canonicalization

An event's hash is SHA-256 over its canonical JSON bytes, so the exact byte encoding is part of what is being protected. Sasana 2.0.0 moved to strict RFC 8785; logs written by 1.0.0 and 1.1.0 used a slightly different number format. Each session therefore declares which one it uses, in the hashed SESSION_START payload:

{"event_type": "SESSION_START", "payload": {"agent_id": "…", "canonicalization": "jcs-rfc8785"}}
Declaration Meaning
"canonicalization": "jcs-rfc8785" RFC 8785. Written by all producers from 2.0.0 (SDK, ledger, OpenClaw bridge). Use this for any Node/JS or Rust producer too.
field absent legacy: the 1.0.0 / 1.1.0 format. Differs from RFC 8785 only in numbers (100.0 vs 100, 1e-05 vs 0.00001, exact big integers vs rounded doubles). Existing logs keep verifying.
anything else ERROR, exit 3. The verifier does not guess.

The verifier reads the declaration once and uses exactly that algorithm for every event; it never tries both. Because the declaration is inside the hashed SESSION_START, it is covered by the chain and by any seal: removing or changing it breaks verification, so a sealed log cannot be quietly re-labelled. Archeion seals a session with the algorithm the session declares. --json and the CLI header report which algorithm was used.

Logs written by the published 1.0.0 and 1.1.0 releases are committed under tests/fixtures/legacy/ and must verify forever, in Python and Rust.


Compliance mapping

Regulation Requirement addressed
EU AI Act Article 12 Automatic event recording for high-risk AI systems, with integrity evidence
SOC 2 CC7.2 System monitoring with a hash-chained, verifiable audit trail
HIPAA §164.312(b) Audit control for healthcare AI; raw PHI never recorded

These map Sasana's output to the requirement text. They are not a certification or legal advice; whether a system complies is a determination for you and your auditor.


What Sasana does not do

  • Does not record raw content. Hashes only — you cannot reconstruct prompts or responses.
  • Does not protect against the operator rewriting a session and having Archeion seal it (A8) unless you control who can reach Archeion; see Against the operator.
  • Does not prevent tampering. Detects it, within limits: a rewrite of the whole file is detected only with a seal verified against a pinned Archeion key. Detection and prevention are different.
  • Does not prove when a session happened. Timestamp checking is an imprint match only; the TSA signature and claimed time are not verified.
  • Does not prove the recorded events are true. It proves what was recorded has not changed since sealing, not that the agent reported honestly.
  • Does not replace LangSmith or Arize. Those tools are for observability. Sasana is for evidence production. They are complementary.
  • Does not have a managed cloud offering. Self-hosted only.

License

MIT

Metadata

Release files for sasana 2.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sasana 2.0.1
File Size Uploaded Provenance
sasana-2.0.1.tar.gz 120.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for sasana 2.0.1
File Interpreter ABI Platform
sasana-2.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 209.3 kB

Release files / sasana-2.0.1.tar.gz

Download URL sasana-2.0.1.tar.gz
Size 120.8 kB
Tags Source
SHA-256 checksum
How to use checksums
bafee200759c1c81949d14b034682c3c0f5174da5465cda3935bf653ba21a60f
BLAKE2b-256 checksum
How to use checksums
4c643bcab17bd8d9e5c41b62df91d4c9c723450f1add0b74084cff35ddc32d83
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.13

Release files / sasana-2.0.1-py3-none-any.whl

Download URL sasana-2.0.1-py3-none-any.whl
Size 88.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
336b193365cd6db79bc3bd20ecbb36a5c00d226238aebd3a6ab95cac6c7adb92
BLAKE2b-256 checksum
How to use checksums
203660dd56fa2f90b7db4dec6becd0261d5050fb0e1b92268faf68d0f0b2374c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.13

Security for sasana 2.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page