Omniscio documentation
Browse all documentation
  1. Getting Started13
  2. Sessions & Agents115
  3. Inbox & Notifications59
  4. Projects & Tasks95
  5. Automation & Scheduling75
  6. Knowledge & Memory26
  7. AI Features60
  8. Integrations100
  9. Plugins & Marketplace33
  10. Cloud & Teams56
  11. Settings & Customization58
  12. Account & Billing28
  13. Troubleshooting84
  14. CLI & API Reference22
  15. Legal & Policies4
  16. Uncategorised22

Cross-Device Sync

Sign in on a second computer and your setup is already there — sessions, projects, recipes, automations, bookmarks and non-secret settings, kept end-to-end encrypted. Covers the opt-in switch, device enrollment and the one-time recovery code, what happens to running sessions and scheduled jobs, and the security guarantees.

Status: available in Settings, OFF by default (opt-in). The cross-device-sync toggle (Settings → Cross-Device Sync) is the real off-switch — sync runs only when you turn it on, and the change takes effect live (see "Turning it on and off" below). The engine, the live background runner, the new-machine experience + device management, the storage housekeeping (oplog compaction + garbage collection), and the diagnostics readout are all built. One hosted step remains an owner action — a functions-only relay deploy that lights up the GC delete/list ops; core push/pull sync uses the live /t/sync relay and GC simply no-ops until then (see "Storage housekeeping").

What it is

Sign into Omniscio on any of your computers and your setup is already there — sessions, projects, recipes, automations, bookmarks, non-secret settings — kept in sync end-to-end encrypted. The cloud is an encrypted courier, not a new authority: your local mission-control.db stays the source of truth, and Omniscio's server holds only ciphertext it cannot read or undetectably tamper with (zero-knowledge). It evolves the folder-based Backup Mirror into a seamless, Omniscio-hosted experience — no cloud drive to own or configure.

Secrets never sync (API keys, OAuth tokens): they're per-machine and re-approved once via the existing Post-Restore Credential Wizard.

Where to find it

Cross-Device Sync has its own section in Settings, reached from the app's Settings gear — that section holds the on/off toggle, your device list, the key-rotation control, and the card that offers to switch you over from the folder-based backup if you were using it. A read-only summary of what sync has been doing appears in Settings → Diagnostics. When another computer asks to join, you are not expected to go looking for any of this: the request arrives as a card in your inbox.

How it behaves

Scheduled jobs run on one computer (not twice)

Your scheduled jobs — crons and scheduled recipes — sync like the rest of your setup, but they run on only one of your computers, not on each one at the same time. Without this a synced cron would fire on every signed-in computer at once, doing the work (and spending) twice.

  • A cron runs on the computer that owns it. A cron you create binds to the computer you made it on; move it by editing it on the computer you want it to run on.
  • Editing an existing cron binds it. Crons created before you turned sync on keep behaving as before until you next edit one — that edit binds it to the current computer. (Scheduled recipes bind automatically the next time they run.)
  • Omniscio's own background jobs still run on every computer — routine maintenance like folder cleanup and drift checks stays per-computer and is never affected.
  • Remove a computer and its jobs move on. De-enroll a computer that owned some jobs and they're released for another computer to pick up — they never silently stop.

This is fully effective once both computers are on the updated app. See cross-device-sync-contract.md (device-scoped-execution).

Sessions from another computer come over Paused (and resumable)

A session that was running on one computer arrives on your others as Paused, not running — its live Claude process only exists on the machine that started it, so nothing auto-runs (and nothing double-charges) when your setup lands elsewhere. Open one and it picks up right where it left off, with its full history. A session you're genuinely running on the receiving computer is never touched.

See cross-device-sync-contract.md (session-status-device-local).

Turning it on and off

Cross-Device Sync is OFF by default — it's opt-in. The Enable Cross-Device Sync toggle in its Settings section is the real switch:

  • Off by default. With the toggle off, the background sync runner never starts, installs no capture triggers, and does nothing — for every user, even signed in. Nothing leaves your machine until you opt in.
  • Takes effect live. Flipping it on starts sync and off stops it immediately — no restart — from any surface (the desktop UI, the CLI, or the mobile app); turning it off tears down the runner and its capture triggers at once.
  • The toggle reflects the real state. It reads on only when you've actually enabled it — it won't snap back to on when it's off.
  • It never slows the app. The background loop paces itself, leaving a minimum rest between sync cycles, so even on a busy machine with many sessions it can't run back-to-back and compete with what you're doing (the app's "never slow the user" rule) — and the pacing can't starve it, so your devices still stay in sync.

Under the hood the runner gates on three things — the feature is visible, you've enabled it, and you're signed in — and a settings change reconciles it on the spot. See cross-device-sync-contract.md (runner-inert-for-real-users).

Coming from folder Backup Mirror?

If you already use the folder-based Backup Mirror, Cross-Device Sync offers a one-time, additive way to switch. Turn the feature on and you'll see a "Switch from Backup Mirror to Cross-Device Sync" card in the sync settings (plus a matching nudge in Settings → Backup & Restore). Adopting is non-destructive — your Backup Mirror keeps working, nothing is deleted, and setting up sync just enrolls this device (the same flow below), so your existing setup rides the encrypted courier from now on. Once you're synced, an optional hint lets you turn folder Backup Mirror off if you no longer want a second backup — reversible anytime. The offer appears only while the feature is on (it's off by default), and it's shown at most once.

The key model (why there's a recovery code, not a password)

One random data key (DK) encrypts everything. Omniscio uses federated sign-in (Google), so there's no password to derive a key from without the server seeing it — instead the unlock is device enrollment (the Signal / 1Password linking pattern):

  • The first device generates the DK, seals it under a machine-local key in the OS keychain, and uploads two wrapped copies to the locker: a recovery-code wrap and a device wrap. It shows the recovery code once.
  • Every later device must enroll to get the DK — two ways (below).
  • The server only ever stores wrapped copies of the DK; day-to-day unlock is silent (the device-local key is already present).

The recovery code is high-entropy (≥128-bit, Crockford base32) so the recovery-wrapped DK can't be brute-forced offline. It is shown once — the app makes you tick "I've saved my recovery code" before you can continue, and there is no way to re-fetch it — so store it somewhere durable (a password manager). It is your only break-glass: lose every enrolled device and the recovery code → the data is permanently unrecoverable (the price of true zero-knowledge; the server holds only wrapped copies it cannot open). A key rotation mints a new code and retires the old one, so save the new one each time you rotate.

The two everyday flows

Steady state (a device you already use): a local change → debounce → append an encrypted oplog segment → atomically advance the manifest. Quiet and continuous, and it self-heals through a key rotation (syncCycleWithRotationRecovery). A throttled maintenance pass rides the same cycle to keep storage bounded (see "Storage housekeeping" below).

New machine (Phase 3): install Omniscio → sign in → enroll this device → the app downloads + decrypts + replays the oplog to rebuild the local DB → the credential wizard walks you through re-approving accounts → live. The modal offers an optional name for this device first (see "Device management" below — you can also set it later); the name is applied once enrollment succeeds, so a failure there never blocks the rebuild. Two enroll paths, offered as tabs in the enrollment modal:

  • Approve on another device — this device publishes a pending request (its public key) to your device registry; a device you already use re-wraps the DK to that key (ECIES over X25519) and drops it in the locker; this device polls, unwraps with its machine-bound private key, and finishes. The approval screen shows a short key fingerprint so you can eyeball-match it.
  • Enter recovery code — paste the one-time code; it unwraps the recovery-wrapped DK directly.

You get told, you don't have to go looking. When a new device asks to join by the "Approve on another device" path, one of your already-set-up computers notices within about a minute and pops up a "A new device wants to join your sync" card in the inbox (plus a native notification if the app isn't focused). One click on it takes you straight to the approval screen — no hunting through Settings. It's still a real approval, not a blind one-tap: you confirm the new device's short code there before approving (approving lets it decrypt your synced data). The card clears itself once the device is approved, denied, or gone, and — like everything here — it only appears while sync is turned on. Approving stays a human-at-the-computer action: an AI agent or your phone can't approve a device for you. If several devices ask at once, they collapse into one card; a long-abandoned request is ignored.

And the new machine gets told too — it can't be left quietly un-joined. The prompt above is the approving side; the waiting side has its own. While a computer is signed in to an account that already syncs but has not joined it yet, that computer shows its own "This computer has not joined your sync yet" card (plus a native notification if the app isn't focused), and it clears itself the moment you join. That matters because the enrollment window that opens by itself lives in one app window's memory: dismiss it, never open Settings, or have the very first check fail, and nothing else used to say a word. The check itself no longer gives up either — it retries on a widening backoff, tells you if it runs out of tries (with a Try again button rather than silence), and re-runs the moment you finish signing in, so a computer switched on mid-sign-in lands in the join window instead of waiting for an app restart. Neither the card nor the window joins or approves anything on its own: they only take you there, and the fingerprint check on your other computer is untouched. See enrollment-never-silent in the contract.

Restore is additive-merge, never a wipe — rows that exist only locally are kept (remote deletes still apply), so adopting sync on a machine that already has data merges rather than destroys. When a remote delete does apply — a session or project you removed on another computer — its dependents are cleared with it (its messages, plus device-local extras like terminal history or tracker rows), so the deletion lands cleanly on every computer instead of leaving orphaned leftovers behind.

Same folder or same name on both computers? If each computer independently created a project for the same folder — or a tag with the same name — sync treats them as one and merges them instead of stalling: your computer keeps its own copy and the other computer's chats (and links) are filed under it, so you end up with a single project/tag carrying all its history, never a duplicate. See cross-device-sync-contract.md (unique-collision-converges).

Attachments follow the same rule — a file deleted on one device is removed on your other devices on the next pull, and SAFELY: each device remembers the set it last synced, so it never deletes a file still referenced by the current set, one you made locally and haven't uploaded yet, or one you've since edited (your edit wins and re-syncs). It's behind the same default-off gate; a delete on the origin device is no longer resurrected by its own next pull.

Computers on different app versions? Sync pauses cleanly

Your setup evolves between releases — a new feature can add a new field to your projects, sessions, and the rest. If one computer is a version or two behind and a newer one sends it data shaped for a field the older one doesn't have yet, sync pauses on the older computer with a clear "update Omniscio on this device to resume syncing" message — it never crashes, and nothing is half-applied (the whole sync attempt rolls back). Update the older computer and sync resumes on its own; the pause clears itself. A newer computer receiving older data is unaffected — a field the older computer didn't send just takes its normal default. See cross-device-sync-contract.md (replay-schema-drift-pauses-cleanly).

Storage housekeeping — compaction + GC (keeps cost bounded)

Left alone, the oplog would grow forever (every change is an append), so a maintenance pass — throttled (≥1h) and riding the normal sync cycle so it never blocks a sync — keeps both the cloud footprint and a new-machine restore bounded to current state, not lifetime history:

  • Compaction folds the accumulated oplog objects into a base snapshot (a delete-preserving coalesce — last write wins per row, so deletions survive) and resets the live oplog. A new machine applies the snapshot first, then the recent tail. A readerFormat gate makes an older app refuse a compacted manifest rather than silently drop the folded history, and the snapshot sits inside the manifest MAC.
  • Garbage collection reclaims the now-orphaned objects, but only after they've been unreferenced across two maintenance passes (≥1h apart) — so an object mid-upload on another device is never collected. It never touches an object the current manifest (including the base snapshot) still references, and it holds even against a lying object list.
  • Account deletion purges your whole locker server-side (best-effort, retried by a daily reconciliation sweep) — a deleted account's devices may never run again to clean up after themselves.

The relay's delete + list operations back GC; until they're deployed the client stays optimistic and GC simply no-ops (the feature is off by default regardless).

Diagnostics readout (observability)

Because the runner works quietly in the background, a read-only diagnostics readout in Settings → Diagnostics ("Cross-device sync diagnostics") shows what it actually did — visible only while the feature flag is on, so a real user never sees it:

  • Current status — synced / syncing / paused (offline · metered · daily-limit) / needs sign-in or enrollment / failed, plus the last-synced time and the last humanized error.
  • Running totals — cycles run, failures, bytes uploaded/downloaded, objects compacted, orphans reclaimed (since the app started or the last account switch).
  • Recent cycles — the last several sync cycles: objects applied, conflicts, bytes moved up and down, the manifest version, and any failure reason.
  • Recent maintenance — the last several housekeeping passes: whether compaction folded (and how many objects → snapshot bytes), orphans reclaimed, and orphans still pending their second strike.
  • Locker size / health — total objects in your cloud locker, how many the current manifest references (the live set), and how many are orphaned garbage, refreshed on each maintenance pass.

Everything is zero-knowledge-safe — it carries only counts, byte totals, versions, timestamps, and already-humanized errors, never a secret, key byte, row value, or object name. The same numbers are also written to the app log as structured [sync-metrics] lines for after-the-fact debugging. The whole readout stays behind the default-off gate: with sync disabled nothing is recorded and the readout is empty, and it resets when you switch accounts.

Device management (Phase 3, in the Settings section)

  • Your devices — a per-user list (each device is public metadata only: label, key fingerprint, last-seen; no key material). It lives in Firestore user_sync_devices/{uid}/devices/{deviceId}, readable/writable only by you.
  • Naming a device — setting up sync asks you to name the computer before it provisions, and the enrollment modal offers an optional name for a machine joining later. Rename (the pencil on any row) changes it afterwards, from any of your machines — including the one you are sitting at, whose row otherwise has no actions. Without a name a device falls back to the neutral "This device", which is unambiguous on one machine and useless on two, so every row you have not named reads identically. The app will never name a device for you from the OS hostname: the label is the one sync field stored in the cloud as readable plaintext, and consumer hostnames embed the owner's real name — so the dialog says so and lets you choose. The rename writes label alone (it cannot disturb the public key device-approval depends on) and carries an exists-precondition, so renaming a device that was removed on another machine fails cleanly instead of resurrecting a ghost record.
  • De-enroll — removes a device from your registry.
  • Rotate keys — the security response to a lost/compromised device: re-encrypts the whole locker under a brand-new DK and revokes any removed device (it is simply not re-wrapped, so it can read nothing new), while your remaining devices adopt the new key automatically on their next sync. Yields a new one-time recovery code. Removing a device then rotating is the full revoke. (A device doing its very first restore at the exact instant a rotation runs retries until it completes — a documented residual window.)

Security guarantees (never-regress)

  • Zero-knowledge — the server only ever holds ciphertext + wrapped DK copies; no plaintext row, DK byte, or enc: secret ever leaves a machine.
  • Cross-tenant isolation — you can reach ONLY your own locker (the relay derives the storage prefix from your verified token, never the request body) and ONLY your own device registry (request.auth.uid == uid).
  • The sync IPC actions are classified human-only (security-boundary-human-only in the CLI-parity manifest): an AI agent can never enroll a device, approve one, or rotate your keys via the CLI — each needs your physical device and/or recovery code.

For agents

Where the code lives

Engine + crypto + formats + the Phase-3 additions (device registry, enrollment, restore, rotation, IPC, the gated Settings UI + enroll modal, the never-silent join prompt — SyncEnrollmentGate on the renderer and sync-enrollment-notifier.ts in main, both answering from the one resolveEnrollmentState the status IPC uses) + the pre-ship housekeeping (oplog compaction, the two-strikes GC + throttled maintenance pass, the relay delete/list ops, the account-deletion purge) are catalogued with their invariants and named tests in the contract: cross-device-sync-contract.md. Architecture north-star: the design spec.

Related

The folder-based Backup Mirror is the older way to get the same outcome, and the page explains when to keep it alongside sync. Secrets deliberately do not travel, so a restored machine re-approves them through the Post-Restore Credential Wizard. What sync does to your account when you leave is on the account deletion and reset page, and the setup that sync replaces as the everyday way to move data between machines is Data Transfer.

Last verified 2026-09-28