outboundctl rules
Every outboundctl rules subcommand: list, add, show, priority, duplicate, merge, notes, global, remove, export, import, import-lsrules, export-lsrules and count, plus allow and deny for a while.
outboundctl rules works on the rule store directly. Listing, exporting and counting are read-only; add, remove, import and import-lsrules change the store and are subject to the edit policy. rules add creates outgoing rules, permanent unless you add --for. To set a schedule or profile interactively, use the rule editor; native JSON import can also carry those fields.
Listing rules
Section titled “Listing rules”outboundctl rules listoutboundctl rules list --jsonThe plain listing prints one tab-separated line per rule with these columns, no header row:
| Column | Contents |
|---|---|
| id | The rule’s UUID |
| action | allow, deny or ask |
| app | The program the rule applies to (see below) |
| remote | The destination (see below) |
| ports | any, ports and ranges such as 443, 80, 443, 8000-8100 |
| protocol | tcp, udp, any or icmp |
| lifetime | forever, until-quit, until-logout, until-restart, until <ISO-8601 date>, or schedule days=1,2 … with the day’s start and end times (all-day when they are the same) |
| source | user, managed, group:<uuid> (with a name for a recognised trusted group) or blocklist:<uuid> |
| on/off | on or off |
The app column shows the same forms the filter matches: any for every program, a signing identifier with its team in parentheses (com.example.app (ABCD1234)), an executable path, or an apple-signed entry from a trusted group. The remote column prints any, local, a domain as *.example.com, a host name, an IP address or a CIDR network.
--json prints a pretty-printed JSON array of the complete rules, each with its id, sorted keys included. Feed it to jq when you need a single field.
Filters and ordering change only what is printed, never the policy:
--source user|managed|blocklist|group,--app <name>,--remote <remote>and--enabled true|false.--direction all|outgoing|incoming|bothand--destination-type all|host|domain|ip|any.--search <text>with--search-scope all|process-name|signing-id|folder|destination|note.--sort name|precedence|last-used|usage-count|date-added|date-modified. Without it, rules print in store order.--affecting-path <path>,--affecting-signing-id <id>,--affecting-team-id <team>and--affecting-apple-signed: only the rules that apply to that program.
Adding a rule
Section titled “Adding a rule”outboundctl rules add --action allow|deny --remote <remote> [--ports <ports>] [--proto tcp|udp|any] [--signing-id <id> [--team-id <team>] | --path <path>] [--countries <codes> | --block-countries <codes>] [--only] [--notify] [--sound <name>] [--for <time>]The command prints the new rule’s UUID, which you use to remove or reference it. Defaults: ports any, protocol any, outgoing direction, permanent (lifetime), and the rule is on.
–action: allow or deny, case-insensitive. Required.
–remote: required. Accepted forms, case-insensitive:
any: every destination.local: the local network.*.example.com: the domain and its subdomains.- An IP address, IPv4 or IPv6: that one host.
- A CIDR network such as
10.0.0.0/8. - A host name, or a fully qualified name written with a trailing dot (
example.com.).
–ports: any, a single port, a comma-separated list (80,443), a range (8000-8100) or a mix (22,8000-8100). Ports run 1 to 65535 and a range’s low end must not exceed its high end. An empty --ports "" value means any.
–proto: tcp, udp or any. Default any.
–signing-id / –team-id / –path: which program the rule applies to. Omit both --signing-id and --path to match every program. Supplying either option with an empty value is an error.
- A signing identifier with a team ID matches that developer’s signed code.
- A signing identifier without a team matches only Apple’s own programs, whose identifiers start with
com.apple.. Any other bare signing ID is refused, with a hint to add--team-idor use--path. --pathnames the executable path. Versioned installation paths can be normalised to a pattern.
–countries / –block-countries / –only: a country rule instead of a remote, with comma-separated two-letter country codes. --only blocks every country outside the list.
–notify / –sound: the rule shows a notification, plays the named system sound, or both, when it decides. See Notification and Sound.
–for: makes the rule temporary: 5m, 1h, 1d, until-logout or until-restart.
The three program selectors are mutually exclusive where it matters: --signing-id and --path cannot be combined, --team-id needs --signing-id, and an empty --path, --signing-id, --team-id value is an error, not a rule for every program.
Two examples:
outboundctl rules add --action deny --remote "*.tracker.example" --proto tcp --signing-id com.example.suite --team-id ABCD1234E5outboundctl rules add --action allow --remote api.example.com --ports 443 --path /usr/local/bin/sync-toolInvalid remote or port values exit 65. Invalid action or protocol options, empty app selectors and incompatible selectors exit 64. These validation failures do not add a rule. The error names the field and the accepted forms.
Allowing or blocking one app and server
Section titled “Allowing or blocking one app and server”outboundctl allow <app> <host> [--team-id <team>] [--ports <ports>] [--proto tcp|udp|any] [--for <time>]outboundctl deny <app> <host> [--team-id <team>] [--ports <ports>] [--proto tcp|udp|any] [--for <time>]<app> is a signing identifier or a path. With --for 5m, 1h, 1d, until-logout or until-restart, the decision is temporary: the rule that decided before is held back and applies again when the time is up. See Allow or block for a while.
Reading and changing one rule
Section titled “Reading and changing one rule”| Command | What it does |
|---|---|
rules show <uuid> |
Shows whether the rule can be edited and, when it is not in effect, why. |
rules enable <uuid>, rules disable <uuid> |
Turns one of your rules on or off. |
rules priority <uuid> on|off |
Raises or removes the rule’s priority and prints the rule. |
rules duplicate <uuid> |
Adds a copy of the rule with its matching fields. |
rules merge <uuid,uuid,…> |
Merges equivalent rules of yours, keeping the oldest one and combining their notes and usage. |
rules note <uuid> --note <text> |
Edits the note and leaves the rule’s creation record alone. An empty string clears it. |
rules restore-note <uuid> |
Puts back the note recorded when the rule was created. |
rules global <uuid> |
Makes the rule apply in every profile. |
rules repair <uuid> |
Binds a path rule to an executable and signer you name with --path, --signing-id and --team-id. |
rules approve <uuid> … |
Marks rules as reviewed without changing any decision. This needs an administrator. |
Each change goes through the same approval as the app: a change that unblocks connections asks for it.
Removing a rule
Section titled “Removing a rule”outboundctl rules remove <uuid>Removes a rule by its UUID and prints removed <uuid>. Two kinds of rule refuse removal with exit 77, and not even root can remove them this way:
- A rule that came from a blocklist: the error names the
blocklist removecommand that unsubscribes the list the rule belongs to. - A rule your organisation’s rule feed manages.
A UUID that is not in the store exits 1 with no rule with id ….
Export and import
Section titled “Export and import”outboundctl rules export <file>outboundctl rules import <file>export writes the whole store, every source included, to <file> and prints how many rules it wrote. import does the careful inverse. It decodes the file before replacing only your own (user) rules with the ones in the file. Duplicate imported user-rule IDs, or IDs that collide with a retained non-user rule, fail the import with exit 1. Managed, blocklist and rule-group rules already in the store stay, and rules in the file that are not user rules are ignored and counted in the summary. That way, importing back a file you exported never deletes the organisation’s rules or a subscription’s.
Import validates through the same store reader the app uses, in a temporary directory first, so a stale neighbouring .bak file can never be imported by mistake. If the existing store is unreadable, import prints a warning and replaces it with the imported rules.
.lsrules files
Section titled “.lsrules files”outboundctl rules import-lsrules <file> [--merge keep-existing|prefer-imported|replace] [--dry-run]outboundctl rules export-lsrules <file> [--ids <uuid,uuid,…>] [--name <text>] [--description <text>] [--strict]import-lsrules reads an .lsrules rule group. --merge decides what happens where the file and your rules differ: keep-existing (the default) keeps yours and adds only what you don’t have, prefer-imported takes the file’s decision, and replace makes your rules that the format can hold match the file. Without root, prefer-imported and replace are refused, because they change rules you already have: import in Outbound instead, or run the command with sudo. --dry-run prints the plan and changes nothing. The summary names the rule group it came from when the file has a name, and prints one skipped: … line for every rule or entry the format allows but Outbound cannot express, with the reason. A file that does not parse exits 65.
export-lsrules writes your user rules that the .lsrules format can express, or only those named with --ids. --name and --description set the group’s name and description. Rules the format cannot express stay out of the file rather than being written with a different meaning, and the summary says how many were left out. --strict writes nothing if any rule would match more programs in the file than it does in Outbound. outboundctl rules export <file> --lsrules does the same with --name and --description. The format reference lists what carries over.
outboundctl rules countPrints one line: the number of rules in the store, all sources included.
Exit status
Section titled “Exit status”Successful operations exit 0. Store and file failures exit 1, including a failed native JSON import. Invalid command syntax exits 64. Invalid remote or port values and malformed .lsrules contents exit 65. Edit-policy refusals and attempts to remove managed or blocklist rules exit 77.