AgentSystems Notary

Cryptographically verifiable audit trails for Agno applications.

Overview

AgentSystems Notary creates tamper-evident audit trails for AI agent interactions.

Why

A recorded payload can be checked against an independently stored receipt to detect later changes to those captured bytes. This does not prove that every interaction was recorded or that the record contains the complete conversation.

How It Works

The Agno adapter registers pre-run and post-run hooks. It captures a run summary in your configured payload storage and writes hashes and metadata to the configured hash destinations.

What gets logged by the Agno adapter:

  • Run input text and final response text.
  • Selected agent metadata and run, session, and model identifiers. String instructions are truncated to 200 characters plus an ellipsis.
  • A SHA-256 hash and metadata for verification against the captured payload.

This is one summary per captured run, not the raw request and response for every model call. Tool rounds, full message arrays, and complete multimodal payloads are not recorded by these hooks.

The storage writes occur in order across independent destinations. They are not an atomic transaction, so a failure can leave only some destinations populated. An ordinary Notary post-hook failure is logged by Agno and does not fail the agent run. Monitor receipt creation and storage errors separately if your application requires complete audit coverage.

During verification, a matching hash establishes that the supplied captured bytes match the receipt. It does not establish completeness or independent truth of the original input.

Hash Storage Options

Hashes (not raw data) can be written to either storage option:

StorageBest ForFeatures
Decentralized (Arweave)No vendor lock-inPublic append-only ledger, open-source verification, no account needed
CustodiedManaged serviceProvider-managed storage and verification; check current plan terms

For storage retention, signing, and compliance commitments, consult the current custodied service terms. These are provider service properties, not guarantees supplied by the Agno hooks.

Prerequisites

Use Python 3.11 or newer for agentsystems-notary 0.8.1. The example also requires OpenSSL, an existing writable S3 bucket, and credentials for the configured storage and model provider. The verification commands require AWS CLI, zip, Node.js, and npm.

pip install "agentsystems-notary[agno]==0.8.1" agno anthropic python-dotenv

Example (Decentralized)

Generate signing key

openssl genrsa -out arweave-key.pem 4096

Retain this key. It is required to prove ownership of on-chain hashes during verification.

For production, use a cloud key management service.

Create .env file

Create a .env file in your project root:

# AWS S3 for captured run-summary payloads
ORG_AWS_S3_BUCKET_NAME=your-bucket
ORG_AWS_S3_ACCESS_KEY_ID=AKIA...
ORG_AWS_S3_SECRET_ACCESS_KEY=...
ORG_AWS_S3_REGION=us-east-1

# Path to signing key
ARWEAVE_PRIVATE_KEY_PATH=./arweave-key.pem

# Anthropic
ANTHROPIC_API_KEY=sk-ant-...

Run the example

import os

from agentsystems_notary import (
    AgnoNotary,
    ArweaveHashStorage,
    AwsS3StorageConfig,
    LocalKeySignerConfig,
    RawPayloadStorage,
)
from agno.agent import Agent
from agno.models.anthropic import Claude
from dotenv import load_dotenv

load_dotenv()

# Your S3 bucket for captured run-summary payloads
s3_config = AwsS3StorageConfig(
    bucket_name=os.environ["ORG_AWS_S3_BUCKET_NAME"],
    aws_access_key_id=os.environ["ORG_AWS_S3_ACCESS_KEY_ID"],
    aws_secret_access_key=os.environ["ORG_AWS_S3_SECRET_ACCESS_KEY"],
    aws_region=os.environ["ORG_AWS_S3_REGION"],
)
raw_payload_storage = RawPayloadStorage(storage=s3_config)

# Local RSA key for signing
signer = LocalKeySignerConfig(
    private_key_path=os.environ["ARWEAVE_PRIVATE_KEY_PATH"],
)

# Arweave for decentralized hash storage
# Namespace is public: written to the ledger and used to segment stored data
# Namespace should be one anonymous ID per customer, agent, or environment
# Retain a record of your namespace mappings
arweave_storage = ArweaveHashStorage(
    namespace="tenant_a1b2c3d4",  # See namespace comments above
    signer=signer,
)

# Assemble notary
notary = AgnoNotary(
    raw_payload_storage=raw_payload_storage,
    hash_storage=[arweave_storage],
    debug=True,
)

# Attach hooks to agent
agent = Agent(
    model=Claude(
        id="claude-sonnet-4-5-20250929",
        api_key=os.environ["ANTHROPIC_API_KEY"],
    ),
    instructions="You are a helpful assistant.",
    **notary.get_hooks(),
)

agent.print_response("What is the capital of France?")

Verification

Decentralized (Arweave): Download raw payloads from your storage bucket, zip them, and verify with the open-source CLI:

aws s3 sync s3://your-bucket/arweave/tenant_a1b2c3d4/ ./logs
zip -r logs.zip logs
npm install -g agentsystems-verify
agentsystems-verify --logs logs.zip

The CLI re-hashes each payload and compares against the hashes stored on Arweave. See the full verification guide for details.

Alternatively, the Verify UI supports both decentralized and custodied verification.

Configuration

Resources