ARMANOS

Session sync

Your session on two computers

Cookies and logins live in a folder on the machine where the browser ran. ARMANOS moves them to your second computer as an encrypted file, through a folder your own cloud already syncs, so we never hold your sessions.
scrypt plus AES-256-GCM
Encryption
your folder, not ours
Storage
1 / 2 / 5 / 20 by plan
Device seats
3 minutes, renewed while open
Profile lock

The second computer is where accounts get lost

Profile settings travel through your account: name, scenario, fingerprint template, proxy, group, tags. Cookies, logins and site storage do not. They sit in the profile directory on the machine that made them, and nowhere else.

So an honest move to a second computer meant signing in to thirty accounts again. That is exactly the work you bought the product to avoid. The shortcuts are worse. Copying a profile folder by hand gives you a broken database. Opening the same account in a normal browser next to it gives a site one account live from two places, behind two different machines, at the same time. That pattern is what gets an account reviewed and closed.

ARMANOS answers it in two halves. A session travels as one encrypted file through a folder you already own. A profile opens on one machine at a time, and the app refuses to touch a profile that is open. The rest of this page is those two halves, in the order the code runs them.

The Profiles screen in the ARMANOS app, with a first steps line about signing in so profiles reach your other computers.
Settings reach the other computer through your account. Cookies and logins do not: they stay in the profile directory on this machine until a session is sent.

The folder is yours, and so is the cloud under it

You point ARMANOS at a folder. Anything your own cloud already syncs works: Dropbox, iCloud Drive, Google Drive, a work network share, a folder on a NAS. The app does not know which one it is and does not ask.

That is a deliberate choice, not a gap. We run no storage for your sessions, so there is nothing of yours for us to hold, lose or hand over. It also meant the feature could ship at once, instead of waiting for object storage to exist.

The folder is tested by writing a small probe file, not by reading permission bits. A network share and a cloud folder can both look available and refuse the first real write. Changing the folder clears every sync mark, because those marks describe the old folder and would carry a recorded conflict into the new one.

One line does reach our server, and only after the work is done: which machine, which direction, whether it worked, and a short error name. No file names, no contents. Without it your dashboard would show nothing at all about sessions, and a sync that broke last Tuesday would stay invisible.

  1. 1

    Choose the folder

    Any folder your cloud syncs. The app writes a probe file to check it can really write there.

  2. 2

    Set a password

    At least 8 characters, and the same on both machines. The other computer cannot open the file without it.

  3. 3

    Name this computer

    Office, laptop, home. That name is what the other machine shows you when a session arrives from here.

  4. 4

    Turn sync on

    Without a folder and a password the switch refuses, instead of saying on and doing nothing.

What is packed, and what is dropped on purpose

Two files per profile land in the folder. The archive is <id>.armanos, encrypted. Beside it sits <id>.json, a small plain label the other machine reads first.

The archive holds the session directories of both engines, each under its own name, so the built-in Chromium and the ARMANOS Browser 153.0.7978.0 travel in one file without a nested archive. Alongside them goes profile.json: the profile id, name, scenario, group, tags, notes, start page, fingerprint template and generation, fingerprint edits, proxy, 2FA secret, saved login, and which extensions are on. The id travels because the fingerprint is derived from it. On the other machine the profile stays the same machine, rather than familiar cookies arriving under a new one.

The cache is dropped deliberately. Caches, GPU and shader caches, Service Worker storage, blob storage and crash dumps are hundreds of megabytes that sites refill by themselves. Lock files such as SingletonLock are skipped too. An archive over 400 MB is refused outright rather than written half way.

The label beside the archive carries no profile name. It holds the time, the size in bytes, and the name you gave this computer. A shared cloud folder is exactly where someone else's access arrives first, and a label reading "Instagram work" would tell them which accounts you run.

WhatDoes it travel?
Cookies, logins, local and IndexedDB storageYes, inside the encrypted archive
Profile id, name, scenario, group, tags, notes, start pageYes, as profile.json inside the archive
Fingerprint template, generation and manual editsYes, and the id with them, so the fingerprint stays the same
Proxy, country, 2FA secret, saved loginYes, and only inside the encrypted file
Which extensions are switched onYes, the choice; the extension files themselves are not packed
Caches, Service Worker, blob storage, crash dumpsNo, sites refill them and they would make the file unliftable
Lock files, DevToolsActivePort, socket filesNo
The profile's name, outside the encrypted fileNo, the label holds only time, size and your computer's name

One password, and it stays on this machine

The file format is the one the manual profile transfer already uses, so a file written by one route opens by the other. A header, a random 16 byte salt, a 12 byte nonce, scrypt with N 32768 to a 256 bit key, then AES-256-GCM. The tag is checked on open, so a wrong password gives an honest refusal rather than garbage.

The password is kept on this machine, and the app says so on screen. Asking for it every time a profile closes would mean no automatic upload at all, which is the same as not building the feature. Where the system offers a keychain, the password is sealed with it; where it cannot, it is written to a file readable only by you. It never goes into the sync folder, and it is never handed back out to the interface.

Changing the password clears every sync mark, so each profile is sent again under the new one, and the app tells you how many. Without that the folder quietly fills with a mixture of two passwords: the second machine refuses honestly, the first says nothing, and you believe you simply changed a password.

Owner-only file permissions are a macOS and Linux fact

On Windows the same call only flips the read-only flag, because permissions there live in access control lists the app does not reach. What protects the file on Windows is that the app data sits inside your user profile. We would rather write that here than imply more.

When the exchange actually happens

Before a profile opens, the app takes a newer session from the folder. It has to be before the window exists: replacing files under a running Chromium leaves a broken profile, and the browser would already have read the old databases.

Four seconds after a profile closes, the session is sent. Chromium keeps writing its databases for a second or two after the window disappears, and the app checks again that you have not reopened the profile. If you close the app inside those seconds, the send is caught up at the next start, one profile at a time with a pause between them, because building thirty sessions at once would freeze the machine right after launch.

While the app is open it watches the folder once a minute. It reads one timestamp on the folder itself, and only reads the per-profile labels when that changed, so two hundred profiles do not mean two hundred cloud reads every minute. It never pulls silently. Bringing someone else's session into a profile you are working in would swap the ground under you, so the app only tells you something new arrived.

Everything else is a button, because the automatic moments are few and a profile you keep open all day never reaches them.

  • Send mine to the folder

    Per profile, with a confirmation that says plainly it replaces what is there.

  • Take the one from the folder

    Per profile, replacing the session this computer holds for it.

  • Send all and Take all

    One after another, never all at once: thirty simultaneous writes into a cloud folder invite duplicate copies. Open profiles are skipped and counted.

  • Find profiles in the folder

    Reads the folder whole and lists profiles that exist there but not here. Settings come from inside the archive, so this works with no account at all.

When both sides changed, nothing is overwritten

One small function makes the decision, and it compares marks rather than clocks. "Changed here" is the newest session file time on this machine against the same number remembered at the last sync. "Changed there" is the label in the folder against the label the app last read or wrote. Two clocks on two machines never enter the comparison.

If both changed since the last sync, two people worked in parallel, and either write would throw away real logins. So nothing is written in either direction. You get a notification naming the other computer, a mark on the row in the profile list, and two buttons in the profile card. The decision is yours because only you know which side matters.

"Send mine" settles the argument for this machine, and the confirmation says so. What it replaces is copied aside first, under a name the exchange ignores, and the last three such copies per profile are kept. A click made without thinking now costs a minute rather than a day of the other machine's work.

A recorded conflict is a cache, not a verdict. It is recomputed before every exchange, because the file may have been deleted, renamed by the cloud, or the folder changed. Otherwise one conflict would disable automatic sending for that profile forever, and only a successful send could clear a flag that itself forbids sending.

State since the last syncWhat the app does
Nothing in the folder for this profileSends as soon as there is a real session to send
Only this machine changedSends
Only the folder changedTakes it, before the profile opens
Neither changedNothing is written
Both changedNothing is touched; you are told and you choose
Label is newer than the file beside itNothing this round; the app comes back when the file arrives

Clouds do things behind your back, and the app expects them

A cloud copies files one at a time, and the small label beats the big archive. So the app checks the size on disk against the size the label promises. If they disagree, the pair is marked not ready and nothing happens that round. Throwing the label away instead would look like an empty folder, and the next send would overwrite fresh work from the other machine.

A missing archive is not an empty folder either. iCloud Optimize Storage, OneDrive Files On-Demand, which is on by default on Windows, and Dropbox Smart Sync take file contents off the disk and leave a stub. A heavy session archive is the first candidate, while the tiny label stays. The rule is simple: a label means a session exists. We cannot take it right now, and we have no right to overwrite it.

Writing goes through a neighbour file and a rename, because half an archive on top of a working session cannot be decrypted. An interrupted write leaves that neighbour behind, and the neighbour is a full copy of a session in a folder whose space you pay for. Once an hour the app sweeps leftovers older than an hour that match its own naming exactly. Somebody else's half-written file is never touched.

The archive also carries its own build time, and it must equal the label beside it. If they differ, the file is the previous one and the label is the new one, so nothing is unpacked and nothing is recorded.

What the cloud didWhat you get
Copied the label first, the archive laterNothing this round, then a normal take once the file lands
Evicted the archive to save disk spaceNothing is taken and nothing is overwritten
Kept two copies after a near-simultaneous writeThe second copy is recognised by name and reported to you once
Left a half-written neighbour file behindSwept after an hour, and only if the name is ours exactly

An open profile is never touched, and it opens on one machine

A running profile is skipped in both directions. Its databases are being written by a live browser, and a copy taken mid-write is a broken profile on the other computer. The refusal sits in three separate places: building the archive, sending, and taking. The batch buttons skip open profiles too, and tell you how many they skipped.

When you are signed in, starting a profile also claims a lock on the server. It is held for three minutes and renewed every minute while the window is open, so a machine that loses power releases the profile by itself instead of holding it forever.

A second machine gets a plain refusal that names the holder and how long it has held it. You can take over on purpose. The previous holder finds out at its next renewal that the lock is no longer its own, and closes its windows. Both the first claim and the takeover are conditional writes, so two simultaneous starts cannot both succeed.

"Mine" means the same machine, not the same person. A request that does not name its machine cannot prove a lock is its own, and does not get it. Without an account there is no lock and none is needed: the profiles live only on this computer, and there is nobody to share them with.

Lock held for
3 minutes
Renewed
every 60 seconds while the profile is open
Second machine
refused, and told who holds it and since when
Profile closed
the lock is released at once
Machine unplugged
the lock expires on its own

Numbers are from apps/server/src/profiles/profiles.service.ts and the desktop lock renewal timer.

A machine takes a seat, and you can take it back

Each installation names itself with a random identifier created once and kept in the app data. It used to be derived from hostname, platform and username, which made two fresh laptops the same machine. That cost a seat, and worse, it let the lock treat both as one and open the same profile twice at the same time.

Seats are a workspace pool counted against the owner, so a team shares one set. They follow the subscription that is live now, not a number stored once: a lapsed plan does not keep every machine it ever registered. When there are more machines than seats, the earliest registrations hold them in a stable order, and the rest get an honest refusal rather than a machine dropping out at random.

Unlinking a machine in the dashboard frees its seat immediately and is remembered. A revoked machine cannot open profiles or renew a lock, and it does not quietly register itself back on the next heartbeat, which is what used to happen. You can unlink your own machine; unlinking someone else's takes an admin. Signing out on the machine itself also hands the seat back, and that is not remembered as a revocation, because you were sitting at it.

PlanDevice seatsProfiles
Free12
Professional210 to 100
Business5200 to 1000
Enterprise205000 and above

What this does not do

  • It does not host anything for you. No folder, or a cloud app that is not running, means no exchange. If your cloud has not finished copying, the app waits and does nothing that round.
  • It does not merge two sessions. When both sides changed you pick one, the other is set aside as a copy, and putting the two together is manual work.
  • It does not touch an open profile in either direction. A profile you keep open all day does not travel until you close it or press Send all.
  • It is not live mirroring. Sessions move before a profile opens, a few seconds after it closes, at app start, and when you press a button.
  • If you lose the password, the files already in the folder cannot be opened. There is no recovery path, by design: we hold no copy of it.

How to check

Every claim above has a file behind it, and most have a stand that runs the real code.

The folder gets an encrypted file, and a full round trip restores the cookies
apps/desktop/src/lib/sessionSync.js · apps/desktop/test/session-sync-folder.js
Both sides changed: nothing is overwritten, and what you replace is kept aside
apps/desktop/src/main/main.js · apps/desktop/test/conflict-keeps-copy.js
An archive the cloud evicted or has not finished copying is not read as an empty folder
apps/desktop/src/lib/sessionSync.js · apps/desktop/test/cloud-evicted-file.js
One profile opens on one machine at a time, and takeover is deliberate
apps/server/src/profiles/profiles.service.ts · apps/server/test/profile-lock.js
Seats follow the live plan, and unlinking a machine is remembered
apps/server/src/devices/devices.service.ts · apps/desktop/test/device-identity.js

Set it up on both computers

Install ARMANOS on the second machine, point both at the same folder, and set the same password. The first session travels the next time you close a profile.