Agents poll /api/agents/agent/{id}/config every minute, and every
successful poll was written as a signed agent_config_fetch audit event.
On a five-agent homelab that was 483,000 of 483,101 audit rows (250 MB)
over 75 days, burying the 64 logins and 25 CSRF failures the log exists
to show; at the default 90-day retention it is roughly 60 MB per agent.
Every failed fetch is still audited. A successful fetch is audited when
it is new audit information: the agent's first delivery after Pulse
starts, a different token or desiredConfig hash, or an unchanged
delivery last recorded 24 hours earlier. Recorded successes now carry
the delivered config hash and the reason. The tracker is per handler,
keyed by org and agent, and bounded to 4,096 entries.
Existing rows are left to age out under retention rather than deleted,
since removing entries from a security log would look like tampering.
AUDIT_LOGGING.md and the API and agent-lifecycle contracts describe the
new rule.
6.6 KiB
Audit Logging
Pulse's audit log records security-relevant events with tamper-evident signatures. Use it for compliance, incident investigation, and tracking who did what.
Requires: Pro, legacy Pro+, or Cloud license with the audit_logging capability to query, export, and verify events via the API. Events are recorded on all plans, but the API endpoints are license-gated.
For plan details, see PULSE_PRO.md. For API endpoints, see API Reference.
What Gets Logged
Pulse automatically captures the following events:
| Event Type | Description | Example |
|---|---|---|
login |
Successful and failed login attempts | User admin logged in from 198.51.100.5 |
logout |
User logouts | User admin logged out |
password_change |
Password modifications | Password changed (Docker/systemd) |
csrf_failure |
Blocked cross-site request forgery attempts | Invalid CSRF token |
lockout_reset |
Account lockout resets | Admin reset lockout for user bob |
oidc_login |
OIDC SSO login attempts (success/failure at each stage) | OIDC login success |
oidc_token_refresh |
OIDC token refresh success/failure (global, not tenant-scoped) | Token refreshed successfully |
oidc_role_assignment |
Automatic role assignment from OIDC groups | Auto-assigned roles: operator, viewer |
saml_login |
SAML SSO login attempts | SAML login success via provider-id |
saml_role_assignment |
Automatic role assignment from SAML groups | Auto-assigned roles: admin |
sso_provider_created |
SSO provider configuration created | Created provider: Authentik |
sso_provider_updated |
SSO provider configuration modified | Updated provider: Authentik |
sso_provider_deleted |
SSO provider configuration removed | Deleted provider: Authentik |
ai_settings_updated |
AI configuration changes | AI settings updated |
agent_profile_assigned |
Agent profile assignments | Profile production assigned to agent |
agent_profile_unassigned |
Agent profile removals | Profile removed from agent |
user_roles_updated |
RBAC role assignments changed | Updated roles for user jane: [operator] |
agent_config_fetch |
Every failed agent configuration fetch. A successful fetch is recorded on the agent's first delivery after Pulse starts, when its token or delivered configuration changes, and otherwise once a day; agents poll every minute, so repeat polls are not recorded | agent_id=… token_id=… config=sha256:… reason=config_changed |
Each event includes:
- Timestamp (UTC)
- Event type
- User who triggered the event
- Client IP address
- Request path
- Success/failure flag
- Details (human-readable description)
- Cryptographic signature (tamper detection)
Viewing Audit Events
UI
Settings → Security → Audit Log
The audit log panel shows events in reverse chronological order with filtering by event type, user, date range, and success/failure.
API
# List recent events
curl http://localhost:7655/api/audit?limit=50 \
-H "Authorization: Bearer $TOKEN"
# Filter by event type and date range
curl "http://localhost:7655/api/audit?event=login&startTime=2026-01-01T00:00:00Z&endTime=2026-01-31T23:59:59Z&success=false" \
-H "Authorization: Bearer $TOKEN"
# Get audit summary
curl http://localhost:7655/api/audit/summary \
-H "Authorization: Bearer $TOKEN"
Query Parameters
| Parameter | Type | Description |
|---|---|---|
limit |
integer | Maximum events to return (default: 100) |
event |
string | Filter by event type (e.g., login, password_change) |
user |
string | Filter by username |
success |
boolean | Filter by success (true) or failure (false) |
startTime |
ISO 8601 | Start of date range |
endTime |
ISO 8601 | End of date range |
Exporting Audit Data
Export the audit log for external analysis or compliance archival:
curl http://localhost:7655/api/audit/export \
-H "Authorization: Bearer $TOKEN" \
-o audit-export.json
The export includes all events matching the current filter criteria.
Tamper Detection
Every audit event is cryptographically signed at creation time. You can verify that an event has not been modified:
curl http://localhost:7655/api/audit/6b3c9c3c-9a2f-4b3c-9a3b-3d0e8c5c5d45/verify \
-H "Authorization: Bearer $TOKEN"
Response:
{
"available": true,
"verified": true,
"message": "Event signature verified"
}
If verified is false, the event data has been tampered with since it was recorded.
Multi-Tenant Audit Isolation
In multi-tenant deployments, most events are scoped to the active organization:
- Tenant-aware events (logins, role changes, config updates) are stored per-organization.
- Some auth lifecycle events (e.g.,
oidc_token_refresh) are global and not tenant-scoped. - Switching organizations shows only that organization's tenant-scoped events.
- The tenant context is determined by
X-Pulse-Org-IDheader or session cookie.
See Multi-Tenant Organizations for details.
Community vs Pro Behavior
| Capability | Community | Pro / legacy Pro+ / Cloud |
|---|---|---|
| Events captured | Yes | Yes |
| Persistent storage (SQLite) | Yes | Yes |
| Query/filter API | License-gated (402) | Full access |
| Signature verification | License-gated (402) | Available |
| Export | License-gated (402) | Available |
persistentLogging API flag |
false |
true |
On all plans, audit events are written to the SQLite database. However, the query, verify, and export API endpoints require the audit_logging license feature and return 402 Payment Required without it. The persistentLogging flag in API responses indicates whether the licensed query capabilities are available.
Storage
Audit events are stored in a SQLite database in the Pulse data directory:
- Single-tenant:
{data-dir}/audit/audit.db - Multi-tenant:
{data-dir}/orgs/{org-id}/audit/audit.db
Data directory locations:
- systemd:
/etc/pulse/ - Docker/Kubernetes:
/data/ - Development:
tmp/dev-config/
Related Documentation
- Plans and Entitlements — Audit logging availability by plan
- RBAC — Role-based access control (role changes are audit logged)
- OIDC / SSO — SSO login events are audit logged
- Security Policy — Core security model
- Multi-Tenant Organizations — Per-tenant audit isolation
- API Reference — Audit log API endpoints