Skip to content

feat: migrate browser data from the legacy web origin to web.phcode.dev - #3130

Open
abose wants to merge 1 commit into
mainfrom
ai
Open

feat: migrate browser data from the legacy web origin to web.phcode.dev#3130
abose wants to merge 1 commit into
mainfrom
ai

Conversation

@abose

@abose abose commented Aug 22, 2026

Copy link
Copy Markdown
Member

Browser storage is per-origin, so a user landing on web.phcode.dev sees an empty editor while their projects, preferences, theme and extensions stay behind on the old origin, invisible to the new one.

On the origin being retired, every boot announces the move with a live countdown to the sunset date. On web.phcode.dev, a hidden iframe pointed at the old origin reports whether anything is worth moving. If there is nothing, absolutely nothing happens - no dialog, no interruption, normal boot. If there is, a progress modal runs the transfer once and sets a flag so it never runs again; after that it only happens if the user asks from Help > Migrate My Data.

This works at all because phcode.dev, staging.phcode.dev and web.phcode.dev are the same site (eTLD+1). Browsers partition third party storage by site rather than by origin, so the helper frame is same-site and reads real unpartitioned IndexedDB. Had the new home been on a different registrable domain this would have silently reported "nothing to migrate" everywhere.

src/migrateAssist.html is deliberately standalone. It loads only virtualfs.js and jszip.js rather than booting Phoenix, and zips one top level folder at a time so peak memory is one folder rather than the whole filesystem. The receiving side hands each zip to ZipUtils.unzipBinDataToLocation, which writes without unlinking first, so files from the old origin win on collision while files that only exist on the new side survive. That is the collision rule we want, with no extra code.

Extensions are copied rather than reinstalled from the registry. Both origins serve the byte identical build, so a folder that worked on one is compatible with the other, and copying also preserves extensions that were sideloaded or have since been unpublished.

That page hands out the user's entire browser filesystem to whoever embeds it, so the origin allowlist is exact match only, checked on every message and not just the handshake, replies always go to the validated origin rather than "*", and reads are confined to three known roots.

Safari and iOS are out of scope by product decision, not because the mechanism would fail there. Those users still get the dialog, with a line asking them to download projects manually, rather than losing data without being told.

Android and ChromeOS users launched from our Trusted Web Activity are offered an app update instead of the new site, because the shipped APK only trusts phcode.dev and navigating it elsewhere surfaces a browser URL bar inside what looks like an app. "Stay here" is a real choice there: managed school fleets can have the Play Store blocked entirely, so the update button may be a dead end through no fault of the user.

The legacy origin is staging.phcode.dev while this is validated end to end. Switching it to phcode.dev is a one line change in constants.js.

Browser storage is per-origin, so a user landing on web.phcode.dev sees an
empty editor while their projects, preferences, theme and extensions stay
behind on the old origin, invisible to the new one.

On the origin being retired, every boot announces the move with a live
countdown to the sunset date. On web.phcode.dev, a hidden iframe pointed at
the old origin reports whether anything is worth moving. If there is nothing,
absolutely nothing happens - no dialog, no interruption, normal boot. If there
is, a progress modal runs the transfer once and sets a flag so it never runs
again; after that it only happens if the user asks from Help > Migrate My Data.

This works at all because phcode.dev, staging.phcode.dev and web.phcode.dev are
the same site (eTLD+1). Browsers partition third party storage by site rather
than by origin, so the helper frame is same-site and reads real unpartitioned
IndexedDB. Had the new home been on a different registrable domain this would
have silently reported "nothing to migrate" everywhere.

src/migrateAssist.html is deliberately standalone. It loads only virtualfs.js
and jszip.js rather than booting Phoenix, and zips one top level folder at a
time so peak memory is one folder rather than the whole filesystem. The
receiving side hands each zip to ZipUtils.unzipBinDataToLocation, which writes
without unlinking first, so files from the old origin win on collision while
files that only exist on the new side survive. That is the collision rule we
want, with no extra code.

Extensions are copied rather than reinstalled from the registry. Both origins
serve the byte identical build, so a folder that worked on one is compatible
with the other, and copying also preserves extensions that were sideloaded or
have since been unpublished.

That page hands out the user's entire browser filesystem to whoever embeds it,
so the origin allowlist is exact match only, checked on every message and not
just the handshake, replies always go to the validated origin rather than "*",
and reads are confined to three known roots.

Safari and iOS are out of scope by product decision, not because the mechanism
would fail there. Those users still get the dialog, with a line asking them to
download projects manually, rather than losing data without being told.

Android and ChromeOS users launched from our Trusted Web Activity are offered
an app update instead of the new site, because the shipped APK only trusts
phcode.dev and navigating it elsewhere surfaces a browser URL bar inside what
looks like an app. "Stay here" is a real choice there: managed school fleets
can have the Play Store blocked entirely, so the update button may be a dead
end through no fault of the user.

The legacy origin is staging.phcode.dev while this is validated end to end.
Switching it to phcode.dev is a one line change in constants.js.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant