Files
housecarl/docs/cli.md
T
abrown38andClaude Opus 5.5 a45c893830 Connections, "every packet" rules, and a packet tracer
- Connections: the kernel's tracked connections, read and closed over
  ctnetlink (internal/conntrack, raw netlink like the drop log), with
  their NAT and TCP state; closing one makes the rules check its next
  packet again (GET /connections, POST /connections/close; hc
  connections; Traffic → Connections).
- Rules can check every packet (every_packet, --every-packet): before
  established connections are accepted, so a drop or reject also stops
  connections already open. They can't accept.
- A packet tracer (internal/trace): a table of its own, inet
  housecarl-trace, marks the packets a filter picks, `nft monitor trace`
  reports every rule and policy they meet in every table, and the tracer
  names what accepted or dropped them, Housecarl's rules by name (POST,
  GET, DELETE /trace; hc trace; Traffic → Trace). One trace at a time,
  at most 200 packets and 20 a second; the table goes when it ends.

e2e: 157 checks, among them an upload a normal block doesn't stop and an
every-packet one does, and a trace naming the blocking rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 20:22:34 +01:00

12 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
--no-confirm leave a change on probation instead of confirming it once hc reaches Housecarl again
-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, and hc confirms it by reaching Housecarl again over a new connection (operations.md):

$ hc rule add input --from trusted --service ssh --comment "SSH from home"
created rule input #31 (from trusted · ssh)
applied and confirmed (history 12): this client reached Housecarl again over a new connection

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; history undo is staged like any change; 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)
connections [--search TEXT] [--limit N], connections close --search TEXT [-y] tracked connections; closing makes the rules check them again (operations.md)
trace [--address A] [--port N] [--protocol P] [--seconds S] follow packets through every table's rules (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, --every-packet, -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] [--auto-confirm on|off] 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.