How to Audit Log Changes with list-audit-events in Agent-Native
The list-audit-events action in BuilderIO/agent-native provides a paginated, filterable interface to query immutable audit records stored in the core audit system.
Agent-Native automatically tracks every state change through its built-in audit infrastructure. The list-audit-events action, defined in packages/core/src/audit/actions/list-audit-events.ts, exposes these logs for compliance, security monitoring, and debugging. This guide explains how to query audit events from React components and agent scripts using the exact interfaces implemented in the source code.
Understanding the AuditEvent Schema
When any action modifies state, the audit store in packages/core/src/audit/store.ts persists an AuditEvent record. Each event contains immutable metadata about the change:
- id: A unique identifier for the audit entry.
- resourceId: The specific ID of the resource that was modified.
- resourceType: The logical type of resource, such as
application_stateoruser. - action: The name of the action that triggered the change (e.g.,
create-user). - actorId: The identifier of the user or agent that performed the operation.
- payload: A JSON object containing snapshots of data before and after the change.
- createdAt: The ISO timestamp when the event was recorded.
The audit log is append-only; existing events are never modified by write-side code. A background cleanup job defined in packages/core/src/audit/cleanup-job.ts periodically purges events older than the retention period configured in packages/core/src/audit/config.ts.
The list-audit-events Action Interface
The action follows the standard Agent-Native contract using defineAction. It accepts Zod-validated input filters and returns a paginated result set.
Input parameters supported by list-audit-events:
resourceId: Filter to a specific resource instance.resourceType: Limit results to a particular resource category.action: Filter by the action name that caused the change.actorId: Show only events performed by a specific actor.sinceanduntil: Date range boundaries for the query.cursor: Pagination cursor for fetching subsequent pages.limit: Number of results per page (default 20, max 100).
Output schema:
events: An array ofAuditEventobjects matching the filters.nextCursor: A string token for the next page, ornullif no more results exist.
The handler implementation delegates to auditStore.listEvents(input), which constructs the SQL query and applies field redaction rules based on audit/config.ts before returning data.
Querying Audit Logs from React Components
Use the useActionQuery hook to fetch audit data in client-side components. This provides automatic loading states and caching.
import { useActionQuery } from "@agent-native/core/client";
export function AuditLogViewer({ resourceId }: { resourceId: string }) {
const { data, isLoading, error } = useActionQuery("list-audit-events", {
resourceId,
limit: 50,
});
if (isLoading) return <p>Loading audit history…</p>;
if (error) return <p>Failed to load audit log.</p>;
return (
<ul>
{data?.events.map((event) => (
<li key={event.id}>
{event.createdAt.toLocaleString()} – {event.actorId} performed {event.action}
</li>
))}
</ul>
);
}
Implementing Cursor Pagination
For large audit histories, use the cursor parameter to paginate through results efficiently:
import { useState } from "react";
import { useActionQuery } from "@agent-native/core/client";
function PaginatedAuditLog() {
const [cursor, setCursor] = useState<string | undefined>(undefined);
const { data, isFetching } = useActionQuery("list-audit-events", {
limit: 20,
cursor,
});
const loadMore = () => {
if (data?.nextCursor) {
setCursor(data.nextCursor);
}
};
return (
<>
<ul>
{data?.events.map((e) => (
<li key={e.id}>{e.action} on {e.resourceType}</li>
))}
</ul>
{data?.nextCursor && (
<button onClick={loadMore} disabled={isFetching}>
Load more
</button>
)}
</>
);
}
Querying Audit Logs from Agent Scripts
Agent scripts can query the audit log programmatically using runAction. This is useful for automated compliance checks or generating reports.
import { runAction } from "@agent-native/core/agent";
async function showRecentChanges() {
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
const result = await runAction("list-audit-events", {
since: oneWeekAgo,
limit: 10,
});
for (const event of result.events) {
console.log(
`[${event.createdAt.toISOString()}] ${event.actorId} ${event.action} ${event.resourceType}:${event.resourceId}`
);
}
}
showRecentChanges();
Filtering Strategies for Security Audits
Target specific change patterns by combining filters:
Isolate deletion events:
const { data } = useActionQuery("list-audit-events", {
action: "delete-user",
limit: 20,
});
Track changes by a specific actor within a date range:
const { data } = useActionQuery("list-audit-events", {
actorId: "agent-001",
since: new Date("2024-01-01"),
until: new Date("2024-01-31"),
resourceType: "application_state",
});
Data Redaction and Retention Configuration
Before events reach the action output, the store applies redaction rules defined in packages/core/src/audit/config.ts. This ensures sensitive fields (like passwords or tokens) are stripped from the payload even when querying historical data.
The retention policy is enforced by packages/core/src/audit/cleanup-job.ts, which runs on a schedule to delete events exceeding the configured age limit. This prevents unbounded storage growth while maintaining compliance windows.
Summary
- The
list-audit-eventsaction inpackages/core/src/audit/actions/list-audit-events.tsprovides the primary interface for reading audit data. - Events are immutable records stored via
packages/core/src/audit/store.tsand contain full before/after payloads. - Query filters include
resourceId,action,actorId, and date ranges (since/until). - Results are paginated using cursor-based navigation via the
cursorandnextCursorfields. - Sensitive data is automatically redacted according to rules in
packages/core/src/audit/config.ts. - Old events are purged by the cleanup job in
packages/core/src/audit/cleanup-job.tsbased on retention settings.
Frequently Asked Questions
What fields are included in each audit event?
Each AuditEvent contains id, resourceId, resourceType, action, actorId, payload (with before/after snapshots), and createdAt. The payload provides the complete state change context, while actorId identifies who initiated the action.
How does pagination work with list-audit-events?
The action uses cursor-based pagination. Pass a limit to control page size, and use the cursor parameter with the value from the previous page's nextCursor field. When nextCursor returns null, you have reached the end of the result set.
Can I query audit events from the command line?
Yes. Agent-Native exposes actions to the CLI, allowing you to run agent run list-audit-events --input '{"limit": 10}' or similar commands depending on your CLI configuration. Programmatic access uses the runAction utility in agent scripts as shown in the examples above.
How long are audit events retained?
Retention is controlled by the configuration in packages/core/src/audit/config.ts and enforced by the background cleanup job in packages/core/src/audit/cleanup-job.ts. Events older than the configured retention period are permanently deleted automatically.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →