Audit log

The audit log records security-relevant actions. Action codes follow the pattern VERB_NOUN. Passwords and secrets must not appear in audit details.

Writing

Inject IAuditWriter, then call it after SaveChanges. Audit codes are always referenced as a constant from NodePilot.Core.Audit.AuditActions — never as a raw string literal at the call site (the guard test AuditActionsCatalogTests enforces this):

await _audit.LogAsync(AuditActions.WorkflowPublished, "Workflow", resourceId, detailsJson, ct);

A write failure must never abort a normal mutation. The exception: arbitrary database-admin write SQL runs fail-closed and is not executed without a previously persisted DBADMIN_SQL_WRITE_ATTEMPTED.

Audit codes

The authoritative catalog lives in NodePilot.Core.Audit.AuditActions — the guard tests keep it complete and free of stale entries. Codes follow the pattern VERB_NOUN:

AreaCodes
WorkflowWORKFLOW_CREATED|UPDATED|DELETED|DUPLICATED|ROLLED_BACK|CANCEL_ALL|STEP_TESTED|ENABLED|DISABLED|CONCURRENCY_LIMIT_CHANGED
Edit lockWORKFLOW_LOCKED|UNLOCKED|PUBLISHED|FORCE_UNLOCKED
MachineMACHINE_CREATED|UPDATED|DELETED|CONNECTION_TESTED|CONNECTION_TEST_FAILED
CredentialCREDENTIAL_CREATED|UPDATED|DELETED|DECRYPTED|DECRYPT_FAILED
GlobalsGLOBAL_VARIABLE_CREATED|UPDATED|DELETED|MOVED
Global-variable foldersGLOBAL_VARIABLE_FOLDER_CREATED|UPDATED|MOVED|DELETED
LoginLOGIN_SUCCESS|FAILED|LOCKED, BREAK_GLASS_LOGIN_SUCCESS, LOGOUT, TOKEN_REFRESHED, USER_CREATED_BOOTSTRAP
UserUSER_CREATED|ACTIVATED|DEACTIVATED|DELETED|ROLE_CHANGED|PASSWORD_RESET|BREAK_GLASS_CHANGED
AD SSO PreviewUSER_{LDAP|WINDOWS}_JIT_CREATED|JIT_UPDATED|REFUSED_COLLISION|REFUSED_BOOTSTRAP|REFUSED_LAST_ADMIN, USER_DIRECTORY_ACCESS_REFUSED|SYNCED|DEPROVISIONED, USER_AUTHORIZATION_STALE
OIDCUSER_OIDC_JIT_CREATED (first sign-in creates the account), USER_DIRECTORY_SYNCED with source Oidc
SCIMUSER_SCIM_PROVISIONED|UPDATED|DEPROVISIONED, SCIM_GROUP_PROVISIONED|UPDATED|DEPROVISIONED|REACTIVATED
Execution / HAEXECUTION_STARTED|CANCELLED|RETRIED|RESUMED|STEP_OVER|DEBUG_STOP|RECOVERED_FAILOVER, CLUSTER_LEADERSHIP_ACQUIRED, DATABASE_RECOVERED
MaintenanceMAINTENANCE_WINDOW_CREATED|UPDATED|DELETED|OVERRIDDEN, EXECUTION_BLOCKED_MAINTENANCE_WINDOW
TriggersWEBHOOK_TRIGGERED, EXTERNAL_TRIGGER_FIRED, TRIGGER_FIRE_SUPPRESSED
Import/exportWORKFLOW_EXPORTED|EXPORTED_BULK|IMPORTED|IMPORTED_SCORCH, CUSTOM_ACTIVITY_EXPORTED, AUDIT_LOG_EXPORTED, SUPPORT_EVENTS_EXPORTED, SUPPORT_LOG_DOWNLOADED, APP_LOG_DOWNLOADED
FoldersFOLDER_CREATED|UPDATED|MOVED|DELETED, WORKFLOW_MOVED, FOLDER_PERMISSION_GRANTED|UPDATED|REVOKED
AIAI_SCRIPT_GENERATED|AI_WORKFLOW_GENERATED|AI_AGENT_TEAM_GENERATED|AI_WORKFLOW_EXPLAINED|AI_PROPOSAL_APPLIED|AI_KNOWLEDGE_ASKED, AGENT_MCP_SERVER_SAVED|DELETED, AGENT_SKILL_IMPORTED|UPDATED|DELETED
AlertingALERT_RULE_CREATED|UPDATED|DELETED|ENABLED|DISABLED|TEST_FIRED, SYSTEM_ALERT_POLICY_CREATED|UPDATED|DELETED|ENABLED|DISABLED|TEST_FIRED
Custom activitiesCUSTOM_ACTIVITY_CREATED|UPDATED|DELETED|ENABLED|DISABLED|IMPORTED|EXPORTED|ROLLED_BACK
Database adminDBADMIN_ROWS_VIEWED|ROW_UPDATED|ROW_DELETED|SQL_EXECUTED|SQL_WRITE_ATTEMPTED|SQL_WRITE
SecretsSECRETS_REENCRYPTED
BackupBACKUP_EXPORTED|RESTORED|RESTORE_DB_COMMITTED
SettingsSETTINGS_{AGENTS|AIKNOWLEDGE|AUTHENTICATION|DBADMIN|ENGINE|EXECUTIONDISPATCH|EXTERNALTRIGGER|FILESYSTEMOPERATION|LLM|LOGGING|OPENTELEMETRY|PERFORMANCE|REMOTE|RESTAPI|RETENTION|SECURITY|SMTP|SQLACTIVITY|STARTPROGRAM|STATS|THREADING|WAITFORCONDITION|WEBHOOK}_UPDATED (one code per settings section), SETTINGS_SMTP_TESTED, SETTINGS_LLM_TESTED, SETTINGS_AUTHENTICATION_TESTED, SETTINGS_SERVICE_RESTART_REQUESTED

Pipeline

Every audit write flows through IAuditStager (in NodePilot.Core/Audit/). HTTP controllers use IAuditWriter (NodePilot.Api/Audit/AuditWriter.cs), which wraps the stager with HttpContextAccessor actor resolution + ECS log forwarding + a support-log allow-list check. Redaction and the 4 KiB cap apply uniformly everywhere.

Archive integrity

AuditLogRetentionService.ArchiveAsync writes gzip-compressed audit-{date}-{ticks}-{rand}.ndjson.gz files plus a SHA-256 sidecar. A periodic verify pass (daily by default) recomputes the hashes and warns through the metric nodepilot.audit_archive.hash_drift on drift.

Access

Admin only, cursor pagination, export as CSV/NDJSON (GET /api/audit/export?format=csv|ndjson). The export itself writes AUDIT_LOG_EXPORTED with the filters and the actual row count.

Alerting

The system-alert source Audit event (category Security on the Alerts page) turns audit entries into notifications without a SIEM: a policy filters on the code (action), the outcome, the user, the IP address or the details JSON and delivers by email or webhook. Presets cover failed logins, lockouts, break-glass sign-ins and privilege changes; the optional actions parameter narrows the scan to specific codes. Username and IP travel in the alert text to the configured recipient — see Alerting.

SIEM forwarding

With Logging:Format=ecs-json, every successful audit row is additionally emitted as a structured ECS event through Serilog — see SIEM logging.

All chapters