outboundctl explain
outboundctl explain: the flags, the text and JSON output, and what "evaluated against the saved rules" means.
outboundctl explain answers one question: with the rules as they are saved right now, what happens to a given connection, and which rule decided it? It runs the same matching the filter runs, in precedence order, against the connection you describe:
outboundctl explain --app /Applications/Safari.app --host example.com --port 443The flags
Section titled “The flags”| Flag | Meaning |
|---|---|
--app <path-or-signing-id> |
Required. An app path or a signing identifier. |
--host <name> |
The destination host name. Exactly one of --host and --address. |
--address <ip> |
The destination IP address, IPv4 or IPv6. |
--port <n> |
Optional; omitted means unknown. 1 to 65535. The remote port for an outgoing connection; the local service port with --incoming. |
--proto tcp|udp |
Default tcp. |
--incoming |
Explain an incoming connection instead of an outgoing one. |
--json |
Print the result as JSON. |
--app is a path when it contains a slash or starts with ~; anything else is read as a signing identifier. A path is resolved through its bundle to the executable, and its real signature is read: the signing identifier, team ID and code hash. A bare signing identifier cannot be verified that way, so it is evaluated as Apple’s own code, the one case where a code-identity rule without a team matches. When you pass a bare signing ID, the output says so and suggests passing a path instead.
The output
Section titled “The output”Decision: allowDecided by: rule 3F2A…Evaluated against: the saved rulesMatching rules (2): Decides 3F2A… Allow connections to example.com on port 443 (TCP) for com.example.app (ABCD1234E5) [user] 8C1D… Allow connections to anywhere on any port for com.example.app (ABCD1234E5) [user]Reason: …Decision: allow, deny or ask. An ask result means the saved policy calls for an alert; this command does not show one.
Decided by: one of four causes. rule <uuid>, with of the trusted group <name> when the deciding rule belongs to a trusted group. <mode> mode default when no rule matched and the mode decided (learn, ask or strict). paused until <date> or paused until restart when a pause overrides everything.
Matching rules: every rule that matches, in precedence order. The deciding one is marked Decides; the others line up under it. Each row describes the rule and ends with its source in square brackets.
Reason lines explain precedence, the mode default, a pause or the bare signing ID assumption described above.
JSON output
Section titled “JSON output”--json prints the same facts as one object:
{ "decision" : "allow", "decidedBy" : { "kind" : "rule", "ruleID" : "…" }, "evaluatedAgainst" : "saved rules", "matching" : [ { "action" : "allow", "id" : "…", "sentence" : "…", "source" : "user" } ], "reasons" : [ ]}decidedBy.kind is rule, mode or paused. For a rule it carries ruleID; for a mode it carries mode with the filter’s own mode name (alert, silentAllow, silentDeny); for a pause it carries until as an ISO-8601 date, or nothing for a pause that lasts until restart.
What the tool can and cannot see
Section titled “What the tool can and cannot see”The tool reads your saved rule store from disk. The filter enforces its own copy. Changes awaiting approval or synchronisation can make the two differ. The output identifies the saved rules as its input.
Exit status
Section titled “Exit status”Exit 0 means the explanation completed, even for a deny or ask decision. Invalid host, address, port or protocol values exit 65. Missing or incompatible required arguments exit 64. A failure to read the app identity, rules or settings exits 1.