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 anAPI_KEY_EDITORorAPI_KEY_ADMINvalue and returns an 8-hour session token.X-Editor-Tokenheader: all authenticated routes now acceptX-Editor-Tokenin addition toX-Api-Key. The header grantseditorscope after HMAC verification.X-Api-Keyis checked first; a valid API key is never blocked by a stale or invalid editor token.mintEditorToken/verifyEditorTokenare exported pure functions inauth.plugin.ts— safe to test in isolation./auth/*routes are exempt from the auth hook (public, no key required).
Editor
LoginGatecomponent: wraps the entire editor. On first load, checks/auth/statusand 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_EDITORonce per browser session → token stored insessionStorage→ sent asX-Editor-Tokenon all API calls → if a 401 is received, the editor automatically returns to the login form. VITE_API_KEYremoved: no build-time credential is embedded in the JS bundle.lib/auth.ts(new): token storage helpers,getAuthStatus(),requestEditorToken(),AUTH_EXPIRED_EVENTconstant.lib/api-config.ts(new): extractsAPI_BASEto avoid circular imports betweenapi.tsandauth.ts.
Migration
Upgrade from v0.14.0
- Remove
VITE_API_KEYfromapps/editor/.env.local(or any CI build environment that injects it). It is no longer read. - Ensure
API_KEY_EDITORis set on the API server (it was already required in v0.14.0). - Rebuild and redeploy. The editor will show the login gate on next load.
- Operators enter the
API_KEY_EDITORvalue 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-tokentransmitsAPI_KEY_EDITORover 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.
sessionStorageclears on tab close. A rotatedAPI_KEY_EDITORimmediately invalidates all outstanding tokens (they were signed with the old key). - Shared credential: all operators share one
API_KEY_EDITORvalue. 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
| File | Change |
|---|---|
apps/api/src/plugins/auth.plugin.ts | Add mintEditorToken, verifyEditorToken; accept X-Editor-Token; decorate app with editorCredentials/authDisabled; exempt /auth/ |
apps/api/src/routes/auth/auth.ts | NEW — GET /auth/status, POST /auth/editor-token |
apps/api/src/server.ts | Register auth routes under /auth prefix |
apps/api/src/__tests__/editor-session.test.ts | NEW — full session token test suite (27 cases) |
apps/api/src/__tests__/auth-scopes.test.ts | Add X-Editor-Token tests (5 new cases) |
apps/editor/src/lib/api-config.ts | NEW — API_BASE extracted to avoid circular import |
apps/editor/src/lib/api.ts | Remove VITE_API_KEY; use X-Editor-Token from sessionStorage; dispatch AUTH_EXPIRED_EVENT on 401 |
apps/editor/src/lib/auth.ts | NEW — token storage, expiry, getAuthStatus, requestEditorToken |
apps/editor/src/lib/auth.test.ts | NEW — unit tests for editor auth utilities |
apps/editor/src/components/auth/LoginGate.tsx | NEW — login gate component |
apps/editor/src/components/auth/LoginGate.test.tsx | NEW — behavioural tests for all LoginGate states |
apps/editor/src/App.tsx | Wrap with <LoginGate> |
apps/editor/src/editor.css | Add login gate CSS classes |
.env.example | Remove VITE_API_KEY; update API_KEY_EDITOR comment |
README.md | Update editor auth description |
docs/api-guide.md | Document GET /auth/status, POST /auth/editor-token; update editor auth section |
docs/editor-guide.md | Replace VITE_API_KEY setup with login gate instructions |
docs/deployment-guide.md | Update API_KEY_EDITOR description; remove VITE_API_KEY reference |
docs/runbook.md | Update checklist; add editor login troubleshooting section |
docs/release-v0.15.0.md | NEW — this file |