Concepts
The box and the Mac
Vyre runs on two kinds of machine. The box is a server you own: a Linux machine, or a Mac that stays on. Your sessions, agents and watchers live there, and the Vyre app talks to it. The Mac is the computer you sit at: it runs Lumen and your own terminal sessions, and reaches the box over your private network. A Windows PC is a device in the same way as the Mac, with an app instead of Lumen (see Windows). Each machine runs one vyred, the Vyre daemon, with a different set of modules switched on.
One process per machine
vyred is the same program everywhere. The machine's kind, machine in ~/.vyre/config.json, decides what it starts:
| Machine | Where | What it starts |
|---|---|---|
server | a Linux server, or a Mac you chose as the server | the whole set, including the address the Vyre app connects to and the relay |
solo | a Mac with nobody else to connect (the default on macOS) | the full local set, Lumen included, and nothing that serves other devices |
device | a computer that joins a server (the default on Windows) | the local set that reaches the server |
A Linux machine defaults to server. A Mac starts as solo, and becomes a server when you choose to make it the server for My Cloud, or run the Mac server installer. Older configs that say "role": "box" or "role": "local" still work and are read as server and as solo. See config.
Every module declares the machines it runs on ("roles" in its module.json: box, local, or both when omitted). What that means today:
| Runs on | Modules |
|---|---|
| Server only | names (address and certificates), network, hooks, computers, glass, artifacts, releases, hands-desktop, and the box's side of chrome |
| Mac only | capsule (Lumen), hands (computer use on macOS), screen, sideview, voice, apps, and the Mac's side of chrome |
| Both | most of the rest, including projects, recall, memory, vault, watchers, threads, sessions, agents, gate, learn, harness, presence, link, relay, onboard, github, team, spend, files, push, settings and update |
Modules has the full list with each module's machines. A Mac chosen as the server runs the Mac-only modules too, so Lumen stays on it.
A module that runs on both machines works on that machine's own data: recall on the Mac searches the Mac's Claude Code transcripts, recall on the box searches the box's. The one exception is reading and messaging a session: on the box, you see the paired Mac's sessions beside the box's own and can message them, through the link (see The box reads the Mac's sessions). vyre modules lists what started on the machine you run it on, and why anything failed.
You can move a module on or off with modules.enable and modules.disable in config.json. enable starts a module even when its roles do not include this machine's kind.
What runs where on a Linux box
A Linux box runs Vyre in Docker Compose, from /srv/vyre. The stack is box/compose.yml:
vyre: vyred and the sessions it runs. A small spawner starts vyred as uid 1000 (vyre) and the sessions Vyre runs itself as a second user,vyre-agent(uid 1001), which cannot open vyred's socket. vyred reaches your other devices over Wink, Vyre's own network core, which it runs itself: there is no sidecar container, no TUN device and no extra capability.docker-api: a filtered Docker API for the agents' computers, under thecomputersprofile.
The container publishes no port on the host. The host needs Docker and nothing else. A small vyre wrapper in /usr/local/bin runs commands inside the container, so vyre status on the host works as it does on a Mac.
The alternative without Docker is a systemd unit, installed with sudo vyre up --system --user <account> (the account vyred runs as, never root). See without Docker.
Who can reach the box
- No port is published on the host. A server has no first-run page: you pair it from your Vyre app.
- The built-in network and the relay carry every connection. Your devices reach vyred through them, not through a port on the host. See your private network.
- Callers are identified from the identity list, never by a header. A device that reaches the box arrives as
device:<id>, and the entry on your identity list says who it is. - vyred runs as uid 1000 (
vyre), not root. Only the spawner is root in the container, with every capability dropped except the few it needs to start a process as another user.
The reasoning is in ADR 0002.
Folders and volumes
The installer puts the stack in /srv/vyre (VYRE_DIR moves it), owned by you, not root:
/srv/vyre/
compose.yml the stack: vyre, and docker-api under the computers profile
compose.build.yml used when COMPOSE_FILE lists it: build the image from VYRE_SOURCE
src/ the unpacked vyre.tgz, when the image is built from it
vyre.env.example copy to vyre.env for CLOUDFLARE_VYRE_TOKEN and similar
vyre.env optional, yours, read by the vyre container
.env COMPOSE_PROJECT_NAME, COMPOSE_FILE, VYRE_SOURCE, DOCKER_GID; yours to add COMPOSE_PROFILES
/usr/local/bin/vyre the host wrapper
The data lives in Docker volumes, all labelled run.vyre=1:
| Volume | Mounted at | Holds |
|---|---|---|
vyre_vyre-home | /home/vyre | .vyre/ (Vyre's home folder, below) and .claude/, .claude.json (Claude Code's own state and transcripts) |
vyre_vyre-work | /work | projects |
vyre_vyre-agent-home | /home/vyre-agent | the home of the sessions Vyre runs (their own ~/.claude and transcripts) |
vyre_vyre-accounts | /home/acct | one private home per signed-in AI account (uids 2000 to 2063), holding that account's own login |
vyre_docker-api-bearer | /var/lib/vyre-secrets | the secret vyred uses to talk to the computers' Docker proxy |
A container can be recreated at any time: nothing in it matters but the volumes. Backing them up and moving them is in Box care.
Vyre's home folder
Every vyred keeps its state in one folder, ~/.vyre (mode 0700), with the same layout on every machine:
On a server
On a Docker box it is /home/vyre/.vyre, inside the vyre_vyre-home volume.
/home/vyre/.vyre/
config.json settings (0600); onboarding writes name, network, onboard
vyre.db, -wal, -shm the store (SQLite, WAL)
vault/ sealed vault items
certs/ acme-production.key, <name>.crt, <name>.key
names/ the name directory key, once the hosted directory exists
modules/, watchers/ what you installed and what Claude wrote
embedder/ the search model's library (about 105 MB), fetched on first use: safe to delete
models/ the search model's weights (about 23 MB), a cache: safe to delete
logs/ YYYY-MM-DD.log from vyred
vyred.sock, vyred.pid
On this Mac
On the Mac it is ~/.vyre in your own home folder. It has the same store, vault, search model and logs, and no certs/ or names/, because the Mac serves nothing to other devices. It also holds link.json (0600), the key that pairs this Mac with its box, and capsule/Vyre.app, Lumen as built on this Mac.
AI accounts and Claude Code on the box
-
Your accounts. Sessions run on your own accounts: Claude (through the Claude Agent SDK), Codex and Grok (through the Agent Client Protocol), and OpenRouter. A login account (Claude, Codex, Grok) is signed in once, by asking your assistant to start the provider's own sign-in (Claude's is also part of setup), and its login lives in that account's own private home on the box (
vyre_vyre-accounts). A key (an Anthropic or OpenRouter API key) lives in the Vault. Either way the secret reaches the session's process at start and is never shown to a model. -
Claude Code itself is installed in the image, and its state,
~/.claude/and~/.claude.json, lives invyre_vyre-home, so a new image keeps it. -
To run
claudeby hand on the box, run it in the container:cd /srv/vyre && docker compose exec vyre claudeIts transcripts land in the same
~/.claude/projects/, which is how Vyre's history sees them.
The agents' computers
Agents get their own containers (Specification, Section 7.9) through docker-api, Vyre's own Docker proxy (core/dockerproxy, run from the same image). It is off until the computers profile is on; turning it on is in Box care.
- The host's Docker socket is mounted into the proxy and nowhere else. vyred reaches it at
http://docker-api:2375on an internal network. - The proxy allows only what agents' computers use (create, start, stop, pause, unpause, inspect, list, remove, exec on a computer, volume inspect) and refuses every other endpoint.
- It checks request bodies too: a create must match
core/computers/driver/policy.jsand the box'sVYRE_COMPUTERS_*settings, and every per-container call is checked against the labels the Docker Engine itself reports. - It runs as uid 1000 with a read-only root, no capabilities, and the socket's group,
DOCKER_GIDin/srv/vyre/.env, which the installer fills in from the socket.
Why Why a proxy, and not the Docker socket?
Whoever holds the Docker socket is root on the host. The proxy keeps the socket in one small container and lets vyred ask only for the calls a computer needs, so a mistake or a prompt injection in a session cannot turn into a privileged container.
What runs on the Mac
On the Mac, vyre up starts vyred in the background with the local set of modules. It opens no listener for other devices. It serves its API on ~/.vyre/vyred.sock to the CLI, the Harness hooks in your terminal sessions, and Lumen. A Mac you chose as the server is different: it also keeps itself awake while it runs.
How the Mac finds the box
vyre up on a Mac with no box configured asks for your server's pairing code. It shows three words, and you confirm they match on both screens and approve with your passkey. You can also give it the address:
vyre box add [email protected] # put the box on a server you can SSH to
vyre up --box # make this Mac the box
vyre up --connect https://alex.vyre.run # a box you already set up
How they talk
The Mac's vyred is a client of the box, at network.box. The box sees the Mac as device:<id>, like any of your devices, because it identifies callers from the entry on your identity list and never by a header (ADR 0002).
The link module turns the two machines into one system:
- Pairing. On the Mac,
vyre link pair <address>(orvyre up --connect <address>, or a yes tovyre up's question) asks the box and shows a code. Starting Vyre never asks a box on its own, and a home other than~/.vyrenever talks to a real box unlessVYRE_ALLOW_REAL_BOX=1is set. Approve the request in the Vyre app and confirm with your passkey (see presence). You can approve from the Mac that is asking or from your phone. From the asking Mac the box asks for a fresh passkey proof and checks the typed code, so a model on the Mac cannot approve for you. The Mac keeps a link key in~/.vyre/link.json(mode 0600) and pins the box's key, so a different box at that address is refused. - Box tools from the Mac. A module on the Mac calls
ctx.remote(tool, input); a surface callslink.call. Both reachPOST /v1/tools/<tool>on the box. - Box events on the Mac. The Mac proxies the box's event stream at
/v1/link/events, so Lumen sees box threads as they happen. - The Mac's sessions on the box. The box reads the paired Mac's sessions through the link. See the next section.
vyre link # on the Mac: paired or not, and whether the box answers
vyre link pair https://alex.vyre.run
vyre link unpair # forget the box
The box reads the Mac's sessions
Your Claude Code history stays on the Mac. The box reads it through the link when you ask, so the Vyre app lists the Mac's sessions beside the box's own (the app reads them and does not act on them; ADR 0021).
- No port on the Mac. While paired, the Mac holds one request open to the box (
link.serve). The box answers it with a question, or with nothing after 60 seconds; the Mac runs the question and sends the answer back (link.reply), then asks again. An idle Mac costs one request a minute. - A short list of reads. Only
projects.catalog,projects.list,recall.search,recall.sessions,recall.thread,recall.transcript,threads.listandthreads.askscross, and both ends check the list (core/link/allow.js). The Mac runs them asmodule:link. - Two writes, yours only.
threads.sendtypes into a Mac session, andthreads.answeranswers one of its questions. Both cross only when you make the call from your own surface, never an agent, MCP, a guest or a module, and the Mac checks that again before it runs one. An answer also carries a one-use proof, signed by the box and tied to that question and those exact words, that the Mac checks. If a terminal or Lumen holds the session, your message waits until it is free and says so ("alex-mac is busy in your terminal"). - Only for you. On the box these tools take
machines: "all"or"local". They ask the Mac when you call them from the Vyre app, the CLI, Lumen or your own device, or when a module passesmachines: "all". Agents, MCP and guests get the box's rows alone. Each row the Mac sends is labelledsource: "mac"andmachine(the Mac's paired name); the box's rows saysource: "box". - Nothing is copied. Nothing the Mac answers is written to the box's store. Only
recall.threadandrecall.transcriptcarry a conversation, and only when you open a session the box does not have. - Marked as the Mac's. A Mac session carries the Mac's name. You can add a Mac session to a box project; the project's thread list reads it from the Mac.
- An absent Mac is an answer. A Mac that is not polling answers
mac_offlineat once and the box shows its own rows, with an "alex-mac offline" chip read fromlink.macs, and a message to it is not sent. A slow Mac delays a read by at most 5 seconds. A Mac that dropped off in the middle of a request looks online for up to a minute, and a read in that window ends intimeout.
The box cannot search the Mac's files: files are not on the list.
When the box is away
The Mac keeps working without the box (floor rule 9, see the security floor). Once a call to the box fails, the next ones fail fast with box_unreachable and are retried with a growing pause, up to 30 seconds. The link emits link.lost and, when the box answers again, link.connected. A module that asked the box for something gets the error at once and can fall back to what the Mac has.
What it will not do
- The Mac does not serve the Vyre app. The app talks to the box.
- The box never trusts the Mac because of a header or a shared secret alone: every connection is identified from the identity list first.
- Link tools on the box (
link.*) cannot be driven throughctx.remoteorlink.call. - The box never runs a tool on the Mac outside the reads and the two writes above, and never as an agent or a module.
Next
- Your private network: addresses, identity and certificates.
- Presence: why approving a pairing needs you, not a model.
- Box care: updating, backing up and moving the box.