Audit Logs & SIEM Integration
Forward Nexus's structured audit-event stream to your SIEM for security monitoring and compliance
Availability depends on your school's enabled tools, provider setup, and account permissions. These settings are managed in the web workspace. A class policy may restrict a feature described here.
Audit logging is available in Nexus v4.3 and later, on self-hosted deployments where you control the log pipeline.
Overview#
Nexus 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 Nexus-side integration to build. You point your log shipper at Nexus'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#
Authentication#
Login success, login failure, logout, registration, password forgot, password reset, and email verification.
User & access management#
User creation (admin invites), deletion, deactivation, reactivation, role changes, and user-group membership changes.
Configuration & resources#
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. Nexus 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 Nexus with
LOG_FORMAT=jsonso 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#
| Field | Type | Description |
|---|---|---|
audit_schema_version | string | Schema version (currently "1.0"). |
ts | number | Event time, epoch seconds. |
action | string | Action taxonomy value, <domain>.<verb> (e.g. llm_provider.update). Stable, append-only. |
ocsf_class | string | authentication, account_change, or api_activity. |
outcome | string | success, failure, or denied. |
tenant_id | string | null | Tenant the action occurred in. |
actor | object | null | { user_id, email, api_key_id, auth_type }. Never contains a secret. |
resource_type | string | null | Affected resource type (e.g. llm_provider, user, api_key). |
resource_id | string | null | Affected resource identifier. |
request_id | string | null | Correlates the event with the rest of that request's logs. |
endpoint | string | null | Route that produced the event. |
source_ip | string | null | Client IP (from X-Forwarded-For). |
extra | object | null | Additional 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.
Run Nexus with JSON logging#
Set LOG_FORMAT=json so every log record is structured and carries a logger and message field.
Ship Nexus's logs#
Point Fluent Bit, Vector, the CloudWatch agent, Filebeat, or your existing collector at the Nexus container stdout (or the mounted log files).
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.
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)'# Match the audit stream by logger prefix.
[FILTER]
Name grep
Match onyx.*
Regex logger ^onyx\.auditFilter on the
onyx.auditprefix to capture everything. The stream is also split into per-class child loggers.onyx.audit.authentication,onyx.audit.account_change,onyx.audit.api_activity, andonyx.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
action | Triggered when | Notes |
|---|---|---|
auth.login | A user successfully signs in | |
auth.login_failure | A sign-in attempt fails | outcome is failure, or denied for a non-interactive login |
auth.logout | A user signs out | |
auth.register | A new account is registered (self-signup) | |
auth.password_forgot | A password-reset email is requested | |
auth.password_reset | A password reset is completed | |
auth.email_verify | An email-verification email is requested |
User & access management#
ocsf_class: account_change. resource_type is user (or user_group).
action | Triggered when | resource_id | Key extra fields |
|---|---|---|---|
user.create | An admin invites new users | . | invited_emails |
user.delete | A user is deleted | user id | target_email |
user.deactivate | A user is deactivated | user id | target_email |
user.reactivate | A user is reactivated | user id | target_email |
user.role_change | A user's role is changed | user id | target_email, old_role, new_role |
user.group_change | A user group's membership changes | user-group id | added_user_ids, removed_user_ids |
Configuration & resources#
ocsf_class: api_activity
action | Triggered when | resource_type | resource_id | Key extra fields |
|---|---|---|---|---|
llm_provider.create / .update / .delete | An LLM provider is created, updated, or deleted | llm_provider | provider id | |
connector.create / .update / .delete | A connector is created, updated, or deleted | connector | connector id | source (on create) |
cc_pair.create / .update / .delete | A connector is linked to a credential, its status changes, or it is unlinked | cc_pair | connector-credential-pair id | connector_id, credential_id; status (on update) |
api_key.create / .regenerate / .delete | An API key is created, regenerated, or revoked | api_key | API-key id | |
credential.create / .update / .delete | A stored credential is created, updated, or deleted | credential | credential id | source (on create) |
credential.access | A stored credential is decrypted for use | . | . | Emitted 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.
| Field | Type | Description |
|---|---|---|
ts | number | Event time, epoch seconds. |
tenant_id | string | null | Tenant the decrypt occurred in. |
credential_type | string | Coarse category, e.g. llm_provider or connector. |
provider | string | null | Provider / source name if known (e.g. openai, google_drive). |
row_id | number | null | DB row id of the credential / LLM provider. |
user_id | string | null | Acting user id, if available (may be null for background jobs). |
auth_type | string | null | How the actor authenticated, if known. |
request_id | string | null | Correlates with the rest of that request's logs. |
endpoint | string | null | Route that produced the event. |
client_ip | string | null | Client IP (from X-Forwarded-For). |
The taxonomy is append-only: new
actionvalues 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#
Credential values should be excluded from logs. Review the configured scrubbing behavior before collecting school logs.
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 a managed deployment (multi-tenant) deployments, audit-log forwarding is handled differently since you don't control the log pipeline. contact us to discuss SIEM delivery options.