Profile sync
Move a profile between your machines through the control plane, encrypted with a key the server never sees: what travels, what deliberately does not, and how ownership works.
A profile is an identity, and an identity that only exists on one laptop is one disk failure away from gone. Sync copies it to your account, encrypted on your machine with a key the control plane never holds, so another machine you own can pick it up.
Every sync action is an operator action on the account. None of it is exposed as an agent tool: an agent driving a browser has no use for "move my profile to the cloud", and giving it one would put the account key inside a tool call.
What travels
The bundle carries what makes the profile that profile: its persona, its row, its session, its stored logins, its passkeys including the private key, its proxy including the credentials, its group by name, and its extensions by reference.
Three things are left out on purpose, and each is worth knowing before you rely on this.
The Chromium data directory does not travel. Measured across 29 development profiles: 17 MB to 714 MB each, 8.7 GB in total, against 20 KB to 1.2 MB of actual session data inside. Size is the cheap argument. The hard one is that it could not work anyway, because on Windows the cookie key is wrapped by DPAPI in Local State and the Cookies file cannot be decrypted on another machine at all.
The history does not travel. Launch logs and agent runs, with their steps and screenshots, are the record of a machine rather than of an identity, and they are also what carries visited addresses and typed text.
Extension packages do not travel, only the reference does. A package the target machine does not have is named in the import report instead of quietly vanishing.
A login that rests on a localStorage token does not survive the move. Only cookies are both harvestable and applicable today. Local storage would have to be written back through an origin that is loaded, and IndexedDB is readable over CDP but only writable through JavaScript injected into every origin, which is a detection risk the benefit does not carry. This is a stated limit, not a footnote.
One owner at a time
A profile that runs in two places at once forks its own session, and both copies are then wrong in a way nothing announces. So a profile has exactly one owner at a time, and starting it claims that ownership through the control plane before the browser comes up. This holds for every profile, whether or not it is backed up.
Starting a profile that is open elsewhere is refused, and the refusal names the machine it runs on. You can take it over from there: the machine holding it is told to close, closes, and only then does the browser open here. That order is the whole safeguard, so a takeover waits a moment rather than opening a second window straight away. A machine that has stopped talking to us altogether is not waited for.
A freshness check alone would not be enough. It catches the stale case and not the simultaneous one, which is why the claim is a compare-and-set rather than a read.
When the control plane cannot be reached, neither "open" nor "closed" is the right answer, so the daemon falls back on what it can prove locally rather than guessing.
The key
The key that wraps everything is created on your machine and stays there. The server holds ciphertext and a manifest, never the key.
- Nothing, and that is the point
Your installation creates the key itself the first time it reaches us, publishes a manifest and starts backing profiles up. There is no form to fill in, because a backup that waits for a form is a backup almost nobody has.
- Write the recovery key down
Base32, 25 characters in five groups. It waits encrypted on that machine until you confirm you have it, so a screen you missed costs you nothing. Once confirmed it is gone from there, and the only way to a key is to issue a new one, which retires the old.
- Unlock on the next machine
With the recovery key. A machine never unlocks itself: that would mean moving the key past us, which is the one thing this design does not do.
- Add a passphrase, if you want one
A second way in, next to the recovery key. It is an addition rather than an entrance, and you can add it at any time without knowing an older one, because there is none.
Running it
In the app, the Sync card under /settings. The desktop client renders that same page, so there is no second surface to learn.
On a server with no browser, the daemon has a subcommand:
scalebrowser-daemon sync status
scalebrowser-daemon sync setup
scalebrowser-daemon sync unlock
scalebrowser-daemon sync push
scalebrowser-daemon sync pullIt talks HTTP to the running daemon rather than to the store, because a running daemon holds the data directory lock.
There is deliberately no --passphrase flag. A flag lands in your shell history and in the process list. The subcommand reads the passphrase from a prompt with no echo, or from SCALEBROWSER_SYNC_PASSPHRASE.
The passphrase reaches the daemon over loopback, where the cryptography lives. Every route that carries a secret refuses a caller that is not on loopback, and says where the request came from.
Without a control plane
A self-hosted daemon with no control plane configured answers every one of these routes with 404 sync_unavailable. That is the same honesty as GET /v1/account reporting licensed: false: the feature is not broken, it is not there.