Pulp Engine v0.47.0 — Release Notes
The bundled editor deployment path is now first-class. The Docker image now includes the
compiled visual editor SPA, served by the API at /editor/. Prior to this release, the Docker
image was API-only: the product claimed “a React-based visual editor that ships with the
platform” but no artifact or deployment path actually delivered it. That gap is closed.
What changed
1. Editor bundled in the Docker image
The Dockerfile builder stage already compiled the editor during pnpm build — the
apps/editor/dist/ output existed in the builder but was never copied into the runtime image.
The runtime image now includes it:
COPY --from=builder /build/apps/editor/dist /app/editor-dist
The editor is built with VITE_EDITOR_BASE_PATH=/editor/ so Vite emits asset URLs prefixed at
/editor/assets/..., matching the subpath where the API serves it.
2. API serves the editor at /editor/
apps/api/src/server.ts now registers a fastifyStatic handler for /editor/ when the
editor-dist/ directory exists at runtime. In local dev and non-Docker source builds the
directory is absent, so the registration is silently skipped — no behaviour change for those
paths.
A permanent redirect from /editor → /editor/ ensures users typing the shorter URL land
correctly.
The startup log now includes Visual editor available at /editor/ when the editor is bundled.
3. Same-origin API wiring
apps/editor/src/lib/api-config.ts previously defaulted to http://127.0.0.1:3001 (wrong port
— the API runs on 3000, and this default was silently broken in all Docker deployments). The
default is now '' (empty string), which causes the SPA to make same-origin relative requests
(/templates, /auth/editor-token, etc.) when served from the API origin.
?? (nullish coalescing) is used instead of || so an explicit empty-string VITE_API_URL
build var is honoured if needed.
4. Local development wiring fixed
apps/editor/.env.development is a new committed file containing:
VITE_API_URL=http://localhost:3000
Vite loads this file automatically in dev mode (pnpm dev / vite serve) but not during
production builds (vite build). This fixes the broken local dev default and requires no
manual environment setup to run the editor against a local API instance.
The Playwright E2E configs explicitly pass VITE_API_URL as a process env var, which takes
higher priority than .env files in Vite’s resolution order — E2E tests are unaffected.
5. Evaluator compose files updated
compose.yaml and compose.postgres.yaml (both labelled evaluator setups) now include:
PREVIEW_ROUTES_ENABLED: "true"
Without this, evaluators can log in and edit templates but the editor’s live preview button
returns 404. This is evaluation posture and is clearly labelled as such in both files.
Preview routes remain off by default in production — see
docs/deployment-guide.md § Visual Editor for production guidance.
Both files also have the editor URL comment corrected from http://localhost:3000 to
http://localhost:3000/editor/.
6. Documentation
docs/deployment-guide.md— new “Visual Editor” section documenting: bundled path, live preview gating (PREVIEW_ROUTES_ENABLED), same-origin CORS behaviour, and local-dev vs Docker topology table.docs/editor-guide.md— new “Deployment modes” section at the top distinguishing local development (:5174) from bundled deployment (/editor/).README.md— editor URL added to Docker run examples; preview posture note separated from editor availability note;CORS_ALLOWED_ORIGINSannotation updated (bundled editor is same-origin — CORS not required for editor-to-API calls).
7. Deployment validation
scripts/validate-deploy.sh now includes check 6, gated on EXPECT_EDITOR=true:
EXPECT_EDITOR=true ./scripts/validate-deploy.sh http://localhost:3000 $API_KEY_ADMIN
When set, GET /editor/ must return HTTP 200 with Content-Type: text/html — failure is a
hard error. When absent, the check is skipped with an advisory notice. Use for Docker image
deployments; skip for API-only or source-build deployments.
docs/runbook.md smoke tests section now includes a “Bundled editor check” subsection covering
the curl checks, browser login flow, and EXPECT_EDITOR script invocation.
Validation
pnpm --filter @pulp-engine/api typecheck— 0 errorspnpm --filter @pulp-engine/editor typecheck— 0 errorspnpm --filter @pulp-engine/api lint— 0 errorspnpm --filter @pulp-engine/editor lint— 0 errorspnpm --filter @pulp-engine/api test:file— 559 passed, 48 skipped, 0 failed
Upgrade
No breaking changes to the API, storage, auth, or render pipeline. All existing env vars, API shapes, and database schemas are unchanged. No migrations required.
Docker image: the image now includes the editor SPA (~1 MB additional). The API starts and operates identically when the editor dist is absent (local dev, non-Docker builds).
CORS_ALLOWED_ORIGINS in hardened mode: for same-origin bundled deployments (editor and
API on the same host), the editor makes same-origin requests and CORS is not involved. However,
HARDEN_PRODUCTION=true still requires CORS_ALLOWED_ORIGINS to be set. Set it to your
deployment URL (e.g. CORS_ALLOWED_ORIGINS=https://pulp-engine.example.com).
Bug fix included in this release
PDF renderer: PUPPETEER_EXECUTABLE_PATH not forwarded to child process worker
The child-process render dispatcher forks its worker with env: {} for secret
isolation. PUPPETEER_EXECUTABLE_PATH and PUPPETEER_CACHE_DIR were not
forwarded — they are configuration paths, not secrets, but the omission meant
the worker’s Puppeteer fell back to its default Chrome cache location, which
differs when the operator (or CI) sets PUPPETEER_EXECUTABLE_PATH to a system
Chrome binary. Symptom: the startup probe passed (used process.env, found
Chrome) while actual renders returned 422 render_error (worker could not find
Chrome). This bug was present since v0.46.0. Fixed by forwarding both vars
when set. (packages/pdf-renderer/src/child-process-dispatcher.ts)
docker pull ghcr.io/OWNER/pulp-engine:v0.47.0