# Housecarl's design Housecarl is a firewall manager with the same shape as [Jarl](https://gitea.adbraun.lol/adam/jarl) and [Steward](https://gitea.adbraun.lol/adam/steward): one daemon, `housecarld`, that serves a REST API and a web UI; one CLI, `hc`, that talks to that API; SQLite for everything. As Jarl does for BIND, it keeps a Linux machine's **nftables** ruleset: the database is the source of truth, and the ruleset is built from it. This page is the starting sketch. Nothing here is built yet; it records what was decided and what is still open. ## How a change is made As in Jarl, every change goes through one engine: 1. the request becomes a change to a copy of the configuration; 2. the whole result is validated, with errors that say what is wrong and how to fix it; 3. the ruleset is rendered, and checked with `nft -c -f` (a dry run by nftables itself); 4. it is applied with `nft -f`, which replaces Housecarl's table in one transaction: the old rules stay in force until the new ones are all in; 5. the change, its history entry and the audit record are saved together. Dry runs, history with undo, users, sessions and scoped API tokens come from the same machinery, as in Jarl and Steward. ### Not locking yourself out A firewall managed over the network can cut off its own manager. Housecarl should guard against that from the start: * **Confirm or roll back.** A risky change (one touching the rules for the machine itself) is applied on probation: unless it is confirmed within a minute or two, from the web UI or `hc confirm`, the previous ruleset comes back by itself. Network equipment calls this "commit confirmed". * **A management rule that can't be dropped by accident**: the API and SSH stay reachable from the management network unless that rule is removed on purpose. ## Its own table only Housecarl writes one nftables table of its own (`inet housecarl`) and leaves every other table alone, such as Docker's or libvirt's. It never flushes the whole ruleset. ## Concepts (draft) * **Zones**: interfaces or VLANs grouped by trust (`lan`, `iot`, `guest`, `wan`), and the default policy between each pair. * **Address sets**: named sets of addresses or prefixes, which rules refer to. An entry may have a **timeout**, which nftables enforces by itself, so an entry expires even if housecarld is down. Other programs can be given a token that may only change one set. * **Rules** between zones: allow or drop, by set, port and protocol, with logging and counters. * **NAT**: masquerading out of the WAN zone, and port forwards. * **Services**: names for ports (`ssh`, `https`, `dns`). ## Working with Steward and Jarl * **Steward's captive portal** (parked until Housecarl exists): a guest network's zone lets clients that haven't signed in reach only DNS and the portal, and sends their web traffic to it. When a guest signs in, Steward adds its address to a set (`guest-signed-in`) for the session's length, with a token that may only change that set. Addresses, not MAC addresses: matching by MAC only works on the guests' own network segment. * **Names instead of addresses**: Steward knows every reservation's and lease's address, so rules could name a device ("the printer") or a subnet; Jarl knows the names in DNS. * **Steward's deny lists** could also be enforced here, so a denied device with an address set by hand is still blocked. ## Open questions * IPv6 from the start, or IPv4 first as Steward did? * Import an existing nftables (or iptables) ruleset, as Jarl imports a BIND configuration? * Connection tracking defaults: established and related traffic allowed everywhere unless a rule says otherwise? * Does Housecarl also run the router's interfaces and routes, or only the firewall? (Only the firewall, for now.) * Rate limits and flow offloading: later.