Files
housecarl/docs/cli.md
T
abrown38andClaude Opus 5.5 2880f7ddc9 Add changesets, bandwidth limits and quotas; generic examples
Changesets, as in Jarl: configuration changes are operations that can be
staged in a changeset, checked as they are added (validation and nft -c),
reviewed with the diff of the ruleset, and applied together as one
history entry, then on probation like any change made over the network.
The web UI collects edits for review by default (pending rows, a bar with
Review and Apply now, a Changes page, a switch for live edits); hc stages
with -C ID or HOUSECARL_CHANGESET (checked like JARL_CHANGESET) and has
hc changeset create/list/show/diff/apply/discard/remove. Undo, import and
takeover refuse a changeset. --dry-run shows the ruleset's diff.

Bandwidth and quotas: rate limits in bytes, per source or destination;
quotas per rule or per address that reset daily, weekly, monthly on a
day, or by hand, with their usage carried across reloads, saved for
reboots, shown by hc quotas and on the rules page, and reset with hc rule
reset-quota. Rules that count traffic are checked before established
connections are accepted (so they see every packet) and can't accept.
Per-address limits group IPv6 by /64 by default (--group-v4/-v6).
Per-source rate meters use update. The importer turns meters, byte rates
and quotas into these fields.

The examples in the web UI, hc's help and the docs use documentation
addresses and generic names instead of the maintainer's network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:18:54 +01:00

11 KiB

The hc CLI

hc talks to housecarld's REST API. hc help COMMAND describes each command and its flags.

Connecting

  • On the firewall, with nothing configured, the CLI uses housecarld's local socket, /run/housecarl/housecarl.sock. Root and the housecarl user are administrators there without a token, and their changes need no confirmation: sudo hc status.

  • From elsewhere, give the server's URL and an API token:

    hc config set server https://fw.example.com:8070
    hc config set token          # asks for it
    

    The settings are in ~/.config/housecarl/config.yaml (on macOS ~/Library/Application Support/housecarl/config.yaml, on Windows %AppData%\housecarl\config.yaml).

Flag Environment
-s, --server URL HOUSECARL_SERVER the server; local for the local socket
--token TOKEN or @FILE HOUSECARL_TOKEN the API token
--config FILE HOUSECARL_CLI_CONFIG the CLI's configuration file
HOUSECARL_SOCKET the local socket's path
--insecure, --ca-cert FILE TLS: skip the check, or trust another CA
-n, --dry-run check and show a change without making it, with the ruleset's diff
-C, --changeset ID HOUSECARL_CHANGESET (checked, see Changesets) stage configuration changes in this changeset instead of making them
-o, --output FORMAT table (default), json, yaml or id
-q, --quiet print only errors (and the IDs of new objects)

Exit codes: 0 ok, 1 error, 3 not authenticated or not allowed, 4 not found, 5 conflict, 6 invalid or refused by validation or nftables, 7 server error.

Changes

Every change is checked as a whole, then by nftables, before it is put in force. A refused change says why, with the problem's code (validation.md). An applied one says its history entry and any new warnings. Over the network, it is on probation:

$ hc rule add input --from trusted --service ssh --comment "SSH from home"
created rule input #31 (from trusted · ssh)
applied on probation (history 12): confirm with 'hc confirm' within 2m0s, or it is rolled back
$ hc confirm
confirmed 1 change; it is kept and loaded at boot

Through the socket the change is kept at once:

applied and saved (history 12; undo with 'hc history undo 12')

-n shows what would change without changing it, with the diff of the ruleset; nftables checks it too.

Changesets

To review several changes and apply them together, as one entry in the history, stage them in a changeset:

export HOUSECARL_CHANGESET=$(hc changeset create "guest network" -q)
hc zone add guest -i eth1.30
hc rule add forward --in guest --out wan
hc changeset diff                 # the operations, problems, nftables' check, the ruleset's diff
hc changeset remove 2             # take operation 2 out
hc changeset apply                # one change; over the network on probation
unset HOUSECARL_CHANGESET
$ hc rule add forward --in guest --out wan
changeset 7 (guest network, 1 operation) from HOUSECARL_CHANGESET
staged: create rule forward #52
staged in changeset 7 as operation 2 (2 operations now); review with 'hc changeset diff 7', apply with 'hc changeset apply 7'

Each change is checked as it is staged: the changeset with it must stay valid, and nftables must accept the ruleset, so make a set before the rule that names it. Applying checks the whole changeset again, on the configuration as it is then; an operation whose object changed since is named (operation 3 (update rule input #12) can't be applied: …), and hc changeset remove takes it out.

Command
changeset create [DESCRIPTION] start one; -q prints only its ID
changeset list [--status open|applied|discarded|""] [--mine] open ones by default
changeset show [ID] its operations
changeset diff [ID] what applying it would do
changeset apply [ID] [--description D] apply it
changeset discard [ID] throw it away
changeset remove [ID] OP take one operation out

show, diff, apply, discard and remove work on the changeset you name, else the one in -C, else the one in HOUSECARL_CHANGESET.

A changeset from HOUSECARL_CHANGESET is checked before anything uses it, because an exported ID easily outlives its changeset:

  • Staging into it, and apply, discard and remove on it, need it to be open and yours. Otherwise the command stops and says why (there is no changeset 99, changeset 42 is applied, changeset 57 is sam's). Commands that only read, and hc changeset create, don't care.
  • Each command says on stderr which changeset it uses: changeset 42 (guest network, 2 operations) from HOUSECARL_CHANGESET (not with -q).
  • After apply or discard, a reminder to unset HOUSECARL_CHANGESET.

-C ID and an ID argument are used as given (housecarld still refuses a changeset that isn't open, or another user's unless you are an administrator). Only configuration changes are staged: dynamic set entries, confirm and rollback are made at once, and history undo, import and takeover can't be staged, so they are refused while a changeset is named. Changesets staged in the web UI show up here too, and the other way round.

Commands

Objects are named by ID or, for zones, sets, services and port forwards, by name.

status and the ruleset

Command
status the ruleset (in force or not), changes on probation, problems, interfaces, other tables
interfaces the machine's interfaces, their addresses and zones
ruleset the nftables ruleset Housecarl builds
counters the hits of every rule, NAT rule, built-in and policy, by mark (r12, f3, b-established, p-input)
quotas what each rule's quota used (each address's, for per-address quotas), and when it resets
log [-f] [--search TEXT] [--limit N] the packets logged by rules and policies (operations.md)

zone, set, service

Command
zone list, zone show ZONE
zone add NAME -i IFACE... [--external] [--comment C]
zone update ZONE [-i IFACE]... [--add IFACE] [--remove IFACE] [--external[=false]] [--name N] [--comment C] -i replaces the interfaces
zone delete ZONE refused while rules use it
set list, set show SET
set add NAME [ENTRY...] [--dynamic] [--timeout 24h] [--comment C]
set update SET [--entry E]... [--add E] [--remove E] [--timeout] [--name] [--comment] --entry replaces the entries
set entries SET, set entries add SET ADDRESS [--timeout 1h], set entries remove SET ADDRESS a dynamic set's entries
set delete SET
service list, service show SERVICE with the built-in ones
service add NAME ENTRY... [--comment C] entries tcp/22, udp/1000-2000, icmp/echo-request, icmpv6/packet-too-big
service update SERVICE, service delete SERVICE

See rules.md.

rule

Command
rule list [CHAIN] in order, with their hits
rule show ID
rule add CHAIN [MATCH] [-a ACTION] [--first|--before ID|--after ID] at the end of its chain unless placed
rule update ID [flags] only the flags given
rule move ID --before ID|--after ID|--first|--last|--chain CHAIN
rule enable|disable ID
rule reset-quota ID start a rule's quota again now
rule delete ID

The match flags (rule add, snat add, redirect add):

--in ZONE, --out ZONE (--not-in, --not-out) zones
--from, --to (--not-from, --not-to) sets, addresses, prefixes, ranges
--service services or entries
--family ipv4|ipv6

And for rules: --state, --status, --days, --hours, --limit 10/minute|1mbytes/second[,burst=N][,over][,per-source|,per-destination], --quota 50gbytes[,over][,per-source|,per-destination][,daily|,weekly|,monthly=DAY], --group-v4 N, --group-v6 N, --conn-limit N, -a accept|drop|reject|log, --reject-with tcp-reset, --log, --log-prefix, --raw RULE, --comment, --disabled (rules.md).

forward, snat, redirect

Command
forward list, forward show FORWARD port forwards, with their hits
forward add PORTS --to HOST[:PORT] [--proto tcp,udp] [--public ADDR]... [--from SRC]... [--hairpin] [--hairpin-from SRC]... [--to6 HOST] [--name N] [--comment C]
forward update FORWARD [flags], forward delete FORWARD
snat list, snat add [MATCH] [--snat-to ADDR], snat update ID, snat delete ID source NAT, in order; masquerade without --snat-to
redirect list, redirect add [MATCH] [--to-port PORT], redirect update ID, redirect delete ID

See nat.md.

settings

Command
settings show
settings set [--input-policy|--forward-policy|--output-policy accept|drop] [--log-drops on|off] [--builtin NAME=on|off]... [--own-prefix P]... [--antispoof-v4 P]... [--antispoof-v6 P]... [--flowtable DEV]... [--probation 2m] lists given replace the old ones

probation, history, import

Command
confirm keep the changes on probation; the ruleset is saved for the boot
rollback undo the changes on probation now
history list [--limit N] changes, newest first
history show ID each object before and after
history undo ID apply the change backwards (refused if something it touched has changed since)
import [FILE|-] [--text FILE] [--defines FILE] [--table T] [--replace] import.md
takeover TABLE... [-y] delete tables Housecarl's ruleset replaces (administrators)

cert

Administrators only (certificates.md).

Command
cert the certificate: name, CA, validity, renewal, the last check
cert check check that the challenge can be published through Jarl and seen, without the CA (exit code 1 if it can't)
cert renew [-w] get a new certificate now; -w waits and shows the result

users, tokens, audit

Command
whoami who you are to the server, and what you may do
user list|add|update|delete users (administrators); user add NAME --role operator
token list [--all], token create NAME [--role R] [--grant G]... [--expires 90d] [--user U] [--save], token revoke ID API tokens
token rotate ID|--current [--grace 1h] [--expires 90d|never] [--save] a new secret for a token, the old one working for the grace period
password change your password
audit [--user U] [--action PREFIX] [--search TEXT] the audit log
config show, config set server|token|insecure|ca_cert [VALUE] this CLI's settings

permissions.md explains roles and grants.

housecarld

The daemon's own commands work on the database directly, as root or the housecarl user (sudo -u housecarl housecarld ...):

Command
housecarld serve run (the systemd unit does)
housecarld init [-u NAME] [--token] create the database and the first administrator (asks for the name on a terminal)
housecarld user add NAME [--admin], user password NAME, user token NAME TOKEN-NAME when the API can't be used
housecarld check check the configuration against this machine's interfaces
housecarld backup [FILE], housecarld restore FILE operations.md
housecarld config [--example] the settings in use, or a commented example
housecarld version

All take -c FILE for another configuration file.