All pages
On this page

The oar command

oar is the command line of the relay machine. It writes the config, starts and stops the relay, prints the address you open in a browser, installs the start-at-login service, updates OAR, checks the install and removes it again. It acts on the install it was started from, on the machine where it runs; it does not talk to a relay on another machine.

Run oar with nothing after it for a menu, or oar help for the usage text. How to install OAR is on Install, the first start on First run.

The commands #

Command What it does
oar A menu in a terminal; the status and the usage text elsewhere
oar setup Writes the config: auth token, access (this machine or LAN) and port; on Windows also the autostart mode
oar start Starts the relay in the background
oar stop Stops the relay once no turn is running
oar restart Restarts a running relay, or starts a stopped one
oar status Says whether the relay runs; exit code 0 or 3
oar url Prints the relay address with the token, and a QR code where a phone can use it
oar update Installs the newest release, or one you name, and restarts the relay
oar service Installs, removes or reports the start-at-login service
oar autostart Added in 0.9.15 Switches how the relay starts by itself: at sign-in, at boot, or off
oar uninstall Added in 0.9.15 Removes the autostart entries and the package; the data stays unless you add --purge
oar copilot Starts the relay and a GitHub Copilot terminal session
oar doctor Prints the install, the config and the provider CLIs it finds
oar help Prints the usage text
oar version Prints the version

Most messages start with [oar]. Errors go to standard error, normal output to standard output.

How the command line is read #

  • The command is the first word that is not an option. Options may come before it: oar --port 3400 setup is oar setup --port 3400.
  • --port, --migrate-from, --to and --autostart take a value, as the next word or as --port=3400. Without a value oar prints --port needs a value. and exits 2. For oar update --to and oar copilot --migrate-from use the space-separated form; see the notes under those commands.
  • --help or -h before a -- prints the usage text and exits 0, whatever else is on the line.
  • --setup, --start, --stop and --status name a command when no other is given: oar --setup --start is oar setup --start.
  • --install-extension, --no-install-extension, --migrate-from or a -- part without a command mean oar copilot. --version or -v without a command print the version.
  • Each command accepts only its own options and prints the usage text after one of these messages (to standard error), exit code 2:
[oar] Unknown command: backup
[oar] Unknown option: --verbose
[oar] Unknown option for oar start: --port
[oar] Unexpected argument: extra
[oar] --lan needs a command.
[oar] oar start takes no "--" part.

Only oar copilot takes a -- part.

In a terminal, oar shows the version, whether the relay runs, the URL (without the token), the port, the access, the service state and any warning, then these entries:

Key Entry Runs
1 Start oar start
2 Stop oar stop
3 Restart oar restart
4 Show URL and QR code oar url
5 Settings oar setup, with its questions
6 Install or remove the service oar service install when none is installed; oar service remove after Remove the service? [y/N] when one is; oar service status where no service is available
7 Update oar update. After a successful update the menu closes, because its own files were replaced
8 Doctor oar doctor
9 Copilot session oar copilot
q Quit

Move with the arrow keys (or j and k) and press Enter, or press a digit or q. After an entry has run, any key goes back to the menu. Ctrl+C inside a question returns to the menu; in the menu it ends oar with exit code 130. The NO_COLOR environment variable switches colour off. A terminal that cannot read single keys gets the same list with a Choose (1-9, q): prompt.

When standard input or output is not a terminal, oar prints what oar status prints, an empty line and the usage text, and exits 0 whatever the status is.

oar setup #

oar setup [--port <port>] [--lan | --local] [--new-token] [--autostart signin|boot|off] [--defaults] [--start] [--migrate-from <dir>]

Writes config.json in OAR's state folder (Where oar keeps its files; the keys are on config.json). Run it after installing and whenever you want another port, access or token. Settings the command does not touch stay as they are in an existing config.

In a terminal, and when you pass none of --port, --lan, --local, --new-token, --autostart or --defaults, it asks three questions and shows the current value in brackets; Enter keeps it. Added in 0.9.15 On Windows a fourth asks how the relay starts, with the mode in force as the default; it is left out when the boot task cannot be read. First run walks through them.

New auth token? It signs every device out. [y/N]
Access: 1 = this machine only, 2 = LAN (other devices on your network) [1]:
Port [3333]:
Autostart: 1 = at sign-in, 2 = at boot, before anyone signs in (one administrator prompt), 3 = off [3]:
Option Value Default Effect
--port digits, 1 to 65535 the config's port; for a new config the first free port from 3333 up (of 20 tried, else 3333) Sets the port without asking. A port that something else holds is refused
--lan none the config's access Access for other devices on your network (localhostOnly: false). Not together with --local
--local none the config's access; this machine only for a new config Access for this machine only
--new-token none keep the token; a new config gets a random one Writes a new auth token. It signs every device out
--autostart signin, boot or off the mode in force Added in 0.9.15 Switches the autostart without asking, as oar autostart does: on Windows the sign-in entry or the boot task (one administrator prompt), on Linux the service. Global installs only: a git checkout is refused and nothing is written
--defaults none off Asks nothing, even in a terminal, and restarts a running relay without asking
--start none off Global installs only (the installer uses it). Where a systemd user session exists it also writes the service unit; then it starts the relay, through the service when there is one. Ignored in a git checkout
--migrate-from a folder none Copies the state of a git checkout from before the project was renamed (<folder>/server) into OAR's state folder. The source is never changed. A relay still running from it blocks the copy. Global installs only. It runs every time you name a folder: a folder already brought over whose database is still there only says <state folder> is already migrated from <folder>; nothing to do.; a folder with nothing to bring over is an error (Nothing to migrate from <folder>: <reason>.); a state folder whose database already has conversations refuses it, and nothing is overwritten. See Files and folders

A port is free when nothing answers on it and it can be bound on this machine. The port of this install's own running relay counts as free. In WSL a port that Windows holds is refused too. For a new config whose port 3333 is taken, setup says Port 3333 is in use — using <port>. A global install also picks up state from the earlier managed config folder once, without the option.

What it does:

  1. Migrates old state (global installs): the earlier managed config folder once by itself, or the folder --migrate-from names. [oar] Migrated <folder or "earlier relay state"> into <state folder> (<n> items, sources untouched; receipt in backups/MIGRATION.md).
  2. Writes config.json (permission 600 outside Windows) and prints [oar] Wrote <path>. A new config holds authToken, port, localhostOnly, pollIntervalMs (3000), processingTimeoutMs (600000) and conversationSessionMode (isolated).
  3. Added in 0.9.15 Switches the autostart, as --autostart or the question says (global installs). It runs oar autostart with that mode and prints its lines.
  4. If the relay runs and the token, port or access changed, it restarts the relay and waits for it to answer with the new settings. In a terminal it asks The relay is running. Restart it now to apply the change? [Y/n]; on no it prints The running relay keeps its earlier settings until it restarts: oar restart. Without a terminal it restarts without asking.
  5. Prints the relay URL with the token, and a QR code when a phone can reach the address (LAN access with a network address that is not internal to WSL). If the autostart step did not go through, it then says The config is written; the autostart step did not go through (see above). and exits 1.
  6. When the relay is not running, says how to start it: oar start (with a hint about oar service install when no service exists); in a git checkout, npm start.
Exit code When
0 The config is written, and the restart or start that was asked for worked
1 The port is taken; the relay did not restart or come up; the migration is refused or fails (a relay running from the source or on the state folder, a database with conversations already there, nothing to migrate from the folder you named); the autostart step did not go through; --autostart in a git checkout
2 --port is not a number from 1 to 65535 (--port expects a number from 1 to 65535.), --autostart is not signin, boot or off (--autostart expects signin, boot or off.), or --lan and --local are both given
130 Ctrl+C in a question

oar start #

oar start

Starts the relay in the background and prints the relay URL (with the token, without a QR code). It has no options. It needs a config: without one it prints No config yet — run: oar setup. A git checkout is refused (This is a git checkout — start the relay with: npm start). Both exit 1.

  • A relay that already answers is left alone: The relay is already running. It keeps the version and settings it started with until you restart it: oar restart.
  • If the relay runs with settings from before a oar setup, oar start says so and points to oar restart.
  • With a systemd user service installed (Linux), the service is enabled and started. Otherwise the relay starts as a background process that outlives the terminal and writes server.log and server-err.log to the log folder. After a reboot, run oar start again, or set up start at login.
  • It waits up to 20 seconds for the relay to answer, and stops waiting early when the process it started ends. Then: The relay did not come up — see <log folder>/server-err.log (through the service: see journalctl --user -u oar). Added in 0.9.14 When a live relay holds the lock, a second line names the lock and says what to do: The lock <path> is held by relay process <pid> (started <time>): oar stop, or end process <pid>. When the lock names a process that cannot be told to be a relay: The lock <path> names process <pid>, which cannot be told to be a relay: if no relay is running, end process <pid> or delete the lock.
  • Added in 0.9.14 A relay that is still bringing over the database it ran on until now (the first start after the update to 0.9.14) is not counted as failed: oar start says The relay (process <pid>) is bringing over the database it ran on until now (…) and is still running; it answers once that is done. and names its log.
  • A lock whose relay is still starting is waited for, up to 5 seconds. A lock of an earlier version whose relay does not answer is removed (Removing a leftover relay lock); Added in 0.9.14 a lock this version wrote is left to the relay, which tells a stale one itself (see One relay per data folder).
  • A port that another program holds, or a relay with another token: Port <port> is in use by another program, or by a relay with another token. and a hint to pick another port with oar setup --port <port>.
  • In WSL it warns when Windows holds the port (WSL).

Exit code 0: the relay runs, or is still bringing over its database. Exit code 1: no config, a git checkout, the port is taken, or the relay did not come up.

oar stop #

oar stop

No options; needs a config (exit 1 without one).

  • With the systemd service running, it runs systemctl --user stop oar at once and says The relay service is stopped. It starts again at the next boot (without lingering: at the next login); to take it out: oar service remove.
  • Any other relay is asked to stop through its own interface and stops once no turn is running. If turns are running, Waiting for the relay's running turns to finish … is printed. oar stop waits up to 30 seconds, then prints The relay is stopped., or The relay is still busy; it stops by itself when its running turns finish. Both exit 0.
  • No relay running: The relay is not running., exit 0.
  • Port held by something else: Port <port> is in use by another program, or by a relay with another token., exit 1. If the lock file names a live process of this install, a second line gives its process id and tells you to restart it from its web UI (⋯ → Restart web relay) or to end that process.
  • The relay did not accept the stop request. or a failed systemctl: exit 1.

On Windows, and where the service is installed but not running, the relay is stopped through its own interface. See Restarting the relay for what a stop does to running turns.

oar restart #

oar restart

Restarts a running relay so that it loads a new version and reads the config again, then prints the relay URL. A stopped relay is started instead, as with oar start. No options; needs a config.

  • With the systemd service running, systemd restarts it at once; a running turn ends (Restarting the relay).
  • Any other relay is asked to restart and waits for its running turns. The command waits up to 60 seconds for the relay to answer on the config's port with the config's token, then prints The relay restarted. If turns are still running: The relay is still busy; it restarts by itself when its running turns finish., exit 0. Added in 0.9.13 The restart is queued as with 🌄 Restart web relay: the browsers show its banner, and after Ask after a queued relay restart has waited it asks there whether to restart now, keep waiting or cancel (The restart waits).
  • Exit 1 with The relay came back on its earlier port and token: it was started with fixed ones (oar copilot). End that session, then run: oar start: a relay started by oar copilot keeps the port and token it was given.
  • Exit 1 with The relay did not accept the restart request. Restart it from the web UI (⋯ → Restart web relay). (Restart web relay), systemctl --user restart oar failed, or The relay did not come back — see <log folder>/server-err.log (through the service: see journalctl --user -u oar).
  • A port held by something else: the message of oar stop, exit 1.

oar status #

oar status

Prints the state of the relay and exits with a code a script can test.

[oar] OAR 0.9.19
[oar] The relay is running on port 3333 (this machine only): http://localhost:3333/
[oar] Service: installed, running
Line Meaning
Not set up yet — run: oar setup No config; only the version line comes before it
The relay is running on port <port> (<access>): <url> The relay answers with the config's token. Access is LAN access or this machine only. The URL has no token; oar url prints it
The relay is stopped (port <port>, <access>). Start it with: oar start Nothing answers on the port
Port <port> is in use by another program, or by a relay with another token. Something answers, but it is not this relay with this token
Service: <state> installed, running, installed or not installed. Left out where there is nothing to report: a git checkout, macOS, a Linux without a systemd user session
Tunnel: <state> Only when the built-in Cloudflare tunnel is on in the config. For a running relay: running, not connected, not connected (<reason>) or off: <reason>. Otherwise managed (cloudflared: <path>) or managed, cannot start: <reason>
a warning The relay runs with its earlier settings; restart it to apply the new ones. after a oar setup that changed the port or token; in WSL, that Windows holds the port

Exit code 0: the relay runs. Exit code 3: it is stopped, the port is held by something else, or there is no config.

oar url #

oar url

Prints the address to open in a browser, with the auth token, the same way oar setup ends. No options; needs a config (exit 1 without one). Otherwise exit 0.

[oar] Relay URL: http://localhost:3333/?token=...
[oar] This machine only. For a phone, switch on LAN access (oar setup --lan) or use a tunnel (README: Remote access).
  • With LAN access, the address is the machine's first non-internal IPv4 address, and a QR code for it follows the first line.
  • With LAN access but no network address: LAN access is on, but this machine has no network address. In WSL's default (NAT) networking the address is internal to WSL; the output says so and names a tunnel or mirrored networking as ways out. No QR code in these cases.
  • When the running relay reports a tunnel address, a Tunnel URL: line follows with the token added, and a QR code for it.
  • A last line says The relay is not running. Start it with: oar start, or, after a setting change, that the relay runs with its earlier settings until oar restart.

oar update #

oar update [--beta] [--to <version>]

Installs a newer release of OAR from npm and asks a running relay to restart. It is the terminal twin of the Update button in Settings → General (Updating OAR). A git checkout prints This install runs from a git checkout — update with git pull, then restart the relay. and exits 1.

Option Value Default Effect
--beta none off Takes the version of the beta channel from the update manifest (Channels). Without it, a beta version follows the beta channel and any other follows stable. A channel with no version falls back to stable
--to X.Y.Z, optionally with a prerelease (X.Y.Z-beta.N) newest of the channel Installs exactly this version straight from npm, without the manifest. Older versions work too; it does not compare with the running one

Write --to with a space

--to 0.9.2 works. --to=0.9.2 passes the check on the command line but is ignored: oar update then installs the newest release of the channel.

What it does:

  1. Without --to, fetches https://oar.sh/latest.json (5 seconds), on every run, whether or not the automatic check is on in the web UI.
  2. If the channel's version is not newer than the running one: Already up to date (<version>)., exit 0.
  3. Prints Updating @oar-sh/oar <old> -> <new> ... and runs npm install -g @oar-sh/oar@<version>, with the flag that lets the install scripts of OAR and its native dependencies run. Away from Windows it runs the npm beside the Node.js that runs oar, into the prefix the package sits in; on Windows, npm.cmd. npm prints its own output; then Installed @oar-sh/oar@<version>.
  4. If a relay runs, asks it to restart once the queue is idle (Relay restart requested — it restarts once the queue is idle.); with none, No running relay detected — the update loads on the next start. A refusal prints Could not request a relay restart; restart it manually to load the update.

Exit code 0: installed, or already up to date. Exit code 1: a git checkout; --to is not a version like 0.9.2; the manifest cannot be fetched (Could not fetch <url>.), has another schemaVersion than 1, or carries no usable version; npm install failed (the running install is unchanged).

Variable Effect
OAR_NO_UPDATE_CHECK=1 Blocks the manifest request. oar update then exits 1 and points to oar update --to X.Y.Z, which still works
OAR_UPDATE_MANIFEST_URL Reads the manifest from this URL instead

oar service #

oar service [install | remove | status]

Starts the relay at every login. oar service alone is oar service status. A git checkout is refused (exit 1). The Start at login page explains the service from the owner's side.

Linux #

A systemd user unit, oar.service, in ~/.config/systemd/user/. It runs the relay with the config, data and log folders of this install and the PATH of your shell plus ~/.local/bin and the folder of the Node.js in use. systemd restarts it 5 seconds after a crash.

Word What it does
install Needs a config. Writes the unit; runs loginctl enable-linger so the relay survives a logout (if that fails it prints the command to run yourself); reloads systemd; stops a relay you started by hand so the service can take the port; enables and starts the service. If that relay does not stop, the service is enabled and takes over at the next boot (without lingering, at the next login), or once that relay has stopped and you run oar start
remove Disables and stops the service, deletes the unit, reloads systemd. The service is not installed. if there is none
status Service: installed, running, installed, not running or not installed

Without a systemd user session the command says so and exits 1. In WSL the message adds how to switch systemd on: systemd=true under [boot] in /etc/wsl.conf, then wsl --terminate <distro> from Windows.

Windows #

An entry in your Startup folder starts the relay when you sign in.

Word What it does
install Needs a config. Switches the sign-in start on; it does not start the relay now (Start it now with: oar start)
remove Switches it off: Autostart is off. A running relay keeps running; stop it with: oar stop
status Autostart: on, at sign-in, on, at boot (scheduled task) or off

A start at boot through a scheduled task is set up with oar autostart boot or in the web UI under Settings → General → Autostart (Windows) (Windows). While that task is ready, install and remove refuse (exit 1) with The relay starts at boot through a scheduled task. Change that with oar autostart signin|off, or in the web UI: Settings → General → Autostart (Windows).; status reports it.

macOS #

The service is not supported on macOS yet. Start the relay with: oar start, exit 1 for every word.

Exit code 0: done, or status. Exit code 1: a git checkout, macOS, no systemd user session, no config on install, a scheduled boot task on Windows, or the relay did not come up after install. Exit code 2: a word other than the three, or a second word.

oar autostart Added in 0.9.15 #

oar autostart [signin | boot | off | status] [--replace]

Switches how the relay of this install starts by itself. oar autostart alone is oar autostart status. A git checkout is refused (This is a git checkout — autostart is for global installs., exit 1). signin and boot need a config; off and status do not. Autostart explains the modes from the owner's side.

Windows #

Word What it does
signin Writes the entry in your Startup folder, and removes the boot task if there is one (one administrator prompt). Prints The relay starts when you sign in to Windows. It does not start the relay now
boot Registers the scheduled task oar-relay and its launcher (one administrator prompt: Confirm the administrator prompt to register the boot task …), and removes the sign-in entry. Prints The relay starts at boot, before anyone signs in (scheduled task \oar-relay). In boot mode already, it changes nothing and asks for no prompt; if the generated launcher is missing or from an earlier version, it registers the task again, with one administrator prompt
off Removes the boot task (one administrator prompt) and the sign-in entry. Autostart is off. A running relay keeps running; stop it with: oar stop
status The mode and every entry, each (generated by OAR) or (hand-made); see Status

The administrator prompt appears on the PC's own desktop and is waited for up to 2 minutes. Only the schtasks call runs elevated. A declined prompt changes nothing (Nothing changed: the administrator prompt was declined.). After a switch with --replace, each copy is named: Kept a copy of <entry> as <copy>. A switch that failed after the prompt puts back the tasks it deleted, and its error line ends with Copies kept: ….

Only entries that OAR generated are deleted or written over. While an entry made or edited by hand would stay beside the new mode, or be replaced by it, nothing changes and the entry is named; see Entries OAR did not make.

Option Effect
--replace Replaces hand-made entries with OAR's own. It keeps a copy of each first, as <name>.bak in the OAR folder: a launcher's beside it, a task's exported XML (<task>.task.xml.bak), a Startup entry's. Without --replace, a hand-made entry is never changed

A boot task that cannot be read (for example Access is denied) stops every switch, --replace included: Nothing changed: the boot task could not be read (<reason>).

Linux and macOS #

On Linux the words map onto oar service: signin and boot run oar service install, off runs oar service remove, status runs oar service status. With lingering the service starts at boot. On macOS: Autostart is not supported on macOS yet. Start the relay with: oar start, exit 1.

Exit code 0: done, or status. Exit code 1: a git checkout, macOS, no config for signin or boot, a hand-made entry in the way, a declined or failed administrator prompt, a boot task that cannot be read, or a sign-in entry that cannot be changed; on Linux, those of oar service. Exit code 2: another word, a second word, or another option.

oar uninstall Added in 0.9.15 #

oar uninstall [--purge]

Takes OAR off the machine and keeps your data. A git checkout is refused (This is a git checkout — there is no global package to uninstall., exit 1). With --purge it first asks its questions (below); then, in order:

  1. Stops the relay, as oar stop does: it waits for running turns. A relay that is still busy afterwards ends the command (The relay finishes its running turns first; it stops by itself after them.), and nothing else is changed. A relay that holds its lock without answering, while it brings over a database or starts, ends it too: The relay (process <pid>) is bringing over its database and does not answer yet; once oar status says it runs, run oar uninstall again., or A relay (process <pid>) holds <lock> without answering; stop it (oar stop, or end process <pid>).
  2. Removes the autostart entries OAR generated for this install: on Windows the boot task (one administrator prompt) with its launcher and task XML, and the sign-in entry; on Linux the systemd unit, if it is the one oar service install wrote. Then the Copilot extension wrapper in ~/.copilot/extensions/web-relay/ that OAR wrote for this install. An entry or wrapper made or changed by hand, or one of another install, stays and is named (Left as it is: <path> (hand-made)).
  3. With --purge, deletes the data (below).
  4. Starts npm uninstall -g @oar-sh/oar in the background, with the npm beside the Node.js that runs oar and into the prefix the package sits in (on Windows, npm.cmd). npm cannot remove a package while it runs, so this waits until oar has ended (up to 2 minutes), then writes npm's outcome to oar-uninstall.log: in the OAR logs folder, or with --purge in the temp folder.

It ends with what it did:

[oar] Done: stopped the relay.
[oar] Done: removed C:\Users\dev\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\oar-web-relay.cmd.
[oar] Your data stays in C:\Users\dev\AppData\Roaming\oar (config, database, uploads, logs). A later install picks it up again; delete the folder yourself if you no longer need it.
[oar] npm removes @oar-sh/oar once this command has ended (log: C:\Users\dev\AppData\Roaming\oar\logs\oar-uninstall.log).

When the data folder lies outside the state folder, the line names both. Session workers keep running when the relay stops, and npm cannot remove the files a running program holds. When programs still run from the package, the command names their process ids, and npm leaves their files in a hidden .oar-* folder beside where the package was; the log names that folder. Delete it once they have ended, at the latest after a reboot.

If a step fails, the command stops with the reason (for example Uninstall stopped: <reason>. or The relay did not stop.), then Done: … and Not done: the autostart entries; the package. (with --purge also the data), and Run oar uninstall again once that is sorted out. (with --purge: oar uninstall --purge).

Option Effect
--purge Also deletes the state folder for good: the config, the database with every conversation, the uploads and the logs. It first says --purge deletes <folder> for good: …, then asks you to type delete (Type "delete" to confirm: ); anything else ends it with Not confirmed. Nothing was changed. Ctrl+C, or no input to read, ends it with exit code 130 and nothing changed

With --purge, some folders get a question of their own, [y/N], after the delete: the state folder when it lies inside a git checkout, rescued-package-data (data an earlier version kept inside its package), and a data folder outside the state folder (dataDir in the config, or COPILOT_WEB_RELAY_DATA_DIR). Whatever you type, --purge deletes nothing in a state folder that is a drive or file-system root, your home folder or a folder above it, a system folder or a folder above one, a folder inside the Windows folder, a git checkout, or a folder without an OAR config or database in it: --purge deletes nothing in <folder> (<reason>). Nothing was changed. An extra folder like that is not offered (Not deleted, and not offered: …).

Exit code 0: the package removal is started. Exit code 1: a git checkout; a state folder --purge refuses; delete not typed; a relay that did not stop or holds its lock without answering; an autostart entry that could not be removed (a declined prompt, a boot task that cannot be read); a folder that could not be deleted; npm could not be started (remove it yourself: npm uninstall -g @oar-sh/oar). Exit code 2: another option. Exit code 130: Ctrl+C at a question.

oar copilot #

oar copilot [--port <port>] [--install-extension] [--no-install-extension] [--migrate-from <dir>] [-- <gh copilot args>]

Starts the relay if none answers, then runs the GitHub Copilot CLI (gh copilot) in your terminal. A relay it started stops when the session ends. GitHub Copilot explains when to use it. The same options without the word copilot still run it: oar -- <args>, oar --install-extension, oar --port 3400 --no-install-extension.

Option Value Default Effect
--port 1 to 65535 the config's port, else 3333 Finds or starts the relay on this port for this run, without changing the config; a config this command creates saves it. A relay already running there is used. A value that is not a port number is ignored without a message
--install-extension none off Writes or refreshes the user-global extension wrapper in ~/.copilot/extensions/web-relay/ (COPILOT_CONFIG_HOME moves ~/.copilot) and exits. Needs no gh
--no-install-extension none off Skips the wrapper for this run. Without it every run writes or refreshes it
--migrate-from a folder none As for oar setup. Write it with a space: --migrate-from=dir is ignored
-- the rest of the line none Passed to gh copilot after a -- of its own

In order: checks that gh exists; refreshes the wrapper (a failure is reported, and only stops --install-extension); migrates old state (global installs); finds the relay on the port; starts it if it does not answer, waiting up to 20 seconds (Relay did not become ready at <url>); then runs gh copilot in the current folder with the terminal attached. A relay that already ran keeps running. A missing config is created with access for this machine only.

--help or -h before the -- prints the general usage text, not one for copilot. A --help or -h after the -- prints the usage of oar copilot and exits 0. --install-extension and --no-install-extension after the -- are read by oar too. None of them reach gh.

The relay of a Copilot session has the port and token it was started with. oar restart cannot move it; see oar restart.

Exit code When
the code of gh copilot The session ran (1 if gh was ended by a signal)
0 --install-extension wrote the wrapper
1 gh is not installed (The Copilot session needs the GitHub CLI (gh)...); the wrapper cannot be written with --install-extension; the port is taken; the relay did not become ready; the relay is still bringing over its database; the state migration failed (a blocked one is reported, Nothing was migrated; starting on the state root as it is., and the session starts anyway)

oar doctor #

oar doctor

Prints what oar finds. No options, changes nothing, exits 0.

OAR 0.9.19
  node          : v22.13.0 (linux)
  mode          : global install
  state root    : /home/dev/.oar
  config        : /home/dev/.oar/config.json
  port          : 3333 (localhostOnly: yes)
  auth token    : set
  tunnel        : disabled
  database      : /home/dev/.oar/data/copilot.db (12.4 MB)
  provider CLIs :
    gh (Copilot)  : gh version 2.60.0 (2025-01-01)
    claude        : 2.1.0 (Claude Code)
    grok          : not found
    cursor        : bundled (@cursor/sdk)
Line Meaning
mode global install or git checkout
state root The state folder; (repo-local server/) in a git checkout
config The config path, with (missing — run: oar setup) when there is none; port, auth token and tunnel are then left out
port The config's port; localhostOnly: yes is this machine only, no is the network
auth token set or MISSING
tunnel disabled, managed (cloudflared: <path>), or managed, cannot start: <reason>
database The path and size in MB, or (not created yet)
provider CLIs gh (Copilot), claude and grok: the first line of <cli> --version (cut at 60 characters), installed if there is none, not found if the command fails or is missing (5 second limit). cursor is always bundled (@cursor/sdk): Cursor needs no CLI
warning In WSL, when Windows holds the relay's port

oar help and oar version #

oar help          # also: oar --help, oar -h, oar <command> --help
oar version       # also: oar --version, oar -v

help prints the usage text. version prints the installed version, for example 0.9.19. Both exit 0. --version and -v work only without a command.

Where oar keeps its files #

oar works on one install: the package it was started from and its state folder.

Install State folder Config Data Logs
Global, macOS and Linux ~/.oar config.json in it data/ logs/
Global, Windows %APPDATA%\oar config.json in it data\ logs\
Git checkout server/ of the checkout server/config.json server/data/ ~/.config/copilot-remote/logs (Windows: %LOCALAPPDATA%\copilot-remote\logs); npm start writes to server/logs

OAR_STATE_ROOT moves the state folder of a global install. A package folder that contains .git counts as a git checkout. The data folder holds the database copilot.db, the lock file relay-server.lock and, between a oar setup and the restart that applies it, relay-previous.json. The logs are server.log and server-err.log. Other places oar writes: ~/.config/systemd/user/oar.service (Linux service), oar-web-relay.cmd in the Windows Startup folder, oar-web-relay-launcher.cmd, the boot task's XML, oar-relay-elevate.ps1, oar-web-relay.log and oar-web-relay-launcher.log in %APPDATA%\oar, and ~/.copilot/extensions/web-relay/extension.mjs. Added in 0.9.15 oar autostart --replace keeps its copies as <name>.bak in the state folder, and oar uninstall writes oar-uninstall.log to the log folder (with --purge, to the temp folder).

Environment variables #

Variable Used by Effect
OAR_STATE_ROOT all The state folder of a global install
OAR_NO_UPDATE_CHECK update 1 blocks the manifest request; --to still works
OAR_UPDATE_MANIFEST_URL update Another URL for the manifest
COPILOT_WEB_RELAY_LOG_DIR all Another log folder
COPILOT_WEB_RELAY_CONFIG copilot only A config file to use when it exists. The other commands always use the install's own config.json
COPILOT_WEB_RELAY_DATA_DIR copilot, start, restart, setup, uninstall, Windows autostart Handed to the relay as its data folder when these start it, except by the Linux service. setup migrates into it, and uninstall names it and offers it under --purge. oar itself looks for the lock file in the default data folder
COPILOT_CLOUDFLARED_MODE, COPILOT_CLOUDFLARED_TOKEN, COPILOT_CLOUDFLARED_BINARY status, doctor Change the Tunnel: line of status and the tunnel line of doctor, as they change the relay's tunnel
COPILOT_CONFIG_HOME copilot The Copilot home folder for the wrapper, default ~/.copilot
APPDATA, USERPROFILE, LOCALAPPDATA, XDG_CONFIG_HOME all Windows and legacy folder locations
NO_COLOR menu Switches colour off
PATH service install The service gets this PATH (plus ~/.local/bin and the Node.js folder)

How oar finds the relay #

oar reads the port and token from the install's config.json and asks http://localhost:<port>/api/status with the token:

Answer oar reads it as
The status of a relay The relay runs
Any other answer (a wrong token, another program), or no reply within 2.5 seconds The port is held by another program, or by a relay with another token
The connection is refused The relay is stopped

A lock file is no proof: it can outlive its relay. oar start and oar copilot wait for a relay that holds it and does not answer yet, and remove only a lock an earlier version wrote. oar update takes a live lock as a running relay. oar uninstall does not go on while a live process holds it. Between an oar setup that changed the port or token and the restart that applies it, the relay still answers on the old ones; oar finds it through relay-previous.json and says it runs with its earlier settings.

With the service installed, start, stop and restart go through systemd on Linux (at once, without waiting for turns); otherwise start launches a detached process and stop and restart use the relay's own interface, which waits until no turn is running. On Windows autostart is never involved in start; status shows Service: installed when autostart or the boot task is on.

WSL #

In WSL, oar detects the distribution (WSL_DISTRO_NAME or WSL_INTEROP, or the kernel release) and asks Windows which program holds a port. oar setup refuses a port Windows holds. oar setup, start, status and doctor print Windows holds port <port> (<program>): a browser on Windows reaches that program, not this relay. Pick another port: oar setup --port N. With LAN access on in WSL's default networking, oar url says the address is internal to WSL.

Exit codes #

Code Meaning Commands
0 Done; stop and restart also when the relay is still busy and finishes by itself all; status only when the relay runs
1 The command could not do what you asked: no config, a git checkout, the port is taken, the relay did not come up or accept the request, npm install failed, gh is missing, an administrator prompt was declined, a hand-made autostart entry is in the way, --purge was not confirmed; also an unexpected error setup, start, stop, restart, url, update, service, autostart, uninstall, copilot
2 Usage error: unknown command or option, a missing value, a bad --port (not copilot) or --autostart, --lan with --local all
3 The relay is not running (stopped, held by something else, or not set up) status only
130 Ctrl+C in a question or in the menu setup, uninstall, the menu
the code of gh copilot The Copilot session ended copilot