Skip to content

How-To: Querying Audit Logs

Brokkr records significant operations to an append-only audit trail. This guide shows how to query it to investigate events, track changes, and monitor security. Entries are never edited or deleted except by the retention job — but writes are asynchronous and best-effort, so read Delivery Guarantees before treating a query result as a complete record.

All audit log queries go through the admin API:

GET /api/v1/admin/audit-logs

Auth: Admin rights. Both an admin PAK and the broker’s zero-config read-only console token satisfy that gate; agent and generator PAKs are rejected. Anyone who can reach the broker’s HTTP port can load the console and therefore read the audit trail, IP addresses and user agents included — restrict access to that port accordingly.

List the most recent audit events:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs" \
-H "Authorization: <admin-pak>" | jq .

Default: returns the 100 most recent entries, ordered by timestamp (newest first). The response is a JSON object with a logs array, plus total, count, limit, and offset fields for pagination.

Two things about entry shape will bite scripts that assume otherwise:

  • Every entry carries an actor_name alongside actor_id, resolved when you read it — the entity’s current name, or null if the ID no longer resolves. Prefer it over joining against the agents/generators endpoints yourself.
  • ip_address and user_agent are omitted from the JSON entirely when they were not recorded, rather than serialized as null. Supply a fallback in jq (.ip_address // "unknown") so those entries don’t silently collapse into nulls.

See all actions performed by agents:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?actor_type=agent" \
-H "Authorization: <admin-pak>" | jq '.logs[] | {action, resource_type, timestamp}'

Valid actor types: admin, agent, generator, system

Find all agent creation events:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?action=agent.created" \
-H "Authorization: <admin-pak>" | jq .

Actions support wildcard prefix matching. To see all agent-related events:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?action=agent.*" \
-H "Authorization: <admin-pak>" | jq .

Track all changes to a specific agent:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?resource_type=agent&resource_id=${AGENT_ID}" \
-H "Authorization: <admin-pak>" | jq .

Query events within a specific window:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?from=2025-01-15T00:00:00Z&to=2025-01-16T00:00:00Z" \
-H "Authorization: <admin-pak>" | jq .

See everything a specific generator has done:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?actor_type=generator&actor_id=${GEN_ID}" \
-H "Authorization: <admin-pak>" | jq .

Use limit and offset for large result sets:

Terminal window
# First page
curl -s "http://localhost:3000/api/v1/admin/audit-logs?limit=50&offset=0" \
-H "Authorization: <admin-pak>" | jq .
# Second page
curl -s "http://localhost:3000/api/v1/admin/audit-logs?limit=50&offset=50" \
-H "Authorization: <admin-pak>" | jq .

Maximum limit is 1000.

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?resource_type=agent&resource_id=${AGENT_ID}" \
-H "Authorization: <admin-pak>" \
| jq '.logs[] | {actor_type, actor_name, actor_id, action, timestamp, details}'
Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?action=auth.failed" \
-H "Authorization: <admin-pak>" \
| jq '.logs[] | {timestamp, ip_address: (.ip_address // "unrecorded"), user_agent: (.user_agent // "unrecorded"), details}'

Security Monitoring: Failed Auth by Source IP

Section titled “Security Monitoring: Failed Auth by Source IP”

Aggregate failed attempts over the last 24 hours to spot attack patterns:

Terminal window
FROM=$(date -u -v-24H +%Y-%m-%dT%H:%M:%SZ) # GNU date: date -u -d '24 hours ago' ...
curl -s "http://localhost:3000/api/v1/admin/audit-logs?action=auth.failed&from=${FROM}&limit=1000" \
-H "Authorization: <admin-pak>" \
| jq '[.logs[].ip_address // "unrecorded"] | group_by(.) | map({ip: .[0], count: length}) | sort_by(-.count)'

Export all actions for a reporting period to a file:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?from=2025-01-01T00:00:00Z&to=2025-02-01T00:00:00Z&limit=1000" \
-H "Authorization: <admin-pak>" > audit-january-2025.json

For periods with more than 1000 entries, page through with offset and concatenate.

Do not present an export as a guaranteed-complete record without a caveat. Audit writes are queued in memory and flushed to the database in batches, so a batch whose insert fails is dropped rather than retried, and anything still queued when the broker stops abruptly never lands. Both cases show up in the broker’s error logs, not in the export. If your compliance process needs a completeness guarantee, reconcile the export against broker logs for the same window.

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?action=pak.rotated" \
-H "Authorization: <admin-pak>" \
| jq '.logs[] | {actor_type, resource_type, resource_id, timestamp}'
Terminal window
TODAY=$(date -u +%Y-%m-%dT00:00:00Z)
curl -s "http://localhost:3000/api/v1/admin/audit-logs?actor_type=admin&from=${TODAY}" \
-H "Authorization: <admin-pak>" | jq .
Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?action=webhook.*" \
-H "Authorization: <admin-pak>" \
| jq '.logs[] | {action, resource_id, timestamp, details}'
Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?action=stack.deleted" \
-H "Authorization: <admin-pak>" \
| jq '.logs[] | {actor_type, actor_name, resource_id, timestamp}'

Registration is the consent boundary that decides which generators’ stacks reach an agent, so its grants and revocations are audited:

Terminal window
curl -s "http://localhost:3000/api/v1/admin/audit-logs?action=agent.registered&resource_id=${AGENT_ID}" \
-H "Authorization: <admin-pak>" \
| jq '.logs[] | {actor_type, actor_name, timestamp, details}'

Swap in action=agent.deregistered to see revocations — the destructive direction, since deregistering prunes the agent’s Kubernetes resources for that generator’s stacks.

Actions follow the pattern resource.verb (e.g., agent.created, pak.rotated, auth.failed). You can use wildcard queries like action=agent.* to match all events for a resource type.

For the complete list of all audit event types, see the Audit Logs Reference.

Audit logs are retained for 90 days by default (broker.audit_log_retention_days). The broker runs a daily cleanup task to purge older entries.

To change retention:

Terminal window
BROKKR__BROKER__AUDIT_LOG_RETENTION_DAYS=365