outboundctl audit
outboundctl audit export and the audit log format: daily JSON Lines files, what a line contains, and what gets copied.
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.
Exporting
Section titled “Exporting”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.
The audit log format
Section titled “The audit log format”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.
Other audit commands
Section titled “Other audit commands”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.--kindtakesflow,config,change,approval,feed,licenceorlifecycle.--followkeeps printing new lines as they are written, sending each batch at once even into a pipe or file; with--output jsonorndjsonit 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, useaudit export.sudo outboundctl audit sinksshows 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.