Skip to content

outboundctl audit

outboundctl audit export and the audit log format: daily JSON Lines files, what a line contains, and what gets copied.

Outbound 1.3.0 · Updated

Outbound keeps an audit log of the connections it decided, for organisations that need the record. The network filter keeps the sealed log in its own folder, readable only by root (/var/root/Library/Application Support/com.outboundfirewall.outbound.filter/audit). The audit folder beside the rule store holds the background agent’s copy. Day files are pruned when their UTC date is more than 30 days old, or more than the AuditRetentionDays your organisation forces. outboundctl audit export copies those files to a directory of your choosing so a collector can pick them up. See the deployment guide for the full setup, including the app’s own scheduled export.

Terminal window
sudo outboundctl audit export --to <dir>

Copies the audit files into <dir>, creating it if needed, and prints the name of each file it copied, one per line. A destination file with the same byte count as the source’s complete lines is skipped. If no files need copying, the command prints nothing.

The export copies whole lines only. A day file that is still being written ends with a line that may not be finished; the export copies up to the file’s last complete line and leaves the rest for the next run, so a collector never sees a partial JSON line. Only regular files named like day files are copied; a symbolic link is skipped. Matching day files are replaced atomically when their complete-line sizes differ. Other destination files are left alone; retention there is your organisation’s policy.

A failure exits 1 with a message naming the source and destination. Without sudo the command exits 77 with “Run with sudo to export the sealed log”. When your organisation forces AuditRemoteDetail to domain or hashed, it exits 77 with “This Mac’s audit export is managed; your organisation collects it from” and the export path.

Each file is named YYYY-MM-DD.N.jsonl: the UTC date of the events in it, then a segment number from 0. A new segment starts after 16 MiB. Each line is one JSON object with sorted keys and an ISO-8601 timestamp:

{"flow":{"direction":"outgoing","path":"/Applications/Example.app/Contents/MacOS/Example","proto":"tcp","remoteAddress":"203.0.113.7","remoteHost":"www.example.com","remotePort":443,"signingID":"com.example.app","teamID":"ABCD1234E5"},"reason":"rule","ruleID":"7C9E6679-7425-40DE-944B-E07FC1F90AE7","timestamp":"2026-10-19T09:30:00Z","verdict":"deny"}

The fields:

Field Contents
timestamp When the connection was decided, ISO-8601
verdict allow or deny, the final decision
ruleID The deciding rule’s UUID, omitted when the mode decided
reason Why: rule, mode default, learned, strict-denied, or a fuller note such as ask-allow or timeout-deny when the path to the final decision adds information
flow What the filter knew about the connection (below)
kind, action What a non-flow line records (config, change, approval, feed, licence, lifecycle) and its event, such as filter.started
seq, prev, mac The sealed chain: sequence number, SHA-256 of the previous line, and a keyed hash only root can check
host, user, appVersion The Mac, the console user and the Outbound version that wrote the line

Inside flow: signingID, teamID, path for the program; remoteHost, remoteAddress, remotePort for the destination; proto and direction. Fields the filter did not know are omitted rather than written as null.

A gap marker is written when events had to be discarded, recorded as an ask verdict with a reason such as Lost 12 audit events from <time> to <time>. or, in the sealed log, Dropped 12 audit events. It records no decision about any connection.

For example, sudo outboundctl audit export --to ~/Desktop/outbound-audit copies the available day files. Success exits 0, including when there is nothing to copy. Missing --to or invalid syntax exits 64.

  • sudo outboundctl audit verify [--from YYYY-MM-DD] checks the sealed chain. A failed check exits 65 and names the first bad sequence when one is known.
  • outboundctl audit tail [--kind <kind>] [--follow] prints the last 50 lines: from the sealed log as root, otherwise from the agent’s copy, and it says so. --kind takes flow, config, change, approval, feed, licence or lifecycle. --follow keeps printing new lines as they are written, sending each batch at once even into a pipe or file; with --output json or ndjson it streams one record per line. The lines follow the disclosure policy, showing website domains rather than full host names and addresses. For the complete record, use audit export.
  • sudo outboundctl audit sinks shows each SIEM sink’s last delivery, backlog and last error.
  • sudo outboundctl audit test-sink [name] sends one marked test event to a configured sink. It is not written to the sealed log.