docs GitHub

Architecture

Boundaries

Each part of Vyre is independently modular. A part talks to another part only through the registry (ctx.call, tools) and events. It never imports another part's files. A part can be switched off without breaking the rest.

  • The kernel is core/config, core/store, core/modules, core/presence and core/daemon. Any part may import it.
  • A part is core/<name> (a folder, or a single file such as core/quiet.js), local/<name> or modules/<name>.
  • A lib is lib/<name>: shared pure code with no feature state (ADR 0033). Any part may import a lib. A lib imports only the kernel and other libs, never a feature, and no lib edge can be frozen.

test/boundaries.test.js scans every runtime .js, .mjs and .cjs file under core/, local/, modules/ and lib/ for relative imports (static, dynamic and require). Tests, testing/ folders and fixtures are out of scope, since a test may reach into what it tests. The test fails on:

  • an import into another part that is not in its allowlist, or a new file behind a frozen edge;
  • an allowlist entry that nothing imports any more (the list only shrinks);
  • an allowlist edge missing from this page.

A new exception needs the lead's OK.

The frozen exceptions

Frozen from main on 27 September 2026: 26 edges, 24 now (connectors moved its shared credential, mail-message and on_behalf helpers into lib/connectors on 2026-09-28, which removed core/google -> core/connectors and core/mcp -> core/connectors entirely rather than freezing their wider imports). "Becomes" says where each remaining one should go:

  • ctx.call: call a tool through the registry instead.
  • lib: the imported file is a pure helper; move it to lib/<name>.
  • surface: the CLI is a surface, and keeps its command-side helpers.
EdgeFilesWhyBecomes
core/cli -> core/namesbackup.js, system.jsvyre backup and vyre up write the box backup and the systemd unit in-processctx.call
core/cli -> core/recallembed.js, progress.jsvyre status and vyre doctor read index progress and the embedder's state directlyctx.call
core/comms -> core/mailimap.jsthe sending service reuses the mail module's IMAP adapter; it becomes a ctx.call when the adapter has a toolctx.call
core/files -> core/googledrive-seam.jslinked Google Drive folders use the Drive seam's client directly; it goes through google.drive.* tools when the bridge is splitctx.call
core/import -> core/sessionscredfiles.jsimport must never move a login file; it shares the one credential-file test, which belongs in liblib
core/runner -> core/sessionscredfiles.jsthe runner skips login files when it reads or syncs a home; it shares the one credential-file test, which belongs in liblib
core/cli -> core/resiliencebackoff.js, node.js, stream.jsthe reference client every surface uses, a pure librarylib
core/cli -> core/vaultbackup.js, cli-io.js, refs.jsvyre vault's terminal side: no-echo prompts, vault:// refs, the sealed backup formatsurface
core/cli -> local/voicetalk.jsvyre voice, push-to-talk from a terminal until the native Lumen has voicectx.call
core/daemon -> core/harnessrules.jsthe kernel runs the security floor on every call's input; the floor belongs in the kernellib
core/daemon -> core/namesguests.jsthe router asks whether a tailnet caller is a guest before the registryctx.call
core/daemon -> core/runnerhomesandbox.js, sandbox.jsthe daemon composes the runner's home sandbox for the Switchboard (core/sessions cannot import core/runner)lib
core/daemon -> core/sessionsdrivers/openrouter.jsthe daemon hands the kernel's inference door its providers: the API-key chat drivers' door side (the door scans first, this only makes the call with the key the session passes); one-way, the drivers import nothing from the daemonlib
core/daemon -> core/runnerhomesandbox.js, sandbox.js, lent-home.jsthe daemon composes the runner's home sandbox for the Switchboard (core/sessions cannot import core/runner)lib
core/daemon -> core/spawnerclient.js, confine.jsa session in the packaged box is confined by its own uid, and the daemon composes the self-test that proves it before every start; it asks the root spawner, which is not a modulelib
core/daemon -> core/switchboardsessions.jsthe router resolves which Claude Code session a call comes fromctx.call
core/daemon -> core/winknode/peer-wire.jsthe daemon composes the home's peer door for a paired device's relay stream (core/daemon/peer-door.js)lib
core/files -> core/linktransport.jsthe legacy folder-as-a-disk path looks up a program the Mac may have (drive.js); it goes with that pathlib
core/hooks -> core/namestailscale.jsruns the tailscale CLIlib
core/link -> core/namestailscale.jsfinds the box on the tailnetlib
core/names -> core/linktransport.jsnames and link import each other; the transport belongs in a lib both uselib
core/network -> core/namesguests.js, identity.js, tailscale.jsthe listeners identify tailnet peers (ADR 0002)lib
core/recall -> core/transcriptsindex.jstranscripts is the one reader of Claude Code's fileslib
core/watchers -> core/spawnerclient.jsthe box's watcher wall is the root spawner's; loaded only when a spawner socket existslib
core/sessions -> core/spawnerclient.jssessions/switchboard split (ADR 0030), cleanup owed by sessions after 0.1.0ctx.call
core/sessions -> core/switchboardrunner.jssessions/switchboard split (ADR 0030), cleanup owed by sessions after 0.1.0ctx.call
core/sessions -> core/transcriptssanitize.jssessions/switchboard split (ADR 0030), cleanup owed by sessions after 0.1.0ctx.call
core/switchboard -> core/harnessrules.jssessions/switchboard split (ADR 0030), cleanup owed by sessions after 0.1.0ctx.call
core/switchboard -> core/sessionsconfig.js, providers.js, sdk.js, spawn.jssessions/switchboard split (ADR 0030), cleanup owed by sessions after 0.1.0ctx.call
core/switchboard -> core/transcriptssanitize.jskeeps credentials out of what it builds from transcriptslib
core/term -> core/filessafety.jsthe path gate every file path passes throughctx.call
core/vyre-core -> core/vaultvault.jsvyre-core hosts the vault's store and crypto in its own process and db (ADR 0040 phase 2)host
core/vault -> core/linktransport.jsthe vault relay between the Mac and the boxlib
core/vault -> core/namesidentity.js, tailscale.jswho is on the other end of a vault relay, and the tailscale CLIlib
local/capsule -> core/clicommands/capsule-native.jswhere the native Lumen app is built, shared with vyre capsulelib
local/hands-mac -> local/screen-macfloor.jsthe floor for Vyre's hands and eyes on the Mac (SPEC section 11)lib
local/sideview -> local/screen-macfloor.js, runner.jsdrives the sight helper and its floor directlyctx.call

What the list says

Two files carry most of the rest. core/names/tailscale.js (6 edges) and core/link/transport.js (3) are shared plumbing, not features: the first move is a small tailnet lib beside the kernel that both names and link use, which also ends the names and link cycle. The ctx.call cases (8) are places where a part reaches into another's state and should ask its tools instead. lib/connectors (auth.js, message.js, behalf.js) is the same move already done for the connectors' own shared helpers.

First cleanup: a tailnet lib

Scheduled after the native-core milestone. core/names/tailscale.js and core/link/transport.js move into one small tailnet lib beside the kernel, with no feature state. That removes about 10 edges from the list above and ends the names and link cycle.