Repository navigation
add a desktop app around the hosted web app - #173
Merged
Merged
Conversation
The desktop app (next commit) can't sign in itself: Google turns embedded browsers away, and a magic link opens in the browser anyway. So it keeps a PKCE verifier and opens the browser at /desktop-auth?challenge=, where the person signs in as usual. The page mints a one-time code bound to the challenge (POST /auth/desktop/code) and hands it over through the app's personalagentproxy:// link; the app trades code and verifier for a session cookie of its own (POST /auth/desktop/session). Codes last five minutes, are kept only as their hash, and are taken only once the verifier checks out, so a wrong one leaves the code to the app that holds the right one.
packages/desktop: Electron, with nothing of the web app or the api in it. The main process keeps the window to the app's own origin (links out of it open in the browser), shows a page of its own where the app would show its login page, with a button that signs in through the browser (previous commit), and takes the personalagentproxy:// link the browser comes back with. A page for no connection, and the window's last size and place. `bun run desktop` runs it on this checkout's web app, using a `bun run dev` already running or starting the stack itself. CI skips the Electron download its install would make.
A service's sign-in page turns the app's window away, as Google's does, and the sign-in's handshake lives in a cookie, so the whole of it has to happen in one browser. From the desktop app it now runs in the person's browser: the web app, which knows the window by its user agent, asks the api for a handoff (POST /auth/desktop/handoff), an address with a one-time code that signs the browser in as the same person, unless it is already, and goes on to the service's sign-in, marked ?desktop=1. That sign-in ends on /desktop-return, which hands back through personalagentproxy://open?path=, and the window opens the page the sign-in would have ended on, with its error where it did not finish. A custom MCP server that asks for a sign-in moves its form to the browser, filled in as it was, to be sent again there.
electron-builder.yml builds the macOS app for Apple silicon and Intel, a disk image and a zip each, with latest-mac.yml, the feed electron-updater checks the repository's latest GitHub release for (src/updates.ts). The desktop workflow builds on a desktop-v<version> tag, checks it names the version in package.json, and publishes the files as the release; a pull request touching the app only builds it. Signing and notarization start once Apple's certificate and credentials are secrets; until then the build is unsigned, which macOS warns about and the updater can't update. A first icon from the brand mark.
Plaid Link runs in its frame over the page, and signs in to an OAuth bank in a popup that reports back to it and closes, which only a window of the app's can do; the desktop app sent every popup to the browser, where Link never heard from it. With Link's frame on the page, the page under it inert, a popup is Link's and becomes a window of the app's, whose own links still go to the browser.
hkonsti
force-pushed
the
hkonsti/pap-electron-shell
branch
from
November 3, 2026 22:35
f542878 to
7227116
Compare
…n-in code The handoff let the app mint a link that signed whichever browser opened it in as the app's person, which any signed-in user could send to anyone else. The browser now goes through /login?callbackUrl=, which sends a signed-in browser straight on and lets any other sign in as itself first.
Every "Sign in again" link hands off to the browser, and the window turns any redirect out of the app's origin into the browser running that page. A custom MCP server's sign-in begun from the app ends back in it, and its form no longer waits on the browser. A second press of the sign-in button shares the first's verifier. Startup takes an early return instead of an else block, and AGENTS.md lists bun run desktop.
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.
Adds
packages/desktop, an Electron window around hosted Personal Agent Proxy with nothing of the web app or the api bundled, in five commits: signing the app in through the browser (a PKCE one-time code handed back throughpersonalagentproxy://), the app itself (origin allowlist, its own sign-in and offline pages, deep links, window state), connecting services through the browser (a one-time handoff signs the browser in as the same person, the sign-in runs there marked?desktop=1, and/desktop-returnhands back to the app), packaging and adesktop-v*release workflow for macOS arm64 and x64 with electron-updater on the repository's latest release, and Plaid Link's OAuth popup as a window of the app's.Service sign-ins have to leave the window because Google turns embedded browsers away and the handshake lives in a cookie, so the whole of a sign-in runs in one browser. Two migrations add the code tables (
v039,v040); codes are hashed, short-lived and single-use. Builds are unsigned until there is an Apple Developer account, whichdesktop.ymlpicks up from secrets;bun run desktopruns the app on a local or bound dev stack.Tested on Linux under Xvfb end to end (sign-in loop, hand-over to a real Google sign-in page and back, the custom MCP form moving to the browser, the popup mechanics with a
cdn.plaid.comframe) and with a mac zip packed by electron-builder; not yet run on macOS, and the Plaid round trip needs sandbox keys.Open workspace in Conductor