Skip to content

Attestation & Audit Logging

OxideShield's attestation layer provides cryptographically signed audit logs that create tamper-evident records of every security check. This enables compliance with regulatory requirements, forensic analysis, and provable security posture.

License Requirement

Attestation features require a Professional or Enterprise license. See Licensing for details.

Why Attestation Matters

When auditors, regulators, or security teams ask "How do you know your AI is secure?", you need more than logs - you need proof.

Challenge How Attestation Solves It
Regulatory Compliance Signed audit trails for SOX, HIPAA, GDPR, FedRAMP
Forensic Analysis Immutable records of what was checked, when, and the outcome
Tamper Detection Ed25519 signatures detect any log modifications
Non-Repudiation Cryptographic proof that checks occurred
Audit Reports Generate signed compliance reports for stakeholders

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        Application                              │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                     AuditedGuard<G>                             │
│  ┌─────────────┐  ┌──────────────┐  ┌─────────────────────┐    │
│  │ Inner Guard │──│ AuditEntry   │──│ SignedAuditEntry    │    │
│  └─────────────┘  └──────────────┘  └─────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                      AuditStorage                               │
│  ┌─────────────────────┐  ┌─────────────────────────────────┐  │
│  │  MemoryAuditStorage │  │  FileAuditStorage (JSONL)       │  │
│  └─────────────────────┘  └─────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│                   AttestationReport                             │
│  ┌───────────────┐  ┌───────────────┐  ┌───────────────────┐   │
│  │ JSON Export   │  │ HTML Export   │  │ Verification      │   │
│  └───────────────┘  └───────────────┘  └───────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

Core Concepts

Audit Entry

Every security check creates an AuditEntry containing:

Field Description
id Unique identifier (UUID v4)
timestamp ISO 8601 timestamp (UTC)
guard_name Name of the guard that ran
guard_version Version of the guard
input_hash SHA-256 hash of the input content
output_hash SHA-256 hash of sanitized output (if any)
result Passed, Blocked, Sanitized, or Error
duration_ns Check duration in nanoseconds
metadata Additional context (session ID, user ID, etc.)

Signed Entries

Each audit entry is cryptographically signed using Ed25519:

SignedAuditEntry {
    entry: AuditEntry,
    signature: [u8; 64],        // Ed25519 signature
    signer_public_key: [u8; 32] // Signer's public key
}

This ensures:

  • Integrity: Any modification invalidates the signature
  • Authentication: Entries can be verified with the public key
  • Non-repudiation: Only the holder of the private key could sign

Attestation Reports

Reports aggregate multiple entries with a signed summary:

AttestationReport {
    report_id: UUID,
    timestamp: DateTime,
    entries: Vec<SignedAuditEntry>,
    summary: ReportSummary,
    signature: [u8; 64]  // Signs entire report
}

Reports include: - Total checks, passed, blocked, sanitized counts - Guards used and their versions - Time range covered - Signature over all entries (prevents cherry-picking)

Quick Start

Rust

use oxide_attestation::{
    AuditedGuard, AuditGuard, AttestationSigner,
    MemoryAuditStorage, AuditFilter,
};
use oxide_guard::guard::LengthGuard;
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create storage and signer
    let storage = Arc::new(MemoryAuditStorage::new());
    let signer = Arc::new(AttestationSigner::generate());

    // Wrap any guard with auditing
    let guard = LengthGuard::new("length")
        .with_max_chars(1000)
        .audited(storage.clone(), signer.clone())
        .with_session_id("user-session-123");

    // Every check is now recorded
    let result = guard.check_async("Hello, world!").await;

    // Generate a signed report
    let report = storage
        .generate_report(&AuditFilter::new(), &signer)
        .await?;

    println!("Total checks: {}", report.summary.total_checks);
    println!("Report verified: {}", report.verify());

    Ok(())
}

Python

from oxideshield import (
    AttestationSigner,
    MemoryAuditStorage,
    AuditFilter,
    length_guard,
    generate_report,
)

# Create signer with new key pair
signer = AttestationSigner.generate()

# Create storage backend
storage = MemoryAuditStorage()

# Wrap a guard with auditing
guard = length_guard(max_chars=1000)
audited_guard = guard.audited(storage, signer)

# Run checks (automatically recorded)
result = audited_guard.check("Hello, world!")

# Generate report
report = generate_report(storage, AuditFilter(), signer)

print(f"Total checks: {report.total_checks}")
print(f"Verified: {report.verify()}")

# Export for auditors
html_report = report.to_html()
json_report = report.to_json()

CLI

# Check audit storage status
oxideshield audit status

# List recent entries
oxideshield audit list --limit 10

# List entries for specific guard
oxideshield audit list --guard PatternGuard --limit 50

# Generate compliance report
oxideshield audit report --format html --output audit-report.html

# Verify a report
oxideshield audit verify --input audit-report.json

# Export entries
oxideshield audit export --format json --output entries.json

Storage Backends

Memory Storage

In-memory storage for testing and development:

storage = MemoryAuditStorage()
  • Fast, no persistence
  • Useful for unit tests
  • Data lost on restart

File Storage (JSONL)

Append-only JSONL files for production:

storage = FileAuditStorage("/var/log/oxideshield/audit.jsonl")
  • Each entry is a single JSON line
  • Append-only (no modifications)
  • Survives restarts
  • Easy to parse and backup

Example JSONL format:

{"entry":{"id":"550e8400-e29b-41d4-a716-446655440000","timestamp":"2026-01-27T10:30:00Z","guard_name":"PatternGuard","input_hash":"a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e","result":{"type":"Blocked","reason":"Prompt injection detected"}},"signature":"base64...","signer_public_key":"base64..."}

Filtering Entries

Query audit entries with filters:

from oxideshield import AuditFilter
from datetime import datetime, timedelta

# Filter by time range
filter = AuditFilter().since(datetime.now() - timedelta(hours=24))

# Filter by guard name
filter = AuditFilter().by_guard("PatternGuard")

# Filter by result type
filter = AuditFilter().blocked_only()

# Filter by session
filter = AuditFilter().by_session("user-session-123")

# Combine filters
filter = (AuditFilter()
    .since(datetime.now() - timedelta(days=7))
    .by_guard("PIIGuard")
    .blocked_only())

entries = storage.list(filter)
# Last 24 hours
oxideshield audit list --since 24h

# Specific guard
oxideshield audit list --guard PIIGuard

# Only blocked entries
oxideshield audit list --result blocked

# By session ID
oxideshield audit list --session user-123

# Combined
oxideshield audit list \
  --since 7d \
  --guard PIIGuard \
  --result blocked \
  --limit 100

Report Generation

HTML Reports

Generate human-readable compliance reports:

oxideshield audit report \
  --format html \
  --since 7d \
  --output weekly-audit.html

HTML reports include: - Executive summary with statistics - Guard-by-guard breakdown - Timeline visualization - Signature verification badge - Exportable data tables

JSON Reports

Generate machine-readable reports for integration:

oxideshield audit report \
  --format json \
  --since 30d \
  --output monthly-audit.json

JSON structure:

{
  "report_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-01-27T10:30:00Z",
  "summary": {
    "total_checks": 15420,
    "passed": 15312,
    "blocked": 98,
    "sanitized": 10,
    "errors": 0,
    "guards_used": ["PatternGuard", "PIIGuard", "ToxicityGuard"]
  },
  "entries": [...],
  "signature": "base64..."
}

Signature Verification

Verify a Report

# Verify report integrity
oxideshield audit verify --input audit-report.json

# Output:
# ✓ Report signature valid
# ✓ 15420 entries verified
# ✓ No tampered entries detected

Verify with Specific Key

# Verify against known public key
oxideshield audit verify \
  --input audit-report.json \
  --public-key /path/to/public.pem

Programmatic Verification

from oxideshield import AttestationReport

# Load report
report = AttestationReport.from_json(json_data)

# Verify report signature
assert report.verify(), "Report signature invalid!"

# Verify with specific public key
assert report.verify_with_key(known_public_key)

# Deep verification (all individual entries)
assert report.verify_deep(), "Entry tampering detected!"

Key Management

Generate Keys

from oxideshield import AttestationSigner

# Generate new key pair
signer = AttestationSigner.generate()

# Export public key for sharing
public_key_pem = signer.public_key_pem()

# Save private key securely (never share!)
# Use environment variables or secret management

Key Rotation

Best practices for key rotation:

  1. Generate new key pair before old key expires
  2. Update signer in your application
  3. Keep old public key for verifying historical entries
  4. Document key changeover date in audit trail
# Support multiple verification keys
verifiers = [
    old_public_key,   # For entries before 2026-01-01
    new_public_key,   # For entries from 2026-01-01
]

for entry in entries:
    for key in verifiers:
        if entry.verify_with_key(key):
            break
    else:
        raise ValueError("Entry failed all verification keys")

Secure Storage

Private Key Security

Never commit private keys to source control. Use:

  • Environment variables (OXIDESHIELD_SIGNING_KEY)
  • Secret management (AWS Secrets Manager, HashiCorp Vault)
  • Hardware security modules (HSMs) for enterprise

Integration Patterns

With Proxy Gateway

The proxy automatically logs all guard checks:

# proxy.yaml
attestation:
  enabled: true
  storage_path: /var/log/oxideshield/audit.jsonl
  signer_key: ${OXIDESHIELD_SIGNING_KEY}
  session_id_header: X-Session-ID

With Python Frameworks

FastAPI

from fastapi import FastAPI, Depends
from oxideshield import AttestationSigner, FileAuditStorage, AuditedGuard

app = FastAPI()

# Global audit infrastructure
storage = FileAuditStorage("/var/log/audit.jsonl")
signer = AttestationSigner.generate()

def get_audited_guard():
    return pattern_guard().audited(storage, signer)

@app.post("/chat")
async def chat(
    message: str,
    guard: AuditedGuard = Depends(get_audited_guard)
):
    result = guard.check(message)
    # Check is automatically logged
    ...

LangChain

from langchain.callbacks import BaseCallbackHandler
from oxideshield import AuditedGuard

class AuditedGuardCallback(BaseCallbackHandler):
    def __init__(self, guard: AuditedGuard):
        self.guard = guard

    def on_llm_start(self, prompts, **kwargs):
        for prompt in prompts:
            result = self.guard.check(prompt)
            if not result.passed:
                raise SecurityException(result.reason)

Compliance Mapping

Attestation helps satisfy requirements from:

Framework Requirement How Attestation Helps
SOX Audit trail for financial systems Immutable, signed records of all AI interactions
HIPAA Access logging for PHI Track all PII detection and handling
GDPR Demonstrate data protection measures Prove security checks were applied
FedRAMP Continuous monitoring Real-time audit trail with integrity verification
PCI-DSS Log access to cardholder data Track credit card detection events

Performance

Attestation adds minimal overhead:

Operation Latency
Create AuditEntry ~100ns
SHA-256 hash (1KB input) ~2μs
Ed25519 sign ~50μs
Ed25519 verify ~100μs
Write to JSONL ~10μs

Total overhead: <100μs per check (negligible compared to LLM latency)

Best Practices

  1. Use file storage in production - Memory storage loses data on restart

  2. Rotate logs regularly - Keep JSONL files under 100MB for efficient queries

    # Logrotate configuration
    /var/log/oxideshield/audit.jsonl {
        daily
        rotate 30
        compress
        copytruncate
    }
    

  3. Back up audit logs - These are your compliance evidence

  4. Include session IDs - Makes filtering by user/request easier

  5. Verify reports periodically - Run oxideshield audit verify in CI/CD

  6. Store public keys safely - You'll need them to verify old entries

Troubleshooting

"License required" error

Attestation requires a Professional license:

export OXIDESHIELD_LICENSE_KEY="OXIDE-xxx-..."

Large audit files

Split files by date:

storage = FileAuditStorage(f"/var/log/audit-{date.today()}.jsonl")

Verification failures

Check that you're using the correct public key for the time period:

oxideshield audit verify --input report.json --public-key /path/to/key.pem

Next Steps