Flow

Audit & Compliance

Structured audit events, SIEM export, webhooks, and dead-letter queues.

Overview

Every state change in the registry generates a structured audit event. Events are emitted via slog as SIEM-ingestible JSON, stored in an in-memory ring buffer for API queries, and optionally forwarded to external systems through the audit export pipeline.

The audit system runs at the registry level — it captures events across all networks, not just enterprise ones. Enterprise features add more event types (RBAC changes, policy updates, directory sync) but the core audit infrastructure is always active.

Audit events

Each audit event contains:

FieldDescription
timestampRFC 3339 timestamp of the event
actionEvent type (e.g., node.registered, member.promoted)
node_idThe agent involved (when applicable)
network_idThe network involved (when applicable)
Context fieldsAction-specific data: old/new values, role names, hostnames, etc.

Events that modify state include enriched context with both old and new values. For example, a hostname.changed event includes old_hostname and new_hostname; a member.promoted event includes old_role and new_role.

Event types

ActionEmitted when
node.registeredA new agent registers with the registry
node.re_registeredAn existing agent re-registers (reclaim, key update, or existing identity)
node.deregisteredAn agent deregisters from the registry
node.reapedAn agent is removed due to stale heartbeat
network.createdA new network is created
network.deletedA network is deleted
network.renamedA network is renamed
network.joinedAn agent joins a network
network.leftAn agent leaves a network
network.enterprise_changedEnterprise mode is toggled on a network
network.ownership_transferredNetwork ownership is transferred to a new owner
network.policy_changedNetwork policy is updated
network.provisionedA network is provisioned via blueprint
network.owner_lostNetwork owner deregistered — network has no owner
member.promotedA member is promoted (member → admin)
member.demotedAn admin is demoted (admin → member)
member.kickedA member is kicked from a network
invite.createdAn invitation is sent to an agent
invite.respondedAn agent accepts or rejects an invitation
invite.expired_cleanupExpired invitations are pruned from an inbox
trust.createdMutual trust is established between two agents
trust.revokedTrust is revoked between two agents
visibility.changedAn agent changes between public and private
hostname.changedAn agent changes its hostname
tags.changedAn agent updates its tags
task_exec.changedAn agent enables or disables task execution
handshake.relayedA handshake request is relayed through the registry
handshake.respondedAn agent responds to a handshake (accept/reject)
key.rotatedAn agent rotates its Ed25519 key
key.expiry_setA key expiry date is set
key.expiry_clearedA key expiry date is cleared
key.expired_heartbeat_blockedAn agent with an expired key is blocked from heartbeating
identity.external_id_setAn external ID is set or changed
idp.configuredAn identity provider is configured
directory.syncedA directory sync operation completes
audit_export.configuredAn audit export endpoint is configured
polo_score.updatedAn agent’s polo score changes from task completion/expiry
polo_score.setAn agent’s polo score is set directly (admin)
beacon.registeredA beacon server registers with the registry

Querying the log

The registry maintains an in-memory ring buffer of the most recent 1,000 audit entries. Query it with the get_audit_log command:

# Get all audit entries (newest first)
pilotctl audit

# Filter by network
pilotctl audit --network <network_id>

Protocol command:

{
  "command": "get_audit_log",
  "network_id": 1,
  "admin_token": "your-admin-token"
}

Returns: entries (array of audit events, newest first). The network_id filter is optional — omit it (or set to 0) to get all events.

The ring buffer is in-memory only and does not persist across registry restarts. For persistent audit trails, use audit export.

Audit export

Audit export forwards events to external systems in real time. Configure an export endpoint with the set_audit_export protocol command or through a blueprint.

{
  "command": "set_audit_export",
  "format": "splunk_hec",
  "endpoint": "https://splunk.example.com:8088/services/collector",
  "token": "your-hec-token",
  "admin_token": "your-admin-token"
}

Three export formats are supported:

FormatValueTarget system
Splunk HECsplunk_hecSplunk HTTP Event Collector
CEF / SyslogcefArcSight, QRadar, any CEF-compatible SIEM
JSONjsonAny HTTP endpoint accepting JSON payloads

Delivery guarantees

ParameterValue
Buffer size1,024 events
Retry attempts3
Retry strategyExponential backoff
DeliveryAsynchronous (non-blocking)

Events are buffered and delivered asynchronously. If the export endpoint is temporarily unavailable, events are retried with exponential backoff up to 3 times. Events that exceed the retry limit are dropped (they remain in the in-memory ring buffer for API queries).

Splunk HEC

Splunk HEC (HTTP Event Collector) integration sends events in Splunk’s native format:

{
  "command": "set_audit_export",
  "format": "splunk_hec",
  "endpoint": "https://splunk.example.com:8088/services/collector",
  "token": "your-hec-token",
  "admin_token": "your-admin-token"
}

Events are formatted as Splunk HEC JSON payloads with the event field containing the audit data. The HEC token is sent in the Authorization header.

CEF / Syslog

Common Event Format (CEF) output is compatible with ArcSight, QRadar, and other SIEM systems that accept CEF-formatted syslog:

{
  "command": "set_audit_export",
  "format": "cef",
  "endpoint": "https://siem.example.com/api/events",
  "admin_token": "your-admin-token"
}

Events are formatted as CEF strings with the Pilot Protocol vendor and product identifiers, severity mapping, and extension fields containing the audit context.

JSON export

Generic JSON export sends the raw audit event as a JSON POST to any HTTP endpoint:

{
  "command": "set_audit_export",
  "format": "json",
  "endpoint": "https://logs.example.com/ingest",
  "admin_token": "your-admin-token"
}

The payload is the audit event object as-is — the same structure returned by get_audit_log. Use this for custom integrations, log aggregators, or data pipelines.

Webhooks & DLQ

Webhooks deliver audit events to HTTP endpoints with delivery guarantees. Each webhook invocation includes a unique event ID for deduplication.

Retry behavior

Failed webhook deliveries are retried with exponential backoff. After all retries are exhausted, the event is moved to a dead-letter queue (DLQ) for manual inspection and replay.

Query the DLQ

{
  "command": "get_webhook_dlq",
  "admin_token": "your-admin-token"
}

Returns: entries (array of failed webhook events with original payload, error, and timestamps).

Configure webhooks

Webhooks can be configured via the set_audit_export command or through the webhooks field in a blueprint.

Metrics

The registry exposes Prometheus metrics for monitoring audit and webhook health:

MetricTypeDescription
pilot_audit_events_totalCounterTotal audit events emitted, by action
pilot_audit_export_sent_totalCounterEvents successfully exported, by format
pilot_audit_export_errors_totalCounterExport delivery failures, by format
pilot_webhook_deliveries_totalCounterTotal webhook delivery attempts
pilot_webhook_dlq_sizeGaugeCurrent number of events in the dead-letter queue

Scrape these from the registry’s metrics endpoint to set up alerts for delivery failures or DLQ growth.

See also: Blueprints — configure audit export declaratively. Webhooks — general webhook concepts.