Operate
Backup & updates
Keeping a copy of your Omnesis data, getting it back, and moving every machine to a new
release: omnesis backup, restore and export,
omnesis update on one machine or the whole fleet, Docker and source installs,
and how the gateway judges which device versions it still supports. Commands run on the
gateway machine unless a section says otherwise.
Backup
omnesis backup takes a snapshot while the gateway keeps running, into
~/.config/omnesis/backups/<timestamp>/ on the gateway machine. It holds
the databases (documents, index, analytics), omnesis.json,
OMNESIS.md, tokens, config secrets, TLS certificates and a manifest.
--note labels a backup, omnesis backup list shows the ones you
have, and --no-index leaves out index.db, the largest file,
which the gateway can rebuild from the documents.
❯ omnesis backup --note before-cleanup
Backup started… ✔ Backup complete: 8 files, 812.4 MB omnesis.db.enc 611.3 MB index.db.enc 182.0 MB analytics.db.enc 18.4 MB … Saved on the gateway host at ~/.config/omnesis/backups/2026-03-14T09-12-05
A backup leaves out four things:
- Models, which can be downloaded again.
-
Agent conversations (
conversations/): copy that directory yourself if you want to keep your chat history. - OAuth sign-ins (Google, Microsoft and similar): sources that use them ask you to sign in again after a restore. Pasted API keys are kept, because a platform that shows a key once cannot show it again.
- The root key of an encrypted install. With encryption at rest on, the backup's files are encrypted with that key, so keep the recovery code as well: without the key or the code, an encrypted backup cannot be opened.
omnesis update takes no backup by default. Add --backup to
request a pre-update backup on the gateway host. After each successful backup, Omnesis
keeps the newest two such pre-update backups and deletes older ones; backups you took
yourself are never deleted. Set backupRetention.preUpdateCount to keep a
different number, or 0 to keep them all.
Restore and export
omnesis restore <backup-dir> puts a backup back into a configuration
directory, ~/.config/omnesis/ or the one --target names. It
replaces what a backup holds: the databases, omnesis.json,
OMNESIS.md, tokens, config secrets, pasted API keys, sweeps, TLS certificates
and the recovery envelope. An item of that kind the backup does not hold is removed, so an
index the backup was taken without does not survive beside the restored documents; the
gateway rebuilds it, and the search graph, at its next start. Everything else in the
directory stays as it was: other backups, models, exports, logs, the keyring passphrase
file and the keyring's own entries, and a Docker install's Compose file. In
.env, the image tag, the keyring settings and the gateway port stay this
machine's.
Restoring over an existing corpus needs --force. The restore unpacks the
backup inside the directory first and moves it into place only once every file is there,
so a failed restore leaves the directory as it was. It refuses to start while a gateway
holds the directory.
An encrypted backup needs its root key. On the same machine, restore uses the key already
in the keyring. On a new machine, pass the recovery code with --code (or on
standard input) and restore rebuilds the key from it. The databases come back plaintext
and the gateway encrypts them again at its next start.
On a source or package install, stop the services around the restore:
❯ omnesis backup --list
❯ omnesis service stop
❯ omnesis restore <backup-dir> --force
❯ omnesis service start
On a Docker install
On a Docker install, run the same command on the host. The omnesis command
there stops the containers, runs the restore in a one-off container of the gateway's image
with the gateway's mounts and environment, starts the containers again and waits for the
gateway to report healthy. A restore that fails leaves the directory as it was, and the
containers are started again all the same.
❯ omnesis backup --list
❯ omnesis restore <backup-dir>
At a terminal it lists what it will do and asks before replacing a corpus. Without one, as
under a script or an agent, it asks nothing and refuses unless --force is
given; its progress goes to standard error, so --json leaves standard output
with the restore's result alone. It exits non-zero if the gateway does not report healthy
within ten minutes; OMNESIS_RESTORE_HEALTH_TIMEOUT sets another limit in
seconds. A backup outside the configuration directory is mounted read-only for the
restore. --target does not apply: a Docker install restores into its own
directory. A host that only collects has no corpus to restore.
omnesis service start, stop, restart,
status and logs run their
docker compose equivalents on a Docker install. The
omnesis command from an older release sends restore and
service into the container instead, which refuses them and names the host
command; omnesis update replaces it.
On a new machine
Install Omnesis on the new machine the way the old one was installed, with encryption at
rest if the backup is encrypted, and keep the configuration directory at the same path.
Copy the backup directory into ~/.config/omnesis/backups/, then restore it
with the recovery code. On a Docker install, the omnesis command on the host
stops and starts the containers itself; on another install, stop the services first and
start them after, as above.
❯ omnesis restore ~/.config/omnesis/backups/2026-03-14T09-12-05 --force --code <recovery code>
The new machine keeps its own keyring passphrase and seals the restored root key with it; the recovery code keeps working. Devices and tokens come back with the backup, so collectors and phones reconnect once the gateway's address reaches the new machine. The backup's certificates come back too: a certificate that names the old machine does not cover a new name, and a device that reaches the gateway by a new name pairs again.
Export
omnesis export writes your documents to
~/.config/omnesis/exports/<timestamp>/, as JSON lines by default or as
CSV, which leaves out the full text and adds one file per analytics table. It is for
taking your data elsewhere, not for restoring. On an encrypted install the export is
encrypted too; decrypt it with
omnesis keyring decrypt-artifact <export-dir> on a machine with the
same root key before using it outside Omnesis.
Updating
Source updates check the target checkout's Node requirement before changing the checkout
or stopping services. If the installed Node version is unsupported, upgrade Node, ensure
node and npm select it on PATH, then rerun
omnesis update. Package installations need only Node 24 or newer.
omnesis update moves a machine to the newest stable release and restarts what
runs there. It works the same for a source or Docker install, and Omnesis never starts it
on its own. Run it on the gateway machine first, then on each other machine, or add
--fleet to have the gateway ask the other machines to run it (see
Updating every machine).
❯ omnesis update
# or, for an unattended rollout
❯ omnesis update --yes
On a machine that runs the gateway, it does the following, in order:
- Installbuild the release, or pull the images
- Restart the gatewayits data migrations run; they only go forward
-
Wait for health
/healthmust report the new version - Restart the collectoronly now, so it never meets a half-migrated gateway
- Refresh agent harnessupdate the plugin; ask before restarting it
If it fails before health
- Automaticthe previous release is put back and restarted
Code returns; migrations that already ran do not.
Restoring an existing backup
- Stopcollector, then gateway
- Restorea backup you previously created
- Startgateway, then collector
By hand, with the commands under Restore.
--backup adds one before any irreversible changes, as described
under How the updater works.
A machine that only collects has nothing to back up: its collector simply restarts on the
new release. A machine with a connected agent harness (OpenClaw or Hermes) refreshes the
installed plugin, then asks before restarting the harness, since that interrupts whatever
the agent is doing; --yes answers that prompt too. If the harness's
authorization has lapsed, the update prints the command to renew it and leaves the harness
alone. omnesis devices list, omnesis doctor and the portal's
Devices page show any plugin refresh or restart still owed, with the command that finishes
it.
If it fails
A build that does not compile, or a gateway that does not come back healthy, returns the machine to the release it was running and restarts it there. Ctrl+C during the install or the health wait does the same once the running step has stopped. Once the gateway serves the new release nothing is undone; a collector the update did not reach is reported with the command to restart it.
The health wait lasts up to ten minutes, so a long migration can finish. A gateway whose process has exited does not use that time: when the service manager (systemd or launchd) has relaunched it twice during the wait, or has held no gateway process for 15 seconds, the update rolls back at once. A gateway that is still running keeps the whole wait.
Rolling back the code does not roll back the data. If the new gateway got far enough to run its migrations, the previous release keeps running on the migrated database. Sources keep their sync positions and continue where they left off, unless the newer version changed how the gateway reads them; only those sources sync again from the start. An existing backup can restore the data state it captured; see Restore.
An older release does not know the settings a newer one added, and drops them from
omnesis.json the next time it changes the configuration, for example when you
add a source or assign a model. So before a rollback or a move to an older release, the
update saves the configuration beside it as omnesis.json.newer-settings. When
a release that knows those settings next starts its gateway, it puts back each one the
configuration lacks, logs which, and removes the saved copy. A setting you changed on the
older release keeps your change. A setting the older release knew and you removed there is
also put back, because the gateway cannot tell it from one that release dropped.
If the update was cut short by a power loss or a forced kill, run
omnesis update again: it notices the unfinished run, says what it will do,
and completes or repeats it.
On a machine with less than about 5 GB of memory in total (or a container limited to
less), a source build (which needs at least 3 GB for itself and about 2 GB beside it)
stops this account's collector and gateway while it builds. The gateway is down until the
build is in place, and the update's own restart brings it up on the new release; the
collector starts again afterwards. If the build is still killed, the update says the
machine most likely ran out of memory and rolls back. Stop the collector and gateway
(omnesis service stop collector, omnesis service stop gateway),
add swap or free memory, and run omnesis update again.
On a source install, npm ci installs over the node_modules the
running release left. If that install fails, the update removes
node_modules and installs once more before it rolls back, and the rollback's
own install is retried the same way. If dependency installation is interrupted, killed, or
still fails after its retry, rollback removes the incomplete dependency tree before
reinstalling the previous release. If removing that tree fails, recovery stops and reports
the failed step.
On Intel macOS, dependency installation also verifies the native vector engine and builds it locally when the bundled binary is incompatible. This applies to fresh installations, updates and rollback reinstalls. Keep Xcode Command Line Tools installed; see Requirements.
Options
| Flag | Effect |
|---|---|
--yes |
Answers every prompt, including harness restarts. Needed in scripts. |
--dry-run |
Prints the plan without changing anything. |
--target-version X.Y.Z |
Moves to one exact release instead of the newest. |
--commit <full-sha> |
Source installs only: fetches that exact 40-character commit from the checkout's
origin, for controlled testing between releases. With --fleet it is
offered only to paired machines verified as source installs; Docker installs and
phone apps are left alone. The portal's update action always targets releases.
|
--edge |
Follows the main branch instead of stable releases (source installs).
A Docker install refuses it.
|
--no-restart |
Installs, then reports which daemons still run the previous release. |
--backup |
Takes a pre-update backup on the gateway host. Off by default. |
--no-backup |
Accepted for compatibility; has no effect. |
--health-timeout <seconds> |
Allows a long migration more than the default ten minutes. |
--wait-for-lock=<minutes> |
Waits for an update already running on this machine instead of refusing to start. |
--allow-rewind |
Source installs only: allows a target that is not a newer release and does not
contain the build installed now, such as going back from an exact-commit test
build. The commits it drops are listed. With --fleet, it is passed to
each machine commanded at that moment.
|
--force |
Allows moving to an older release, and implies --allow-rewind. There
are no down-migrations. Sources keep their sync positions, except where the older
release predates a change in how the gateway reads them; those sources sync again
from the start. Settings it does not know are saved for the next upgrade, as
described under If it fails. An OpenClaw or Hermes
plugin that has followed a certificate renewal stops connecting when moved back to
a release whose plugin pins the certificate; afterwards, run
omnesis connect <harness> --refresh --trust-fingerprint
sha256:<served>
on its machine, with the fingerprint omnesis tls status shows on the
gateway machine.
|
--fleet |
Afterwards, asks the gateway to update the other machines. |
Unless you pass --edge, the update installs only stable release tags. It
refuses a source checkout it did not install or adopt, a
checkout with local changes, and a tag whose contents do not match its name.
A source install that was moved to an exact commit, for example an unmerged test build,
updates to the next release normally: a newer release is a forward update even when it was
cut without that commit, and the update lists the commits it leaves behind. A target at
the same or an older version that does not contain the installed build is refused unless
you pass --allow-rewind.
The same rule covers a repository whose history was replaced, so that the target shares no
commits with the installed build: a newer version is a forward update, and the update says
the history was replaced instead of listing commits. This holds for stable and edge
installs alike. When the installer itself moves an existing checkout, as it does with
--reconfigure, it applies the same rules, with --force in place
of --allow-rewind.
Running the install command again on a machine it already set up is an update: the
installer finds the checkout the earlier install recorded, makes sure it can fetch from
its origin, and runs omnesis update on the machine, so the update backs up,
rebuilds, refreshes the service definitions, restarts and rolls back exactly as described
above. The certificate, keyring, embedding model and pairing stay as they are, so a
collector does not pair again. A plain re-run updates any machine that runs a gateway or a
collector; to turn a collector machine into a gateway, pass --reconfigure.
The full install also runs for --collector or --client-only on a
machine with another role, for a first install that never registered its services, and for
a flag that sets one of the kept choices, such as --code,
--mkcert or --embedder.
Several collectors, one source
Update the gateway first, then every collector. A collector newer than its gateway reports
Gateway upgrade required and waits. If several collectors host the same
source, update all of them: until they are all on the new release, the older ones may be
unable to sync that source or change its settings, and the sync status of each collector
that needs updating says so. Sources hosted only by older collectors keep working.
Release notices
The gateway checks for a newer stable release shortly after it starts and every 24 hours,
asking the source checkout's origin or the container registry, according to how it was
installed. Clients do not check. A newer release shows as a quiet line in the portal
sidebar and in omnesis doctor; failures say nothing. The check never
downloads or installs anything. What each release changes is in the
release notes and the
changelog.
It does tell that Git host or registry the gateway's network address. Omnesis adds no
install identifier, corpus data or version information to the request. Turn it off with
omnesis config set /releaseCheck false.
Daemons the update cannot restart
The update restarts only services that belong to the account running it. A daemon started
by hand, a hardened gateway, or a named instance is
reported as still running the previous release, with the command to restart it when the
machine knows it. A collector is restarted only once its own gateway is serving the new
release; if the gateway could not be restarted, the collector is reported instead. A
hardened gateway is updated with its own root
command, which omnesis update prints.
How the updater works
The update runs in two halves. The release already installed finds the target and puts
it in place. Everything after that (an optional backup when it can wait, the restarts,
the health wait, the harness refresh and any rollback) is run by the newly installed
release, so a fix to those steps applies to the update that delivers it. Only one update
runs on a machine at a time; a second is refused with the running process and its
current step, or waits with --wait-for-lock. The lock of a killed update is
recovered automatically after a short interval.
With --backup, a gateway that runs from the installation being updated is
backed up before the install. A gateway in a container or on a hardened release keeps
serving its current build during the install and is backed up after it, before anything
restarts. A gateway that is not running is backed up by copying its closed databases
into the same backups folder.
Before restarting a daemon, the update rewrites its launchd plist or systemd unit to
what the new release's omnesis service install would write, keeping its
executable, configuration directory, keyring wiring and environment. A unit it cannot
rewrite without losing something (a systemd drop-in, EnvironmentFile=, a
comment, or a setting service install does not write) is left alone and
reported; a hardened gateway's unit and named instances are never rewritten.
A source install writes a completion record once its build succeeds, and a Docker
install once the gateway answers with the new image; the next run uses it to decide
whether an interrupted update must be repeated. A source build sizes its memory from the
machine as the installer does, and never overrides a heap size you set in
NODE_OPTIONS.
Updating every machine
omnesis update --fleet on the gateway machine updates that machine, waits up
to 90 seconds for the collectors and agent harnesses that were connected to reconnect, and
then asks each one to run omnesis update on itself. You confirm each machine
after seeing its current and target version; --yes confirms them all. It then
waits, up to 45 minutes, for each commanded machine to report: updated, restart still
owed, or failed with its reason. The command exits with an error when any machine failed
or never answered, so a machine that refused cannot pass for one that updated.
# on the gateway machine
❯ omnesis update --fleet
No code travels over the connection. A device receives only a version number, runs its own update against its own release source, and refuses a version that is not a real release there, so a tampered gateway cannot make a machine run code of someone else's choosing. A device is only ever sent the version the gateway is serving, so it can never be moved past its gateway; if the gateway could not be restarted, the fleet step stops and says so. A device that is offline keeps the request and runs it when it next connects.
A collector restarts on the new release when it finishes, through its service or, when it was started some other way, by exiting for its supervisor to restart. A machine with an agent harness installs the new plugin and restarts the harness. OpenClaw restarts at once, interrupting any agent run in progress. OpenClaw restarts even when its running gateway saved the new plugin but could not load it; the restart is what loads it. Hermes lets running work finish for up to its drain timeout (three minutes by default) first. When the harness cannot be restarted from there, its device row records the restart still owed and the command to run.
--dry-run cannot be combined with --fleet, because the fleet
step depends on what the gateway serves after this machine moves; preview this machine
with omnesis update --dry-run. --edge cannot be combined with it
either, since a branch build has no release number to send.
The gateway will not command these devices, and says which ones to update yourself:
- Phones and the browser extension, whose builds are installed outside the gateway (see Getting the apps).
-
A device that has never reported its version, or one below the oldest
release the gateway supports (the unknown and unsupported states under
Version compatibility). Run
omnesis updateon that machine. - A machine with no Omnesis daemon, such as a paired CLI, a portal browser or an external agent integration.
- A revoked device, until it is paired again.
-
A device whose build cannot take the command. This is not a failure:
omnesis devices list, the portal andomnesis doctorname what to run on that machine, which for an agent harness isomnesis update, thenomnesis connect hermes --refresh(oropenclaw) and the harness's own restart.
From the portal
The portal's Devices page offers the same actions: Update on one device and Update all for the fleet, with the same version comparison and confirmation. Each device's card shows where its update stands: waiting to be sent, running, failed, installed but still needing a restart, or to be updated on its own machine. A failure or restart notice stays until the device reconnects on the requested release or a newer one.
When the gateway's release notice reports a newer version, Update fleet
updates the gateway itself first, then the devices. The confirmation names the gateway's
version change and lists which devices will be updated and which you must handle
separately. The portal may be unreachable briefly while the gateway restarts, and picks
the progress up again when it is back; no device is sent anything until
/health reports the confirmed version.
Update fleet works for source installs running as the standard user
service. For named instances, hand-started or hardened gateways and Docker installs, the
button explains why it is disabled; run omnesis update --fleet on the gateway
machine instead.
External MCP clients keep their own sign-in, which an ordinary update does not affect. If
a release's notes say otherwise, reconnect them after the update, and run
omnesis connect openclaw --refresh or
omnesis connect hermes --refresh for those harnesses.
Docker installs
A machine installed with install.sh --docker updates with the same
omnesis update and the same steps. Installing means bumping the image tag
recorded beside the Compose file and pulling the new images, the updater's included,
quietly: one line for the pull, and Docker's own errors if it fails. With
--backup, the backup runs after the pull, while the gateway still serves the
previous image. Rolling back is quick: the previous tag is written back, and its image is
still on the machine. Once the gateway answers on the new release and the collector has
restarted onto it, the update removes the previous release's images of this install, about
a gigabyte each; it never removes another image. One that a container still uses, such as
the updater image the update itself started from, is kept and removed by the next update,
or by the next run of the installer if that comes first. An image it cannot remove is
reported with the docker image rm command that does, and never fails the
update. Re-running the installer onto another release does the same once its gateway
serves: the previous release's images of this install go, and one a container still uses
stays. --target-version pins a release tag. A Docker install follows releases
only, so it refuses --edge before anything changes.
To run an exact commit in Docker, a maintainer publishes its gateway, collector and
updater images with the repository's docker-publish workflow, which tags them
sha-<short-commit>; there is no combined image for a commit. Pin that
tag on a host that runs the gateway and collector as separate containers, then pull and
recreate them:
❯ sed -i.bak 's/^OMNESIS_IMAGE_TAG=.*/OMNESIS_IMAGE_TAG=sha-0123abc/' ~/.config/omnesis/.env
❯ docker compose -f ~/.config/omnesis/docker-compose.yml --profile update pull
❯ docker compose -f ~/.config/omnesis/docker-compose.yml up -d --force-recreate
A commit image can carry migrations no release has.
The update runs in a short-lived updater container that holds the Docker socket for that
one run. The socket gives root-equivalent control of the host, which is why the
long-running gateway and collector containers never hold it. If a run is interrupted, run
omnesis update again; it checks what the gateway actually serves before
trusting the recorded tag. A host that only collects has no gateway to ask, so there it
checks the tag Docker started the collector container from.
The updater runs as your account and joins the group that owns the Docker socket as a
container sees it. Where the daemon runs in a virtual machine, as on a Mac, or rootless,
that group differs from the one the socket has on the host, so the installer asks a
container which group it is. Before changing anything, the update checks that its
container can reach the daemon. When it cannot, it stops, says nothing was changed, and
writes the socket's group into the Compose file's updater; run
omnesis update again. When it cannot correct the Compose file, it prints the
installer line to run once, with the options the install was made with, before
omnesis update.
The omnesis command on the machine is the one part of a Docker install that
no image carries, so once the containers run the new release, the update also replaces
that command with the new release's, through a one-shot container that mounts only its
directory. It replaces only the file the installer wrote for this install. An install made
before the location was recorded is found at ~/.local/bin/omnesis when its
configuration directory is ~/.config/omnesis; otherwise the update says to
run the installer again with the options you installed with.
Stable Docker updates select the newest release tag present in every image controlled by
OMNESIS_IMAGE_TAG in the Compose file, including the updater. This lookup
uses the container registry rather than npm, so a release does not need npm packages.
Independently versioned sidecars do not participate in release discovery. The combined
image is updated through its gateway service, preserving the mounted config
directory and paired collector identity. An updater image from before registry discovery
looks the release up on npm instead and stops with
Could not resolve omnesis@latest; name the release once with
omnesis update --target-version X.Y.Z, and later updates find it on their
own.
Registry tag discovery is anonymous. For a private registry, authenticate Docker on the
host and rerun the installer with --docker --version X.Y.Z and the same
topology flags, config directory and image repository. A one-shot updater cannot use the
host's credential helper through the Docker socket alone. If you explicitly mount a
self-contained Docker credential configuration into the updater, you can instead use
omnesis update --target-version X.Y.Z; keep that credential mount out of the
running daemons. For a registry addressed as localhost, use Linux host networking or an
explicit target version; bridge-network registry discovery reaches the updater container’s
loopback, rather than the host’s. Standalone Compose installations update with a pinned
OMNESIS_IMAGE_TAG, docker compose pull, then
docker compose up -d.
Adopting a hand-made source install
A source checkout that you cloned and built by hand has no marker saying the installer
owns it, so omnesis update refuses to touch it. If that checkout is an
installation rather than a development tree, adopt it from the repository root, then run
the first update through npm run cli; after that,
omnesis update works as usual.
❯ npm run cli -- update adopt-source
# then follow stable releases
❯ npm run cli -- update
# or follow the main branch instead
❯ npm run cli -- update --edge
Adoption requires a standalone clone of the official repository with a single official
origin, a detached HEAD, the full history and tree, and no local
changes. It then checks with the remote that HEAD is a stable release tag or
part of main. A linked worktree, local branch, fork, shallow clone or
checkout that changes during the check is refused with the reason. Adoption only records
the marker; the first update then rebuilds the checkout.
To update a checkout by hand instead, fetch the exact release tag, detach at it, install, build, restart the gateway, confirm it reports the new version, and only then restart the collector:
❯ git fetch origin refs/tags/vX.Y.Z:refs/tags/vX.Y.Z
❯ git checkout --detach vX.Y.Z
❯ npm ci
❯ npm run build
❯ npm run cli -- service restart gateway
❯ npm run cli -- status
❯ npm run cli -- service restart collector
To follow main by hand, fetch it with
git fetch origin refs/heads/main:refs/remotes/origin/main and detach at
origin/main, then build and restart in the same order. If a fetch fails,
stop: comparing against a stale origin/main can report nothing to update
while the remote is unreachable.
Version compatibility
Every Omnesis release and app build shares one product version, and each device reports
its own when it connects; the browser extension, which holds no connection open, reports
it with its uploads and periodic checks. The gateway compares it with its own version and
with the oldest build it still supports for that kind of device.
omnesis devices list, the portal's Devices page and
omnesis doctor all show the same verdict:
| State | Meaning | What to do |
|---|---|---|
| current | On the gateway's version or newer. | Nothing. |
| behind | Older than the gateway but still supported. Normal: a phone build or a collector on another machine often lags. | Update when convenient; omnesis doctor does not fail. |
| unsupported | Older than the oldest build the gateway supports, or speaking a connection protocol it no longer speaks. |
Update the device. The portal flags it and omnesis doctor fails.
|
| unknown | The device reported no version, or one that is not a version number. | Nothing; the device keeps working. Update it by hand when you update. |
A command-line token, such as the one the installer creates, reports a version only while
it holds a connection open, as omnesis status --watch does. Until it has
reported one it has no state: omnesis devices list shows —, and
the device counts in omnesis status and omnesis doctor leave it
out.
A fleet update leaves unknown and unsupported devices to you, because the gateway will not tell a build it cannot identify, or one below its floor, to replace itself. Reporting a version is optional, and a device is never refused for leaving it out.
What the gateway does refuse is a device speaking a different connection protocol: both sides exchange a protocol number when they connect. The browser extension checks the gateway's version before pairing and refuses only a gateway older than the oldest one its page capture works with; a newer extension on an older gateway keeps capturing pages and simply does not offer what that gateway lacks. Within a minor release, what the gateway accepts only grows, which is why the gateway is always updated first. A CLI that has not been updated yet keeps working against it: a model backend, certificate source or other value newer than the CLI is shown by its name instead of stopping the command.
GET /health needs no token and returns the gateway's version and a
compat block with the connection and pairing protocol numbers and the
database schema version, enough for a client to decide whether it can talk to this
gateway. GET /admin/compat, with an admin token, returns the full picture:
every store and how it is versioned, every protocol and the versions it accepts, and the
oldest supported build per device kind.