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 ciphertext | Visible 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.