Pulp Engine Document Rendering
Get started
Release v0.15.0

Pulp Engine v0.15.0 — Release Notes

Date: 2026-03-22


Summary

v0.15.0 replaces the static VITE_API_KEY browser credential with a short-lived editor session token obtained via an interactive login gate. The API_KEY_EDITOR value is no longer baked into the JavaScript bundle at build time. Instead, operators enter it interactively and receive an 8-hour HMAC-signed session token stored in sessionStorage (cleared on tab close).

This is the smallest strong improvement to the editor auth model — no new auth platform, no OAuth, no user accounts. Pure node:crypto, no new npm dependencies.


What changed

API

  • GET /auth/status (new, public): returns { authRequired: boolean; editorLoginAvailable: boolean } so the editor can decide whether to show the login gate, the config-error card, or skip auth entirely.
  • POST /auth/editor-token (new, public, 10 req/min per IP): accepts an API_KEY_EDITOR or API_KEY_ADMIN value and returns an 8-hour session token.
  • X-Editor-Token header: all authenticated routes now accept X-Editor-Token in addition to X-Api-Key. The header grants editor scope after HMAC verification. X-Api-Key is checked first; a valid API key is never blocked by a stale or invalid editor token.
  • mintEditorToken / verifyEditorToken are exported pure functions in auth.plugin.ts — safe to test in isolation.
  • /auth/* routes are exempt from the auth hook (public, no key required).

Editor

  • LoginGate component: wraps the entire editor. On first load, checks /auth/status and either (a) renders children immediately (dev mode), (b) shows a config-error card, or (c) shows a “Sign in” form.
  • Session token flow: operator enters API_KEY_EDITOR once per browser session → token stored in sessionStorage → sent as X-Editor-Token on all API calls → if a 401 is received, the editor automatically returns to the login form.
  • VITE_API_KEY removed: no build-time credential is embedded in the JS bundle.
  • lib/auth.ts (new): token storage helpers, getAuthStatus(), requestEditorToken(), AUTH_EXPIRED_EVENT constant.
  • lib/api-config.ts (new): extracts API_BASE to avoid circular imports between api.ts and auth.ts.

Migration

Upgrade from v0.14.0

  1. Remove VITE_API_KEY from apps/editor/.env.local (or any CI build environment that injects it). It is no longer read.
  2. Ensure API_KEY_EDITOR is set on the API server (it was already required in v0.14.0).
  3. Rebuild and redeploy. The editor will show the login gate on next load.
  4. Operators enter the API_KEY_EDITOR value once per browser session; no configuration change is required on client machines.

No migration required for server-to-server callers

X-Api-Key continues to work unchanged for CI, Postman, and any automation that calls the API directly. Only the browser editor path changed.


Breaking changes

None for server-to-server callers (X-Api-Key path unchanged).

Editor operators: VITE_API_KEY is ignored — remove it from your editor build environment to avoid confusion. The editor login gate replaces it.


Security notes

  • HTTPS required in production: POST /auth/editor-token transmits API_KEY_EDITOR over the network. Without HTTPS, a network observer can capture the key at login time. The key has left the JS bundle, but the login exchange is still a plaintext credential on HTTP. Run the API behind a TLS-terminating reverse proxy in production.
  • Token lifetime: 8 hours. sessionStorage clears on tab close. A rotated API_KEY_EDITOR immediately invalidates all outstanding tokens (they were signed with the old key).
  • Shared credential: all operators share one API_KEY_EDITOR value. Per-user sessions would require user management infrastructure (not planned for v0.15).
  • Dev mode auto-bypass: when no credentials are configured, the login gate is skipped entirely. The API also accepts all requests in that state. Do not use in production without credentials.

Files changed

FileChange
apps/api/src/plugins/auth.plugin.tsAdd mintEditorToken, verifyEditorToken; accept X-Editor-Token; decorate app with editorCredentials/authDisabled; exempt /auth/
apps/api/src/routes/auth/auth.tsNEWGET /auth/status, POST /auth/editor-token
apps/api/src/server.tsRegister auth routes under /auth prefix
apps/api/src/__tests__/editor-session.test.tsNEW — full session token test suite (27 cases)
apps/api/src/__tests__/auth-scopes.test.tsAdd X-Editor-Token tests (5 new cases)
apps/editor/src/lib/api-config.tsNEWAPI_BASE extracted to avoid circular import
apps/editor/src/lib/api.tsRemove VITE_API_KEY; use X-Editor-Token from sessionStorage; dispatch AUTH_EXPIRED_EVENT on 401
apps/editor/src/lib/auth.tsNEW — token storage, expiry, getAuthStatus, requestEditorToken
apps/editor/src/lib/auth.test.tsNEW — unit tests for editor auth utilities
apps/editor/src/components/auth/LoginGate.tsxNEW — login gate component
apps/editor/src/components/auth/LoginGate.test.tsxNEW — behavioural tests for all LoginGate states
apps/editor/src/App.tsxWrap with <LoginGate>
apps/editor/src/editor.cssAdd login gate CSS classes
.env.exampleRemove VITE_API_KEY; update API_KEY_EDITOR comment
README.mdUpdate editor auth description
docs/api-guide.mdDocument GET /auth/status, POST /auth/editor-token; update editor auth section
docs/editor-guide.mdReplace VITE_API_KEY setup with login gate instructions
docs/deployment-guide.mdUpdate API_KEY_EDITOR description; remove VITE_API_KEY reference
docs/runbook.mdUpdate checklist; add editor login troubleshooting section
docs/release-v0.15.0.mdNEW — this file