# How Audit Logging Tracks Changes in Agent-Native: A Complete Implementation Guide

> Discover how Agent-Native audit logging tracks every change using an append-only SQL table. Learn about strict tenant isolation and secure access for authorized users. Implement comprehensive change tracking.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: how-to-guide
- Published: 2026-06-29

---

**Agent-Native records every mutation performed by human users or AI agents in an append-only SQL table called `agent_audit_log`, enforcing strict tenant isolation so that only authorized users within the same organization can access audit records.**

The BuilderIO/agent-native framework provides comprehensive audit logging capabilities that capture every mutating operation in your application. The system stores these events in a dedicated SQL table with built-in privacy controls, ensuring that sensitive change histories remain accessible only to authorized users. This implementation creates an immutable trail of who changed what and when, while maintaining strict data isolation between tenants.

## Core Storage Architecture

### The agent_audit_log Table Schema

In [`packages/core/src/audit/store.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts), the **`ensureAuditTables()`** function creates the `agent_audit_log` table with optimized indexes for owner, organization, target, turn, actor, and created time fields. This structure supports efficient querying while maintaining the append-only nature of the audit trail.

### Row Structure and Data Fields

Each audit row captures comprehensive metadata including identifiers (id, action, caller), actor information (kind, email), organizational context (org ID, thread/turn IDs), target details (type, id), execution status, and visibility settings. The schema stores a redacted `input` payload and visibility levels (`private` or `org`) to control access granularity.

[Source](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts#L25-L44)

## Writing Audit Events

Whenever the framework executes a mutating operation, it invokes **`insertAuditEvent(event)`** from [`packages/core/src/audit/store.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts). This helper constructs a database row from an `AuditEvent` object and appends it to the table, creating an immutable record of the change.

The implementation handles both human-initiated actions and automated AI agent operations, capturing the caller type and actor details for complete traceability.

[Source](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts#L101-L132)

## Reading Audit Events with Tenant Isolation

The system exposes audit data through two high-level actions that enforce strict read-scoping through the caller's `userEmail` and `orgId` in the action context (`ctx`).

**list-audit-events**: Located in [`packages/core/src/audit/actions/list-audit-events.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/actions/list-audit-events.ts), this action returns paginated results filtered by the caller's permissions. It supports filtering by target, actor kind, status, thread/turn, action name, and time range, but only returns rows where the user is the owner or the event is marked as organization-visible.

**get-audit-event**: Implemented in [`packages/core/src/audit/actions/get-audit-event.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/actions/get-audit-event.ts), this retrieves a single event—including the redacted `input` payload—if the caller has access rights.

Both actions utilize the **`scopeClause()`** function in [`store.ts`](https://github.com/BuilderIO/agent-native/blob/main/store.ts) to construct SQL `WHERE` clauses that enforce the same-tenant rule, ensuring audit data never leaks across organizational boundaries.

[Source](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts#L161-L188)

## Lifecycle Management and Data Retention

For long-term maintenance, the system provides **`deleteOldAuditEvents(cutoffMs)`** in [`packages/core/src/audit/store.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts). This utility enables background jobs to prune obsolete audit records based on configurable retention policies, helping manage database growth while preserving recent compliance data.

[Source](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts#L61-L71)

## Implementation Examples

Client-side developers can query audit trails using the framework's action hooks:

```typescript
// List recent audit events for the current user
import { useActionQuery } from '@agent-native/core/client';

const { data, isLoading } = useActionQuery('list-audit-events', {
  limit: 50,
  sinceMs: Date.now() - 7 * 24 * 60 * 60 * 1000, // last week
});

```

To retrieve full details of a specific change:

```typescript
// Fetch the full payload of a single event (e.g., to show a diff)
import { useActionQuery } from '@agent-native/core/client';

const { data: event } = useActionQuery('get-audit-event', {
  id: 'audit-12345',
});

```

Server-side manual insertion (typically handled automatically by the framework):

```typescript
import { insertAuditEvent } from '@agent-native/core/audit/store';

await insertAuditEvent({
  id: crypto.randomUUID(),
  createdAt: Date.now(),
  action: 'update-record',
  caller: 'agent',
  actorKind: 'agent',
  actorEmail: null,
  orgId: ctx.orgId,
  threadId: ctx.threadId,
  turnId: ctx.turnId,
  targetType: 'record',
  targetId: recordId,
  status: 'success',
  summary: 'Updated record fields',
  input: JSON.stringify({ changes: {...} }),
  errorCode: null,
  ownerEmail: ctx.userEmail,
  visibility: 'private',
});

```

## Summary

- **Immutable storage**: All mutations append to the `agent_audit_log` table with comprehensive metadata and redacted inputs.
- **Automatic capture**: The framework calls `insertAuditEvent()` during mutating operations, recording both human and AI agent actions.
- **Strict isolation**: The `scopeClause()` function enforces tenant boundaries in [`packages/core/src/audit/store.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts), ensuring users only see audit events from their organization or private scope.
- **Flexible querying**: `list-audit-events` and `get-audit-event` actions provide paginated access with filtering capabilities.
- **Retention control**: Background jobs can prune old records using `deleteOldAuditEvents()` to manage database size.

## Frequently Asked Questions

### How does Agent-Native prevent audit log data from leaking between organizations?

The system implements row-level security through the `scopeClause()` function in [`packages/core/src/audit/store.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/audit/store.ts). This generates SQL `WHERE` clauses that filter results based on the caller's `userEmail` and `orgId`, ensuring queries only return rows where the user is the owner or the event visibility is set to `org` within the same organization. This tenant isolation is enforced at the database query level, not just application logic.

### What information is captured in each audit log entry?

Each row stores the action name, caller type (human or agent), actor email and kind, organization ID, thread and turn IDs for conversation context, target type and ID, execution status, a redacted input payload, timestamps, and visibility settings. This comprehensive metadata allows you to reconstruct exactly who changed what resource and when the change occurred.

### Can I manually insert audit events for custom operations?

Yes, although the framework automatically logs most mutations, you can manually insert events by importing `insertAuditEvent` from `@agent-native/core/audit/store`. You must provide a complete `AuditEvent` object including unique ID, timestamps, action details, actor information, and visibility settings. This is useful for tracking changes in custom business logic that falls outside standard framework operations.

### How do I query audit logs for a specific time period or target?

Use the `list-audit-events` action with filtering parameters. The action accepts filters for target type/ID, actor kind, status, thread/turn IDs, action name, and time ranges (sinceMs). The client-side `useActionQuery` hook provides a React-friendly interface for these queries, automatically handling pagination and permission scoping based on the current user's context.