Skip to content

outboundctl

The outboundctl command-line tool: where it lives, how it finds the rule store, what it may change, its output formats and its exit codes.

Outbound 1.3.0 · Updated

outboundctl is the command-line companion that ships inside Outbound.app. It reads and writes the same rule store and settings the app uses, so scripts, MDM post-install jobs and terminal sessions can list rules, subscribe to blocklists, export history and more.

The pages in this chapter cover every command. This page covers the parts they all share.

If Outbound is installed in /Applications, its bundled tool is at /Applications/Outbound.app/Contents/MacOS/outboundctl. Run it directly, or add an alias to your shell profile:

Terminal window
alias outboundctl=/Applications/Outbound.app/Contents/MacOS/outboundctl

Running the copy inside the app matters: it is the copy that updates with the app. When another program launches outboundctl for you (the MCP server installer, for example) it registers this same path.

outboundctl reads the saved store directly. Most changes go through Outbound’s background agent, which asks for the same approval the app does and passes the change to the filter extension. Some commands, such as answering an alert or activating a licence, work only through the agent, so the agent must be running. Saving a rule is not confirmation that the filter has accepted it; changes that loosen protection wait for approval in the app.

The rule store is rules.json in the Outbound app group container:

~/Library/Group Containers/5GS535L4GD.com.outboundfirewall.outbound/rules.json

settings.json (blocklists, profiles and the app’s policy choices) sits beside it, and so do the audit log, the analytics database and the folders the background agent uses. Rule and settings commands resolve the rules path once per invocation.

Set OUTBOUND_STORE_PATH to work on a different rules file, for example one you are preparing for deployment:

Terminal window
OUTBOUND_STORE_PATH=/tmp/staging/rules.json outboundctl rules list

An empty value counts as unset. The override points at the rules file itself. Settings and store lock files sit beside it. History has its own directory and override, described under history. Symlinks in the override are resolved once at the start of the command.

Under sudo or an MDM post-install script, root’s own home has no app group container, so outboundctl without OUTBOUND_STORE_PATH uses the container of the user signed in at the console instead. When nobody is signed in (a login window or an SSH-only Mac), the tool stops with an error instead of silently writing under /var/root:

running as root with no user logged in at the console; set OUTBOUND_STORE_PATH to the rules file to use

Root-created files keep root as their owner on purpose: a root-owned rules.json or settings.json is how the app recognises an administrator’s override. Inside the console user’s container, new directories and the store’s .bak and .lock files receive that user’s ownership so the app can keep writing there.

Root is the administrator, and it may change rules even when the organisation has turned editing off for users. What root cannot do is bypass the outboundctl switch or the settings lock, and it cannot restore a backup or set a preference that loosens protection. See the settings lock and managed preferences.

Settings › Security › Command Line has a picker, outboundctl changes, that decides what the tool may change: “Loosening protection and AI changes always ask; Off makes the command line read-only.”

  • Allow tightening: changes that only tighten protection are made straight away; anything that loosens protection asks for your approval in Outbound.
  • Ask every time (the default): every change waits for your approval in Outbound.
  • Off: read-only. Commands that would change something stop with status 77.

Changes also stop with status 77 when:

  • The settings lock covers the area the command changes. Unlock it with an administrator account in the app.
  • The organisation has turned off editing of rules (UsersMayEditRules): the refusal names it and says to run as root to override. Root passes this gate.

prefs set and backup restore also refuse changes that loosen protection, even for root, with status 77. See Security settings.

  • --dry-run previews a change without making it. For changes that go through the agent, it prints a plan_id, what the change would add, remove or change, and the store revision it was planned against.
  • --plan <id> applies a saved plan. Approval is still required, and a plan made against an older store is refused with status 65.
  • --no-wait returns while a change waits for approval. outboundctl requests list shows waiting requests, requests status <id> reads a decision without approving it, and requests cancel <id> withdraws one.
  • outboundctl policy show prints the effective policy and whether each value comes from you or from your organisation.

Closing Terminal, or stopping the command, also closes its waiting approval prompt without applying the change.

Read commands print plain text by default. Add --output to choose another format:

Format What you get
text Plain text for reading (the default)
json One JSON document; --json is short for --output json
ndjson One JSON object per line; following commands such as history tail --follow stream this way
tsv Tab-separated rows for spreadsheets and shell tools
legacy-json The older JSON shape of commands such as rules list and history list, kept for existing scripts

Other flags work with every command:

  • --quiet suppresses success text but keeps machine output and errors.
  • --timeout <seconds> bounds calls to Outbound’s services (default 30 seconds).

outboundctl schema prints the bundled JSON Schemas for structured replies; outboundctl schema <kind> prints one. In history list, history tail and audit output, small numbers such as 0 and 1 stay numbers (history export writes every JSON Lines value as a string), and following output reaches a file or another program as each batch arrives.

With --output json, ndjson, tsv or legacy-json, outboundctl follows the same disclosure policy as AI tools. It shows website domains instead of full host names, “address” instead of IP addresses and networks, “local network” for local names, and app names instead of paths. User names are never shown. history list, history tail, history export and alerts list follow the same rule in plain text too, and so does any line of plain text that is a JSON record. See What AI tools can see.

Code Meaning
0 Success, including when something was already in the state you asked for
1 Runtime failure
2 Declined: the change was not approved in Outbound
64 Usage error: unknown command, missing or unknown option, wrong number of arguments
65 Not valid: a bad rule, address, port, pin, feed or value; a stale plan or revision; an alert that was already answered; or a broken audit chain, naming the first bad sequence
66 Not found: a rule, blocklist, profile or file
69 Unavailable: missing analytics, the background agent is off, the request was cancelled, or it timed out
75 Pending, rate limited or expired
77 Not allowed: policy, the settings lock, a managed restriction, a missing root key, or audit export without root

outboundctl help exit-codes prints the same list. Command errors go to stderr with the outboundctl: prefix. Many usage errors also point to outboundctl --help. The MCP server reports protocol and tool errors in its JSON replies.

outboundctl --help (or -h, or help) prints the full command list and syntax, and outboundctl help <command> prints one command’s flags. outboundctl version prints the tool’s own version string. Running outboundctl with no command prints the usage text as an error.

The manual page ships inside the app. Copy Outbound.app/Contents/Resources/man/outboundctl.1 to /usr/local/share/man/man1/ to read it with man outboundctl.

Shell completions:

Terminal window
outboundctl completion zsh > ~/.zfunc/_outboundctl # add ~/.zfunc to fpath before compinit
source <(outboundctl completion bash)
outboundctl completion fish > ~/.config/fish/completions/outboundctl.fish