All pages
On this page

Files and folders

OAR keeps everything it owns in one folder, the state folder: the config file, the database with your conversations, the logs and the uploaded files. This page says where that folder is, what is in it, which parts you can delete, which hold secrets and what the relay removes by itself. Some data is not in the state folder at all: each provider keeps its own session data and logins in its own folders.

The state folder #

Install State folder
Installer or npm install -g, macOS, Linux and WSL ~/.oar
Installer or npm install -g, Windows %APPDATA%\oar
Global install with OAR_STATE_ROOT set the folder it names
Git checkout inside the checkout: server/config.json, server/data/, server/uploads/, server/logs/

npm install -g and oar update replace the package but never touch the state folder. OAR_STATE_ROOT has to be set in every shell where you run oar, or the command looks in the default folder; see Environment variables. oar doctor prints the paths in use: the state folder, the config file, the database and its size. The same paths are listed for each install in Install.

Three environment variables move single parts: COPILOT_WEB_RELAY_CONFIG the config file, COPILOT_WEB_RELAY_DATA_DIR the data folder (database, lock and uploads) and COPILOT_WEB_RELAY_LOG_DIR the log folder. In config.json the key dataDir moves the data folder too; the environment variable wins. A relative dataDir counts from the folder of config.json.

Added in 0.9.14 Uploads follow the data folder too: <dataDir>/uploads. Uploads that earlier versions left in server/uploads, or that a migration copied into the state folder's uploads/, are still read there in place.

Added in 0.9.14 A global install's relay runs on the state folder's data folder however it is started: oar start, the Linux service, both Windows autostart modes and a plain gh copilot with OAR's extension. A dataDir in config.json that an earlier autostart relay used is copied from once and left as it is.

Before 0.9.14: Windows autostart and gh copilot

Up to 0.9.13 the two Windows autostart entries, and the relay a plain gh copilot started, set only the config file (gh copilot not even that). A relay they started kept its database, lock file, uploads and logs in server\data, server\uploads and server\logs inside the installed package folder, which npm install -g and oar update replace. The update to 0.9.14 copies that data before npm removes it; see the upgrade note.

On macOS and Linux the relay makes the data folder readable by you only (mode 700), and the config file and the database files owner-only (mode 600).

Moving an old install #

Before the project was renamed, state lived in a git checkout's server/ folder, and the config file and logs of a checkout's launcher in ~/.config/copilot-remote ($XDG_CONFIG_HOME/copilot-remote when that is set; Windows: %LOCALAPPDATA%\copilot-remote). A global install copies the config file and logs of that folder into the state folder once, with its first oar setup or oar copilot. oar start does not migrate. To import the state of a checkout, which includes its database:

oar setup --migrate-from /home/dev/projects/old-oar

The folder is the checkout itself, not its server/ folder. What happens:

  • It copies the database (with its -wal and -shm files), the config file, the Cursor agent stores that were in data/ (only when there was a database), the uploads folder and the logs folder when that is 50 MB or smaller.
  • It never changes the source. Delete the old folder yourself when you are satisfied.
  • It refuses while a relay still runs from the source, and says so.
  • It checks the copy before it counts: the database must pass SQLite's integrity check and hold the same number of conversations and messages. If not, it removes what it copied, Added in 0.9.14 no longer the whole state folder, and leaves a message.
  • Added in 0.9.14 It never overwrites. A config.json already in the state folder is kept. The empty database an installer's relay created is moved into backups/ (stop the relay first: oar stop); a database with conversations makes it refuse and say what to do instead. While it copies a database it holds the relay's lock, so no relay starts on it meanwhile.
  • It writes a receipt to backups/MIGRATION.md and a marker file .migrated in the state folder.

After the installer Added in 0.9.14

The marker is written only after a real copy, and a folder you name with --migrate-from is always migrated, with a line saying what was copied or why nothing was. So the option also works after an installer has run oar setup. The same folder a second time only says it is already migrated. Before 0.9.14 the first oar setup or oar copilot wrote the marker even with nothing to import, and a later --migrate-from did nothing without a word.

Added in 0.9.14 Migrated uploads land in data/uploads/, where the relay reads them, and the Cursor agent stores in data/cursor-agents/, which the relay reads by default. Uploads an earlier migration left in the state folder's uploads/ are merged into data/uploads/ at the next relay start; nothing is overwritten or deleted. Symlinked folders in the checkout are copied as real folders.

What is inside #

Paths are relative to the state folder. "Safe to delete" means the relay works afterwards; it says nothing about what you lose.

Path What it holds Safe to delete?
config.json Port, access, the access token, timers and optional tunnels and previews. See config.json Stop the relay first. oar setup writes a new one with a new token, which signs every device out. Tunnel settings in it are lost
data/copilot.db The database: your conversations and settings. See The database No: this is your history. Back it up first
data/copilot.db-wal, data/copilot.db-shm The database's write-ahead log and its index, present while the relay runs Never alone. Delete them only together with copilot.db
data/uploads/<sha256> One file per uploaded or pasted attachment, named by its SHA-256 hash, so the same file is stored once. A <sha256>.<pid>.<number>.tmp file exists while an upload is written Not by hand: the database refers to these files, and a conversation that used one shows a missing attachment. The relay removes unused ones, see Retention
data/relay-server.lock The one-relay-per-folder lock, see One relay per data folder Only when no relay runs
data/cursor-agents/ Added in 0.9.14 One folder per Cursor conversation: its agent's state. See Outside the state folder Not while you use Cursor: a conversation without its store gets a new agent
data/deepseek-claude/ Added in 0.9.17 The Claude Code state of DeepSeek chats: transcripts and session folders under projects/, what each Claude Code process leaves (sessions/, session-env/, shell-snapshots/), Claude Code's own settings, and oar-cli-starts.json, the relay's record of each Claude Code start, which a delete uses to find the right shell snapshots. No Claude login. Readable by you only (mode 700 on macOS and Linux). Deleting a DeepSeek conversation removes its part, see Retention Not while you use DeepSeek chats: their Claude Code sessions are here. The worker creates the folder again when it starts
data/diagnostics/ Added in 0.9.19 Heap snapshots (*.heapsnapshot) the relay writes when its heap nears its limit, see The relay ran out of memory. Readable by you only (mode 700 on macOS and Linux). A snapshot holds the access token and the API keys Yes. The relay never deletes snapshots: delete each one once it has been read
data/rescued-package-data/<time>/ Added in 0.9.14 The data an earlier version kept inside the package, copied there by the update before npm removed it. The relay takes it over at its next start Once the relay has brought it over ([data] Brought over the database … in relay-console.log)
backups/<time>/ Added in 0.9.14 A database the relay replaced with a newer one it brought over; also the empty database a migration moved aside Once you are sure you do not need it
data/relay-previous.json The port and token the running relay started with, kept between an oar setup that changed them and the restart that applies them. Removed by itself Yes
logs/server.log, logs/server-err.log What oar start or oar copilot printed when it started the relay itself, standard output and errors. Only written when oar starts the relay itself; the service and a relay run in a terminal do not write them Yes. They never rotate and grow
logs/relay-console.log A copy of the relay's console with a time stamp per line, written by every relay Yes
logs/worker-<session>.log Output of one session worker, see Logs Yes
logs/worker-<session>.mcp.json The tool configuration of one session. It holds no secrets Yes. The relay writes a new one when the session's worker starts
logs/ext-debug.log, logs/relay-banner-state.json Written by the Copilot extension engine's extension Yes
npm/ The OAR package, when the installer put it here because npm's global folder needed root Not while you use this install. It is the program
runtime/node/ OAR's own Node.js, when the machine had none new enough Not while oar runs on it
logs/oar-uninstall.log Added in 0.9.15 npm's outcome after oar uninstall, and any hidden .oar-* folder npm left behind. With --purge it goes to the system's temp folder instead Yes
oar-web-relay-launcher.cmd, oar-relay-task.xml (Windows) The launcher the boot task runs, and the task definition OAR registers. See Autostart Not while the relay starts at boot. oar autostart off removes both
*.bak, *.bak-2, <task>.task.xml.bak (Windows) Added in 0.9.15 Copies of hand-made autostart entries that oar autostart --replace replaced, and of the tasks a failed switch put back: a launcher, a Startup entry, a task's exported XML (schtasks /create /xml takes it back). Copies of entries OAR generated go once a switch has worked Yes, once you do not want the entry back
backups/MIGRATION.md, .migrated The receipt and marker of a migration The receipt: yes. The marker: leave it, or the automatic migration from the earlier config folder could run again

Outside the state folder, the installer's oar launcher is in ~/.local/bin/oar (only in the cases where it installs into ~/.oar/npm), and the Linux service is ~/.config/systemd/user/oar.service. Removing them is described in Removing OAR. Added in 0.9.15 oar uninstall removes the service and the package and leaves the state folder as it is; oar uninstall --purge deletes it, after you type delete.

A git checkout keeps the same files in server/. Generated images are not in the state folder, see Outside the state folder.

The database #

The database is one SQLite file, copilot.db. The relay creates it on first start. It holds what you would call your data:

  • conversations and their messages, with the transcript, attachments' index and pinned messages
  • the message queue, queued and waiting messages, and questions an agent asked
  • the relay's settings that you change in the web UI (the app-wide switches, provider settings, retention choice, update preferences, the model lists)
  • the OpenAI, Cursor and Added in 0.9.17 DeepSeek API keys, if you entered them
  • paired relays and their tokens, and the subscriptions that watch sessions on this and on paired relays
  • the web-push subscriptions of your devices and the relay's push keys
  • shared-conversation links
  • sub-agent and workflow records, activity and thoughts per conversation
  • the index of uploaded files and which messages use them
  • usage snapshots for the plan meters (Check Usage), and Added in 0.9.17 what the DeepSeek card counts: the tokens and searches of DeepSeek turns per model and per day, the last totals each DeepSeek session reported, and the balance readings its bar is measured against
  • image chats: their operations and the links between generated images
  • preview cards

Migrations run by themselves every time the relay starts. They create what is missing and add columns, and running them again changes nothing. You do not run them and cannot skip them.

Backing up #

OAR has no backup command. The database runs in SQLite's write-ahead mode, so recent changes can sit in the -wal file next to it. To back it up:

  1. Stop the relay with oar stop.
  2. Copy copilot.db and, if they exist, copilot.db-wal and copilot.db-shm together.
  3. Start the relay again.

To restore, stop the relay and put the copied files back as they were. Do not copy copilot.db alone while the relay runs, and do not mix a database file with the -wal file of another copy. A backup contains your API keys and tokens, see Where secrets live; keep it as private as the state folder.

Logs #

File Written by Rotation
relay-console.log the relay, always At 5 MB the file becomes relay-console.log.1, the older ones shift to .2 and .3, and the oldest is dropped. Lines show your access token as [token]. OAR_NO_CONSOLE_LOG=1 switches the file off
server.log, server-err.log the shell of oar start or oar copilot, when it starts the relay in the background none
worker-<session>.log each Node session worker (Claude, Claude Cloud, Cursor, Grok, Added in 0.9.17 DeepSeek and the Copilot SDK engine), appended across worker restarts At each launch a file over 10 MB becomes worker-<session>.log.1, replacing the previous one. Files of deleted conversations stay
ext-debug.log the extension of the Copilot extension engine none

The Copilot extension engine has no worker log: it is the Copilot CLI's own terminal. A relay run as a systemd service also writes to the journal, see Start the relay automatically.

A git checkout started with npm start keeps its logs in server/logs. One started with oar start or oar copilot keeps them in ~/.config/copilot-remote/logs (Windows: %LOCALAPPDATA%\copilot-remote\logs), unless COPILOT_WEB_RELAY_LOG_DIR names another folder.

Where secrets live #

Secret Where it is stored
The access token config.json, in plain text. Treat the file, copies of it and the sign-in address (?token=) like a password. A browser holds it as a sign-in cookie for 30 days. The relay's lock file keeps only a hash of it
The previous access token, between oar setup and the restart data/relay-previous.json, in plain text, readable by you only; removed by itself
A Cloudflare tunnel token (managed tunnel) config.json, key cloudflaredTunnel.token, or the environment variable COPILOT_CLOUDFLARED_TOKEN
OpenAI API key The database. The panel never shows it again and it is not sent to browsers
Cursor API key The database, the same way
DeepSeek API key Added in 0.9.17 The database, the same way (setting deepseek_api_key). The relay sends it only to the DeepSeek base URL, and hands it only to the workers of DeepSeek chats. See DeepSeek
A paired relay's token The database. Never sent to browsers or agents. A pairing that uses this relay's own token stores none
Shared-conversation link tokens The database
Web-push keys and device subscriptions The database
Heap snapshots Added in 0.9.19 data/diagnostics/: everything that was in the relay's memory, the access token and the API keys included. Never share one as it is. See The relay ran out of memory
Claude login The Claude CLI's own folder on the relay's machine: ~/.claude, or the folder in CLAUDE_CONFIG_DIR. The relay stores none. Claude Cloud reads the same login and keeps it in memory. DeepSeek chats do not use it
Grok login The Grok CLI's own folder: ~/.grok/auth.json
GitHub Copilot login The Copilot CLI's own login on the relay's machine

While a worker starts under tmux, the relay writes its provider key (the OpenAI, Cursor or Added in 0.9.17 DeepSeek key, or a CLAUDE_CODE_OAUTH_TOKEN from the relay's environment for a Claude Cloud or, Added in 0.9.17, a Claude worker) to a temporary file in the system's temp folder, readable by you only. The worker's shell reads it once and deletes it. Each worker gets only its own provider's key.

Retention #

The relay removes these by itself. Nothing else is deleted unless you delete a conversation.

What Rule
Unused uploads A file that no message or draft refers to is removed once it is older than 24 hours. The check runs when the relay starts and every 6 hours. There is no setting. Deleting a conversation also removes the files only it used
Sub-agent records The tool inputs and results of finished sub-agents are removed after Keep sub-agent tool output days, in Settings → General: 7, 14, 30 or 90 days, or Always. The default is 90. Thoughts, replies, notes and the one-line summary of each call stay. The check runs 30 seconds after start, once a day and 5 seconds after you change the setting. The database file does not shrink; SQLite reuses the freed space
Relay events for watching sessions Events the watching conversation has been given are removed after 2 hours. A subscription that ended is removed after 7 days. Details in Watching sessions. The sweep runs every 10 minutes
Events a paired relay has not fetched Per subscription the newest 500 are kept, none older than 7 days, and a subscription nobody fetched for 7 days ends. Questions and errors are dropped last. See Remote relays
relay-console.log Rotates at 5 MB and keeps three older files, see Logs
worker-<session>.log Moves to .1 over 10 MB at launch. Nothing removes the logs of deleted conversations
Generated images Never by age. Deleting the conversation deletes its images
The lock file Removed when the relay stops. After a crash, the next start replaces it, see One relay per data folder

Deleting a conversation removes its messages, queue entries, questions and sub-agent records from the database, and the Claude transcript and the generated images belonging to it. Added in 0.9.17 For a DeepSeek conversation it also removes, in data/deepseek-claude/, the transcript, the session's folders, what its stopped Claude Code left (its registration, hook environment and shell snapshot) and the project folder once no other session uses it, and from the database the last totals its sessions reported. What it added to the DeepSeek card's totals and days stays. What each provider keeps itself is in the next section.

Outside the state folder #

What Where
Claude transcripts and session folders Under projects in the Claude configuration folder (~/.claude, or CLAUDE_CONFIG_DIR). Deleting a conversation deletes its transcript. See Claude. Added in 0.9.17 DeepSeek chats keep theirs in the data folder instead: data/deepseek-claude/ above
GitHub Copilot session state ~/.copilot/session-state, or the folder in COPILOT_SESSION_STATE_DIR
Pictures from OpenAI Image chats In the Copilot session-state folder, in <session>/generated-images/ under the conversation. See OpenAI
Cursor agent stores Added in 0.9.14 One folder per conversation, in cursor-agents in the relay's data folder (~/.oar/data/cursor-agents, %APPDATA%\oar\data\cursor-agents), or in the folder named by CURSOR_AGENT_STORE_DIR. In the state folder on a global install; listed here because earlier versions kept them elsewhere. See Cursor
The Grok CLI and its login ~/.grok
The Copilot extension wrapper ~/.copilot/extensions/web-relay/, written by oar copilot, or oar copilot --install-extension. Added in 0.9.15 oar uninstall removes it when it is the one OAR wrote for this install
Claude Cloud Nothing on disk. A cloud session runs at Anthropic; its results come back as pushed branches
Temporary files The short-lived provider-key file above, in the system's temp folder

Before 0.9.14 the Cursor stores sat in server/data/cursor-agents inside the package folder by default, which an update replaces, so a conversation could lose its stored agent and get a new one (see Cursor). A conversation whose store is still at that old place keeps using it there, and one with a store in both places uses the one changed last; nothing is moved or deleted.

Your agents' work, the files in your project folders, is outside OAR's storage entirely.

One relay per data folder #

The relay creates relay-server.lock in the data folder when it starts and removes it when it stops. The file holds the relay's process number, a hash of its token and its start time.

If a second relay starts on the same data folder while the first one runs, the second one prints one line and exits, Added in 0.9.14 with code 78 (code 1 before), which the Linux service does not retry:

Relay already running (pid=4242, startedAt=2026-01-05T09:12:40.000Z; lock /home/dev/.oar/data/relay-server.lock). It is held by relay process 4242: oar stop, or end process 4242.

Relay appears to be owned by another live process instead of Relay already running means the running relay has another token. When the holder cannot be told to be a relay, the hint reads If no relay is running, end process 4242 or delete the lock. oar start prints the same hint after The relay did not come up. The first relay is not touched. Messages stay pending when the relay you expected is not the one that runs; oar doctor shows the config and database in use.

After a crash or a power loss the lock file is left behind. Added in 0.9.14 A lock from before the last boot, or whose process number now belongs to a process started after it, is stale: the next start moves it aside, logs Recovered stale singleton lock, and takes the lock over. This no longer goes by the clock, so a clock step does not make a running relay look gone, and two starts recovering the same lock do not both run. A lock that cannot be read (an empty one a power loss left) is read again and counts as held unless its time is more than 10 seconds off. oar start no longer removes a lock this version wrote; it still removes one of an earlier version when nothing answers on the relay's port.

Two relays can run side by side when they have separate data folders and ports. See The oar command for how oar finds the relay, and Restarts and resume for what a restart keeps.