CLI reference
aifw is the local command-line interface to the AiFw daemon — it talks directly to the SQLite database and the local pf engine, not to the HTTP API. Use it when:
- You’re on the appliance console (no UI) and need to inspect, recover, or change something.
- You want to script bulk imports or one-off changes without standing up an API client.
- You’re writing post-install hooks or HA failover scripts that run as root locally.
For remote / unprivileged callers, use the REST API instead — it does the same things but with full RBAC.
Global flags
aifw [--db <path>] <subcommand> [<args>...]
| Flag | Default | Notes |
|---|---|---|
--db <path> |
/var/db/aifw/aifw.db |
Path to the AiFw SQLite database. Override for tests or alternate installs. |
--help / -h |
— | Show help for any subcommand. |
--version |
— | Print the AiFw version string. |
Most list commands accept --json for machine-readable output. Most failures exit non-zero and print a one-line error to stderr.
Subcommand groups
| Subcommand | Purpose |
|---|---|
aifw init |
Initialise the AiFw database file |
aifw rules |
Manage firewall rules |
aifw nat |
Manage NAT rules |
aifw queue |
Manage traffic queues (CoDel / HFSC / PRIQ) |
aifw ratelimit |
Manage rate-limit rules |
aifw geoip |
Manage geo-IP filtering |
aifw vpn |
Manage WireGuard / IPsec tunnels |
aifw config |
Versioned config history, import / export, rollback |
aifw routes |
Manage static routes |
aifw dns |
Manage DNS nameservers + post-switch probe |
aifw syslog |
Remote syslog forwarding (server, categories, test) |
aifw logrotate |
Rotation policy for AiFw service logs |
aifw dhcp |
Manage the DHCP server (rDHCP) |
aifw users |
Manage local users |
aifw reverse-proxy |
Control TrafficCop |
aifw update |
AiFw firmware + OS updates |
aifw multiwan |
Multi-WAN instances, gateways, groups, policies, leaks |
aifw cluster |
HA / cluster operations (CARP, pfsync, nodes, health) |
aifw interfaces |
Show network interfaces |
aifw status |
Show firewall status |
aifw reload |
Reload rules from DB and apply to pf |
aifw reconcile |
Reconcile kernel pf state and rc.conf against DB / pf.conf.aifw |
Source: aifw-cli/src/main.rs.
init
Initialise the database (run once at install time; the installer wizard does this for you).
aifw init
aifw init --path /var/db/aifw/aifw.db
rules
Manage stateful firewall rules in the aifw pf anchor.
# List rules
aifw rules list
aifw rules list --json
# Add a rule
aifw rules add \
--action pass --direction in --proto tcp \
--src any --dst any --dst-port 443 \
--interface em0 --priority 100 \
--label "https-inbound"
# Remove by UUID
aifw rules remove 8e2c1a9f-5c0e-4f7e-9a01-94f1b3a2e678
Field reference: action is pass / block / block-drop / block-return; direction is in / out / any; priority is 0–10000 (lower applies first).
nat
Manage NAT rules in the aifw-nat anchor.
aifw nat list
# Port-forward 443 to an internal host
aifw nat add \
--nat-type dnat --interface em0 --proto tcp \
--dst any --dst-port 443 \
--redirect 10.0.0.20 --redirect-port 443 \
--label "ingress-https"
# Outbound NAT (masquerade)
aifw nat add \
--nat-type masquerade --interface em0 --proto any \
--src 10.0.0.0/24 --dst any --redirect any
# NAT64 — IPv6-only clients reach IPv4 hosts (dst defaults to 64:ff9b::/96)
aifw nat add --nat-type nat64 --interface em1 --redirect 203.0.113.1
# NAT46 — IPv4 clients reach an IPv6 server on the address it already has
# (omit --af-to-dst to use the RFC 6052 embedded address instead)
aifw nat add --nat-type nat46 --interface em0 --dst 192.0.2.80 \
--redirect 2001:db8:2::1 --af-to-dst 2001:db8:2::80
# Print the RFC 6052 embedded address clients use for an IPv4 host
aifw nat embed 64:ff9b::/96 10.1.2.3 # -> 64:ff9b::a01:203
Supported NAT types: snat, dnat, masquerade, binat, nat64, nat46 (cross-family types translate via pf af-to, FreeBSD 15+).
queue & ratelimit
Traffic shaping and connection-rate limits.
# Create a CoDel queue capped at 100 Mb/s on em0
aifw queue add \
--name uplink --interface em0 \
--type codel --bandwidth 100Mb --class default --default
aifw queue list
# Limit any single source IP to 60 new TCP/443 conns per minute
aifw ratelimit add \
--name https-flood --proto tcp \
--max-conn 60 --window 60 \
--table flooders --dst-port 443 --interface em0
Overload offenders land in the named pf table (flooders above) so you can block them with a rule.
geoip
Country-level allow / block rules + interactive lookup.
aifw geoip add --country CN --action block --label "block China"
aifw geoip list
aifw geoip lookup 8.8.8.8
# => US — Google LLC (AS15169)
aifw geoip remove <id>
vpn
WireGuard and IPsec tunnels in one tree.
# WireGuard tunnel
aifw vpn wg-add --name road-warriors \
--interface wg0 --port 51820 --address 10.50.0.1/24
# Add a peer
aifw vpn wg-peer-add --tunnel <tunnel-id> \
--name laptop --pubkey <peer-public-key> \
--allowed-ips 10.50.0.10/32 --keepalive 25
# IPsec IKEv2 site-to-site tunnel (PSK; use the web UI/API for cert auth)
aifw vpn ipsec-add --name site-to-site \
--remote 203.0.113.7 --psk <16+ char secret> \
--local-ts 10.0.0.0/24 --remote-ts 10.1.0.0/24
# Live negotiated state from charon; manual initiate/terminate
aifw vpn ipsec-status
aifw vpn ipsec-start <tunnel-id>
aifw vpn ipsec-stop <tunnel-id>
aifw vpn list
config
Versioned configuration: every aifw reload snapshots the active config so you can diff or roll back.
aifw config show
aifw config history --limit 20
# Diff version 17 against version 22
aifw config diff 17 22
# Rollback
aifw config rollback 17
# Export / import (full JSON dump; secrets redacted by default)
aifw config export > backup.json
aifw config import backup.json
# Portable backup — secrets passphrase-wrapped (Argon2id + AES-256-GCM).
# The passphrase comes from --passphrase-file (first line) or $AIFW_BACKUP_PASSPHRASE.
aifw config export --secrets passphrase --passphrase-file /root/.backup-pass > backup.json
aifw config import backup.json --passphrase-file /root/.backup-pass
# Plaintext secrets (prints a warning — protect the output)
aifw config export --secrets plain > backup.json
routes
Static routes (kernel-level, separate from multi-WAN policy routing).
aifw routes add --dest 10.20.0.0/16 --gateway 10.0.0.1 --metric 0
aifw routes list
aifw routes system # netstat -rn
aifw routes remove <id>
dns
Manage the resolver’s upstream nameservers and the post-switch probe (auto-rollback if :53 goes silent after a backend swap).
aifw dns list
aifw dns set "1.1.1.1, 9.9.9.9"
aifw dns probe on # enable auto-rollback
aifw dns probe off
aifw dns probe status
syslog
Forward pf packet logs, IDS alerts, and application logs to a remote syslog server / SIEM. set only changes the flags you pass; all AiFw services pick up changes within 60 seconds.
aifw syslog show # human-readable summary (--json for JSON)
aifw syslog set --host 192.168.1.10 --port 514 --pf true
aifw syslog enable # master switch (requires a host)
aifw syslog disable
aifw syslog set --transport tcp --format rfc5424 --facility local3
aifw syslog set --app true --app-min-level warn
aifw syslog set --disable-local true # stop local pf/app log files while forwarding
aifw syslog test # one test message with the saved config
aifw syslog test --host 10.0.0.9 --port 5514
logrotate
One size-based rotation policy for every AiFw-managed service log (API, daemon, IDS, watchdog, rDNS, rDHCP, rTIME, TrafficCop). AiFw renders it into /usr/local/etc/newsyslog.conf.d/aifw.conf; FreeBSD’s hourly newsyslog cron job does the rotating, and the rc.d scripts run daemon(8) with -H so the log is reopened after each rotation.
aifw logrotate show # policy + current size of every managed log (--json for JSON)
aifw logrotate set --max-size 20 --keep 5 # rotate above 20 MB, keep 5 generations
aifw logrotate set --compression zstd # gzip (default), bzip2, xz, zstd, none
aifw logrotate rotate # rotate anything currently over its limit
aifw logrotate rotate --path /var/log/rtime/rtime.log # force-rotate one log now
Defaults: rotate above 5 MB, keep 7 generations, gzip.
dhcp
Drive the rDHCP companion service end-to-end.
aifw dhcp status
aifw dhcp start | stop | restart
# Subnets and reservations
aifw dhcp subnets
aifw dhcp subnet-add \
--network 10.0.0.0/24 \
--pool-start 10.0.0.100 --pool-end 10.0.0.200 \
--gateway 10.0.0.1 --dns "10.0.0.1" \
--domain "lan" --lease-time 86400
aifw dhcp reservations
aifw dhcp reservation-add \
--mac aa:bb:cc:dd:ee:ff --ip 10.0.0.50 \
--hostname printer-1
aifw dhcp leases
aifw dhcp apply # write config + restart rDHCP
users
Local-account CRUD. For SSO / OAuth users use the API or the UI.
aifw users add --username alice --password "...." --role admin
aifw users list
aifw users disable <id>
aifw users enable <id>
aifw users remove <id>
Roles: admin / operator / viewer (see Auth & RBAC).
reverse-proxy
Control the local TrafficCop instance.
aifw reverse-proxy status
aifw reverse-proxy start | stop | restart
aifw reverse-proxy validate # dry-run validate generated YAML
aifw reverse-proxy apply # generate + write + reload
aifw reverse-proxy routers --json
aifw reverse-proxy services --json
aifw reverse-proxy middlewares
aifw reverse-proxy entrypoints
aifw reverse-proxy show-config # print generated YAML
update
AiFw firmware updates plus host OS / package updates.
# AiFw firmware
aifw update check
aifw update install # download + install latest
aifw update install --restart # also restart services
aifw update install --from /tmp/aifw.tar.xz # local tarball
aifw update rollback # revert to previous version
aifw update restart # bounce services after install
aifw update reboot # full reboot
# OS / packages
aifw update os-check
aifw update os-install
install and rollback do not restart services automatically — run aifw update restart (or pass --restart / -y) when you’re ready for the brief outage.
multiwan
Inspect and apply multi-WAN configuration.
aifw multiwan instances
aifw multiwan gateways # live health
aifw multiwan groups
aifw multiwan policies
aifw multiwan leaks
aifw multiwan flows # live pf flows tagged by FIB
aifw multiwan fib-info
aifw multiwan apply # recompile + apply all anchors
aifw multiwan seed-mgmt # seed management-escape leaks
# Force a probe outcome (testing only)
aifw multiwan probe <gateway-id> ok
aifw multiwan probe <gateway-id> fail
# Export / import the full multi-WAN config as JSON
aifw multiwan export > mwan.json
aifw multiwan import mwan.json
cluster
CARP + pfsync HA operations.
aifw cluster status --json
# CARP VIPs
aifw cluster carp list
aifw cluster carp add --vhid 10 --interface em0 \
--vip 192.0.2.1/24 --password "carp-shared-secret"
aifw cluster carp remove <id>
# pfsync
aifw cluster pfsync get
aifw cluster pfsync set --sync-interface em1 --sync-peer 10.99.0.2 \
--defer true --latency-profile conservative
# Cluster nodes
aifw cluster nodes list
aifw cluster nodes add --name node-b --address 10.99.0.2 --role secondary
# Health checks
aifw cluster health list
aifw cluster health add --name uplink --check-type ping \
--target 8.8.8.8 --interval-secs 10
aifw cluster health run # probe everything now
# Failover
aifw cluster promote # this node -> master immediately
aifw cluster demote # this node -> backup immediately
aifw cluster sync # pull a snapshot from peer
aifw cluster verify --json # exit non-zero if unhealthy
interfaces / status / reload / reconcile
Diagnostic + apply commands.
aifw interfaces # list interfaces with addresses + flags
aifw status # daemon + pf health summary
aifw reload # reload rules from DB and apply to pf
aifw reconcile # repair drift between DB, pf.conf.aifw, and rc.conf
reconcile is the safe choice when you suspect the kernel pf state has drifted from the database — for example after a manual pfctl -F or a partial freebsd-update run. It re-applies anchor hooks if missing and fixes rc.conf DNS-backend flags that disagree with the database.
Output formats
Subcommands that return lists accept --json to emit raw JSON for piping into jq or another tool:
aifw rules list --json | jq '.[] | select(.action == "block")'
aifw multiwan gateways | jq '.[] | {name, state}'
Subcommands without --json print human-readable tables.
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Generic error (validation, missing record, runtime failure) — reason on stderr |
2 |
Usage error (clap-level: bad args, unknown subcommand) |
aifw cluster verify is the most explicit user of non-zero exits — cron / monitoring scripts can rely on its exit code as a single-bit health signal.