orbit: prefix. The rule orbit:wireguard-members admits every WireGuard member on the Node’s WireGuard address, as private network trust describes. The firewall family adds operator rules beside them, each identified by a stable name on one Node.
The Gateway applies each rule over SSH, verifies it in the UFW status output, and records the result. The Node firewall catalog is the single source of Orbit-managed rules, so role provisioning and firewall commands use the same projection. Doctor checks the firewall family and reports a rule that is missing or different from its record without changing the machine.
Commands
Read the managed and live rules
The Gateway serves three read-only lists. The CLI has no command for them. The web app’s Node page and the MCP tools use them.
The first role convergence removes
orbit:public-ssh-recovery. So a Node with an active role does not list public port 22 as intended. The managed list comes from the same catalog that convergence applies.
In the live list, each live rule has a match value. exact means it equals a managed or operator rule. drift means its comment names such a rule but the rule differs. unmanaged means its comment names no managed or operator rule, or it has no comment. A managed or operator rule that is absent from UFW is listed as missing. An inactive, absent, or unreachable UFW returns no live rows. The web app lists the live rules, shows drift and unmanaged rules in red, and flags missing rules. When it cannot read live UFW, it shows the operator rules and the managed catalog instead.
Rules and names
Every command accepts--json and requires --node with a numeric Node ID. A rule name is 1 through 63 characters of lowercase letters, digits, dots, underscores, and hyphens, starting and ending with a letter or digit; it cannot contain a colon, so an operator name never collides with an orbit: rule. The Gateway tags each UFW rule with the comment orbit:node:<node-id>:firewall:<name> and uses that comment for every later change.
All targets and rule values are explicit; these commands do not prompt for missing arguments or options. Human requests show an indeterminate progress tree while the Gateway works, then the verified result and request ID. Piped output has no animation or escape codes. JSON contains only the existing response or error.
orbit firewall:allow
Allow traffic to one destination port or port range, optionally from one source.firewall.name_taken; remove the rule and create it again to change it. Human output reports Firewall rule [name] is active. with the request ID.
orbit firewall:deny
Deny traffic to one destination port or port range, optionally from one source. It accepts the same argument and options asfirewall:allow.
orbit firewall:list
List the named rules recorded for one Node with their action, source, port, protocol, and lifecycle status.
Human output uses a read-only table with NAME, ACTION, SOURCE, PORT, PROTOCOL, and STATUS columns. Values wrap inside their cells; below the minimum width, labeled records retain every field. An empty result says
No firewall rules..
A rule’s status is provisioning, active, failed, or removing. A failed rule keeps failed_step and error_code so the same command can be repeated after the cause is fixed. Orbit-owned orbit: rules are not part of this list.
orbit firewall:remove
Remove one named rule from UFW and delete its record. Before prompting, the CLI resolves the rule with a read-only lookup, then asksRemove firewall rule [name] from Node #ID? with No selected. Supply --yes for automation, including JSON; JSON never implies consent.
removing, deletes every UFW rule that carries its comment, verifies that none remains, and then deletes the record. A failure leaves the rule failed with the step that stopped; repeat the command to retry.
Declining, Ctrl-C, or end of input before removal exits 1 with input.cancelled. Noninteractive removal without --yes exits 1 with input.confirmation_required and an instruction to supply --yes. Refused confirmations send no removal request. Existing lookup, authorization, and safety errors retain their codes.