Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 0 additions & 59 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ members = [
"crates/switchyard-nemo-relay-plugin",
"crates/switchyard-runner",
"crates/switchyard-desktop",
"crates/switchyard-menubar",
"crates/switchyard-costs",
"crates/switchyard-server",
"crates/switchyard-skill-distillation",
Expand Down
140 changes: 119 additions & 21 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,73 @@ See [Getting Started](docs/getting_started.md#server-path) for a complete TOML
deployment and [`switchyard-server`](crates/switchyard-server/README.md) for the
configuration reference.

## Desktop installer
## Server Keys

In a server config, each LLM client and decision client names its API key
variable with `api_key_env`. A server you start from a terminal reads those
variables from your shell, as it always has. The installed server does not: on
macOS launchd starts it, on Linux systemd, and on Windows Task Scheduler. None
of them runs your shell, so the installed server never sees the variables that
`~/.zshrc` or `~/.bashrc` exports.

The installed server reads each key from the system credential store instead:
macOS Keychain, Windows Credential Manager, or the Linux Secret Service (GNOME
Keyring on Ubuntu and GNOME, KDE Wallet on Plasma). A key is saved under its
variable name, such as `NVIDIA_API_KEY`. Before each start, the installed
helper reads the keys that the config names and passes them to the server. A
saved key replaces any value the service manager passes. If a variable has no
saved key, the server does not start, and the server log names the variable.

To save, replace, or remove a key in the desktop app, open **Settings → Server
keys**. Type or paste the key, or choose **Import from shell** to read the
variable once from your login shell. From a terminal on macOS or Linux, run the
installed helper:

From a source checkout, run `cargo desktop --help` for the commands and
`cargo desktop install --dry-run` to preview installation. This checkout has
the installer helper but does not yet build the desktop app executable, so
full installation cannot complete. Uninstall keeps settings, history, and
recovery copies.
```bash
~/.switchyard/bin/switchyard-desktop-install key status --config ~/.switchyard/composite.toml
~/.switchyard/bin/switchyard-desktop-install key set NVIDIA_API_KEY # prompts without echo
~/.switchyard/bin/switchyard-desktop-install key import NVIDIA_API_KEY # reads it from your login shell
~/.switchyard/bin/switchyard-desktop-install key remove NVIDIA_API_KEY
```

The installer requires the repository's Rust toolchain. macOS uses LaunchAgents;
Linux requires a systemd user session. Windows requires MSVC build tools,
WebView2, and a Task Scheduler user session.
On Windows, the installer puts the helper in
`%LOCALAPPDATA%\Programs\Switchyard`. There `key set` reads the key from
standard input without a prompt, and `key import` reads the variable from your
Windows environment variables. From PowerShell:

```powershell
$helper = "$env:LOCALAPPDATA\Programs\Switchyard\switchyard-desktop-install.exe"
& $helper key status --config "$env:USERPROFILE\.switchyard\composite.toml"
[Net.NetworkCredential]::new('', (Read-Host 'Key' -AsSecureString)).Password | & $helper key set NVIDIA_API_KEY
& $helper key import NVIDIA_API_KEY
& $helper key remove NVIDIA_API_KEY
```

On Windows, run install and uninstall from the source checkout with Cargo.
The installed helper rejects changes to its own installation because Windows
locks running executables. The app's Update command uses a temporary helper
copy. The installed helper still supports `uninstall --dry-run`.
Restart the server after you change a key. If you rename a variable in
`api_key_env`, save a key under the new name. The old key stays saved until you
remove it in Keychain Access, Credential Manager, or Seahorse.

`cargo desktop install` never saves, reads, or changes a key, even when your
terminal sets the variable. It checks the config without keys, then lists each
variable that has no saved key. On a first install, the server starts after you
save those keys in **Settings → Server keys** and restart it. In the terminal
app, press `7` to open the Settings page. There `k` lists the variables that the
config names and whether each has a saved key, `s` saves a key, and `i` imports
it from your login shell. Press `b` and then `y` to restart the server.
Uninstalling keeps the saved keys, the config, and history. Switchyard never
writes keys to app settings, service definitions, or logs. Model-list keys that
you enter in Routes are stored separately.

On macOS, Keychain ties each key to the helper that saved it. Keychain treats a
rebuilt helper as a new program, so after `cargo desktop install` macOS may ask
once per key whether to allow `switchyard-desktop-install`. Choose **Always
Allow**.

On Linux, the login keyring must be unlocked. A desktop login normally unlocks
it. When the server service starts after login, it waits up to a minute for the
keyring to unlock. Over SSH without a desktop session, or with a window manager
that does not unlock the keyring, the server does not start and its log says
the keyring is locked.

## Linux Codex Service

Expand All @@ -63,13 +114,16 @@ port while the service is stopped and receive your login and prompts.
From a checkout, preview or install the service:

```bash
make install-linux-dry-run
make install-linux
cargo desktop install --dry-run
cargo desktop install
systemctl --user status switchyard
codex -p sy
```

The installer builds and installs `~/.switchyard/bin/switchyard-server`.
The installer builds and installs `~/.switchyard/bin/switchyard-server` and
`~/.switchyard/bin/switchyard-desktop`. Run the same command to reinstall from
the current checkout; update the checkout first to install newer code.
Run `~/.switchyard/bin/switchyard-desktop --tui` to open the terminal app.
It creates `~/.switchyard/composite.toml` if missing and keeps existing edits.
It replaces `~/.config/systemd/user/switchyard.service` and restarts the service.
It writes `~/.codex/sy.config.toml`, backing up a changed profile first.
Expand All @@ -79,20 +133,61 @@ The profile format requires [Codex 0.134.0 or newer](https://developers.openai.c
Remove any old `[profiles.sy]` table from `~/.codex/config.toml` before using it.

Set `SY_HOME` or `SY_PORT` to change the server directory or port (default 4123).
`SY_HOME` must not contain whitespace, control characters, or a trailing
backslash; `SY_PORT` must contain only digits. `XDG_CONFIG_HOME` and `CODEX_HOME`
`SY_HOME` must be an absolute UTF-8 path without ASCII control characters.
`SY_PORT` must be a number from 1 to 65535. `XDG_CONFIG_HOME` and `CODEX_HOME`
set the systemd and Codex config directories.

Read server logs with `journalctl --user -u switchyard`. Routing records are
stored in `~/.switchyard/routing.jsonl`. Use `systemctl --user edit switchyard`
for service changes that survive reinstalling.

Remove the service and profile with `make uninstall-linux`. This also removes
Remove the service and profile with `cargo desktop uninstall`. This also removes
marked Codex aliases left by older installs from existing `.bashrc` and
`.zshrc` files. If upgrading an older install, uninstall first and run
`unalias codex` in any open shell. The server directory, routing records,
profile backups, and systemd drop-in files stay in place. Use the same path
overrides when installing and uninstalling.
profile backups, and systemd drop-in files stay in place. Uninstall uses the
recorded installation. Conflicting environment overrides require
`cargo desktop uninstall --settings FILE` to select it explicitly.

## Windows Desktop App

From PowerShell in the repository, run:

```powershell
cargo desktop install --dry-run
cargo desktop install
```

Windows needs the Rust MSVC toolchain, Visual Studio C++ build tools, and
[WebView2](https://v2.tauri.app/start/prerequisites/#windows).
The installer uses Task Scheduler for the current logged-in user. It installs
executables in `%LOCALAPPDATA%\Programs\Switchyard`, falling back to
`%USERPROFILE%\AppData\Local\Programs\Switchyard` when `LOCALAPPDATA` is unset.
It saves that location and adds a Start menu shortcut. Two independent tasks
start the server and desktop app after login with ordinary user privileges.
Quitting the app leaves the server running.

Run `cargo desktop install` again to build and install the current checkout.
This Cargo command waits for installation to finish. The app's source update
uses the saved checkout and runs from a temporary Rust executable so Windows
can release and replace the installed files; read `SY_HOME\logs\update.log` for
its progress and errors. The installed installer cannot install, update, or
uninstall its own installation, because Windows keeps a running program from
being replaced. Use `cargo desktop` from the source checkout instead.
The installer builds and validates before stopping the tasks. If a binary
replacement fails, it tries to restore the previous binaries. If restoration
also fails, the error names the directory containing recovery files.
Rerun installation if a later task or settings operation fails; the complete
installation is not one transaction.

Settings, server config, accounts, and routing history stay in
`%USERPROFILE%\.switchyard` unless `SY_HOME` changes the directory. Server and app
logs are in `SY_HOME\logs`. Model-list keys entered in Routes are saved in the
current user's Windows Credential Manager. Coding tools keep their own login files.

Use `cargo desktop uninstall` to stop and remove both tasks, the shortcut, and
the installed executables. Uninstall preserves settings, accounts, and history,
and makes a recovery copy of the Codex profile before removing it.

## Rust Libraries

Expand Down Expand Up @@ -123,3 +218,6 @@ uv run pytest tests/ -v

The `dev` dependency group contains testing and linting tools and is not exposed
in the published wheel metadata.

See the [desktop and terminal quickstart](crates/switchyard-desktop/README.md) for
first-run setup, reviewed settings changes, usage, recovery, and safe uninstall.
37 changes: 0 additions & 37 deletions Makefile

This file was deleted.

5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ LiteLLM and NeMo Relay run in your own deployment. Follow each gateway's guidanc

[Try routing locally](docs/getting_started.md#server-path) with the standalone proxy for demos and evaluation, then [verify routing is active](docs/getting_started.md#verify-routing-is-active).

The [desktop and terminal quickstart](crates/switchyard-desktop/README.md) covers
local installation, coding-tool connections, sessions, usage, recovery, and uninstall.

Agent-specific guides are available for [pi](docs/integrations/pi.md) and
[Oh My Pi](docs/integrations/oh_my_pi.md).

Expand All @@ -80,7 +83,7 @@ Features and integrations recently merged into `main`.
Select models in Node.js or try automatic routing through Pi’s virtual models. [TypeScript bindings →](bindings/typescript/README.md) · [Pi example →](examples/pi/README.md)

- **2026-10-08 · Run Codex with Switchyard on macOS**\
Install a background router and Codex profile, then track usage and estimated savings from the menu bar. [Setup guide →](crates/switchyard-menubar/README.md)
Install a background router and Codex profile, then track usage and estimated savings in the desktop app. [Setup guide →](crates/switchyard-desktop/README.md)

## Routing algorithms

Expand Down
2 changes: 1 addition & 1 deletion crates/switchyard-desktop/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ pub fn run(controller: Controller, settings: PathBuf) -> Result<(), String> {
_ => {}
})
.build(app)?;
// Like the menu bar app, the tray rereads usage on the settings interval.
// Like the earlier menu bar app, the tray rereads usage on the settings interval.
let handle = app.handle().clone();
std::thread::spawn(move || {
loop {
Expand Down
31 changes: 0 additions & 31 deletions crates/switchyard-menubar/Cargo.toml

This file was deleted.

Loading
Loading