experiment: render the real PostHog webapp in-app via local auth proxy#3475
Draft
mariusandra wants to merge 3 commits into
Draft
experiment: render the real PostHog webapp in-app via local auth proxy#3475mariusandra wants to merge 3 commits into
mariusandra wants to merge 3 commits into
Conversation
…uth proxy EXPERIMENT: <PostHogApp url="/notebooks" /> renders the cloud webapp inside an iframe, isolated from the React 19 host tree. - EmbeddedAppProxyService (workspace-server, bound in main like AuthProxy): serves an HTML shell booting the webapp's `embed` vite entry, and proxies /api, /_preflight, /uploaded_media, /static to PostHog cloud with a fresh OAuth Bearer token from AuthService — the iframe sees a same-origin API, no CORS, no credentials in the renderer. - host-router embeddedApp.getUrl starts the proxy lazily and returns its URL. - packages/ui PostHogApp component + /embedded-app route: iframe plus postMessage bridge (navigate, setTheme, ready, routeChanged) synced to the host theme store. - bootstrap: POSTHOG_CODE_APP_NAME env (dev only) isolates userData and the single-instance lock so a second dev instance can run beside another checkout's app. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An iframe reload sends ready again but the ready state is already true, so the theme effect wouldn't re-run and the embed could boot with a stale theme. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
React Doctor found 2 issues in 1 file · 2 warnings. 2 warnings
Reviewed by React Doctor for commit |
The embedded webapp is now a first-class browser tab:
- /embedded-app takes a `path` search param; each distinct webapp path is
its own tab identity, encoded into the tab's appView string
("embedded-app:<path>") — the browser-tabs domain treats appView as an
opaque string, so dedup/focus works per path with no schema change.
- useAppView maps the route (with its search path), BrowserTabStrip labels
embedded tabs "PostHog <path>" with a globe icon and navigates them back
to /embedded-app?path=... on select.
- useOpenEmbeddedAppTab opens (or focuses) a NEW tab: it mirrors the
strip's open flow — openOrFocus transform applied local-first, persisted
in the background, then a navigation stamped with the tab id — because a
plain router navigation is in-tab by design and would retarget the
current tab.
- PostHogApp handles the bridge's `openTab` messages (window.open /
target=_blank inside the webapp) by opening a host tab at the normalized
path (/project/<id> prefix stripped so identities stay stable), and grows
a "+ New tab" toolbar affordance for the current embedded page.
Verified live: nav chrome hidden (posthog-side embedMode), scene
full-width, window.open in the embed spawns a second "PostHog /notebooks/…"
tab, tabs persist across app restart and switch correctly, theme and
navigation bridges intact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Experiment 2/3:
<PostHogApp url="/notebooks" />Renders the real PostHog webapp (React 18 + kea) inside the code app (React 19), isolated in an iframe with a postMessage bridge. Verified live in the desktop app: the notebooks scene with a live-querying embedded insight, the feature-flags scene, and a heavy production dashboard computing trends/funnels — all with host-synced light/dark theme and bidirectional navigation.
Architecture
EmbeddedAppProxyService(workspace-server, main process, modeled onAuthProxyService): serves an HTML shell booting the webapp's newembedvite entry, and stream-proxies/api/*,/_preflight, media/static to the region cloud host, injecting a fresh OAuth Bearer viaAuthService.authenticatedFetch. The iframe sees a plain same-origin API — no CORS, token never enters any renderer.embeddedApp.getUrlhost-router procedure lazy-starts the proxy on an OS-assigned port.PostHogAppcomponent inpackages/ui(route/embedded-app): iframe + bridge client — host→iframenavigate/setTheme, iframe→hostready/routeChanged/openTab; theme re-synced on everyready(the webapp hard-reloads its document on some scene transitions).Chrome-less mode + browser-tab integration (round 2)
navigationLogicgained anembedModereducer that short-circuits itsmodeselector to'none'(the existing tested no-chrome path), so the scene fills the iframe and the code app provides the surrounding chrome.appViewstring ("embedded-app:<path>"), giving per-path tab identity, dedup, and restart persistence with zero schema change. Tabs get a globe icon + "PostHog " label.window.open/target="_blank"inside the embed bridges out asopenTaband spawns a real host tab (verified live: two PostHog tabs coexisting, switching, and surviving app restart); external URLs keep native behavior (system browser). A "+ New tab" button on the embedded-app toolbar opens the current page as a new tab (identity-deduped, browser-like).Broken-endpoint catalogue (the key finding)
One failure class: DRF actions without
required_scopesreject token auth with 403 — the typed query subresources (POST /query/TrendsQuery/etc.) break Activity, Persons, and fresh insight creation, plustagsanduser_product_list. Classic/query/works (which is why dashboards render). Small, enumerable upstream fix. (These are the orange tiles visible in the screenshots.)Dragons
Outlet, no keep-alive) — each switch reloads the webapp; a persistent portaled iframe host would be the fix if this graduates.embedentry into dist, serve manifest-resolved assets from the proxy, decide webapp update cadence) designed but not built.Screenshots
Chrome-less embed, dark theme — no webapp sidebar, scene edge-to-edge, "PostHog /notebooks" as a browser tab, "+ New tab" in the toolbar (orange tiles are the documented
required_scopes403s):Same notebook, host + embed in light theme, two PostHog tabs in the strip (restart persistence visible):
Result of
window.openinside the embed — a second real host tab:🤖 Generated with Claude Code