feat(canvas): live events + realtime stats for freeform canvases - #3993
Draft
fuziontech wants to merge 1 commit into
Draft
feat(canvas): live events + realtime stats for freeform canvases#3993fuziontech wants to merge 1 commit into
fuziontech wants to merge 1 commit into
Conversation
Freeform canvases can now subscribe to the project's live event stream and read realtime counters — two agents can build live event feeds and users-online readouts without re-polling insights.
New `ph.*` streaming capability, brokered host-side so the sandboxed iframe never holds the project-scoped `live_events_token` JWT:
- `ph.subscribeLiveEvents(params, onEvent, onEnd)` — the canvas posts a `live-subscribe` frame; the renderer opens the SSE to the livestream service (`live.{region}.posthog.com/events`) using a brokered connection config and fans each event into the iframe as a `live-event` postMessage frame. `params.properties` filters only support the livestream operator allowlist (`LIVE_EVENTS_SUPPORTED_OPERATORS`, mirroring the app's live feed).
- `ph.liveStats()` — a ONE-SHOT poll riding the generic `data-request` frame with `method: "liveStats"` → `GET <live host>/stats` → `{ users_on_product, active_recordings }` (the same "users online" pill the Activity page shows). A canvas also derives a client-side 60s sliding-window users-online count from the streamed `distinct_id`s (pattern documented in the agent system prompt) to match the Web-Analytics-Live readout.
Plumbing:
- core: live message frames + schemas on the postMessage protocol, `getLiveEventsUrlFromRegion` (shared), `CanvasDataService.liveConnectionConfig`/`liveStats`, `ICanvasDataService` surface.
- host-router: `canvasData.liveConnectionConfig` + `canvasData.liveStats` procedures.
- ui: `openLiveEventsStream` + `buildLiveEventsUrl`/`parseSseChunk` in the data bridge, `FreeformCanvas` routes the live frames and owns stream lifecycle (close on unmount/unsubscribe).
- prompt: `FREEFORM_BASE` documents the live APIs + the users-online pattern; canvas-templates skill updated with the extension walkthrough.
Tests: schema round-trip cases for the new frames + operators allowlist, and unit tests for SSE URL building/chunk parsing.
Generated-By: PostHog Code
Task-Id: 49f7e918-8341-4c95-a2aa-97ae2aa6a8b9
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
React Doctor found 1 issue in 1 file · 1 warning. 1 warning
Reviewed by React Doctor for commit |
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.
Problem
Freeform canvases (agent-built React apps in the desktop app) could only fetch historical data via
ph.query/ph.loadInsight— saved insights re-run against ClickHouse. Anything that should update as events arrive (a live event feed, a "users online" counter) had to re-poll an insight, which lags ingestion and hammers/query. PostHog's own Activity → Live events and Web Analytics Live surfaces already solve this with a dedicated livestream SSE service, but canvases had no way to reach it: the sandboxed iframe can't hold the project-scopedlive_events_tokenJWT, and its CSP blocks the host.This adds a host-brokered live events avenue so canvases can subscribe to the live stream and read realtime counters — keeping the token server-side, matching the existing
ph.*security model.Changes
New streaming capability on the
ph.*canvas bridge (shim → postMessage → ui bridge → tRPC → core service, the documented extension path in the canvas-templates skill):ph.subscribeLiveEvents(params, onEvent, onEnd)— opens a live SSE subscription. The canvas posts alive-subscribeframe; the renderer fetches a brokered connection config (canvasData.liveConnectionConfig→CanvasDataService.liveConnectionConfig), opens the SSE tolive.{region}.posthog.com/eventsitself, and fans each event into the iframe as alive-eventframe. The scoped JWT lives only in the renderer — it never crosses into the sandbox.params.propertiesfilters only support the livestream operator allowlist (LIVE_EVENTS_SUPPORTED_OPERATORS, mirroring the app's live feed) — validated by the schema and spelled out in the agent prompt.ph.liveStats()— a ONE-SHOT poll (rides the genericdata-requestframe,method: "liveStats") →canvasData.liveStats→GET <live host>/stats→{ users_on_product, active_recordings }, the same "users online" pill the Activity page shows. For a live readout the prompt also documents the client-side 60s sliding-window count derived from the streameddistinct_ids — matching the Web-Analytics-Live tab without an extra host method.Plumbing:
live-subscribe/live-unsubscribe/live-event/live-stream-status),getLiveEventsUrlFromRegion(shared),CanvasDataService.liveConnectionConfig/liveStats,ICanvasDataServicesurface, cached per-project live JWT.canvasData.liveConnectionConfig+canvasData.liveStatsprocedures.openLiveEventsStream+ exportedbuildLiveEventsUrl/parseSseChunkin the data bridge;FreeformCanvasroutes the live frames and owns stream lifecycle (close on unmount/unsubscribe, replace on resubscribe); sandbox shim getsph.subscribeLiveEvents/ph.liveStats+ event dispatch.FREEFORM_BASEdocuments the live APIs + users-online pattern; canvas-templates SKILL.md gains a "Live / realtime (SSE)" section. Companion MCP PR documents the same in the canvas-publish tool description (see PostHog/posthog).How did you test this?
/eventsURL builder (freeformDataBridge.live.test.ts), and schema round-trip cases for the new live frames + operator allowlist (freeformSchemas.test.ts). Full@posthog/core(2,844) and@posthog/ui(2,413) suites green;core/ui/host-routertypecheck clean;biome checkclean on all touched packages.Automatic notifications
Created with PostHog Code