Using Agent-Native Audit Log to Track Agent vs Human Mutations
Agent-Native provides a built-in audit-log subsystem that automatically records every data mutation, capturing the identity of the caller—whether a human user or the AI agent—along with the specific run that caused the change.
The agent-native audit log in BuilderIO/agent-native creates an immutable, append-only record of all mutating actions, enabling compliance auditing, debugging, and governance without manual instrumentation. This subsystem captures the actor type, run context, and sanitized payload for every operation performed through the framework's action layer.
Core Architecture Components
The audit system spans multiple packages, with schema definitions in Dispatch and automatic capture handled by the Core framework.
The agent_audit_log Table Schema
The database schema in packages/dispatch/src/db/schema.ts defines the agent_audit_log table as the durable storage layer. Each row captures:
actor_id– The identifier of the user or agent performing the actionactor_type– Enum value ofhumanoragentdistinguishing the caller typerun_id– The specific agent-run UUID that performed the mutationapp_id– The target application namespace (e.g.,assets,slides)summary– A redacted, safe description of the operationpayload– Truncated JSON representation of the change
This structure ensures that every mutation is traceable to its originator and execution context.
Automatic Action Wrapping
Every action created with defineAction is automatically wrapped by the audit layer. As noted in packages/core/CHANGELOG.md (line 1051), mutating actions are logged by default, while read-only actions can opt-in via audit.onRead.
When an action executes, the framework inspects request.actor from the context—either a signed-in user ID or the synthetic "agent" identity—and persists this as actor_type in the audit record.
Security and Data Handling
Before persistence, the audit layer applies security controls documented in packages/core/docs/content/audit-log.md (lines 8-22):
- Secret Redaction: Fields containing tokens, passwords, or bearer strings are automatically stripped
- Size Limits: Oversized payloads are truncated to prevent database bloat
- Safe Summaries: The
summaryfield contains only redacted, safe descriptions
These guarantees ensure the audit log never becomes a credential dump while maintaining forensic value.
Distinguishing Agent vs Human Mutations
The framework differentiates between agent and human mutations through the actor_type field. When the AI agent performs operations, request.actor contains the synthetic agent identity; for human users, it contains their authenticated user ID.
For example, in templates/assets/actions/generate-image.ts (line 36), actions add a source field ("chat", "ui", "a2a") that appears in the audit row, making it trivial to answer: "Did the agent generate this image, or did a user click Generate?"
Querying and Accessing Audit Events
The system exposes two core actions for retrieving audit data, registered in packages/core/src/server/action-discovery.ts (lines 594-595):
list-audit-events– Paginated search with filtering capabilitiesget-audit-event– Fetch a single row by primary key
Both actions enforce access-filtering logic identical to standard data queries. Organization administrators can view the entire org-wide feed, while regular users see only their own runs.
Filtering by Actor Type
You can query specifically for agent or human mutations:
import listAuditEvents from "./list-audit-events.js";
const agentRuns = await listAuditEvents.run(
{ limit: 50, where: { actor_type: "agent", app_id: "assets" } },
ctx
);
The UI for the Audit view consumes this same API, as implemented in templates/assets/app/hooks/use-navigation-state.ts (line 99).
Configuration and Retention
The audit subsystem is configurable through environment variables:
AGENT_NATIVE_AUDIT_RETENTION_DAYS– Defaults to 365 days; controls table purgingAGENT_NATIVE_AUDIT_ENABLED– Set tofalseto disable the subsystem entirely
These settings provide operational flexibility for storage management and compliance requirements.
Practical Implementation Examples
Logging Custom Mutating Actions
When you create actions using defineAction, audit entries are automatic. You only need to implement the business logic:
import { defineAction } from "@agent-native/core";
import { z } from "zod";
export default defineAction({
// Mutating action: creates a new note
input: z.object({ title: z.string(), body: z.string() }),
// No extra audit config needed – the system logs actor, run, and payload
async handler({ input, ctx }) {
await db.note.insert({ ...input, createdBy: ctx.request.actor.id });
},
});
Filtering Audit Events by Actor
To programmatically distinguish between agent and human activity:
import listAuditEvents from "./list-audit-events.js";
async function recentAgentMutations(ctx) {
const rows = await listAuditEvents.run(
{ limit: 10, where: { actor_type: "agent" } },
ctx
);
return rows.map(r => ({
id: r.id,
summary: r.summary,
when: r.created_at,
app: r.app_id,
}));
}
Displaying Audit Provenance in UI
The Assets template demonstrates UI integration in templates/assets/app/hooks/use-navigation-state.ts:
function AuditTab() {
const { data } = useActionQuery(listAuditEvents, { limit: 50 });
return (
<section>
<h2>Audit Log</h2>
<ul>
{data?.map(ev => (
<li key={ev.id}>
{ev.actor_type === "agent" ? "🤖 Agent" : "👤 Human"} – {ev.summary}
</li>
))}
</ul>
</section>
);
}
The i18n strings in templates/assets/app/i18n/en-US.ts (lines 455-460) provide admin-only view distinctions for organization-wide auditing.
Summary
- The agent-native audit log automatically captures every mutation with
actor_type,run_id, and sanitized payloads stored in theagent_audit_logtable - Agent vs human mutations are distinguished via the
actor_typefield populated fromrequest.actorcontext - Query using
list-audit-eventsandget-audit-eventactions with role-based access control enforced atpackages/core/src/server/action-discovery.ts - Security controls include automatic secret redaction and payload truncation as documented in
packages/core/docs/content/audit-log.md - Configure retention via
AGENT_NATIVE_AUDIT_RETENTION_DAYS(default 365) and disable withAGENT_NATIVE_AUDIT_ENABLED=false
Frequently Asked Questions
How does agent-native distinguish between agent and human mutations?
The framework inspects request.actor from the execution context, which contains either a signed-in user ID or a synthetic agent identity. This value is persisted in the actor_type column as either human or agent, allowing queries to filter mutations by their originator without additional application code.
Can I disable the audit log if I don't need compliance tracking?
Yes. Set the environment variable AGENT_NATIVE_AUDIT_ENABLED=false to disable the subsystem entirely. When enabled, you can control data retention using AGENT_NATIVE_AUDIT_RETENTION_DAYS (default 365 days) to automatically purge old records and manage storage costs.
Do I need to manually instrument my actions to support audit logging?
No. Any action created with defineAction is automatically wrapped by the audit layer. Mutating actions are logged by default; read-only actions can opt-in via audit.onRead. The system automatically captures the actor, run ID, and payload without requiring boilerplate code in your handlers.
How are sensitive values prevented from leaking into the audit log?
Before persistence, the audit layer scans payloads for patterns matching secrets, tokens, and passwords, stripping them automatically. Additionally, oversized payloads are truncated and summaries are redacted to safe descriptions, ensuring compliance with security policies while maintaining forensic utility.
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 →