Updates and versioning
Hostwarden uses semantic versioning. The
current version is in the VERSION file; CHANGELOG.md lists what
that release changed, changes not released yet are in changelog.d/.
Each release is a signed tag vX.Y.Z with a GitHub release titled
Hostwarden X.Y.Z, whose notes link to that version's section of
CHANGELOG.md at the tag; an earlier release's notes are read there,
since CHANGELOG.md holds only its own release. A checkout updates
from the tag; the source archives GitHub lists with every release are
no way to install, since a copy without git cannot update
(Installation).
Which updates a checkout takes
From the first release of 1.0.0 or later on, an operations checkout
follows the major line of the newest release: it checks out release
tags and moves to each new one on that line, never to main in
between. A checkout that was on main and never chose anything
settles on that line with its next update, and keeps it when the
next major is released. Before any such release exists, it follows
main. A development checkout never settles on a line by itself.
Release signatures
Every release tag from v1.0.0 on is an annotated tag signed with Hostwarden's release key, an SSH key used for nothing else:
release@hostwarden namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA0cMXwoEAtpFv+0ptBS/Za6djrMigZ6P6NIUZq8YHI7
Its fingerprint is
SHA256:Ut0GmoL9jbqB/BRdlNZmyjdakDDLKsR1Y/OcU8l8nmk. The same line
is in .github/release-signers, which the release workflow checks
each tag against before it pushes it. To check a tag yourself, save the
lines above to a file outside the checkout and run:
git -c gpg.ssh.allowedSignersFile=<that file> verify-tag vX.Y.Z
GitHub shows each release tag as verified by the GitHub account
hostwarden-release, which holds the key for that alone. That is a
courtesy: verify-tag against the line on this page is the check
that counts.
Take the lines from this page, not from the checkout: a file in the
checkout proves nothing about the tag that brought it. A key retired
in the normal course stays listed, bounded with valid-before, so
an older release still verifies; a key that leaked is removed.
An operations checkout does this itself before every checkout of a
release tag, with --follow, --pin and every update on a line.
The key it trusts is the .github/release-signers of the version it
is on, copied aside before anything is fetched, never the file of the
tag it checks; a checkout on main settling on its first release
line uses main's. Each release in between that verifies hands its
own file on, so a new key listed by a release the old key signed is
trusted from there on, even where the update skips that release. A
tag that does not verify, or carries another release's name, is
refused: the update says so and stops, and the checkout stays where
it is, main included. --check names such a tag too. Verifying
needs git 2.34 or later, and an ssh-keygen from OpenSSH 8.7 or
later as the program git verifies with: gpg.ssh.program in git's
configuration, ssh-keygen on the PATH unless set. What ssh -V
reports does not count, since that can be another binary.
bin/hostwarden-doctor checks both, the program by having it verify
a signature of its own. After a refusal, compare the
checkout's .github/release-signers with the lines above. Where
they differ, as after a leaked key was replaced, check the tag by
hand with the lines from this page, as above, and only once it
verifies, git checkout vX.Y.Z; from there on the updater trusts
the new key. A development checkout verifies nothing.
Automatic updates
On every session start in an operations checkout, a hook (Claude
Code) moves to the newest release on the line — or, on main, runs
git pull — and reports version changes: for every release between
the old version and the new one, the lead clause of each entry, read
from the CHANGELOG.md at that release's tag. The session shows
that list and asks whether to go on before it does any server work;
the full text is there on request. A run no one answers, such as
claude -p or bin/hostwarden-fleet-run, asks nothing and goes on;
the fleet run writes the list to the timer's log. No action needed.
Auto-update is skipped in a development checkout, when pinned to a
tag (see below), when on a non-main branch,
or when HOSTWARDEN_NO_UPDATE=1 is set.
Updating by hand
With OpenCode or any other tool:
bin/hostwarden-update # update
bin/hostwarden-update --check # check without updating
Choosing a release line
bin/hostwarden-update --follow 1 # every 1.x.y release
bin/hostwarden-update --follow 1.2 # 1.2.x fixes only
The choice is kept in this checkout's own git config
(hostwarden.follow), so each machine chooses its own. An update
writes it only once, when a checkout that never chose settles on its
line. The auto-update checks out the highest vX.Y.Z tag on the
line; pre-releases do not count. A line only moves up: an update
never checks out a release older than the version the checkout is
on, unless that release descends from it, as 1.0.0 descends from the
main that still carried Heinzel's 2.22.0. A mirror that withholds
the newer releases therefore stops the update instead of taking the
checkout back to an older release; the refusal names the --pin
that goes back on purpose. Only the newest release gets fixes
(SECURITY.md):
once a release outside the line is out, every update and --check
say so, and name the line to follow next and where to read what it
changes, or, where that release does not verify, that an update
refuses it.
Following main
To test what is not released yet:
bin/hostwarden-update --unpin # follow main
The checkout then pulls main on every update, and stays there.
After each pull, bin/hostwarden-update says how the changes no
release has yet moved, under "On main, not released yet": the lead
clause of each entry that is new or changed in changelog.d/ or
under ## Unreleased in CHANGELOG.md, and each one no longer
listed: withdrawn, reworded under another lead clause, or only on
the branch it left.
Pinning a version
To skip auto-updates for a stable version:
bin/hostwarden-update --pin vX.Y.Z # pin
A pin replaces a release line or main; --follow or --unpin
leaves it again.
Turning auto-update off
Without pinning:
export HOSTWARDEN_NO_UPDATE=1
In the desktop app, set it in the env of
.claude/settings.local.json instead — see
Claude Code Desktop.