Conversation
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.
The suite failed in the Electron runner. The cause was the test, not the code: the desktop shell's origin is phtauri://localhost, which migrateAssist.html refuses, so the handshake never completed and the helper page specs sat there until awaitsFor timed out. That refusal is correct and worth keeping. The migration is browser only and a native shell has no business reading a web origin's filesystem, so adding phtauri://localhost to the allowlist would trade a real security boundary for specs that assert nothing useful on desktop. Early return on Phoenix.isNativeApp instead, the same shape already used by Extn-Git-integ-test.js. The suite now registers nothing at all in the desktop runner and is unchanged in browsers.
The suite failed in CI at the round trip spec: "Expected undefined to be truthy", then a TypeError reading .id of undefined. The spec took scan.bundles[0], so it silently depended on the machine already having projects in /fs/local. That held on a developer machine and did not in a fresh CI checkout, where the VFS is empty and the scan correctly returned no bundles at all. The suite did have a seeded folder, but under /temp, which is not one of the roots migrateAssist.html will serve, so it never appeared in a scan. The same spec asserted that /temp is refused, which should have been the clue. Seeds a project under /fs/local instead, which is a served root, and looks the bundle up by destination rather than by position. The seed carries a nested file and a binary file containing NUL and 0xFF, so the round trip now also proves paths keep their shape and bytes survive intact rather than being mangled through a text decode. hasData is asserted too, which is meaningful now that the suite controls whether there is anything to migrate. Verified against a clean browser profile, which is the state CI starts in: the previous spec fails there and this one passes, and the full unit category is 2730 of 2730.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



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.