The audit log records what the Artifact Firewall decided, one entry per package evaluated. It is the record of what was blocked and why, and in report mode it is the record of what would have been blocked.
An entry is written when a package is evaluated against the rulesets, which happens once per artifact download: the Virtual Registry asks the firewall to authorize the download, and that evaluation is logged. Filtering a manifest writes no entries, so a npm install of a package with fifty versions produces one entry for the version that was fetched, not fifty.
Each entry is one JSON line.
{"purl":"pkg:npm/%40company/foo@1.2.3","rule_id":"block-vulnerable","ruleset_id":"security-policy","action":"deny","effective_action":"deny"}
purl: Package URL of the artifact that was requested.rule_id: ID of the rule that matched, or default when no rule matched and default_action decided.ruleset_id: ID of the ruleset holding that rule, or default.action: what the rule asked for, one of allow, hide or deny.effective_action: what was enforced.The two action fields differ when the firewall mode downgrades a decision. In normal mode they are always equal. In report mode a denied package is logged as "action":"deny","effective_action":"allow" and served anyway. Both fields are always present, so a query filtering on effective_action shows what reached the client whatever the mode.
audit_log_output takes one destination:
stdout: JSON on standard output. This is the default, and under Docker or Kubernetes it puts the audit log in the container log.stderr: JSON on standard error.otel: OpenTelemetry log records instead of JSON, described below.A JSON destination and otel are mutually exclusive, so exporting over OpenTelemetry means the JSON stream stops.
The file is opened when the firewall starts and held open for the life of the process. A rotation tool that renames the file leaves the firewall writing to the renamed file, so rotation has to copy the file and truncate it in place. With logrotate that is the copytruncate option.
With audit_log_output: otel, each entry becomes an OpenTelemetry log record instead of a JSON line, carrying the same five fields as attributes. The body is package evaluation, and the severity follows the effective action: WARN for deny and hide, INFO for allow. Records appear under the instrumentation scope artifact-firewall/audit, separate from the process log, and each one is correlated with the trace of the request that produced it.
The records travel through the same logs pipeline as the rest of Orca, so otel.logs has to be configured with an endpoint in the same configuration file. Without one there is nowhere to export to, and the entries are dropped rather than falling back to JSON.