Skip to content

Command line

vxnsin edited this page Sep 2, 2026 · 11 revisions

Command line

warden                 introduce itself and list the commands
warden --version       print the version

warden serve           run the registry
warden tui             open the dashboard

warden run             start a program on a port warden picked
warden env             claim a port and print it as environment
warden register        claim a port and print it
warden get             print the address of one service
warden ls              list every registered service
warden release         give a port back
warden reap            release ports held by something that is gone
warden heartbeat       push a lease out before it runs out
warden pool            show how much of the pool is in use
warden history         what happened to a port, or to a service

warden ports           show what is listening on this machine
warden kill            stop whatever is holding a port

warden nodes           list the wardens this one knows about

warden doctor          check everything and say what is wrong
warden service         start warden with the machine
warden setup           answer a few questions, once
warden settings        show what is in effect, and where it came from
warden update          say whether a newer warden exists, and fetch it

Every command that talks to a warden takes --url / -u and --token, both of which also read WARDEN_URL and WARDEN_TOKEN. Every command that prints a table also takes --json.

Two flags run through the fleet, and they mean different things:

Flag On Means
--all ls, pool, ports, tui Ask every warden, not just this one
--node NAME register, release, heartbeat Do it on that one warden, through this one

warden kill reads the local machine and ignores --url entirely. warden ports does too, unless --all is given, which needs a hub to ask.

--install-completion teaches your shell to complete every one of them; see Installation.


warden run

Start a program on a port warden picked, hold the port while it runs, and give it back when it exits.

$ warden run -- npm run dev
shop-api  ->  8000

  VITE ready, listening on http://localhost:8000

Everything after -- is the command. The child gets PORT, WARDEN_PORT, WARDEN_HOST, WARDEN_ADDRESS and WARDEN_SERVICE added to its environment — PORT because every framework already reads it.

Flag Meaning
--name Register under this name, default the current directory's
--kind, -k What the service is, default service
--project Group services of one codebase
--host Interface the process will bind to
--preferred-port Wish for this port, take another if it is not free
--require-port Insist on this port, and fail if it is not free
--ttl Hold a lease this long, renewed while the process runs
--anyway Start on a free port when no warden answers

Three things it does that a shell wrapper gets wrong:

  • The child's exit code becomes warden's, so nothing swallows a failure.
  • The port is released whether the program exits, crashes or is interrupted.
  • The registration carries the child's process id, which is what later tells a held port from an abandoned one. See warden reap.

With no warden reachable it refuses rather than picking a port itself, because guessing at a port is the thing warden exists to stop. --anyway overrides that, takes a free port from the pool, and says on standard error that nothing is registered.

warden env

The same claim, printed instead of wrapped — for an IDE run configuration, a Makefile, or anything else that cannot be started by warden.

$ warden env shop-api --kind backend
PORT=8000
WARDEN_PORT=8000
WARDEN_HOST=127.0.0.1
WARDEN_ADDRESS=127.0.0.1:8000
WARDEN_SERVICE=shop-api
Flag Meaning
--kind, -k What the service is, default service
--project, --host, --preferred-port, --require-port As warden register
--export Prefix each line with export
--write .env Set those keys in a dotenv file, leaving every other line alone
eval $(warden env shop-api --export)
warden env shop-api --write .env

--write replaces each key where it already stands and appends the rest. A .env is usually somebody else's file too, so nothing warden did not write is touched.

warden serve

Run the registry.

Flag Meaning
--host Interface to listen on
--port Port to listen on
--pool 8000-8999 Range of ports to hand out
--reserved 8080,9000-9010 Ports never handed out
--database Path to the registry database
--no-probe Do not test ports for existing listeners

Fleet settings have no flags; they are environment only. See Configuration.

warden register

Claim a port. Prints the port and nothing else, so it drops into a script.

Flag Meaning
--kind, -k Required. What the service is
--project Group services of one codebase
--host Interface the service will bind to, default 127.0.0.1
--preferred-port Wish for this port, take another if it is not free
--require-port Insist on this port, fail if it is not free
--ttl Release the port again after this many seconds
--pid Process id of the service
--node Register on that warden in the fleet, through this one

--preferred-port and --require-port cannot both be given.

$ warden register shop-api --kind backend --project shop
8000

warden get

Print one service's address.

$ warden get shop-api
127.0.0.1:8000

Given as node/service, it asks that node through the hub:

$ warden get build-01/build-runner --url http://hub:7010
127.0.0.1:9000

Exits 1 with no service registered as 'x' when it is not there.

warden ls

List registered services.

Flag Meaning
--project Only this project
--kind Only this kind
--holders Also say whether each holder is still there
--stale Only the ones whose holder is gone
--all Ask every warden in the fleet, not just this one
$ warden ls --project shop
SERVICE   KIND      PROJECT  ADDRESS         PID
shop-api  backend   shop     127.0.0.1:8000  -
shop-web  frontend  shop     127.0.0.1:8001  -

--all asks every warden in the fleet and adds a NODE column. Nodes that did not answer are named on standard error, so a pipe still gets a clean table. See Cluster.

--holders adds a HOLDER column saying whether whoever asked for each port is still there:

$ warden ls --holders
SERVICE   KIND     ADDRESS         PID    HOLDER
shop-api  backend  127.0.0.1:8000  14204  running
old-job   worker   127.0.0.1:8001  9930   gone

A holder is gone when the process it named no longer exists, or when nothing is listening on its port. It costs one sweep of the machine's sockets, which is why it is asked for rather than always done, and it cannot be combined with --all: only the machine a service runs on can see whether it is still there.

A registration touched in the last 30 seconds is never called gone. A service that has registered and not yet bound its socket is starting, not dead.

warden release

Give a port back to the pool.

$ warden release shop-web
released shop-web

--node build-01 releases it on that warden instead, through the one you asked.

warden reap

Release the ports of services that are no longer there.

$ warden reap
release old-job? nothing is on 8001 and pid 9930 is gone [y/N]: y
released 1

It asks per service and names why each one qualifies. --yes skips the questions, for a machine that runs it on a schedule.

Nothing is ever reclaimed on its own. A service in the middle of a restart would lose its port to a timer, and a registry that takes ports back without being asked is worse than one holding a few dead rows.

warden heartbeat

Push a lease out before it runs out, for a service registered with a --ttl.

$ warden heartbeat ci-runner --ttl 600
127.0.0.1:9000
Flag Meaning
--ttl Seconds from now; without it, the lease it registered with
--pid Update the recorded process id at the same time
--node Do it on that warden in the fleet

warden pool

$ warden pool
8000-8999  2 allocated  997 free  1 reserved

Counts only ports inside the pool. A service on a --require-port outside the range is registered but not counted here.

--all reports every node and the fleet totals, with FREE coloured by how little is left — the point of the view being to spot the machine about to run out:

$ warden pool --all --url http://hub:7010
NODE      POOL       HELD  FREE  RESERVED
build-01  9000-9099  4     96    1
hub       8000-8999  2     997   1
web-02    9000-9099  98    2     1

The totals are a sum of what is left, never one pool the fleet shares: two nodes may hand out the same numbers on different machines.

warden history

What happened to a port, kept after it stopped being true.

$ warden history 8000
WHEN    WHAT        SERVICE   KIND     ADDRESS         PID
7s ago  released    shop-api  backend  127.0.0.1:8000  14204
2h ago  registered  shop-api  backend  127.0.0.1:8000  14204
Argument Meaning
what A port, or a service name. Everything recent without one
Flag Meaning
--limit How many to show, default 20

Every registration, renewal, move, release and expiry is written down as it happens, so warden history 8000 still answers for a service released weeks ago. Rows are dropped past a cap, so a warden left running for a year does not grow a database nobody asked for.

warden ports

What is listening on this machine. Needs no server.

Flag Meaning
--port Only this port
--udp / --no-udp Include UDP sockets, on by default
--all Ask every warden in the fleet, and add a NODE column

The WARDEN column names the service when the port came from the registry, which needs a warden to be reachable. Without one the column is simply blank; the rest still works.

See Ports and processes.

warden kill

Stop whatever is holding a port. Needs no server.

Argument Meaning
target A port, or a process id with --pid
Flag Meaning
--pid Read the number as a process id
--force Kill it outright if it will not stop politely
--yes, -y Do not ask first

warden nodes

List the wardens this one knows about.

Flag Meaning
--forget NAME Remove a node that is not coming back
$ warden nodes --url http://hub:7010
NODE      URL                   POOL       VERSION  STATUS  LAST SEEN
build-01  http://build-01:7010  9000-9099  0.1.0    online  4s ago

warden tui

The dashboard. --interval sets the refresh in seconds, default 2. --all shows the whole fleet instead of one warden.

Key Action
j k Move
tab Switch between services and ports
n Step through one node at a time, with --all
r Reload now
d Release the service, or stop the process
q Quit

The ports view works with no warden running at all: it falls back to reading the machine directly, says so in the subtitle, and d stops a process the same way warden kill does. The services view still needs a registry.

Exit codes

0 on success, 1 on any refusal, with the reason on standard error. The message is the same one the API would return, so a script can act on it:

if ! PORT=$(warden register shop-api --kind backend 2>/dev/null); then
    echo "no port available" >&2
    exit 1
fi

warden doctor

Everything worth checking, in one command, so "why is this not working" does not mean running four others and comparing what they said.

$ warden doctor
ok    warden 0.1.0 answering at http://127.0.0.1:7010, role hub
ok    settings from ~/.config/warden/warden.toml
warn  listening on 0.0.0.0 with no token set - anyone who can reach this
      machine can hand out and release ports
ok    pool 8000-8999, 4 held, 995 free
warn  2 of 6 registrations held by something that is gone - warden reap
ok    build-01 online, last seen 4s ago
note  could not check for updates - github is unreachable
Level Means
ok Checked, nothing wrong
note Worth knowing, or a check that could not run at all
warn Works, but somebody should look at it
fail Broken

Exits 1 only when something failed, and 0 on warnings, so it drops into a health check without an unset token being read as the machine being down.

warden service

Have this machine start warden serve at login.

$ warden service install
systemd user unit
  ~/.config/systemd/user/warden.service
  [Unit]
  Description=warden - hands out ports and says who holds them
  ...
  systemctl --user daemon-reload
  systemctl --user enable --now warden.service
Write it? [y/N]:
Command Does
warden service install Show what it would write, then write it
warden service uninstall Take it away again
warden service status Say whether it is installed, and whether it is up

--yes on install and uninstall skips the question.

System What gets written
Linux A systemd user unit in ~/.config/systemd/user
macOS A launchd agent in ~/Library/LaunchAgents
Windows A command in the Startup folder

Always as the account that ran it, never as root or SYSTEM: a warden started by another user reads that user's settings and hands out ports from a registry nobody else can see.

On Windows it is a Startup command rather than a scheduled task, because creating a task is refused outright on plenty of managed machines. That also means there is no service manager to ask, so status says running when a warden is answering on the configured port, whoever started it.

warden setup

Asks the handful of questions that matter and writes the answers to the config file. Enter accepts every default, and it keeps any setting it did not ask about. See Configuration.

warden settings

Every setting, its value, and which source that value came from.

warden settings                    # the table
warden settings --json             # the same as data
warden settings set pool_end 4199  # write one to the config file
warden settings unset probe        # take it back out

set types and checks the value before writing it, so the file never holds something warden would refuse to start with, and it says so when the environment will override what you just wrote anyway.

Clone this wiki locally