Files
steward/docs/cli.md
T
abrown38andClaude Opus 5.5 3ad2f981d4 Allow and deny lists per subnet; every option of a DHCP message in words
- A subnet is a deny-list subnet (the default: everyone not denied) or
  an allow-list subnet (only clients allowed or with a reservation);
  the deny list counts in both. Entries are MAC addresses, prefixes
  (aa:bb:cc:*) or client IDs. A refused client gets no answer (DENIED
  in the activity log, with an Allow button) or a NAK on renewal.
  steward access, the subnet's Access tab. Schema migration 7.
- The activity log keeps each message's header fields and options,
  decoded into words (dhcp4.Describe): the parameter request list by
  name, client IDs, lease times. ▸ on an Activity row, steward
  activity show N / --details, GET /events/{id}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 20:27:51 +01:00

207 lines
9.4 KiB
Markdown

# The steward CLI
`steward` talks to stewardd's REST API. `steward help COMMAND` describes
each command and its flags.
## Connecting
* **On the server**, with nothing configured, the CLI uses stewardd's
local socket, `/run/steward/steward.sock`. Root and the `steward` user
are administrators there without a token: `sudo steward status`.
* **From elsewhere**, give the server's URL and an API token:
```sh
steward config set server https://dhcp.example.com:8067
steward config set token # asks for it
```
The settings are in `~/.config/steward/config.yaml` (on macOS
`~/Library/Application Support/steward/config.yaml`, on Windows
`%AppData%\steward\config.yaml`).
| Flag | Environment | |
|---|---|---|
| `-s, --server URL` | `STEWARD_SERVER` | the server; `local` for the local socket |
| `--token TOKEN` or `@FILE` | `STEWARD_TOKEN` | the API token |
| `--config FILE` | `STEWARD_CLI_CONFIG` | the CLI's configuration file |
| | `STEWARD_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 |
| `-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, 7 server error.
## Changes
Every change is checked as a whole first. A refused change says why and
gives the problem's code ([validation.md](validation.md)). A saved one
says its history entry, and any new warnings:
```
$ steward subnet add 10.0.20.0/24 --interface eth0.20 --name iot --pool 10.0.20.100-10.0.20.200
created subnet iot (10.0.20.0/24)
saved (history 2; undo with 'steward history undo 2')
warning: subnet iot (10.0.20.0/24): this server has no interface eth0.20 (it has eth0, lo); the subnet is served once it appears [interface_unknown]
```
`-n` shows what would change without changing it.
## Commands
### status
`steward status`: the version, each interface and whether it is served
(and why not), each subnet's pool use, leases by state, DHCP messages
since the start, and problems.
### subnet
| Command | |
|---|---|
| `subnet list` | subnets, with interface, pools, use, reservations, lease time |
| `subnet show SUBNET` | one subnet; SUBNET is its ID, name or prefix |
| `subnet add PREFIX --interface IFACE [--name N] [--pool A-B[:ORDER]]... [--pool-order listed\|random] [--address-order sequential\|reverse\|random] [--lease-time 12h] [--option NAME=VALUE]... [--comment C]` | add one |
| `subnet update SUBNET [--name] [--interface] [--prefix] [--pool]... [--add-pool] [--remove-pool] [--pool-order] [--address-order ...\|default] [--lease-time 1h\|default] [--option NAME=VALUE]... [--unset-option NAME]... [--comment]` | change what is given; `--add-pool` of a pool it has changes that pool's order ([subnets.md](subnets.md#address-order)) |
| `subnet enable\|disable SUBNET` | answer its clients, or not |
| `subnet delete SUBNET [-y]` | delete it and its reservations |
`--pool` replaces the pools; `--add-pool` and `--remove-pool` change one.
`--option` sets an option and keeps the others; `--unset-option` removes
one. An option Steward doesn't know: `--option 224:string=hello`.
### reservation
| Command | |
|---|---|
| `reservation list [--subnet SUBNET]` | reservations, whether each client is online, and when it last answered a ping |
| `reservation show RES` | RES is its ID, address or MAC address |
| `reservation add IP MAC [--hostname H] [--client-id HEX] [--lease-time 7d] [--option NAME=VALUE]... [--comment C]` | `-` for MAC with `--client-id`; with both, either matches |
| `reservation update RES [--ip] [--mac] [--client-id] [--hostname] [--lease-time 7d\|default] [--option]... [--unset-option]... [--comment]` | |
| `reservation enable\|disable RES` | use a reservation, or keep it but ignore it |
| `reservation delete RES` | |
### access
| Command | |
|---|---|
| `access show SUBNET` | the subnet's mode and its allow and deny lists |
| `access mode SUBNET deny-list\|allow-list` | answer every client not denied, or only the allowed (and reserved) |
| `access allow SUBNET ENTRY... [--comment C]` | add to the allow list (`aa:bb:cc:dd:ee:ff`, `aa:bb:cc:*` or `id HEX`); an entry it has gets the new comment |
| `access deny SUBNET ENTRY... [--comment C]` | add to the deny list |
| `access remove SUBNET ENTRY...` | take off both lists |
See [subnets.md](subnets.md#access-allow-and-deny-lists).
### lease
| Command | |
|---|---|
| `lease list [--subnet S] [--state STATE] [--interface I] [--search TEXT] [-a]` | active and held-back leases; `-a` all |
| `lease show IP` | one lease |
| `lease reserve IP [--hostname H] [--by mac\|client-id\|both]` | make the address its client's reservation, by its MAC address and client ID (either matches) unless `--by` says one |
| `lease delete IP [-y]` | free the address at once |
### activity
`steward activity [--mac MAC] [--subnet SUBNET] [--interface I] [--limit N] [-d] [-f]`:
the latest DHCP messages, received and sent, oldest first. `--subnet`
takes an ID, name or prefix; a message without an address (a DISCOVER,
"no address") shows for every subnet on its interface. `-f` keeps
watching, which is useful while a device tries to get an address:
```
#1042 11:02:13.120 eth0 DHCPDISCOVER 3c:22:fb:11:22:33 - adams-macbook
#1043 11:02:13.622 eth0 DHCPOFFER 3c:22:fb:11:22:33 10.0.10.150 adams-macbook
#1044 11:02:13.630 eth0 DHCPREQUEST 3c:22:fb:11:22:33 10.0.10.150 adams-macbook
#1045 11:02:13.631 eth0 DHCPACK 3c:22:fb:11:22:33 10.0.10.150 adams-macbook
```
Each line starts with the message's number. `-d` (`--details`) adds every
field and option of each message in words, and `steward activity show N`
shows one: what the client asked for (its parameter request list, by
name), its client identifier and host name, and what Steward sent back
(lease time, routers, DNS servers...):
```
$ steward activity show 1042
#1042 DHCPDISCOVER on eth0 at 2026-10-18 11:02:13.120, received from the client
transaction ID: 0x3a1c99f0
client hardware address: 3c:22:fb:11:22:33
flags: broadcast (the client wants replies broadcast)
option 53 message-type: DHCPDISCOVER
option 61 client-identifier: 01:3c:22:fb:11:22:33 (type 1: the MAC address 3c:22:fb:11:22:33)
option 55 parameter-request-list: subnet-mask (1), routers (3), domain-name-servers (6), domain-name (15), v4-captive-portal (114)
option 12 host-name: adams-macbook
```
In the web UI, ▸ on an Activity row opens the same.
`NO ADDRESS` means the pools on that interface are full, and `CONFLICT`
an address that answered the ping check. A `DHCPNAK` says why it was
sent.
### settings and options
| Command | |
|---|---|
| `settings show` | |
| `settings set [--lease-time 1d] [--ping-check on\|off] [--ping-timeout 500ms] [--decline-hold 24h] [--authoritative on\|off] [--address-order sequential\|reverse\|random] [--option NAME=VALUE]... [--unset-option NAME]...` | |
| `option list` | the options Steward knows ([options.md](options.md)) |
| `interface list` | the server's interfaces, their addresses and subnets (not those in `interfaces.exclude`) |
### history
| Command | |
|---|---|
| `history list [--limit N]` | changes, newest first |
| `history show ID` | each object's fields before and after |
| `history undo ID` | apply the change backwards (refused if something it touched has changed since) |
### import
`steward import kea CONFIG [--leases FILE]... [--interface PREFIX=IFACE]... [--replace]`:
see [kea.md](kea.md).
### cert
Administrators only ([certificates.md](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 (fails with 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](permissions.md) explains roles and grants.
## stewardd
The daemon's own commands work on the database directly, as root or the
`steward` user (`sudo -u steward stewardd ...`):
| Command | |
|---|---|
| `stewardd serve` | run (the systemd unit does) |
| `stewardd init [-u NAME] [--token]` | create the database and the first administrator (asks for the name on a terminal) |
| `stewardd user add NAME [--admin]`, `user password NAME`, `user token NAME TOKEN-NAME` | when the API can't be used |
| `stewardd check` | check the configuration against this machine's interfaces |
| `stewardd backup [FILE]`, `stewardd restore FILE` | [operations.md](operations.md) |
| `stewardd config [--example]` | the settings in use, or a commented example |
| `stewardd version` | |
All take `-c FILE` for another configuration file.