Skip to content

Updates

vxnsin edited this page Sep 7, 2026 · 3 revisions

Updates

warden can tell you when a newer version exists, and a hub can ask every machine in a fleet to go and fetch it.

Is there a newer one, and what would fetch it?

$ warden update
warden 0.4.0 is out, this is 0.3.0
https://github.com/vxnsin/warden/releases/tag/v0.4.0

installed as uv tool - ~/.local/share/uv/tools/warden-ports
to update: uv tool upgrade warden-ports
`warden update --apply` runs it here.

A running warden asks GitHub for the newest release every six hours and keeps the answer in memory, so the command returns instantly and nothing waits on the network. If it has no answer it says why rather than guessing:

0.1.0; vxnsin/warden has published no releases yet

Nothing is ever compared to a version that is not a version: a tag like nightly is ignored, and a pre-release is not treated as newer than the release it precedes.

Turn the check off entirely with WARDEN_UPDATE_CHECK=false. It is the only thing warden sends anywhere, it fails silently when the machine is offline, and it never blocks a command.

How it knows the command

From where the process actually lives. Guessing wrong between uv tool upgrade and pip install -U installs a second copy beside the one you are running, and then a version number stops meaning anything.

Where warden is What it says
uv/tools/warden-ports uv tool upgrade warden-ports
pipx/venvs/warden-ports pipx upgrade warden-ports
a project .venv with a uv.lock beside it uv sync --upgrade-package warden-ports
a project .venv without one <that interpreter> -m pip install --upgrade warden-ports
a checkout git pull && uv sync — printed, never run
a container docker pull ghcr.io/vxnsin/warden:latest — printed, never run
anywhere else <this interpreter> -m pip install --upgrade warden-ports

The last two are advice rather than commands warden will run: one is two commands and a shell, the other has to happen outside the container it is about. warden update --apply prints them and stops.

warden doctor says the same thing, so a machine that is behind explains itself without being asked twice.

Installed under the wrong name

warden publishes as warden-portswarden on PyPI is an unrelated project. A copy installed as warden is told to replace itself rather than upgrade:

installed as `warden`, which is a different project on PyPI; this one
publishes as `warden-ports`
to update: uv tool uninstall warden && uv tool install warden-ports

Upgrading that name would fetch somebody else's program. Replacing it also clears a stale copy shadowing the new one on PATH, which is how a machine ends up reporting a version it stopped running weeks ago.

Updating one machine

$ warden update --apply
Update the warden at http://127.0.0.1:7010? [y/N]: y
Successfully installed warden-ports-0.4.0

This runs the command that machine would print — or the one it is configured with, if WARDEN_UPDATE_COMMAND is set. What is written down always wins: somebody who wrote a command knows something about the machine that no amount of looking would find.

Updating a fleet

From the hub:

$ warden update --fleet --yes --url http://hub:7010
NODE      RESULT   DETAIL
build-01  updated  Successfully installed warden-0.2.0
db-03     refused  updating over the API is switched off - set
                   WARDEN_ALLOW_REMOTE_UPDATE=true on this warden to allow it
hub       updated  Successfully installed warden-0.2.0
web-02    updated  Successfully installed warden-0.2.0

Every node is asked and every answer is reported. One machine refusing, or being unreachable, does not stop the others and does not fail the command — you get a row per node saying what happened to it.

The hub sends an intent, never a command

This is the part worth being careful about, so it is worth stating plainly.

POST /v1/update means "update yourself". It carries no payload. What updating means on a given machine comes from that machine's own WARDEN_UPDATE_COMMAND and from nowhere else.

The alternative — letting the hub send a command to run — would make warden a way to execute anything, anywhere in the fleet. One leaked cluster token would then be worth every machine it can reach. As it stands, the worst a leaked token can do is ask machines to run the update they were already configured to run.

Two switches have to be on before anything happens at all, and both live on the machine being asked:

Setting Without it
WARDEN_ALLOW_REMOTE_UPDATE=true 403, "updating over the API is switched off"
WARDEN_UPDATE_COMMAND only where the machine has no single command of its own - a checkout, a container

WARDEN_ALLOW_REMOTE_UPDATE defaults to off, so a warden you have not deliberately configured for this refuses and says so. WARDEN_UPDATE_COMMAND is no longer required: a machine that knows how it was installed knows what to run. One where that is not a single command still has to be told.

Choosing the command

It runs without a shell, so it is one program with arguments rather than a pipeline. Put anything longer in a script and point at the script.

Installed from PyPI, which is the usual case:

WARDEN_UPDATE_COMMAND="/usr/local/bin/update-warden.sh"
#!/bin/sh
set -e
uv tool upgrade warden-ports
systemctl --user restart warden

From a checkout, if the machine runs your own copy:

#!/bin/sh
set -e
cd /opt/warden
git pull --ff-only
uv tool install . --force
systemctl --user restart warden

The restart is your job. warden does not restart itself: it cannot report the result of a command that ends the process running it. Whatever you configure should finish by restarting the service, and the row you get back is the output of the command up to that point.

Note also that the machine has to be able to reach wherever it pulls from. A node that can talk to the hub but not to your git host will refuse with whatever git said, which is the point of passing the output back.

Settings

Variable Default Meaning
WARDEN_UPDATE_CHECK true Ask GitHub whether a newer release exists
WARDEN_UPDATE_REPO vxnsin/warden Which repository to ask about
WARDEN_UPDATE_INTERVAL 21600 Seconds between checks, at least 300
WARDEN_ALLOW_REMOTE_UPDATE false Let a caller ask this warden to update itself
WARDEN_UPDATE_COMMAND empty What updating means here, when how it was installed is not enough

Endpoints

Method Path Token Purpose
GET /v1/update either Whether a newer warden exists
POST /v1/update either Ask this warden to update itself
POST /v1/fleet/update API Ask every warden in the fleet to update itself

"Either" means the API token or the cluster token: a hub does this on its rounds, an operator does it by hand, and WARDEN_ALLOW_REMOTE_UPDATE is the gate that actually decides.

Clone this wiki locally