Repository navigation
fix(desktop): T3 Connect sign-in works in the fork - #172
Conversation
The fork served its window from t3code-fork://app. T3's production Clerk instance only accepts t3code://app, so GET /v1/client failed with origin_invalid, Clerk went to status error, and T3 Connect sat at "Loading sign-in..." with no way to sign in. The window is served from upstream's t3code://app again. The Clerk bridge, the installer and the Linux desktop entry keep t3code-fork:// as the link scheme the OS routes to the app, so an official T3 Code install keeps its t3code:// links. Google and GitHub sign-in return to t3code://app/, which belongs to the official install, so they do not come back to the fork. Email and password sign-in stays in the window. Browser storage belongs to an origin. On first launch, ForkRendererStorageMigration copies localStorage (drafts, prompt stash, themes, sidebar and second mate folds) from t3code-fork://app to t3code://app once, never over a key the new origin holds, then flushes it to disk and writes a marker in userData.
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Code reviewTwo read-only passes: one against the filed task, one against AGENTS.md, CONTRIBUTING.md and a code smell baseline. Nothing is a hard violation. Every finding is listed with its confidence and severity. Against the task (5 findings)
Checked and fine: the copy covers every key the task names, all in localStorage. Skipping IndexedDB matches the PR's reasoning. Against the repo standards and smell baseline (14 judgement calls)
Checked and fine: one concern per PR holds, since the origin switch and the migration depend on each other. No em dashes added. Worth resolving before merge
|
A failed or timed-out copy wrote no marker, so every later launch ran it again before the window opened. The marker now records "failed <n>" after each failed launch, and startup skips the copy once three launches have failed. A later success still writes "copied". The copy never overwrites a key the new origin holds, including on a retry. The timeout drops from 20 to 10 seconds. Loading two blank pages takes far less, so this only bounds how long a stuck copy can hold the window. Review follow-ups in the same lines: the result field "kept" is now "skipped", the error builder is "failedAt", planLegacyStorageCopy infers its return type, and the test reads the schemes from ElectronProtocol.
|
Fix round, commits 8b985cf and 422ee55 (the second makes two new helpers private for the unused-exports check). Against the task:
Against the repo standards:
|
The unused-exports check flags MAX_MIGRATION_ATTEMPTS and parseMigrationMarker, since nothing outside the module imports them.
The fork's desktop app could not sign in to T3 Connect, so it could never reach app.t3.codes/settings/connections. Settings > Connections showed the T3 Connect rows, but there was no sign-in control and the switches stayed off.
Cause
The fork served its window from
t3code-fork://app. That was part of the fork's own app identity (620cfc1). T3's production Clerk instance only accepts requests fromt3code://app. Its allowed origins live on T3's Clerk account, which the fork cannot change.Evidence:
mainbuild, run unpacked on an empty temp home:GET https://clerk.t3.codes/v1/clientreturns 400 with{"code":"origin_invalid","long_message":"The Request HTTP Origin header must be equal to or a subdomain of the requesting URL."}. The console logsClerk: Production Keys are only allowed for domain "t3.codes".window.Clerk.statusiserror, and the welcome screen shows "T3 Connect - Loading sign-in..." forever.t3code://app200,https://app.t3.codes200,t3code-fork://app400, an unknown origin 400.t3code:/v1/client200,Clerk.statusready.It was not the build values. #137/#138 already copy
.env.exampleto.envin the build. The installed 0.0.43-ap.9 carriesrelay.t3.codes, thepk_live_key andt3-relayin its web client, and the ap.9 CI log saysCopied .env.example to .env. No newer upstream ref is needed either: upstream still serves its window fromt3code://app.What changed
ElectronProtocol.ts:getDesktopSchemeis upstream'st3code/t3code-devagain, so the window's origin ist3code://app. A newgetDesktopLinkSchemekeepst3code-fork/t3code-fork-dev. All four schemes go in the one privileged-scheme call, because Electron allows that call once and the old origin needs web storage for the copy below.DesktopClerk.ts,DesktopLinuxUrlHandler.ts,DesktopPreReadyPlatform.ts: pass the link scheme. The Clerk bridge registers its scheme as the OS link handler on every launch, so the official install keepst3code://. The installer's protocol list was alreadyt3code-fork.ForkRendererStorageMigration.ts(new, fork-only): browser storage belongs to an origin. On first launch, before the window loads, it copies localStorage fromt3code-fork://apptot3code://app. That covers composer drafts, the prompt stash, custom themes, and sidebar and second mate folds. It never overwrites a key the new origin holds, flushes storage to disk, then writes a marker in userData so it runs once. It loads both origins in aWebContentsView, not a window, because closing the last window quits the app on Windows. A failed or timed-out run (10 second limit) recordsfailed <n>in the marker. Startup tries again on the next launch and gives up after three failed launches, so a lasting fault cannot hold every start.DesktopApp.ts: calls it before the renderer scheme gets its real handler.fork-features.json: newfork-connect-renderer-originentry. Thefork-app-identitykeep line now says the link scheme is ours and the renderer scheme is upstream's.docs/operations/fork-windows-build.md: explains the origin, the storage copy and the known limit.Saved environments and client settings are files on desktop, so the origin change never touched them. IndexedDB holds caches and T3 Connect auth keys, which never worked at the old origin, so it is not copied.
Clerk bridge scheme
The bridge gets the link scheme, not the window scheme. That does not affect email and password sign-in. In the installed
@clerk/electron0.0.44 (dist/esm/index.js),renderer.schemeis used in four places only:buildRedirectUrl(line 39) builds the OAuth return link from it.setupOAuthTransportIpcHandlers(line 88):app.setAsDefaultProtocolClient(options.renderer.scheme);createClerkBridge(line 294):scheme: options.renderer.scheme,insideprotocol.registerSchemesAsPrivileged.assertValidRendererOriginConfig(lines 240-241) checks its format.The only check on who is calling is
isMainFrameEvent(lines 5-7):return sender.getType() === "window" && Boolean(senderFrame) && senderFrame === sender.mainFrame;. It never compares an origin or a scheme. The token cache and passkey handlers use that same check. On the page side,react/index.jstakes the allowed redirect protocol fromwindow.location.protocol, so it ist3code:. Email and password sign-in goes from the page straight to Clerk with the window's own origin, and the built app shows/v1/clientreturning 200 fromt3code://app.Known limit
Clerk sends a Google, GitHub, Apple or Microsoft sign-in back to
t3code://app/, which belongs to the official T3 Code install. So those sign-ins do not come back to the fork. Sign in with email and password, which stays in the window. This was chosen to keep the official install's links its own.Screenshots
Empty temp home, made-up machine name.
Before (
main): the rows show, but there is no "Sign in to T3 Connect" control, and Clerk is inerror.After: the same page, with "Sign in to T3 Connect" at the bottom of the settings sidebar.
After: clicking it opens Clerk's sign-in form.
Tests that fail first
The first two were run with the six changed source files put back to
origin/mainand the new tests kept:ForkRendererStorageMigration.test.tsgives each origin its own store and runs the module's real write script. It checks four things. Drafts, stash, themes and second mate folds reach the new origin. A value the new origin holds wins. The flush runs before the marker. A second launch does nothing. Two more tests cover a failing copy. One checks that three failed launches recordfailed 1,failed 2andfailed 3, and that the fourth skips the copy. The other checks that a success after a failure still never overwrites.Against the module as it was before the retry cap (
91f4b2c67b), the retry test fails because a failed launch wrote no marker:The whole file on that module gives
Tests 3 failed | 1 passed (4).End to end
The app was built from this branch with
node scripts/build-desktop-artifact.ts --platform win --target nsis --keep-stage. It ran unpacked from the stage, not installed.APPDATAandT3CODE_HOMEpointed at an empty folder underC:/Users/Glyn/AppData/Local/Temp/fm-t3-connect, with a free port andclient-settings.jsonmarking onboarding done, so the first-run project scan never ran. A DevTools protocol client drove the app.clerk 200 /v1/environment,clerk 200 /v1/client,{"origin":"t3code://app","clerkLoaded":true,"clerkStatus":"ready"}. Settings > Connections lists["T3 Connect","Publish agent activity"]and the "Sign in to T3 Connect" control. Clicking it opens "Sign in to T3 Code" with the email address field, Continue and "Use passkey instead".mainbuild wrote four made-up values att3code-fork://appand quit. The branch build then loggedresult: { _tag: 'Copied', copied: 5, skipped: 0 }, which is the four plus one key the app writes itself. All four read back att3code://app. A second launch loggedresult: { _tag: 'AlreadyMigrated' }, and the values were still there.HKCU\Software\Classes\t3codestill pointed at the official install. Onlyt3code-forkhad been re-pointed at the unpacked build. Both keys were exported before every run and restored after.The run stopped at the email address field. The next step needs the user's T3 account.
The built bundle carries the public values.
server.asarholds the web client:relay.t3.codes4 matches, thepk_live_key 5,t3-relay3, the OAuth client id 4.app.asarholds the key once and"t3code-fork"4 times.To finish connecting, after this ships
Checks
Those 17 fail the same on
origin/main(a81e7f894e) on this Windows machine. With the changed files put back tomain:The full desktop suite (
(apps/desktop) vp test run) gaveTest Files 4 failed | 105 passed | 4 skipped,Tests 23 failed | 1352 passed | 35 skipped. All 23 are Linux-only tests: Niri, KDE and Hyprland screenshot helpers, and a Unix file-permission launcher test. Onmain, the same four files fail 22. The 23rd is the KDE test "retains the frozen screenshot for feedback...". It failed once under full-suite load withEPERM: operation not permitted, rmdiron a temp folder, and passed alone 3 of 3 times. It is a Windows temp-folder flake in code this change does not touch.