Audit Log

June 2, 2026 · View on GitHub

The audit log records sensitive operations as JSON Lines, one event per line, append-only. Configure the path via audit.file in horizon.yaml (see Setup → audit).

Event Fields

Each event has these fields:

FieldMeaning
tsISO-8601 timestamp.
actorUsername, or null for system events.
actionOperation name, such as auth.login or addOrUpdate.
verbRBAC verb checked, when applicable.
targetResource id or name, when applicable.
outcomesuccess / ok / break-glass for normal flows; the OAP applyStatus or http_<code> on an upstream failure.
detailsExtra context for the operation.
fromIpRequester IP.
sessionIdSession id, when a session exists.

One event per line, \n-terminated. Use jq -c to filter:

tail -f horizon-audit.jsonl | jq -c 'select(.action | startswith("auth."))'

Recorded actions

The recorded set can grow between releases. The current set:

ActionOutcome valuesNotes
auth.loginsuccess, failureStandard login. details.backend is local or ldap. On success details.roles carries the resolved role list.
auth.login.break-glassbreak-glassEmergency admin login. Logged in addition to a WARN application log line.
auth.logoutsuccessExplicit logout (cookie cleared). Sessions that simply expire are not logged.
addOrUpdateOAP applyStatus, or http_<code> on failureDSL Management — create / update a rule. target is the rule name; details.catalog is the rule catalog (alarm, MAL, OAL, …).
inactivateOAP applyStatus, or http_<code>DSL Management — deactivate a rule.
deleteOAP applyStatus, or http_<code>DSL Management — delete a rule. details.mode is the delete mode.
alarms.config.saveok, or an error status on failureAlarm page setup save.
setup.savesuccess, or an error status on failurePer-user setup state write (service / instance / endpoint setup).
debug.startok, or an error status on failureLive Debugger — start a session.
debug.stopok, or an error status on failureLive Debugger — stop a session.

outcome is a short literal for normal flows (success, ok, or break-glass) and, when the underlying OAP call fails, the OAP applyStatus value or http_<code>. This makes audit-time error correlation straightforward: an entry with outcome: "http_503" tells you OAP returned a server error.

Example entries

Successful local login

{
  "ts": "2026-05-18T09:14:02.118Z",
  "actor": "alice",
  "action": "auth.login",
  "outcome": "success",
  "fromIp": "10.0.5.12",
  "sessionId": "k7r...",
  "details": { "backend": "local", "roles": ["operator"] }
}

Failed LDAP login

{
  "ts": "2026-05-18T09:14:08.221Z",
  "actor": "alice",
  "action": "auth.login",
  "outcome": "failure",
  "fromIp": "10.0.5.12",
  "details": { "backend": "ldap" }
}

(No sessionId — no session was created.)

Break-glass login

{
  "ts": "2026-05-18T14:29:33.456Z",
  "actor": "emergency-admin",
  "action": "auth.login.break-glass",
  "outcome": "break-glass",
  "fromIp": "192.0.2.10",
  "sessionId": "z3a...",
  "details": { "backend": "ldap" }
}

Rule write

{
  "ts": "2026-05-18T15:02:11.004Z",
  "actor": "alice",
  "action": "addOrUpdate",
  "verb": "rule:write",
  "target": "service_resp_time_rule",
  "outcome": "success",
  "details": { "catalog": "alarm" },
  "fromIp": "10.0.5.12",
  "sessionId": "k7r..."
}

File format

  • JSON Lines. One JSON object per line, \n-terminated.
  • Append-only. The file is only ever appended to — never truncated or rotated.
  • No rotation built in. Pair with logrotate, vector, fluent-bit, or a sidecar shipper.

Storage placement

  • Durable storage required. Break-glass logins, rule edits, and setup changes should outlive the container.
  • Filesystem perms matter for forensic integrity — typically 0640 (BFF user write, ops group read), not world-readable. Adjust to your operations posture.
  • Encrypted at rest if your compliance posture requires it. Use disk-level encryption (LUKS, EBS encryption) — the BFF itself does not encrypt.

What is NOT in the audit log

  • Read operations. Dashboard fetches, alarm queries, MQE reads are not logged (volume would be unworkable, and reads have no side effects). Configure debugLog.enabled for wire-level read logging.
  • Typed passwords. Never logged. Failed logins show the actor but not the attempted password.
  • OAP response bodies. Audit entries reference what was attempted, not the underlying response payload. For payload-level visibility, use debugLog (./horizon-wire.jsonl).

In-memory "seen cache"

In addition to the on-disk audit log, the BFF keeps an in-memory record of recent successful logins:

  • Records: username, source (local / ldap / break-glass), roles, last-seen timestamp, last IP.
  • Reset on BFF restart.
  • Visible on the Users admin page.

This is a UX convenience — it lets the Users page show "who has logged in to this BFF instance recently" without parsing the audit log. For historical / cluster-wide analysis, parse the JSONL file directly.

Wire-up to log pipelines

JSON Lines is ingestable by almost everything. Common pipelines:

PipelineConfiguration
Vector[sources.horizon] type = "file", format = "json"
Fluent BitINPUT tail, Parser json
Promtail / Lokipipeline_stages: - json
Elastic Filebeatfilebeat.inputs: - type: filestream, parsers: - ndjson
Splunk Universal ForwarderINDEXED_EXTRACTIONS=JSON

Index on actor, action, outcome for the common queries ("all admin actions by user X in the last 24h").