feat(web): cloud-tasks browser host#3447
Conversation
Bring up apps/web as a cloud-tasks-only browser host: the same @posthog/ui shell and @posthog/core services as desktop, over web platform adapters (WebCrypto refresh-token cipher, popup PKCE OAuth, localStorage per-device stores, in-browser host-router slice via unstable_localLink, posthog-js analytics/flags, fflate skill bundler, @pierre/diffs worker). Vendor + route code-splitting, a hermetic Playwright happy-path e2e, and mprocs procs. Depends on (merge first): async token cipher, host-capabilities seam, and the shared SKILL.md serializer. Red until those land; rebase then. Generated-By: PostHog Code Task-Id: 77d13a30-444d-4045-9d80-5e2a9f2e68ae
|
⏳ Waiting to start tests on this pull request - details. |
|
React Doctor found no issues in the changed files. 🎉 Reviewed by React Doctor for commit |
|
Reviews (1): Last reviewed commit: "feat(web): cloud-tasks browser host" | Re-trigger Greptile |
Resolve conflicts in apps/web/vite.config.ts (keep web-host refactor, carry over main's git alias into posthogSrcAliases) and mprocs.yaml (adopt main's layer-grouping model, slot in the new web/web-playwright/ desktop-playwright procs). Generated-By: PostHog Code Task-Id: d5bcaf27-c54b-45d5-920d-cc4f65c9e9fb
The popup used a fixed window name ("posthog-code-oauth"), so a second
tab starting sign-in before the first finished could reuse and navigate
away the first tab's popup. The first flow's callback then never fired,
and the shared BroadcastChannel only carried the second flow's state, so
the first tab waited until the five-minute timeout.
Name each popup uniquely per flow (suffixing the per-flow OAuth state) so
concurrent sign-ins open independent windows. State-based callback
routing over the shared channel already delivers each callback to its own
flow.
Generated-By: PostHog Code
Task-Id: d5bcaf27-c54b-45d5-920d-cc4f65c9e9fb
CI's `pnpm install --frozen-lockfile` failed on this branch: the merge of main left pnpm-lock.yaml inconsistent with apps/web/package.json. - apps/web recorded zod specifier `^4.4.3`, but main's override (`zod@^4.0.0: 4.4.3`) pins it to `4.4.3`; align it like apps/code and packages/ui already do. - Restore the missing webpack@5.105.0(esbuild@0.27.2) and terser-webpack-plugin@5.3.16 snapshot entries (pulled in by apps/web's @tanstack/router-plugin) that the merge dropped, which otherwise broke the lockfile with ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY. Surgical edits only, so no unrelated transitive version drift. Generated-By: PostHog Code Task-Id: d5bcaf27-c54b-45d5-920d-cc4f65c9e9fb
Resolve conflict in apps/web/src/web-container.ts: keep the web-host container bindings and fold in main's taskThreadCoreModule (import + container.load, grouped with canvasCoreModule). Also drop the web LLM gateway host's invalidatePlanCacheUrl binding and its now-removed getGatewayInvalidatePlanCacheUrl import, matching main's trimmed LlmGatewayEndpoints interface (mirrors apps/code's binding). Generated-By: PostHog Code Task-Id: d5bcaf27-c54b-45d5-920d-cc4f65c9e9fb
| container | ||
| .bind(AUTH_TOKEN_OVERRIDE) | ||
| .toConstantValue( | ||
| (import.meta.env.VITE_POSTHOG_ACCESS_TOKEN_OVERRIDE as |
There was a problem hiding this comment.
Medium: Development access token can be bundled in production
Vite replaces this value with a string literal in the client bundle. An attacker can extract and use the static access token from deployed assets whenever the override is accidentally configured during a production build; gate this binding on import.meta.env.DEV or fail production builds when the variable is set.
PR overviewThis pull request adds a web-facing cloud-tasks browser host, with changes in the web container code to support the new runtime/browser-host behavior. One security issue remains open after one prior issue was addressed. The remaining concern is that a development access token override can be compiled into the client bundle if it is accidentally configured during a production build, allowing extraction from deployed assets. Because exploitation depends on that production misconfiguration being present, the current whole-PR risk is moderate but conditional. Open issues (1)
Fixed/addressed: 1 · PR risk: 4/10 |
|
✅ Visual changes approved by @gantoine — baseline updated in 7 changed. |
7 updated Run: 01703e08-05db-400a-b7d3-dd7ba8b8e6be Co-authored-by: gantoine <3247106+gantoine@users.noreply.github.com>
jonathanlab
left a comment
There was a problem hiding this comment.
This looks really good: I noticed we're using a mix of local storage and indexed DB, I think it would make sense to choose 1 source of truth for handling browser storage instead of having this split across 2 mechanisms.
I think it would also be good if we updated our AGENT.md or relevant agent instructions with pointers on how to build for web.
# Conflicts: # apps/code/snapshots.yml # apps/web/package.json # apps/web/src/web-container.ts # apps/web/src/web-trpc.ts # pnpm-lock.yaml
|
/trunk merge |
Summary
Brings up
apps/webas a cloud-tasks-only browser host for PostHog Code — the same@posthog/uishell and@posthog/coreservices as the desktop app, running in a plain browser tab over web platform adapters (no Electron, no local workspace-server). An authenticated user can reach the cloud task list, create a cloud task, stream the SSE chat, review diffs, attach files, run/skillcommands, and use Channels/Canvas — entirely client-side.Also a portability smoke test for the layered architecture:
@posthog/coreand@posthog/uirun unchanged; everything web-specific lives in theapps/webcomposition root.What's here
/callback→ BroadcastChannel relay), refresh token encrypted at rest with a non-extractable Web Crypto key in IndexedDB.AuthService/CloudTaskService(host-agnostic core) over an in-process host-router slice via tRPCunstable_localLink; SSE chat, diffs, log history, sidebar.desktop_file_systemAPI)./skillcommands (fflate bundler), posthog-js analytics/error-tracking/flags, browser notifications, per-device stores (localStorage).autoCodeSplitting)./callback), wired into CI alongside the desktop suite;mprocsprocs for local dev.This is the additive web-host layer split out of #3353. It builds on three small enabling PRs and is red until they land, then a rebase turns it green:
refactor(auth): make the token cipher asyncfeat(platform): host capabilities seam + boot-time binding guardrefactor(shared): move the SKILL.md serializer into @posthog/sharedVerified locally: with all three applied on top of current
main,@posthog/webandapps/codetypecheck clean and the web e2e passes. The standalone red is exclusively unresolved symbols from those three (async cipher,@posthog/platform/host-capabilities+@posthog/di/hostCapabilities+REQUIRED_HOST_CAPABILITIES,serializeSkillMarkdown).Notes
apps/webis a new app; nothing inmainimports it, so its blast radius on the desktop host is nil. The shared-package/desktop changes that do carry risk were peeled into the three enabler PRs above.Created with PostHog Code