Sync & security

ResearchOS syncs through a server you control, and that server only ever stores ciphertext. This page explains precisely what that means.

Local-first, always

Your workspace on disk is the source of truth. The app works fully offline; sync is a replication layer. Every change also appends an operation to a local journal, inside the same database transaction. Syncing means pushing your new operations and pulling everyone else's.

The key hierarchy

  • Your passphrase derives a key (argon2id) that wraps a random master key — the master key never exists unwrapped anywhere but your devices.
  • A recovery code, shown exactly once at registration — store it safely — wraps the same master key. Forgot the passphrase? The recovery flow unwraps the master key on your device and sets a new passphrase. Lose both, and your synced data is unrecoverable — by design, for everyone including the server operator.
  • Each synced workspace has its own space key, wrapped by your master key. Operations and files are encrypted with it (XSalsa20-Poly1305) before leaving your machine.
  • Every device is registered under your account and listed in Settings → Devices (and ros account devices); revoking a device cuts its access immediately. The phone can log in by scanning a QR code from the desktop app instead of typing.

What the server sees

Stored as ciphertextVisible metadata
Note titles & bodies, todo texts, event details, run commands/params/metrics, PDFs, attachments, chat and direct messages, the compass Account email, device names, entity types, ids, timestamps, sizes, space membership — and, inside a lab, the org layer: lab and team names, membership and roles, plus any expertise profile a member chose to publish

This blindness is enforced by automated tests that dump the server's database after a full sync and search for known plaintext.

One deliberate, clearly-marked exception exists: if you select the plan credits AI provider, that question (and the workspace context it needs) is relayed by the sync server to the model provider in plaintext — then forgotten, never stored or logged. It only happens when you explicitly choose that provider; with your own API key the prompt goes directly from your machine to the provider, and with a lab-local model it never leaves the lab network. Sync itself is never affected.

Project files

Optionally, the workspace's working files (scripts, data, drafts — up to 50 MB per file by default) travel through the same encrypted pipeline: enable Sync project files in Settings or with ros sync files on (a joined computer gets it automatically), keep exclusions in a .rosignore file. Conflicting edits are never lost — the losing version stays next to the file as name.conflict-<device>. This complements git rather than replacing it (.git is always ignored) — and since the server keeps only ciphertext, it doubles as an encrypted off-site backup of your working files.

Remote runs

Queue a run from one computer; another computer of yours that has opted in executes it and the result syncs back as an ordinary run. Enable the executor on the machine that has the data (Settings → Sync → "Allow remote runs on this computer", or ros sync remote-exec on — a per-machine choice that is never synced), then queue from the start-run or re-run dialog ("Run on another computer") or with ros run --remote "python train.py". Requests are E2EE entities like everything else — the server never sees your commands. With project files sync on, your freshly edited scripts reach the executor before the run starts. The executor resolves your project's environment against its own disk — a workspace .venv, environment.yml (conda), uv.lock or poetry.lock is detected automatically — and if it lacks the exact interpreter name (python vs python3 across OSes), the daemon swaps it. When the executor's Python version differs from the one your machine would have used, the run still executes but carries a visible env-mismatch tag recording both versions. The daemon starts at login on Linux and Windows installs, so the executor works with the app closed.

The run comes back whole. When it finishes, the executor ships the run's stdout/stderr logs and its artifact files (up to 100 MB per file) back through the same encrypted blob pipeline, and your other devices write them at the same workspace-relative paths — a remote run then looks exactly like a local one, Logs tab included. Logs arrive at the end of the run; live streaming during a remote run is not there yet. And if you have opted in two executors, a claim is confirmed through a sync round before execution: the machine whose claim loses the merge stands down, so the request runs once.

Conflicts

Merging is per-field: the newest change wins, ordered by hybrid logical clocks so "newest" is well-defined even across offline devices. For long texts, the losing version is kept as a revision — nothing is silently discarded. Deletions travel as tombstones.

Sharing is explicit

Every user's spaces are isolated — by server authorization and by cryptography (another user's data would be undecryptable ciphertext even if authorization failed; tests attempt exactly that). Sharing happens only through sealed-box invites: the space key is encrypted to the invitee's device public key, relayed — unreadable — by the server.

Conversations and the lab

Direct messages, group chats and announcements are ordinary encrypted spaces that happen to contain only messages — no new machinery, the same keys and sealed-box invites as everything else. Messaging a colleague creates the two-person space and seals its key to their devices; their app accepts automatically. The consequence is worth stating plainly: the lab can never read direct messages. There is no organization-level key that can open a member's spaces — a lab admin reads only the spaces they are a member of, like everyone else. What a lab does see is its organization layer, which is server-side metadata by necessity (you cannot discover people through ciphertext): lab and team names, membership, roles, and the opt-in expertise profiles members publish by hand. Admin oversight is built from that metadata — activity counts, an audit trail — plus progress reports members explicitly choose to publish into a shared space. Outside a lab, the server enforces one rule on conversations: you can only start one with someone you already share a space with.

Sealed logbook entries

Sealing computes a sha-256 hash over the entry's canonical content, chained to the previous seal. The store refuses any later edit or deletion; verification recomputes the chain and localizes tampering to the exact entry. Beyond the local chain, a sealed entry can be anchored to an RFC 3161 timestamping authority (ros note timestamp) — independent proof the seal existed at that moment — and exported as a self-contained evidence package (ros note probative) that a third party can verify without ResearchOS. Timestamps from a qualified (eIDAS) provider are planned for the Pro plan.

If the server dies

Nothing is lost: local data is the truth, on every device. Re-create the space and any one device re-uploads the full history.