feat(canvas): add secure application build pipeline#73727
Conversation
Add normalized canvas source and build persistence, deterministic cloud validation and builds, signed user-content artifact delivery, universal agent tooling, and run-level attribution. Generated-By: PostHog Code Task-Id: f2d133f0-a9e6-4a16-8ecd-fc0a20bab2c3
|
Hey @k11kirky! 👋 It looks like your git author email on this PR isn't your
You can fix it for this repo with: git config user.email "you@posthog.com"Or set it globally with |
🤖 CI report✅ Bundle size — no changeUncompressed size of every built Total: 64.44 MiB · no change No file changed by more than 1000 B. Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report ✅ Eager graph — within budgetHow much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy
🟢 Largest files eagerly shipped from
|
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.5 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 3.9 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.4 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 789 B | src/scenes/ChunkLoadErrorBoundary.tsx |
| 762 B | src/index.tsx |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 281.5 KiB | ../node_modules/.pnpm/posthog-js@1.407.2/node_modules/posthog-js/dist/rrweb.js |
| 267.7 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 236.0 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 226.1 KiB | ../node_modules/.pnpm/posthog-js@1.407.2/node_modules/posthog-js/dist/module.js |
| 154.3 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 106.2 KiB | src/lib/api.ts |
| 94.0 KiB | ../packages/quill/packages/quill/dist/index.js |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 90.6 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Toolbar bundle — eager 2.18 MiB within budget
What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.
| Metric | Size | Δ vs base | Budget |
|---|---|---|---|
| Eager (shipped) entry + static imports |
2.18 MiB · 17 files | no change | ████░░░░░░ 38.1% of 5.72 MiB |
| Deferred (lazy) | 2.07 MiB · 33 files | no change | n/a — loads on demand |
Loader dist/toolbar.js |
1.1 KiB | no change | █░░░░░░░░░ 5.8% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 716.5 KiB | dist/toolbar/toolbar-app-HMV4VZ5O.css |
| 545.2 KiB | dist/toolbar/chunk-chunk-3RADJBLD.js |
| 484.3 KiB | dist/toolbar/chunk-chunk-ZXJK34VQ.js |
| 133.6 KiB | dist/toolbar/chunk-chunk-6SQZIKIH.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-T5KY5WYR.js |
| 71.0 KiB | dist/toolbar/toolbar-app-G6ARZY2E.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-27JL52RE.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-P4AKBHPC.js |
| 20.9 KiB | dist/toolbar/chunk-chunk-B7POBA4G.js |
| 12.2 KiB | dist/toolbar/chunk-chunk-PIK3PADE.js |
Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile
✅ Dist folder size — 🔺 +44.6 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1355.92 MiB · 🔺 +44.6 KiB (+0.0%)
ℹ️ MCP UI apps size — 32 app(s), 17065.5 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 599.5 KB | 187.7 KB |
| action | 457.8 KB | 187.7 KB |
| action-list | 564.3 KB | 187.7 KB |
| cohort | 456.8 KB | 187.7 KB |
| cohort-list | 563.3 KB | 187.7 KB |
| email-template | 456.6 KB | 187.7 KB |
| error-details | 472.4 KB | 187.7 KB |
| error-issue | 457.5 KB | 187.7 KB |
| error-issue-list | 564.2 KB | 187.7 KB |
| experiment | 561.5 KB | 187.7 KB |
| experiment-list | 565.1 KB | 187.7 KB |
| experiment-results | 563.2 KB | 187.7 KB |
| feature-flag | 567.1 KB | 187.7 KB |
| feature-flag-list | 570.9 KB | 187.7 KB |
| feature-flag-testing | 461.0 KB | 187.7 KB |
| insight-actors | 562.1 KB | 187.7 KB |
| invite-email-preview | 456.0 KB | 187.7 KB |
| llm-costs | 559.5 KB | 187.7 KB |
| session-recording | 458.6 KB | 187.7 KB |
| session-summary | 463.9 KB | 187.7 KB |
| survey | 458.4 KB | 187.7 KB |
| survey-global-stats | 562.2 KB | 187.7 KB |
| survey-list | 565.0 KB | 187.7 KB |
| survey-stats | 562.2 KB | 187.7 KB |
| trace-span | 457.2 KB | 187.7 KB |
| trace-span-list | 564.2 KB | 187.7 KB |
| workflow | 457.1 KB | 187.7 KB |
| workflow-list | 563.7 KB | 187.7 KB |
| loops-review | 461.2 KB | 187.7 KB |
| query-results | 745.5 KB | 187.7 KB |
| render-ui | 826.2 KB | 187.7 KB |
| visual-review-snapshots | 461.6 KB | 187.7 KB |
⚠️ MCP snapshots — 5 updated (0 modified, 5 added, 0 deleted)
Snapshots: MCP unit test snapshots updated
Changes: 5 snapshots (0 modified, 5 added, 0 deleted)
What this means:
- Snapshots have been automatically updated to match current output
Next steps:
- Review the changes to ensure they're intentional
- If unexpected, investigate what caused the output to change
⚠️ Django migration SQL — 1 new migration to review
We've detected new migrations on this PR. Review the SQL output for each migration:
posthog/migrations/1265_canvas_application_builds.py
BEGIN;
--
-- Create model CanvasSourceVersion
--
CREATE TABLE "posthog_canvassourceversion" ("id" uuid NOT NULL PRIMARY KEY, "task_id" uuid NOT NULL, "task_run_id" uuid NOT NULL, "source_hash" varchar(64) NOT NULL, "source_object_key" varchar(500) NOT NULL, "source_size" integer NOT NULL CHECK ("source_size" >= 0), "prompt" text NULL, "created_by_id" bigint NULL, "created_at" timestamp with time zone NOT NULL, "canvas_id" uuid NOT NULL, "parent_version_id" uuid NULL, "team_id" integer NOT NULL);
--
-- Create model CanvasBuild
--
CREATE TABLE "posthog_canvasbuild" ("id" uuid NOT NULL PRIMARY KEY, "build_status" varchar(16) NOT NULL, "artifact_object_prefix" varchar(500) NULL, "integrity" varchar(100) NULL, "diagnostics" jsonb NOT NULL, "manifest" jsonb NULL, "pinned" boolean NOT NULL, "created_at" timestamp with time zone NOT NULL, "started_at" timestamp with time zone NULL, "completed_at" timestamp with time zone NULL, "canvas_id" uuid NOT NULL, "team_id" integer NOT NULL, "source_version_id" uuid NOT NULL);
--
-- Create model CanvasApplication
--
CREATE TABLE "posthog_canvasapplication" ("id" uuid NOT NULL PRIMARY KEY, "created_at" timestamp with time zone NOT NULL, "updated_at" timestamp with time zone NOT NULL, "canvas_id" uuid NOT NULL UNIQUE, "team_id" integer NOT NULL, "active_build_id" uuid NULL, "previous_build_id" uuid NULL, "current_source_version_id" uuid NULL);
--
-- Create index canvas_source_history_idx on field(s) team, canvas, created_at of model canvassourceversion
--
CREATE INDEX "canvas_source_history_idx" ON "posthog_canvassourceversion" ("team_id", "canvas_id", "created_at");
--
-- Create index canvas_source_hash_idx on field(s) team, source_hash of model canvassourceversion
--
CREATE INDEX "canvas_source_hash_idx" ON "posthog_canvassourceversion" ("team_id", "source_hash");
--
-- Create constraint canvas_source_unique_run on model canvassourceversion
--
ALTER TABLE "posthog_canvassourceversion" ADD CONSTRAINT "canvas_source_unique_run" UNIQUE ("canvas_id", "task_run_id");
--
-- Create index canvas_build_history_idx on field(s) team, canvas, created_at of model canvasbuild
--
CREATE INDEX "canvas_build_history_idx" ON "posthog_canvasbuild" ("team_id", "canvas_id", "created_at");
--
-- Create index canvas_build_retention_idx on field(s) build_status, completed_at of model canvasbuild
--
CREATE INDEX "canvas_build_retention_idx" ON "posthog_canvasbuild" ("build_status", "completed_at");
ALTER TABLE "posthog_canvassourceversion" ADD CONSTRAINT "posthog_canvassource_parent_version_id_6334ec9f_fk_posthog_c" FOREIGN KEY ("parent_version_id") REFERENCES "posthog_canvassourceversion" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "posthog_canvassourceversion_canvas_id_19a2a00f" ON "posthog_canvassourceversion" ("canvas_id");
CREATE INDEX "posthog_canvassourceversion_parent_version_id_6334ec9f" ON "posthog_canvassourceversion" ("parent_version_id");
CREATE INDEX "posthog_canvassourceversion_team_id_1945a64c" ON "posthog_canvassourceversion" ("team_id");
ALTER TABLE "posthog_canvasbuild" ADD CONSTRAINT "posthog_canvasbuild_source_version_id_bd4a4399_fk_posthog_c" FOREIGN KEY ("source_version_id") REFERENCES "posthog_canvassourceversion" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "posthog_canvasbuild_canvas_id_1de5b7b5" ON "posthog_canvasbuild" ("canvas_id");
CREATE INDEX "posthog_canvasbuild_team_id_72863759" ON "posthog_canvasbuild" ("team_id");
CREATE INDEX "posthog_canvasbuild_source_version_id_bd4a4399" ON "posthog_canvasbuild" ("source_version_id");
CREATE INDEX "posthog_canvasapplication_team_id_4ce7957e" ON "posthog_canvasapplication" ("team_id");
CREATE INDEX "posthog_canvasapplication_active_build_id_c95390f0" ON "posthog_canvasapplication" ("active_build_id");
CREATE INDEX "posthog_canvasapplication_previous_build_id_f7237016" ON "posthog_canvasapplication" ("previous_build_id");
CREATE INDEX "posthog_canvasapplication_current_source_version_id_bf914651" ON "posthog_canvasapplication" ("current_source_version_id");
COMMIT;Last updated: 2026-07-26 10:28 UTC (0f590b2)
✅ Django migration risk — migration analysis complete
We've analyzed your migrations for potential risks.
Summary: 1 Safe | 0 Needs Review | 0 Blocked
✅ Safe
Brief or no lock, backwards compatible
posthog.1265_canvas_application_builds
└─ #1 ✅ CreateModel
Creating new table is safe
model: CanvasSourceVersion
└─ #2 ✅ CreateModel
Creating new table is safe
model: CanvasBuild
└─ #3 ✅ CreateModel
Creating new table is safe
model: CanvasApplication
│
└──> ℹ️ INFO:
ℹ️ Skipped operations on newly created tables (empty tables
don't cause lock contention).
Last updated: 2026-07-26 10:28 UTC (0f590b2)
ℹ️ Docs preview — preview build triggered
Docs from this PR will be published at posthog.com.
| Project | Preview | Updated (UTC) |
|---|---|---|
| posthog.com | Open preview | Jul 26, 2026, 10:25 AM |
The preview should be ready in about 10 minutes. Open the preview at /handbook/engineering/.
Include the standalone builder in the Docker context, cover canvas models in IDOR rules, and enforce builder lint conventions. Generated-By: PostHog Code Task-Id: f2d133f0-a9e6-4a16-8ecd-fc0a20bab2c3
…line Generated-By: PostHog Code Task-Id: f2d133f0-a9e6-4a16-8ecd-fc0a20bab2c3
Generated-By: PostHog Code Task-Id: f2d133f0-a9e6-4a16-8ecd-fc0a20bab2c3
Generated-By: PostHog Code Task-Id: f2d133f0-a9e6-4a16-8ecd-fc0a20bab2c3
Generated-By: PostHog Code Task-Id: f2d133f0-a9e6-4a16-8ecd-fc0a20bab2c3
Generated-By: PostHog Code Task-Id: f2d133f0-a9e6-4a16-8ecd-fc0a20bab2c3
Problem
Canvas generation needs a secure and reproducible backend for arbitrary client-side browser applications, available from every task while preserving last-known-good canvases.
Changes
The client contracts, local validation, artifact host, and generation flow are implemented in PostHog/code#3823.
How did you test this code?
Automatic notifications
Docs update
Added internal deployment and retention documentation for artifact origin, signing-key rotation, object storage, and future serverless scope.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
The implementation follows the approved four-phase canvas application architecture. Source and artifacts stay out of PostgreSQL, authoritative builds run without registry access or lifecycle scripts, artifacts require a dedicated user-content origin with short-lived signatures, and direct canvas egress remains denied until a user-facing capability approval flow exists.
Created with PostHog Code