Skip to content

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.

Outbound 1.3.0 · Updated

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.

Terminal window
outboundctl rules list
outboundctl rules list --json

The 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|both and --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.
Terminal window
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-id or use --path.
  • --path names 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:

Terminal window
outboundctl rules add --action deny --remote "*.tracker.example" --proto tcp --signing-id com.example.suite --team-id ABCD1234E5
outboundctl rules add --action allow --remote api.example.com --ports 443 --path /usr/local/bin/sync-tool

Invalid 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.

Terminal window
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.

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.

Terminal window
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 remove command 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 ….

Terminal window
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.

Terminal window
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.

Terminal window
outboundctl rules count

Prints one line: the number of rules in the store, all sources included.

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.