All pages
On this page

config.json

config.json is the relay's own configuration file: its port, who may reach it, the access token, the timers that decide when a turn counts as stuck, and the optional tunnels and preview lane. oar setup creates it. You edit it by hand for everything setup does not ask about.

Most of what you set in the web UI is not in this file. See Settings in the web UI and the environment.

Where the file is #

Install Path
Global install on macOS, Linux and WSL ~/.oar/config.json
Global install on Windows %APPDATA%\oar\config.json
Global install with OAR_STATE_ROOT set config.json in that folder
Git checkout server/config.json in the checkout

OAR_STATE_ROOT moves the whole state folder of a global install (config, data and logs); see Where OAR keeps its files.

COPILOT_WEB_RELAY_CONFIG points the relay at one file, wherever it is, but only where the relay is started without oar's own launcher: node server/server.js, or oar copilot (when the file exists). oar setup, oar start, oar restart and the service always use the install's own config.json, whatever the variable says. In a checkout with no server/config.json, oar copilot creates its file in ~/.config/copilot-remote instead (%LOCALAPPDATA%\copilot-remote on Windows). See Environment variables.

The relay keeps the file readable by its owner only (mode 0600 on macOS and Linux), because it holds the access token in plain text. Treat a copy of it like a password.

Who writes it #

  • oar setup writes the token, localhostOnly and port, and keeps every other key as it finds it. A file it creates, or one that lacks them, also gets pollIntervalMs, processingTimeoutMs (600000) and conversationSessionMode. See oar setup and first run.
  • The relay writes cliBinaries when you install or update a provider CLI from Settings. It changes only that key. If it cannot read the file as JSON, it refuses to write and keeps the binding for the running process only.
  • The relay creates the file when none exists at start-up, with an empty authToken, port 3333, localhostOnly: true, pollIntervalMs and conversationSessionMode. It also removes a features key left over from older releases (see Deprecated, ignored and internal keys).
  • You, in any text editor.

Editing it by hand #

The file is one JSON object. Keys you leave out take their defaults, and a nested block such as previews is read as a whole. Edit it, then restart the relay: the relay reads the file once, at start-up. Nothing a running relay holds changes when you save the file. Restart with oar restart (global installs), or with 🌄 Restart web relay in a conversation's ⋯ menu.

  • An unknown key is ignored and kept. Setup and the relay's own writes leave it in place.
  • Invalid JSON does not stop the relay. It logs Failed to read config.json, using defaults. and runs with the defaults: port 3333, this machine only, and a random token that is new on every start and printed in the log. Your saved token no longer works until you fix the file. A second reader, the one that chooses the data folder, falls back to the default folder. The relay does not touch the broken file.
  • A wrong value is handled key by key. The defaults below are what the relay uses when a timer or limit is missing or unusable. The tunnels and the preview lane stay off and log the reason; the relay itself keeps running.
  • The command line wins over the file for one run: --token <value> and --port <number> on node server/server.js (oar copilot passes them too; oar start does not) override authToken and port and are never saved to the file. A --port that is not a number from 1 to 65535 is ignored with a warning.

Do not run oar setup on a file that is not valid JSON

oar setup reads a file it cannot parse as missing. It then writes a new config.json over it with a new token, and every other key in the old file is lost. Fix the JSON first. Keep a copy of a file you edit by hand.

Network and access #

Key Type Default What it does
authToken string none: a random token per start The token every browser, worker and API client signs in with. If it is empty, the relay makes a random one on every start, prints it to its log and does not save it. oar setup writes a stable one.
port number 3333 The HTTP and WebSocket port, a whole number from 1 to 65535.
localhostOnly boolean true true binds the relay to 127.0.0.1 so only this machine reaches it. Any other value, including false, null, 1 and "yes", binds to all interfaces (0.0.0.0) so your LAN can reach it. Only true and the text "true" (any case) count as on; the default applies only when the key is missing. It does not affect the tunnels below.
remotePath string "" A path prefix, when a reverse proxy serves the relay under a subpath, for example /oar. A leading slash is added and trailing slashes are removed. The relay takes the prefix off incoming requests, and puts it into the sign-in cookie's path, the socket path, and the addresses of files and shared links.
trustProxy Express trust proxy value "loopback" Which proxies may set the X-Forwarded-* headers the relay believes. The client address (rate limits, the sign-in lockout, the share access log) and the host and scheme of new share links are read through it, so a direct client cannot forge them. The default suits a proxy or tunnel on the same machine. Use a hop count (1), an address or subnet ("10.0.0.0/8"), or a list of them when a proxy on another host forwards to the relay (Reverse proxy).
allowedOrigins string array [] Added in 0.9.11 Extra browser origins (scheme, host, and a port other than the default, such as "https://relay.example.com") that the live Socket.IO connection accepts besides the relay's own. Needed for a proxy that rewrites the Host header. See Reverse proxy.
publicHostnames string array [] Hostnames that reach the relay itself. The preview lane refuses a previews.publicBaseUrl on one of them (also localhost and 127.0.0.1), so a previewed app never shares a login cookie with the relay.
tunnelMarkerHeaders string array [] Extra header names that a tunnel's edge adds to traffic. A request that carries cf-ray or one of these headers cannot reach the session-worker paths (the sockets your own agents' workers use, which only run on this machine). cf-ray is always on the list. A comma-separated string works too. The names in the environment variable COPILOT_TUNNEL_MARKER_HEADERS (comma separated) are added to these; an empty list here does not switch them off.
pushVapidSubject string mailto:copilot-remote@example.com The contact address the relay sends to push services with Web Push requests (notifications). Set it to a mailto: address of your own.
sharedPresenceMaxPerConversation number 200 Most live viewers counted for one shared conversation link. At least 1. When it is reached, the longest-idle viewer is dropped to make room.
sharedPresenceMaxGlobal number 5000 Most live shared-link viewers counted in total, same rule. Never lower than the per-conversation limit.

For the first connection, the token and LAN access, see first run.

Turns and timers #

Key Type Default What it does
processingTimeoutMs number, ms 600000 How long a turn may go without a sign of life from its worker before the relay puts it back in the queue. It is not a limit on a turn's length. A value of 0 or less uses the default. See When a turn is stuck.
maxRequeueRetries number 5 How often one message may be attempted before it fails for good. At least 1. See Requeueing.
contextIndicatorMode "default" or "bar" "default" How the composer shows how full the context is, as a line along its top edge. default is a full-width line whose colour moves from green to red as the context fills. bar is the same line growing from the left to the share of context used. Any other value, in any case, is default.

The max turn duration, the background task timeout and the stall watches are not here: see When a turn is stuck.

Restarts and shutdown #

These shape the planned restart of the relay (🌄 Restart web relay). See Restarts and resume. Each timer takes milliseconds; a value of 0 or less, or one that is not a number, uses the default.

Key Type Default What it does
restartShutdownTimeoutMs number, ms 45000 How long a restart waits for the turn that is running to finish before it gives up with a shutdown-timeout failure.
restartGracefulTimeoutMs number, ms 8000 How long the relay waits for a graceful stop before it forces the restart.
restartSpawnTimeoutMs number, ms 18000 How long one attempt waits for the restart or resume phase before it counts as failed (spawn-timeout).
restartRebindTimeoutMs number, ms 20000 How long one attempt waits for the sessions to rebind after the restart (rebind-timeout).
restartReadyCooldownMs number, ms 1000 How long the queue stays paused right after a restart reports ready.
restartMaxAttempts number 3 How many attempts a restart gets before it is final. At least 1.
restartRetryBackoffMs number array, ms [1000, 3000, 7000] The wait before attempt 2, 3 and so on. When there are more attempts than entries, the last entry repeats. Entries that are not positive numbers are dropped; an empty result uses the default.

Memory Added in 0.9.19 #

Key Type Default What it does
heapLimitMb number, MB 60 % of RAM, from 2048 to 8192 The relay's heap limit, a whole number of at least 512. Every way of starting the relay reads it: oar start, the systemd service, Windows autostart and the CLI extension. It applies from the next start. A value that is not a whole number of at least 512 is ignored with a line in the log; one above the machine's RAM, or above a container's memory limit, is lowered to that. A --max-old-space-size in NODE_OPTIONS, or on the node command line that starts server.js, wins. See The relay ran out of memory.

Workspace and data #

Key Type Default What it does
workspaceRootAllowList string array unset Absolute folders that conversations may start in, together with everything inside them. Unset or empty allows any existing folder. An entry that is not an existing folder is logged and left out. The environment variable COPILOT_WORKSPACE_ROOT_ALLOW_LIST wins when set. See Limiting where chats may start.
dataDir string data/ beside the server Where the database and the relay's lock file live, and Added in 0.9.17 deepseek-claude, the Claude Code state of DeepSeek chats. A relative path counts from the folder that holds config.json. Uploads follow it into uploads/ inside it; only a relay on the default data/ beside the server, without COPILOT_WEB_RELAY_DATA_DIR, keeps them in uploads/ beside the server. COPILOT_WEB_RELAY_DATA_DIR wins when set. oar start, oar restart and the Linux systemd service always set it (to ~/.oar/data, %APPDATA%\oar\data, or your own value), so for a relay started that way this key has no effect. It is for a checkout started directly. The Windows startup launcher pins only the config file, not this variable.

Provider CLIs #

Key Type Default What it does
cliBinaries object written by the relay Paths to provider CLIs, by provider: claude and grok. The relay writes it when an install from Settings resolves a path, and uses it at the next start so the same program is found again. You can set a path by hand. An entry that is not an executable file is logged and ignored. Other names, copilot included, are not used.
sdkPath string detected Path to the Copilot SDK's index.js. The search for an installed Copilot CLI is skipped only when sdkPath and cliPath are both set. Alone, it also tells the relay where to look for the extension engine's start-up file, next to it.
cliPath string detected Path to the Copilot CLI's runtime entry (app.js). Used with sdkPath. The relay also hands it to the Copilot workers it starts as their CLI program, unless the environment already names one.
sdkVersion string newest found An exact version such as 1.0.63. The search for an installed Copilot CLI then accepts only that version. Ignored when sdkPath and cliPath are both set.

These matter where the relay talks to the installed Copilot CLI itself, for example to import existing Copilot sessions and to list models. The search looks in the Copilot CLI's package cache (COPILOT_PKG_DIR adds one folder to search first). See GitHub Copilot.

Previews #

The previews block turns on the preview lane: a second listener on its own port and hostname that publishes local dev servers and folders. Setup, hostnames and the front proxy are on the preview tool's page.

Key Type Default What it does
previews.enabled boolean false Turns the lane on. Only true counts.
previews.port number relay port + 1 The port of the preview listener. At least 1024, and not the relay's port; 0 picks a free port each start.
previews.bindHost string 127.0.0.1 The address the listener binds to. An address that is not loopback needs previews.allowPublicBind.
previews.allowPublicBind boolean false Allows a previews.bindHost that is not loopback. Every preview is then reachable from the network without the front proxy.
previews.publicBaseUrl string none The public http(s) address of the lane. Required when previews are on. Its hostname must differ from the relay's (see publicHostnames). A trailing slash is removed.
previews.allowedTargetHosts string array [] Hosts, besides this machine, that a port preview may point at, such as the address of a container or VM.
previews.maxLive number 8 Most previews at one time for the whole relay. A value that is not a positive whole number uses 8.

If one of these rules fails while previews are on, the relay logs PREVIEW LANE disabled with the reason, leaves the lane off and keeps running.

These environment variables override the matching keys: COPILOT_PREVIEWS_ENABLED (1, true, 0, false), COPILOT_PREVIEWS_PORT, COPILOT_PREVIEWS_BIND_HOST, COPILOT_PREVIEWS_PUBLIC_BASE_URL and COPILOT_PREVIEWS_ALLOWED_TARGET_HOSTS (comma separated).

Tunnels #

Two tunnels can reach the relay from outside your network. They are independent and can run at the same time. Both are off by default, and a managed tunnel that is missing a required value stays off and logs why.

sshTunnel #

A reverse SSH tunnel to a server of yours. See SSH tunnel.

Key Type Default What it does
sshTunnel.mode "disabled" or "managed" "disabled" managed makes the relay run and restart the tunnel.
sshTunnel.required boolean false While the managed tunnel is not connected, including before it first connects and when a required value is missing, the relay pauses taking messages from the queue.
sshTunnel.user string none The SSH user on the server. Required when managed.
sshTunnel.host string none The server's host name or address. Required when managed.
sshTunnel.remotePort number none The port that opens on the server (1 to 65535). Required when managed.
sshTunnel.remoteBind "loopback" or "public" "loopback" Whether the remote port is open only on the server itself or on all its interfaces. Independent of localhostOnly.
sshTunnel.command string ssh The SSH program. Use a name on PATH or an absolute path: a relative path counts from the relay's program folder.
sshTunnel.identityFile string none The private key to use; ~ is expanded. Without it SSH uses its agent or default keys.
sshTunnel.autoReclaimPort boolean true When the remote port is already held, run a cleanup on the server and try again. Only false turns it off.
sshTunnel.reclaimStaleSshSessions boolean false As part of that cleanup, also end your own SSH sessions on the server that run nothing.
sshTunnel.remoteCleanupCommand string built in Your own cleanup command to run on the server instead of the built-in one.

cloudflaredTunnel #

Runs Cloudflare's cloudflared, which you install yourself; OAR ships none. See Cloudflare Tunnel.

Key Type Default What it does
cloudflaredTunnel.mode "disabled" or "managed" "disabled" managed makes the relay run cloudflared. COPILOT_CLOUDFLARED_MODE overrides it.
cloudflaredTunnel.required boolean false While the managed tunnel is not connected, including when a required value is missing, the relay pauses taking messages from the queue.
cloudflaredTunnel.token string none The token of your own Cloudflare tunnel. Required when managed. COPILOT_CLOUDFLARED_TOKEN overrides it.
cloudflaredTunnel.binary string cloudflared The program, as an absolute path or a name looked up on PATH. COPILOT_CLOUDFLARED_BINARY overrides it.
cloudflaredTunnel.extraArgs string array [] Arguments added after cloudflared tunnel run --token <token>.

Settings in the web UI and the environment #

Settings in the web UI are not in this file. Provider switches and keys, the model lists, the max turn duration, the background task timeout and feature flags are stored in the relay's database, in the data folder. The file holds what the relay needs before the database opens (the data folder, the port, the token) and facts about the machine (CLI paths, tunnels). The one thing the UI writes into the file is cliBinaries. Added in 0.9.17 DeepSeek has no key here either: its API key, base URL and budget are saved from Settings → Providers → DeepSeek into the database.

Environment variables. The full list is on Environment variables. Where a variable exists for a key, the variable wins over the file: COPILOT_WEB_RELAY_DATA_DIR (dataDir), COPILOT_WORKSPACE_ROOT_ALLOW_LIST, COPILOT_CLOUDFLARED_* and COPILOT_PREVIEWS_* (listed in their sections). COPILOT_TUNNEL_MARKER_HEADERS is the exception: its names are added to tunnelMarkerHeaders. The --token and --port options win over both for one run. COPILOT_WEB_RELAY_CONFIG itself is covered under Where the file is.

An example #

All values are made up. Delete the blocks you do not use.

{
  "authToken": "0f3c1a9e-example-token-do-not-copy",
  "port": 3333,
  "localhostOnly": false,
  "remotePath": "",
  "trustProxy": "loopback",
  "allowedOrigins": [],
  "publicHostnames": ["relay.example.com"],
  "tunnelMarkerHeaders": ["x-edge-request-id"],
  "processingTimeoutMs": 600000,
  "maxRequeueRetries": 5,
  "contextIndicatorMode": "bar",
  "workspaceRootAllowList": ["/home/dev/projects", "/srv/sites"],
  "dataDir": "data",
  "cliBinaries": {
    "claude": "/home/dev/.local/bin/claude",
    "grok": "/home/dev/.grok/bin/grok"
  },
  "restartShutdownTimeoutMs": 45000,
  "restartGracefulTimeoutMs": 8000,
  "restartSpawnTimeoutMs": 18000,
  "restartRebindTimeoutMs": 20000,
  "restartReadyCooldownMs": 1000,
  "restartMaxAttempts": 3,
  "restartRetryBackoffMs": [1000, 3000, 7000],
  "previews": {
    "enabled": true,
    "port": 3334,
    "bindHost": "127.0.0.1",
    "allowPublicBind": false,
    "publicBaseUrl": "https://preview.example.com",
    "allowedTargetHosts": [],
    "maxLive": 8
  },
  "sshTunnel": {
    "mode": "managed",
    "required": false,
    "user": "ubuntu",
    "host": "vps.example.com",
    "remotePort": 4444,
    "remoteBind": "loopback",
    "identityFile": "~/.ssh/id_ed25519"
  },
  "cloudflaredTunnel": {
    "mode": "disabled",
    "token": "",
    "binary": "cloudflared",
    "extraArgs": []
  }
}

Deprecated, ignored and internal keys #

If you find one of these in a file, you can leave it or delete it:

Key What it is
features Retired. Feature flags are now stored in the relay's database (Settings → Features). At start-up the relay copies the values it finds here into the database, unless a value is already stored there, and removes the key from the file.
sshTunnel.enabled, cloudflaredTunnel.enabled Old form of mode. true means managed, and is read only when mode is not disabled or managed.
pollIntervalMs Written into new files, but the relay does not read it. It has no effect.
conversationSessionMode isolated or shared. Written into new files. The relay stores the value on each session record and reports it in its status, but nothing in OAR behaves differently for either value. It has no effect for you.
cliPort, model Used only by a standalone mode that you start by hand (a developer escape hatch; no oar command starts it). The relay you start with oar start ignores model. It reads cliPort for one thing: the preview lane never picks that port.
host Read only by the programs that connect to the relay (the session workers and the Copilot extension), to build the address they use; the relay's own listener ignores it (see localhostOnly). Leave it out.