Using VyreDraft
Box care
Your box is the always-on server that runs Vyre. On a Linux server it is a Docker Compose stack in
/srv/vyre with the vyre container, and its data in Docker volumes. You look
after it from your Mac with vyre box ..., which runs each step over SSH so you never open a shell
on the server, or on the server itself with the host's vyre command. This page covers the care
tasks. What each folder and volume holds, and who can reach the box, is in
The box and the Mac.
Check on it
On this Mac
vyre box # the box's address and SSH target, and whether it answers from here
You should see
your box https://alex.vyre.run · [email protected]
answering · 0.0.1
On a server
vyre status # running or not, version, role, how many modules started
vyre modules # each module, and the error of any that failed
You should see
vyred running · 0.0.1 · box · pid 7 · up 5312s
17 modules running
On the server, the host's vyre is a small shell script, /usr/local/bin/vyre. It runs every
command in the vyre container, except three it handles itself: vyre up starts the stack if it
is not running, waits up to a minute for Vyre, then prints the setup link or your address;
vyre update upgrades (below); vyre logs follows Vyre's output.
If this happens
vyre boxsays "no box yet: vyre box add user@host"The
vyre boxcommands work only for a box this Mac knows the SSH target of. If you installed the box with the curl installer on the server, runvyre box add [email protected]once: it finds Vyre already there and carries on from where it stands.
If this happens
vyre boxsays "not answering from here"This Mac cannot reach your box, or the box is down. Check the box with
vyre statuson the server, and runvyre doctoron the Mac for the path and the relay.
Read its logs
On the box:
vyre logs # follow Vyre's output (docker compose logs -f vyre)
Vyre also writes a log file per day, ~/.vyre/logs/YYYY-MM-DD.log. On a Docker box that is
/home/vyre/.vyre/logs/ inside the vyre_vyre-home volume.
Cap what it spends
Vyre keeps one ledger of what sessions, agents and memory spend, per provider and per day (UTC). Some figures are estimates, which the list marks, because they are tokens times a price rather than a cost the provider reported. A daily cap per provider stops the spending without asking you on every call.
vyre spend # today's spend per provider against its cap
vyre spend raise claude 20 # cap Claude at $20 a day
vyre spend raise claude +5 # add $5 to the cap
vyre spend raise all 50 # one cap over every provider together
vyre spend raise claude off # no cap
At the cap, the thread that was spending is paused with one line that says what happened and the command to raise the cap, and memory answers from facts and search until the next UTC day or until you raise it. A provider with no cap has none. The Vyre app shows the same list under Settings, Spending limits, with Change cap for each one.
Upgrade
On this Mac
-
Run:
vyre box updateIt runs
vyre updateon the server over SSH, then compares versions. -
If the box is now newer than your Mac, it says so and prints the command that upgrades the Mac:
You should see
the box runs 0.0.2, newer than this Mac's 0.0.1: npm install -g https://vyre.run/box/vyre.tgz && vyre up
On a server
vyre update
vyre update brings the box to the newest release. It asks GitHub Releases for vyre-ai/vyre:
stable (the default) is the newest release that is not a prerelease, and beta is the newest of
either. A stable box never takes a prerelease, such as 0.2.0-rc.1: only a box you set to beta
would. Pick one with vyre update --channel beta or VYRE_CHANNEL=beta, or a release by version
with --to 0.2.0. While GitHub has no release yet, or when you set VYRE_BOX_URL yourself, it
uses the site instead (https://vyre.run/box/ by default), as it always has.
To look without changing anything, run vyre update --check on the server. It says which version
an update would install and from which channel, and whether the server already runs it. It exits 0
when the server is current and 1 when a newer release waits. vyre update --check --json prints one
line, { "current", "channel", "latest", "update", "source" }. It downloads nothing but the release
list, and writes nothing. When the server already runs the newest release, vyre update says "already
on" that version and stops: it does not restart the server. vyre update --reinstall puts the same
version on again anyway.
Vyre looks for a newer release once a day and keeps the answer, so Settings can say "Update available" and show what changed. On a server set up by the install line, Settings also has an Update to 0.x button. It does not run anything itself: it drops a one-line request into /var/lib/vyre-update/request, a folder that only Vyre can write to and that root owns along with every folder above it, and a small systemd unit on the host (vyre-update.path, owned by root) runs vyre update for it. That update never accepts --allow-unsigned, never goes back to an older version than the newest this server has had (a version the host keeps in a file only root writes), waits at least ten minutes between updates, backs up your data first, and puts the old version back on its own if the new one does not start. The card shows each step and the result. The command above always works too, and a server with no systemd shows only the command.
Turn on Update automatically in Settings (off by default) and Vyre asks for a new release by itself between 2 and 5 in the morning, once per version, the same way. The channel is the host's own: set VYRE_CHANNEL=beta in the server's .env, or leave it on stable. Set auto to "off" inside the update object in config.json to stop the daily look. On a Mac there is no update button: run vyre update (see Update Vyre on a Mac, below).
Updates are signed. Every file comes from the release and is checked against its SHA256SUMS
before anything on the box changes. The SHA256SUMS list itself is checked against its Ed25519
signature (SHA256SUMS.sig) from Vyre's release key, which is built into the vyre command. An
unsigned or badly signed release is refused, and vyre update --allow-unsigned installs it anyway
after a plain warning that nothing proves it came from Vyre. The same holds for vyre update on
a Mac. A box that pulls its image instead of building it also checks every Vyre image of the
release with cosign against the release workflow's identity, and refuses to pull one it cannot
check. Then, in order:
- It backs up the database with
vyre backup, into/home/vyre/.vyre/backups/pre-<version>.tar.gzin the container, with a copy in/srv/vyre/backups/that only you can read. It is sealed like anyvyre backupfile and holds the sealed vault, so treat it like the vault. - It tags the image that runs now as
vyre:prev, and keeps the box files as they are in/srv/vyre/box.prev/. - It refreshes the box files (
compose.yml,compose.build.yml,vyre.env.example,Dockerfile,dockerignore) from the release..envandvyre.envare never touched. - When
/srv/vyre/.envlistscompose.build.ymlinCOMPOSE_FILEandVYRE_SOURCEis/srv/vyre/src, it swaps in the newvyre.tgzand keeps the old one as/srv/vyre/src.prev, then rebuilds the image with fresh base images. Otherwise it pulls the new image. - It recreates what changed and waits up to a minute for Vyre. A server that has not answered yet but is still working (its log moved in the last 20 seconds) is waited for, up to ten minutes in all, since setting up the Records types after a restart can take that long.
If Vyre does not come up in that time, or goes quiet without answering, the update undoes itself: the old source, the old image,
the old box files and the database from step 1 all go back, and vyre update exits 1 saying it
rolled back. Store migrations only go forward, which is why the database comes back from the
backup. That means anything written between the backup and the rollback (a chat, a note, a
setting) is lost, and the update says so as it rolls back. A slow start that is still working is
waited for, so a rollback should be rare; if one happens, check vyre logs before you try again.
The automatic updater also keeps a floor, the newest version this server has had, and refuses anything older. After an update that rolled back following its publication, the floor can sit above the version that runs; that blocks only older releases, never the forward update. sudo vyre raise-floor raises it to the version installed here after an update you ran yourself, and takes no version. Once an update has come up healthy, nothing restores the database on its own, so nothing
you write after that is lost.
After a healthy update, when the release carries an Android build, it brings the phone app along:
it checks the APK and android.json against SHA256SUMS, copies the APK into /home/vyre/.vyre/releases/android/,
then android.json last (the old one stays as android.json.prev), and runs
vyre call releases.sign. If signing refuses (no_release or release_mismatch), the update
says so and still succeeds. A release without an Android build leaves the folder alone. Last, it
replaces the vyre command itself with the release's copy, and prints what vyre up prints.
Your volumes carry over, and each module migrates its own data at start (Specification, Section 7.1).
To go back by hand:
vyre update --rollback
It puts the previous source, image, box files and phone app manifest back, and keeps the database as it is now. Run it again to go forward. To put the database from before the update back too:
vyre update --rollback --restore-data
That drops everything written since the update, so it says what it would drop and asks you to
type restore. Off a terminal, pass --yes instead.
The image carries the Claude Agent SDK that Vyre's own sessions run on, with the Claude Code it
bundles, in /opt/vyre-sessions-sdk (ADR 0030). The box never
downloads it at runtime. Its version is pinned as VERSION in core/sessions/sdk-pin.js: a bump there
rebuilds the image, and vyre update brings it in like any other change.
vyre update refreshes the box files and the wrapper. Running the installer again does too, and
it also leaves .env and vyre.env alone:
curl -fsSL https://vyre.run/install.sh | sh
Update Vyre on a Mac
On a Mac, or any machine where you installed Vyre with npm, vyre update updates Vyre itself:
vyre update --check # is a newer release out? exit 0 when current, 1 when one waits
vyre update # show what changed, then install it
You should see
0.1.0 → 0.2.0 · stable
0.2.0
what 0.2.0 changed
Update to 0.2.0 now? (y/N) y
updated 0.1.0 → 0.2.0 · vyred answering
It reads the releases of vyre-ai/vyre on GitHub and shows the notes of every release between
the version you run and the new one. When you say yes it:
- backs up your data into
~/.vyre/backups/pre-<version>/; - downloads the release into
~/.vyre/releases/<version>/and checks every file against the release'sSHA256SUMS, so a damaged or wrong download stops it before anything changes; - installs it with
npm install -g, restarts Vyre the wayvyre updoes after an upgrade, and waits for Vyre to report the new version.
If Vyre does not come back on the new version, vyre update puts the previous version back,
restores the backup, and says it rolled back. Once the new version has answered, it never touches
your data again on its own.
| Option | Does |
|---|---|
--channel stable or --channel beta | which releases to follow; the default is stable, or channel inside the update object in config.json |
--to <version> | a given release, when an update says to step through one first |
--yes | installs without asking; needed when there is no terminal to ask on |
--rollback | puts the previous release back and keeps your current data |
--rollback --restore-data | also puts back the data from before the last update |
--json | one line of JSON; --check --json prints { current, latest, channel } |
--restore-data drops everything written since that backup, so it says the backup's date and
asks you to type restore. With --json, or with no terminal, it needs --yes instead.
~/.vyre/releases keeps the two newest releases and the one you run.
If this happens
"this vyre runs from a checkout; update it with git"
You run Vyre from a clone of the repository, not an npm install. Run
git pull, thenvyre up.
If this happens
"0.3.0 updates only from 0.2.0 or newer"
A release sometimes needs an older one in between. Run the command it names, for example
vyre update --to 0.2.0, thenvyre updateagain.
On a box, the host's vyre update (above) does the job; inside the box's container,
vyre update says so and stops.
Run the installer by hand
vyre box add from the Mac runs the installer for you. On the server you can run it yourself:
curl -fsSL https://vyre.run/install.sh | sh
It is scripts/install-box.sh. It is safe to run again: every run converges on the same state.
Pass options after sh -s --, for example curl -fsSL https://vyre.run/install.sh | sh -s -- --dry-run.
| Option | Does |
|---|---|
--dry-run | prints every change and makes none (read-only checks still run) |
--yes, -y | answers yes to every question |
--from DIR | uses the box files in a local checkout and builds the image from it |
--print-link | ends with only two lines on stdout for a program to read, VYRE_LINK with the setup link and, when there is one, VYRE_SSH with the ssh -L line; everything else goes to stderr |
--uninstall | stops the stack and removes /usr/local/bin/vyre; the volumes stay |
--purge | with --uninstall: also deletes the volumes, after listing them and asking |
| Variable | Default | Does |
|---|---|---|
VYRE_DIR | /srv/vyre | where the stack goes |
VYRE_BOX_URL | https://vyre.run/box/ | where the box files come from |
VYRE_IMAGE | ghcr.io/vyre-ai/vyre:latest | the image to pull |
VYRE_BUILD=tgz | builds from vyre.tgz even when the image can be pulled | |
VYRE_LINK_ONLY=1 | the same as --print-link | |
VYRE_NO_UP=1 | installs everything but does not start it |
What it does, in order:
- Checks this is Linux. On a Mac it prints a hint and stops.
- Checks for Docker with Compose 2.24 or newer. If Docker is missing it asks before running
curl -fsSL https://get.docker.com | sh; with no terminal to ask on, or a no, it prints the command and stops. An old Compose gets the same kind of message and a stop. - Picks how to get the image: it pulls
VYRE_IMAGEwhen the registry has it, and otherwise builds fromvyre.tgz. - Downloads
SHA256SUMS, thencompose.yml,compose.build.yml,vyre.env.example, the host wrapper and, for a build,vyre.tgz, and checks each against its line. A missing line or a different hash stops the install before anything is written. With--from DIRthe files come from the checkout instead. - Creates
/srv/vyre, owned by you (the account that ransudo, if you did), writes the box files into it, and unpacksvyre.tgzinto/srv/vyre/srcfor a build. - Writes
/srv/vyre/.env(mode 0600) withCOMPOSE_PROJECT_NAME=vyre,COMPOSE_FILE(plusVYRE_SOURCEfor a build) andDOCKER_GID, the group that owns the Docker socket. It writes the file only if it is not there. The one change it makes to an existing.envis to addDOCKER_GIDwhen the line is missing. - Installs the host wrapper to
/usr/local/bin/vyre. If something else already has that name, it asks first. - Runs
vyre up, orvyre up --print-linkwith--print-link.
sudo is used only for Docker's own install, for /srv/vyre when /srv is root's, and for
/usr/local/bin/vyre. If your account cannot reach Docker, the installer runs the stack through
sudo and says so; from then on the host's commands need sudo vyre.
Why Why not just add me to the docker group?
Membership of the docker group saves typing sudo vyre, and makes your account
root-equivalent on that server: anyone who can talk to Docker can start a container that mounts
/. The installer leaves that choice to you. vyre box add offers it in its plan, because
later steps over SSH cannot type a sudo password.
If this happens
"Docker is installed but not running."
Start it with
sudo systemctl start docker, then run the installer again.
If this happens
"checksum mismatch for ..." or "SHA256SUMS is not a checksum list"
Nothing was installed. The download was damaged, or
VYRE_BOX_URLpoints somewhere that is not a Vyre release. UnsetVYRE_BOX_URLand try again.
Back up
There are two kinds of backup.
Vyre's own data, taken while Vyre runs, on the box:
vyre backup # vyre-backup-YYYY-MM-DD.vyre in /home/vyre, asks for a passphrase
It asks for a passphrase twice (12 characters or more) and seals the file with it: there is no
unencrypted backup, and the file opens only with that passphrase. It holds config.json, a
consistent copy of the store (taken with SQLite's VACUUM INTO while Vyre writes), vault/,
watchers/, modules/, certs/, names/ and data/ (your artifacts), the Drive of every Space on the box (your documents and each project's files, still sealed), plus your project files
(the box's /work) and your session transcripts. Add --skip-projects if your projects live in
git or Drive, and --skip-transcripts to leave the transcripts out. It leaves out models/,
logs/, the socket and the pid file, and your Claude, Codex and Grok sign-ins: sign in again after
a restore, or pass --with-provider-logins to carry them. An unfinished backup resumes when you run
the same command again. The file lands in /home/vyre, inside the vyre_vyre-home volume. Copy
it off the box:
cd /srv/vyre && docker compose cp vyre:/home/vyre/vyre-backup-2026-09-27.vyre .
The Space's members, grants and sealed values come back too, once you have given your recovery code a single time (the spaces.bundle.enrol tool, or ask your assistant to set up the Space bundle). From then on Vyre writes space-bundles/<space>.vyb into the home by itself, every hour, with no further asking, and every vyre backup carries the folder. Every Space on the box has its own bundle and its own owner's code: a team's Space is turned on by its owner (Settings, Backups), and until then that screen says plainly "Space Harbor isn't backed up: its owner hasn't turned on backups." A bundle holds the Space's id, its members and grants, a copy of its store (for a team's Space), and each sealed value (an SSN, a bank number) sealed under a key that only that owner's recovery code opens. None of the old box's own keys is in it, and nothing in it is readable without the code.
On a fresh box, restore the backup and the Space in one step:
vyre restore vyre-backup-2026-10-10.vyre --force --recovery-code <your code>
If the box holds Spaces with different owners, vyre restore asks for each owner's code in turn (Enter leaves one for later), brings back every Space it has a code for, and names the rest.
The Space keeps its id, so every link and record reference still works. Its sealed values are sealed again under the new box's own keys. Your members are back with their roles, and the log says the Space was restored and from what point. What a member sealed on their own device (their vault, their chats' keys) is carried as ciphertext, never re-keyed: it opens again when that person's device next connects to the restored Space. The old log itself is not carried, only where it ended.
Everything, Claude Code's sign-in and transcripts, and your projects in /work, from the Mac:
vyre box backup # vyre-box-backup-YYYY-MM-DD.tar.gz here
vyre box backup ~/Backups/box.tar.gz
The box is stopped while this runs: it stops the stack, copies the vyre-home and vyre-work
volumes into one file on your Mac (mode 0600), and starts the stack again, even
if the copy fails or you press Control-C. --force replaces an existing file.
Both files contain your sealed vault. Keep them somewhere only you can read. The vyre backup
file is already sealed with its passphrase; encrypt the vyre box backup file yourself
(age -r <key> file).
Restore
A vyre backup file goes back with Vyre stopped. Vyre is the container's main process, so
restore runs in a one-off container:
cd /srv/vyre
docker compose cp vyre-backup-2026-09-27.vyre vyre:/home/vyre/
docker compose stop vyre
docker compose run --rm vyre vyre restore /home/vyre/vyre-backup-2026-09-27.vyre --force
vyre up
Restore asks for the backup's passphrase. Without --force, it refuses to replace a store the box already has. It also refuses an
archive with paths outside the known folders, or with links in it.
The backup carries the artifacts your agents made (data/artifacts: every version, a dashboard's
data, a deck's images, and the archive and 30-day undo state). A restore swaps them in by moving the
box's current artifacts folder aside first and deleting it only once the new one is in place, so a
failed restore keeps what was there. If the power fails between those two steps, a folder named
.artifacts.old-<number> is left under data/; it is safe to delete once you have checked that
your artifacts are there. Public links come back as they were when the backup was made: a link that
was on then is on again.
A vyre box backup file holds the two volumes as folders (vyre-home/ and
vyre-work/). To put it on a server that has no Vyre volumes yet, install without starting,
create the volumes the way Compose would, unpack into them, then start:
curl -fsSL https://vyre.run/install.sh | VYRE_NO_UP=1 sh
for v in vyre-home vyre-work; do
docker volume create --label run.vyre=1 --label com.docker.compose.project=vyre \
--label com.docker.compose.volume=$v vyre_$v
done
docker run --rm -i -v vyre_vyre-home:/b/vyre-home -v vyre_vyre-work:/b/vyre-work \
alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc tar xzf - -C /b < vyre-box-backup-2026-09-27.tar.gz
vyre up
Start the old server's stack again only after you have taken Vyre off it: two boxes with
one identity fight over it. To go from one live server to another, vyre box move (next) does all
of this for you.
Move to another server
From the Mac:
vyre box move [email protected]
It installs Vyre on the new server, stops the old stack, streams the two volumes from old to new through your Mac, starts the new one, and waits for it to answer at the same name and address. Once the new box answers, Vyre is taken off the old server, whose volumes stay until you delete them.
The new server must not already hold a box or Vyre volumes. If anything fails after the old stack stops, the move stops the new one and starts the old one again, and says which.
Agents' computers
Agents' computers run as their own containers through Vyre's Docker proxy. It runs on every Vyre
server from the install: the vyre command names the computers profile itself, so there is nothing to
add to /srv/vyre/.env.
What the proxy allows is in The box and the Mac.
Remove it
A server holds no name of its own: the name belongs to your space. Removing the server does not free the name; take the server off the space in the Vyre app if you want it to stop serving the name.
On this Mac
vyre box remove # stop the stack, remove /usr/local/bin/vyre, keep the volumes
vyre box remove --purge # also delete the volumes, after the server asks
It lists what it will do and asks first. Afterwards this Mac forgets the box.
On a server
curl -fsSL https://vyre.run/install.sh | sh -s -- --uninstall
curl -fsSL https://vyre.run/install.sh | sh -s -- --uninstall --purge
Both run docker compose down in /srv/vyre and remove /usr/local/bin/vyre. Without --purge,
the volumes and /srv/vyre stay, so a reinstall picks up where it left off. --purge lists every
volume labelled run.vyre=1 in project vyre and, after you say yes, deletes them: the vault,
Claude's sign-in, the store and /work. /srv/vyre stays either way, with its .env; remove it
with sudo rm -rf /srv/vyre. The installer never removes Docker, and leaves the images
(docker image rm them if you like).
On the Mac, vyre down && npm rm -g vyre removes Vyre and leaves ~/.vyre.
A box without Docker
A Linux box installed from npm with systemd units is upgraded with
sudo npm install -g https://vyre.run/box/vyre.tgz && vyre up, and removed with
sudo vyre uninstall --system && sudo npm rm -g vyre (--purge also deletes the data). See
Without Docker.
Next
- The box and the Mac: what runs where, the folders and volumes, and who can reach the box.
- Troubleshooting, when something does not start.
- CLI reference, every
vyre boxform.
What root runs, and what it reads
Updates that run as root (the automatic path from Settings and sudo vyre update) never read a file you or an agent on your account can write as configuration. Root starts compose from its own copies of the released compose.yml, in a folder only root can write, with a root-written env file and every file, project and folder named explicitly. It refuses an override file or a COMPOSE_* setting, takes how the box is built (pulled, built from the released source, or from your own checkout) from a record it made when the updater was installed, and checks every ghcr.io/vyre-ai image of a release with cosign before pulling. sudo vyre up and the other root commands use the same copies once the updater is installed. A host with no updater (no systemd) keeps the stack folder's files, as the installer laid them down.
One thing no script can fix: if your account may run sudo without a password, anything running as you can already become root, and none of this protects you from it. Keep sudo asking for a password on a box where agents run as your account.
Being in the docker group is equivalent to root on that host, so only the owner should be in it. The installer's own first vyre up runs as you, straight after it lays down the files it just verified; root's copies exist from the next step, when the updater is installed.