To migrate anti-detect browser profiles into Mosaic, you drop the other product's export on the import dialog and it works out which product wrote it from the fields inside, never from the file name. It keeps the name, notes, proxy and whatever hardware and locale values that product recorded. Everything else is drawn fresh, and the dialog lists what it left behind before anything is imported.

The importer is src/main/store/foreign-import.ts, and it reads exports from GoLogin, Dolphin Anty, AdsPower, Octo Browser and Multilogin. We wrote it because somebody with two hundred profiles somewhere else is not going to rebuild them by hand. If we answered their export with "this archive was not made by Mosaic", we would really be telling them not to switch.

The file has three rules in it, and the comment at the top spells them out.

Rule one: a file name proves nothing

You can call a zip gologin-export.zip and put an AdsPower record in it. And if GoLogin renamed its export next month, anything keyed on the name would stop working the same day. So we look for fields instead. Each product has a signature, a set of keys that only that product writes:

ProductWhat the signature looks for
AdsPowersnake_case user_proxy_config or fingerprint_config
Dolphin Antya useragent wrapped as { mode, value }, beside webglInfo
GoLoginnavigator.resolution
Octo Browsera title (not name) with a nested fingerprint
MultiloginbrowserType, with navigator or network

No record can match two rows of that table; we picked the keys so it couldn't. Getting the vendor wrong is worse than not knowing it. Call a Dolphin file "GoLogin" and the import still says it worked, then goes looking for the proxy under proxy.mode, where Dolphin never put it.

There's a check for this in the build, npm run check:import. For each product it makes a sample file in the documented shape and asks three questions. Is it recognised? Is it recognised as itself, and not as a neighbour? Do the locale and the proxy actually arrive, from bare JSON and from a zip? We wrote it because the failure makes no noise. A vendor renames one field, the signature stops matching, the file drops to the generic reader, most of each profile is lost, and the dialog reports success anyway.

Every profile is drawn from one real computer, not from separate listsIn Mosaic: Machines that exist. GPU, screen, CPU and fonts from one real device. Free holds 3 profiles. Pro is $49 a month for any number.

How it works: Machines that exist

Rule two: never invent a value

None of these products writes down a whole machine. GoLogin has a screen size and a User-Agent. Octo has CPU and RAM, spelled its own way. Not one of the five readers gets a GPU out of the file. You could plug each hole with something common, say a 1920×1080 screen and whatever GPU is most popular this year.

We do the opposite. What the vendor recorded is passed to the generator as a constraint, and the generator draws everything else to agree with it from real hardware records. Filling gaps with constants is how two hundred imported profiles end up sharing one screen size and one GPU. That is worse than two hundred freshly drawn profiles, because a value shared across a library is a join key. The values a page compares are listed in browser fingerprint.

For the same reason, a vendor's canvas and audio seeds are dropped. They were tuned for that vendor's engine and mean nothing in ours.

Rule three: say what was dropped

The dialog reads every file as it lands and prints its verdict before anything is created: "Detected as Dolphin Anty" with the count, and a note that every profile "will be tagged Dolphin Anty so you can tell later where it came from". Under it is a line starting "Not carried across" that names the fields staying behind, such as Dolphin statuses or AdsPower group membership.

Mosaic profiles filtered by one tag, with the tag filter open beside the Tags column
The vendor's name arrives as a tag you can filter by

The tag is there for later. Six months on, the only way to know why a profile has an odd screen size is to know where it came from. The tag is added inside the importer rather than by whoever called it, so provenance survives whether the import started in the window, the REST API or the MCP server.

The file we should have been surest about

One bug report against this code was about our own export. On 18 September 2026, dropping a mosaic-profiles.zip back into the import dialog reported "Unrecognised format · 1 profile". The zip held two profiles, and the dialog warned that the fingerprint would be lost on a round trip that in fact kept everything.

The import itself was fine. It had always read a Mosaic archive by its mosaic-profile.json entries. The detector was at fault. It only asked "which competitor is this?", so our own file fell through to the generic branch, which counts a profile only when some JSON document has a top-level name. Detection now counts mosaic-profile.json entries first and answers "Mosaic · 2 profiles · certain". It counts entry names rather than parsing, because entry names are what the import keys on, and a second description of the archive's shape is the copy that drifts.

Migrating anti-detect browser profiles from a product not on the list

Identory is missing on purpose. Its export format is not documented well enough for a signature that would not misfire, and a wrong signature is worse than none: it would claim a vendor and then map fields wrongly. Its files go to the generic reader, which takes the name, notes, proxy and any locale fields it finds. The dialog offers that reader explicitly, with the words "Not recognised as any known product", and never picks it in silence.

Moving profiles keeps what identifies each account. It does not change what a platform allows, so the accounts you bring across should be ones you are entitled to run. Which signals still have to stay apart after a move (cookies, exits and timing) is covered in how to manage multiple accounts without getting linked.