Skip to main content
Audit logging is available in Onyx v4.3 and later, on self-hosted deployments where you control the log pipeline.

Overview

Onyx emits a normalized, structured audit-event stream for security-relevant actions — authentication, user and access-control changes, admin configuration changes, and credential access. Every event is a single JSON object, so you can forward the stream to any SIEM — Splunk, Microsoft Sentinel, Elastic, Google Chronicle, AWS Security Lake — with no Onyx-side integration to build. You point your log shipper at Onyx’s logs, filter the audit stream, and parse the JSON. Field names and the action taxonomy are shaped toward OCSF (the Open Cybersecurity Schema Framework), so events map cleanly onto the event classes most SIEMs already understand. This maps directly onto common compliance controls — SOC 2 CC7 and the NIST 800-53 / FedRAMP AU family (AU-2 auditable events, AU-3 record content, AU-6 review, AU-12 generation).

What gets captured

Login success, login failure, logout, registration, password forgot, password reset, and email verification.
User creation (admin invites), deletion, deactivation, reactivation, role changes, and user-group membership changes.
Create / update / delete of LLM providers, connectors, connector-credential pairs, API keys, and credentials — plus credential-access (decrypt) events.
Each event records who did what, to which resource, when, from where, and whether it succeeded — the core of an audit trail.

How it works

Audit events are emitted as INFO log records on a dedicated onyx.audit logger. The message body of each record is a single JSON object — Onyx serializes the event itself, so the audit line is identical regardless of your log format. Emission is fail-safe: it never interrupts a user request or a connector run, even if context gathering or logging fails. High-volume event classes are de-duplicated within a short (10-minute) window using Redis. De-duplication is fail-open — if Redis is unavailable, the dedup check is skipped and the event is still emitted, so an audit event is never silently dropped due to infrastructure trouble (you may just see a duplicate). The subsystem is always on — there is no enable/disable flag, output-format, or destination setting, and the de-duplication window is fixed. The only related knob is LOG_FORMAT (below), which shapes the surrounding log records; the audit JSON body itself is identical either way.
Run Onyx with LOG_FORMAT=json so that all log records — not just audit events — are machine-parseable, and request/tenant context is promoted to top-level fields. This makes filtering and parsing in your shipper much simpler.

Event schema

FieldTypeDescription
audit_schema_versionstringSchema version (currently "1.0").
tsnumberEvent time, epoch seconds.
actionstringAction taxonomy value, <domain>.<verb> (e.g. llm_provider.update). Stable, append-only.
ocsf_classstringauthentication, account_change, or api_activity.
outcomestringsuccess, failure, or denied.
tenant_idstring | nullTenant the action occurred in.
actorobject | null{ user_id, email, api_key_id, auth_type }. Never contains a secret.
resource_typestring | nullAffected resource type (e.g. llm_provider, user, api_key).
resource_idstring | nullAffected resource identifier.
request_idstring | nullCorrelates the event with the rest of that request’s logs.
endpointstring | nullRoute that produced the event.
source_ipstring | nullClient IP (from X-Forwarded-For).
extraobject | nullAdditional non-secret context.
The action values are a stable, append-only contract — safe to filter and build dashboards against. New actions may be added over time; existing ones do not change meaning.

Example event

{
  "audit_schema_version": "1.0",
  "ts": 1750000000.123,
  "action": "llm_provider.update",
  "ocsf_class": "api_activity",
  "outcome": "success",
  "tenant_id": "tenant_abc",
  "actor": { "user_id": "u-42", "email": "admin@example.com", "api_key_id": null, "auth_type": "oauth" },
  "resource_type": "llm_provider",
  "resource_id": "7",
  "request_id": "01J...",
  "endpoint": "PUT /admin/llm/provider",
  "source_ip": "203.0.113.5",
  "extra": null
}

Forward to your SIEM

Because audit events are just JSON log lines on a known logger prefix, any log shipper works.
1

Run Onyx with JSON logging

Set LOG_FORMAT=json so every log record is structured and carries a logger and message field.
2

Ship Onyx's logs

Point Fluent Bit, Vector, the CloudWatch agent, Filebeat, or your existing collector at the Onyx container stdout (or the mounted log files).
3

Isolate and parse the audit stream

Keep only records whose logger starts with onyx.audit, then parse each record’s message field as JSON to recover the event fields above.
4

Route to your SIEM

Send the parsed events to your SIEM’s ingest endpoint. The OCSF-shaped fields map onto Authentication, Account Change, and API Activity event classes.

Shipper examples

# Keep only audit records, then replace the event with the parsed JSON payload.
[transforms.onyx_audit]
type = "filter"
inputs = ["onyx_logs"]
condition = '''starts_with(string!(.logger), "onyx.audit")'''

[transforms.onyx_audit_parsed]
type = "remap"
inputs = ["onyx_audit"]
source = '. = parse_json!(.message)'
Filter on the onyx.audit prefix to capture everything. The stream is also split into per-class child loggers — onyx.audit.authentication, onyx.audit.account_change, onyx.audit.api_activity, and onyx.audit.credential_access — if you want to route classes to different destinations.

Event reference

Every action value, what triggers it, and the fields most useful for building SIEM detection rules. actor, tenant_id, request_id, endpoint, source_ip, and outcome are present on every event (see Event schema) and are omitted from the tables below.

Authentication

ocsf_class: authentication
actionTriggered whenNotes
auth.loginA user successfully signs in
auth.login_failureA sign-in attempt failsoutcome is failure, or denied for a non-interactive login
auth.logoutA user signs out
auth.registerA new account is registered (self-signup)
auth.password_forgotA password-reset email is requested
auth.password_resetA password reset is completed
auth.email_verifyAn email-verification email is requested

User & access management

ocsf_class: account_changeresource_type is user (or user_group).
actionTriggered whenresource_idKey extra fields
user.createAn admin invites new usersinvited_emails
user.deleteA user is deleteduser idtarget_email
user.deactivateA user is deactivateduser idtarget_email
user.reactivateA user is reactivateduser idtarget_email
user.role_changeA user’s role is changeduser idtarget_email, old_role, new_role
user.group_changeA user group’s membership changesuser-group idadded_user_ids, removed_user_ids

Configuration & resources

ocsf_class: api_activity
actionTriggered whenresource_typeresource_idKey extra fields
llm_provider.create / .update / .deleteAn LLM provider is created, updated, or deletedllm_providerprovider id
connector.create / .update / .deleteA connector is created, updated, or deletedconnectorconnector idsource (on create)
cc_pair.create / .update / .deleteA connector is linked to a credential, its status changes, or it is unlinkedcc_pairconnector-credential-pair idconnector_id, credential_id; status (on update)
api_key.create / .regenerate / .deleteAn API key is created, regenerated, or revokedapi_keyAPI-key id
credential.create / .update / .deleteA stored credential is created, updated, or deletedcredentialcredential idsource (on create)
credential.accessA stored credential is decrypted for useEmitted on a different schema — see Credential access below

Credential access

onyx.audit.credential_access Credential-decrypt events predate the generalized schema and are emitted on a flat, legacy field set for backward compatibility. They share the onyx.audit logger tree and the same fail-safe / fail-open behavior, but do not carry the generalized fields — there is no action, ocsf_class, outcome, resource_type/resource_id, extra, or nested actor object, and the client IP is client_ip (not source_ip). Parse this stream separately; detection rules written against the generalized schema will not match it.
FieldTypeDescription
tsnumberEvent time, epoch seconds.
tenant_idstring | nullTenant the decrypt occurred in.
credential_typestringCoarse category, e.g. llm_provider or connector.
providerstring | nullProvider / source name if known (e.g. openai, google_drive).
row_idnumber | nullDB row id of the credential / LLM provider.
user_idstring | nullActing user id, if available (may be null for background jobs).
auth_typestring | nullHow the actor authenticated, if known.
request_idstring | nullCorrelates with the rest of that request’s logs.
endpointstring | nullRoute that produced the event.
client_ipstring | nullClient IP (from X-Forwarded-For).
The taxonomy is append-only: new action values may be added in future releases, but existing ones never change meaning — so detection rules and dashboards built against these values keep working across upgrades.

Notes

  • Secrets are never logged. Actor objects and extra context are scrubbed of credential values by design.
  • Credential-access events (onyx.audit.credential_access) use a separate, flat field set than the generalized schema for backward compatibility — see Credential access for the full field list. They share the same logger tree and fail-safe behavior.
  • For Onyx Cloud (multi-tenant) deployments, audit-log forwarding is handled differently since you don’t control the log pipeline — contact us to discuss SIEM delivery options.