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:
- Fast, no persistence
- Useful for unit tests
- Data lost on restart
File Storage (JSONL)¶
Append-only JSONL files for production:
- 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:
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:
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:
- Generate new key pair before old key expires
- Update signer in your application
- Keep old public key for verifying historical entries
- 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¶
-
Use file storage in production - Memory storage loses data on restart
-
Rotate logs regularly - Keep JSONL files under 100MB for efficient queries
-
Back up audit logs - These are your compliance evidence
-
Include session IDs - Makes filtering by user/request easier
-
Verify reports periodically - Run
oxideshield audit verifyin CI/CD -
Store public keys safely - You'll need them to verify old entries
Troubleshooting¶
"License required" error¶
Attestation requires a Professional license:
Large audit files¶
Split files by date:
Verification failures¶
Check that you're using the correct public key for the time period:
Next Steps¶
- CLI Reference - Full
auditcommand documentation - Python Attestation - Detailed Python API guide
- Compliance Overview - Framework mappings
- Proxy Gateway - Automatic attestation with proxy