Skip to content

Audit Module

Audit logging infrastructure including the main logger, encryption utilities, and storage backends.


AuditLogger

Main audit logging interface for AI compliance.

The AuditLogger provides a high-level interface for recording AI interactions, including inputs, outputs, safety evaluations, and performance metrics. It supports optional encryption of content and configurable retention policies.

Parameters:

Name Type Description Default
storage Union[StorageBackend, str]

Either a StorageBackend instance or a file path string. If a string is provided, FileStorage is automatically created.

required
encryption EncryptionManager | None

Optional EncryptionManager for encrypting stored content.

None
store_content bool

If True, stores actual content. If False, only stores content hashes for privacy (default: False).

False
retention_days int

Number of days to retain entries before cleanup (default: 365).

365

Attributes:

Name Type Description
storage StorageBackend

The underlying storage backend.

encryption

The encryption manager (if provided).

store_content

Whether to store actual content.

retention_days

Retention period in days.

Example

Basic usage with file storage:

logger = AuditLogger("/var/log/audit") entry_id = await logger.log( ... input="What is 2+2?", ... output="4", ... provider="openai", ... model="gpt-4", ... )

With encryption and content storage:

from rotalabs_comply.audit import EncryptionManager encryption = EncryptionManager() logger = AuditLogger( ... "/var/log/audit", ... encryption=encryption, ... store_content=True, ... ) entry_id = await logger.log( ... input="Sensitive question", ... output="Sensitive answer", ... safety_passed=True, ... )

With custom storage backend:

from rotalabs_comply.audit import MemoryStorage storage = MemoryStorage(max_entries=10000) logger = AuditLogger(storage)

__init__(storage, encryption=None, store_content=False, retention_days=365)

Initialize the audit logger.

Parameters:

Name Type Description Default
storage Union[StorageBackend, str]

StorageBackend instance or file path string.

required
encryption EncryptionManager | None

Optional encryption manager for content encryption.

None
store_content bool

Whether to store actual content (default: False).

False
retention_days int

Days to retain entries (default: 365).

365

cleanup_expired() async

Delete entries older than the retention period.

Removes all entries with timestamps older than retention_days from the current time.

Returns:

Name Type Description
int int

Number of entries deleted.

Example

Delete entries older than retention_days

deleted_count = await logger.cleanup_expired() print(f"Cleaned up {deleted_count} expired entries")

Note

This operation may be slow for large datasets. Consider running during off-peak hours or using storage-native lifecycle policies (e.g., S3 lifecycle rules) for better performance.

decrypt_content(encrypted_content)

Decrypt encrypted content from an audit entry.

Convenience method for decrypting content stored in audit entries when encryption is enabled.

Parameters:

Name Type Description Default
encrypted_content str

The encrypted content string from an AuditEntry.

required

Returns:

Name Type Description
str str

The decrypted original content.

Raises:

Type Description
ValueError

If no encryption manager is configured.

InvalidToken

If decryption fails.

Example

entry = await logger.get_entry("abc-123") if entry and entry.input_content: ... original_input = logger.decrypt_content(entry.input_content)

get_entries(start, end) async

Retrieve all audit entries within a time range.

Parameters:

Name Type Description Default
start datetime

Start of the time range (inclusive).

required
end datetime

End of the time range (inclusive).

required

Returns:

Type Description
List[AuditEntry]

List[AuditEntry]: All entries within the specified time range.

Example

from datetime import datetime, timedelta end = datetime.utcnow() start = end - timedelta(days=7) entries = await logger.get_entries(start, end) print(f"Found {len(entries)} entries in the last week")

get_entry(entry_id) async

Retrieve an audit entry by ID.

If encryption is enabled and store_content is True, the content will still be encrypted in the returned entry. Use the encryption manager to decrypt if needed.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry.

required

Returns:

Type Description
AuditEntry | None

AuditEntry | None: The entry if found, None otherwise.

Example

entry = await logger.get_entry("abc-123-def") if entry: ... print(f"Safety passed: {entry.safety_passed}")

log(input, output, provider=None, model=None, conversation_id=None, safety_passed=True, detectors_triggered=None, block_reason=None, alerts=None, latency_ms=0.0, input_tokens=None, output_tokens=None, metadata=None) async

Log an AI interaction.

Creates an audit entry for the interaction and stores it in the configured storage backend.

Parameters:

Name Type Description Default
input str

The user input or prompt.

required
output str

The AI-generated output or response.

required
provider str | None

The AI provider (e.g., "openai", "anthropic").

None
model str | None

The model identifier (e.g., "gpt-4", "claude-3-opus").

None
conversation_id str | None

Optional ID to link related interactions.

None
safety_passed bool

Whether the interaction passed safety checks (default: True).

True
detectors_triggered List[str] | None

List of safety detector names that triggered.

None
block_reason str | None

Reason for blocking, if the request was blocked.

None
alerts List[str] | None

List of alert messages generated.

None
latency_ms float

Time taken to process the request in milliseconds (default: 0.0).

0.0
input_tokens int | None

Number of tokens in the input.

None
output_tokens int | None

Number of tokens in the output.

None
metadata Dict[str, Any] | None

Additional custom metadata dictionary.

None

Returns:

Name Type Description
str str

The unique entry ID for this audit log entry.

Example

entry_id = await logger.log( ... input="Tell me a joke", ... output="Why did the chicken...", ... provider="anthropic", ... model="claude-3-opus", ... safety_passed=True, ... latency_ms=250.5, ... input_tokens=5, ... output_tokens=20, ... metadata={"session_id": "abc123"}, ... )

Main audit logging interface for AI compliance.

Constructor

AuditLogger(
    storage: Union[StorageBackend, str],
    encryption: Optional[EncryptionManager] = None,
    store_content: bool = False,
    retention_days: int = 365,
)

Parameters:

Parameter Type Default Description
storage Union[StorageBackend, str] Required Storage backend or file path
encryption Optional[EncryptionManager] None Encryption manager for content
store_content bool False Store actual content vs hashes
retention_days int 365 Days to retain entries

Methods

log

async def log(
    input: str,
    output: str,
    provider: Optional[str] = None,
    model: Optional[str] = None,
    conversation_id: Optional[str] = None,
    safety_passed: bool = True,
    detectors_triggered: Optional[List[str]] = None,
    block_reason: Optional[str] = None,
    alerts: Optional[List[str]] = None,
    latency_ms: float = 0.0,
    input_tokens: Optional[int] = None,
    output_tokens: Optional[int] = None,
    metadata: Optional[Dict[str, Any]] = None,
) -> str

Log an AI interaction and return the entry ID.

Example:

entry_id = await logger.log(
    input="Tell me a joke",
    output="Why did the chicken...",
    provider="anthropic",
    model="claude-3-opus",
    safety_passed=True,
    latency_ms=250.5,
    metadata={"session_id": "abc123"},
)

get_entry

async def get_entry(entry_id: str) -> Optional[AuditEntry]

Retrieve an audit entry by ID.

Example:

entry = await logger.get_entry("abc-123-def")
if entry:
    print(f"Safety passed: {entry.safety_passed}")

get_entries

async def get_entries(start: datetime, end: datetime) -> List[AuditEntry]

Retrieve all entries within a time range.

Example:

from datetime import datetime, timedelta

end = datetime.utcnow()
start = end - timedelta(days=7)
entries = await logger.get_entries(start, end)

cleanup_expired

async def cleanup_expired() -> int

Delete entries older than the retention period. Returns count of deleted entries.

Example:

deleted = await logger.cleanup_expired()
print(f"Cleaned up {deleted} expired entries")

decrypt_content

def decrypt_content(encrypted_content: str) -> str

Decrypt encrypted content from an audit entry.

Raises:

  • ValueError: If no encryption manager is configured
  • cryptography.fernet.InvalidToken: If decryption fails

Example:

entry = await logger.get_entry("abc-123")
if entry and entry.input_content:
    original = logger.decrypt_content(entry.input_content)

Encryption

EncryptionManager

High-level encryption manager for string data.

Provides a convenient interface for encrypting and decrypting string data, with automatic key generation if not provided.

Parameters:

Name Type Description Default
key bytes | None

Optional Fernet encryption key. If not provided, a new key is generated.

None

Attributes:

Name Type Description
_key

The encryption key (kept private).

_fernet

The Fernet cipher instance.

Example

manager = EncryptionManager() encrypted = manager.encrypt("sensitive data") manager.decrypt(encrypted) 'sensitive data'

Use existing key

key = generate_key() manager = EncryptionManager(key) manager.get_key() == key True

__init__(key=None)

Initialize the encryption manager.

Parameters:

Name Type Description Default
key bytes | None

Optional Fernet encryption key. If None, generates a new key.

None

decrypt(data)

Decrypt a base64-encoded encrypted string.

Parameters:

Name Type Description Default
data str

The base64-encoded encrypted string (from encrypt()).

required

Returns:

Name Type Description
str str

The original decrypted string.

Raises:

Type Description
InvalidToken

If decryption fails.

Example

manager = EncryptionManager() encrypted = manager.encrypt("secret") manager.decrypt(encrypted) 'secret'

encrypt(data)

Encrypt a string and return base64-encoded result.

Parameters:

Name Type Description Default
data str

The string to encrypt.

required

Returns:

Name Type Description
str str

Base64-encoded encrypted string, safe for storage in JSON.

Example

manager = EncryptionManager() encrypted = manager.encrypt("secret") isinstance(encrypted, str) True

get_key()

Get the encryption key.

Returns:

Name Type Description
bytes bytes

The Fernet encryption key. Store this securely!

Warning

The encryption key must be stored securely. If lost, encrypted data cannot be recovered.

Example

manager = EncryptionManager() key = manager.get_key() len(key) 44

High-level encryption manager for string data.

Constructor

EncryptionManager(key: Optional[bytes] = None)

Parameters:

Parameter Type Default Description
key Optional[bytes] Auto-gen Fernet encryption key

Methods

encrypt

def encrypt(data: str) -> str

Encrypt a string and return base64-encoded result.

decrypt

def decrypt(data: str) -> str

Decrypt a base64-encoded encrypted string.

get_key

def get_key() -> bytes

Get the encryption key. Store this securely!

Example:

from rotalabs_comply import EncryptionManager

manager = EncryptionManager()
encrypted = manager.encrypt("sensitive data")
decrypted = manager.decrypt(encrypted)

# Save key securely
key = manager.get_key()

Helper Functions

generate_key

def generate_key() -> bytes

Generate a new Fernet encryption key.

Example:

from rotalabs_comply import generate_key

key = generate_key()
print(len(key))  # 44

encrypt

def encrypt(data: bytes, key: bytes) -> bytes

Encrypt raw bytes using Fernet symmetric encryption.

decrypt

def decrypt(data: bytes, key: bytes) -> bytes

Decrypt data that was encrypted with Fernet.

hash_content

def hash_content(content: str) -> str

Compute SHA-256 hash of string content.

Example:

from rotalabs_comply import hash_content

content_hash = hash_content("hello world")
# Returns: 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'

Storage Backends

StorageBackend Protocol

Protocol defining the interface for audit log storage backends.

All storage backends must implement these async methods to support writing, reading, listing, deleting, and counting audit entries.

count() abstractmethod async

Count total number of entries in storage.

Returns:

Name Type Description
int int

Total number of stored entries.

delete(entry_id) abstractmethod async

Delete an entry by ID.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry to delete.

required

Returns:

Name Type Description
bool bool

True if the entry was deleted, False if not found.

list_entries(start, end) abstractmethod async

List all entries within a time range.

Parameters:

Name Type Description Default
start datetime

Start of the time range (inclusive).

required
end datetime

End of the time range (inclusive).

required

Returns:

Type Description
List[AuditEntry]

List[AuditEntry]: Entries within the specified time range.

read(entry_id) abstractmethod async

Read an audit entry by ID.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry.

required

Returns:

Type Description
AuditEntry | None

AuditEntry | None: The entry if found, None otherwise.

write(entry) abstractmethod async

Write an audit entry to storage.

Parameters:

Name Type Description Default
entry AuditEntry

The audit entry to store.

required

Returns:

Name Type Description
str str

The entry ID (same as entry.id).

Protocol defining the interface for audit log storage backends.

Required Methods:

Method Signature Description
write async (entry: AuditEntry) -> str Write entry, return ID
read async (entry_id: str) -> Optional[AuditEntry] Read entry by ID
list_entries async (start: datetime, end: datetime) -> List[AuditEntry] List entries in range
delete async (entry_id: str) -> bool Delete entry, return success
count async () -> int Count total entries

FileStorage

File-based storage backend using JSONL format.

Stores audit entries as JSON Lines files with automatic rotation when files exceed the configured size limit.

Parameters:

Name Type Description Default
path str

Directory path for storing audit files.

required
rotation_size_mb int

Maximum file size in MB before rotation (default: 100).

100

Attributes:

Name Type Description
path

The storage directory path.

rotation_size_bytes

Maximum file size in bytes.

Example

storage = FileStorage("/var/log/audit") entry_id = await storage.write(entry) retrieved = await storage.read(entry_id)

__init__(path, rotation_size_mb=100)

Initialize file storage.

Parameters:

Name Type Description Default
path str

Directory path for storing audit files.

required
rotation_size_mb int

Maximum file size in MB before rotation.

100

count() async

Count total number of entries in storage.

Returns:

Name Type Description
int int

Total number of stored entries.

delete(entry_id) async

Delete an entry by ID.

Note: This rewrites the file without the deleted entry, which may be slow for large files. Consider using retention policies instead.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry to delete.

required

Returns:

Name Type Description
bool bool

True if the entry was deleted, False if not found.

list_entries(start, end) async

List all entries within a time range.

Parameters:

Name Type Description Default
start datetime

Start of the time range (inclusive).

required
end datetime

End of the time range (inclusive).

required

Returns:

Type Description
List[AuditEntry]

List[AuditEntry]: Entries within the specified time range.

read(entry_id) async

Read an audit entry by ID.

Searches through all JSONL files if the entry is not in the index.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry.

required

Returns:

Type Description
AuditEntry | None

AuditEntry | None: The entry if found, None otherwise.

write(entry) async

Write an audit entry to a JSONL file.

Auto-rotates the file if it exceeds the configured size limit.

Parameters:

Name Type Description Default
entry AuditEntry

The audit entry to store.

required

Returns:

Name Type Description
str str

The entry ID.

File-based storage backend using JSONL format.

Constructor

FileStorage(path: str, rotation_size_mb: int = 100)

Parameters:

Parameter Type Default Description
path str Required Directory for audit files
rotation_size_mb int 100 Max file size before rotation

File Structure:

{path}/
├── audit_20260128.jsonl
├── audit_20260128_001.jsonl  # Rotated
├── audit_20260129.jsonl
└── ...

Example:

from rotalabs_comply.audit import FileStorage

storage = FileStorage("/var/log/audit", rotation_size_mb=50)
entry_id = await storage.write(entry)

MemoryStorage

In-memory storage backend for testing and development.

Stores entries in a dictionary. Data is lost when the process ends.

Parameters:

Name Type Description Default
max_entries int | None

Optional maximum number of entries to store. When exceeded, oldest entries are removed.

None
Example

storage = MemoryStorage(max_entries=1000) entry_id = await storage.write(entry) count = await storage.count()

__init__(max_entries=None)

Initialize memory storage.

Parameters:

Name Type Description Default
max_entries int | None

Optional maximum number of entries to store.

None

count() async

Count total number of entries in storage.

Returns:

Name Type Description
int int

Total number of stored entries.

delete(entry_id) async

Delete an entry by ID.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry to delete.

required

Returns:

Name Type Description
bool bool

True if the entry was deleted, False if not found.

list_entries(start, end) async

List all entries within a time range.

Parameters:

Name Type Description Default
start datetime

Start of the time range (inclusive).

required
end datetime

End of the time range (inclusive).

required

Returns:

Type Description
List[AuditEntry]

List[AuditEntry]: Entries within the specified time range.

read(entry_id) async

Read an audit entry by ID.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry.

required

Returns:

Type Description
AuditEntry | None

AuditEntry | None: The entry if found, None otherwise.

write(entry) async

Write an audit entry to memory.

If max_entries is set and exceeded, removes the oldest entry.

Parameters:

Name Type Description Default
entry AuditEntry

The audit entry to store.

required

Returns:

Name Type Description
str str

The entry ID.

In-memory storage backend for testing and development.

Constructor

MemoryStorage(max_entries: Optional[int] = None)

Parameters:

Parameter Type Default Description
max_entries Optional[int] None Max entries (LRU eviction)

Example:

from rotalabs_comply.audit import MemoryStorage

storage = MemoryStorage(max_entries=1000)
count = await storage.count()

Data Persistence

Data is lost when the process ends. Use only for testing.


S3Storage

AWS S3 storage backend for audit logs.

Stores each audit entry as a separate JSON file in S3, organized by date for easy querying and lifecycle management.

Requires boto3 to be installed (optional dependency).

Parameters:

Name Type Description Default
bucket str

S3 bucket name.

required
prefix str

Key prefix for audit files (default: "audit/").

'audit/'
region str | None

AWS region (optional, uses default if not specified).

None
File structure

{prefix}{YYYY-MM-DD}/{entry_id}.json

Example

storage = S3Storage("my-audit-bucket", prefix="logs/audit/") entry_id = await storage.write(entry)

Stored at: s3://my-audit-bucket/logs/audit/2024-01-15/abc123.json

__init__(bucket, prefix='audit/', region=None)

Initialize S3 storage.

Parameters:

Name Type Description Default
bucket str

S3 bucket name.

required
prefix str

Key prefix for audit files.

'audit/'
region str | None

AWS region (optional).

None

count() async

Count total number of entries in storage.

Returns:

Name Type Description
int int

Total number of stored entries.

delete(entry_id) async

Delete an entry by ID.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry to delete.

required

Returns:

Name Type Description
bool bool

True if the entry was deleted, False if not found.

list_entries(start, end) async

List all entries within a time range.

Parameters:

Name Type Description Default
start datetime

Start of the time range (inclusive).

required
end datetime

End of the time range (inclusive).

required

Returns:

Type Description
List[AuditEntry]

List[AuditEntry]: Entries within the specified time range.

read(entry_id) async

Read an audit entry by ID.

Note: This searches through date prefixes which may be slow. Consider maintaining an index for production use.

Parameters:

Name Type Description Default
entry_id str

The unique identifier of the entry.

required

Returns:

Type Description
AuditEntry | None

AuditEntry | None: The entry if found, None otherwise.

write(entry) async

Write an audit entry to S3.

Parameters:

Name Type Description Default
entry AuditEntry

The audit entry to store.

required

Returns:

Name Type Description
str str

The entry ID.

AWS S3 storage backend for audit logs.

Constructor

S3Storage(
    bucket: str,
    prefix: str = "audit/",
    region: Optional[str] = None,
)

Parameters:

Parameter Type Default Description
bucket str Required S3 bucket name
prefix str "audit/" Key prefix for files
region Optional[str] None AWS region

Key Structure:

s3://{bucket}/{prefix}{YYYY-MM-DD}/{entry_id}.json

Example:

from rotalabs_comply.audit import S3Storage

storage = S3Storage(
    bucket="my-audit-bucket",
    prefix="prod/audit/",
    region="us-west-2",
)

Dependency

Requires boto3. Install with pip install rotalabs-comply[s3].


AuditEntry (Storage)

Represents a single audit log entry.

Captures all relevant information about an AI interaction including inputs, outputs, safety evaluations, and performance metrics.

Attributes:

Name Type Description
id str

Unique identifier for this entry.

timestamp str

When the interaction occurred (ISO format).

input_hash str

SHA-256 hash of the input content.

output_hash str

SHA-256 hash of the output content.

input_content str | None

Actual input content (if store_content=True, may be encrypted).

output_content str | None

Actual output content (if store_content=True, may be encrypted).

provider str | None

The AI provider (e.g., "openai", "anthropic").

model str | None

The model identifier (e.g., "gpt-4", "claude-3-opus").

conversation_id str | None

Optional ID linking related interactions.

safety_passed bool

Whether the interaction passed all safety checks.

detectors_triggered List[str]

List of safety detector names that triggered.

block_reason str | None

Reason for blocking, if the request was blocked.

alerts List[str]

List of alert messages generated.

latency_ms float

Time taken to process the request in milliseconds.

input_tokens int | None

Number of tokens in the input.

output_tokens int | None

Number of tokens in the output.

metadata Dict[str, Any]

Additional custom metadata.

from_dict(data) classmethod

Create entry from dictionary.

to_dict()

Convert entry to dictionary for serialization.

Dataclass representing a single audit log entry in storage.

Attributes:

Attribute Type Description
id str Unique identifier
timestamp str ISO format timestamp
input_hash str SHA-256 of input
output_hash str SHA-256 of output
input_content Optional[str] Input content (if stored)
output_content Optional[str] Output content (if stored)
provider Optional[str] AI provider
model Optional[str] Model identifier
conversation_id Optional[str] Conversation ID
safety_passed bool Safety check result
detectors_triggered List[str] Triggered detectors
block_reason Optional[str] Block reason
alerts List[str] Alert messages
latency_ms float Response latency
input_tokens Optional[int] Input token count
output_tokens Optional[int] Output token count
metadata Dict[str, Any] Custom metadata

Methods:

Method Description
to_dict() Convert to dictionary
from_dict(data) Create from dictionary

Helper Functions

create_entry_id

def create_entry_id() -> str

Generate a unique entry ID (UUID v4).

Example:

from rotalabs_comply.audit.storage import create_entry_id

entry_id = create_entry_id()
# Returns: "550e8400-e29b-41d4-a716-446655440000"