Release v0.78.4 — OpenAPI contract truth (audit Workstream C)
Date: 2026-05-08
Tag: v0.78.4
Summary
Audit-remediation patch on the v0.78.x line. Closes Workstream C of the 2026-05-07 7.8/10 audit-remediation plan: the published OpenAPI surface was advertising “no auth required” on routes whose runtime auth was enforced, and was advertising 0.0.0-extracted as the spec version. Both gaps closed.
No wire-shape changes. No behaviour changes. The 7 affected routes already required auth at runtime via apps/api/src/plugins/auth.plugin.ts — only the spec’s documentation of that fact was missing. v0.78.4 ships strictly additive metadata: declarations the spec should have had since those routes were introduced.
What landed
Real version stamp in openapi.json
scripts/extract-openapi.ts was hardcoding spec.info.version = '0.0.0-extracted' to suppress local-vs-CI churn from APP_VERSION drift. The CI freshness gate (Verify openapi.json is fresh in .github/workflows/ci.yml) already keeps the on-disk spec in lockstep with the running server, so reading the version from the canonical root package.json (the same source scripts/check-version.mjs uses) is unambiguous. Every routine version bump now produces a 1-line info.version diff in openapi.json plus the matching SDK typed-artifact diff — the new step in docs/release-checklist.md Pre-release section captures this explicitly:
pnpm extract-openapi # writes openapi.json with the new info.version
pnpm --filter @pulp-engine/sdk codegen # writes packages/sdk-typescript/openapi.d.ts
git add openapi.json packages/sdk-typescript/openapi.d.ts
Seven routes now declare security
The audit’s spec-walk identified 11 operations without a security field in openapi.json. Four were intentionally public (the curated allowlist below); the other 7 were drift — auth enforced at runtime, not declared at the schema level — so SDK generators and evaluator-trust readers saw “no auth required” on real-auth routes:
GET /admin/users/POST /admin/users/PUT /admin/users/{id}DELETE /admin/users/{id}POST /admin/users/reloadPOST /nodes/rewritePOST /nodes/rewrite/outcome
All 7 now declare security: DUAL_SECURITY (the [{ ApiKeyAuth: [] }, { EditorTokenAuth: [] }] constant from apps/api/src/schemas/shared.ts) plus standard 401 + 403 response shapes — and the rewrite SSE route also gained 400/409/429/503 declarations for JSON error envelopes the route was already emitting that the spec previously didn’t document. Runtime auth is unchanged.
Public-route allowlist (post-fix)
After v0.78.4, the only operations in openapi.json without a security field are these 4, all intentional:
GET /health— liveness probe; must be reachable without credentials so a load balancer or K8s readiness check can assess instance health before any auth configuration is even known.GET /health/ready— readiness probe; same reason.GET /auth/status— auth bootstrap; the editor needs to discover whether auth is required and which login modes are available before it has a credential to send.POST /auth/editor-token— editor session-token mint; auth is enforced by the handler body (API key as a body field, not a routed Fastify auth check), not at the schema level.
Contract test pins the audit’s findings
apps/api/src/__tests__/openapi-auth-coverage.test.ts — new 4-spec test that walks every operation in openapi.json and asserts every non-allowlisted route declares a non-empty security field. The test reads the on-disk openapi.json so it composes naturally with the existing CI freshness gate (the on-disk file matches the running server). Three guarantees:
- Every non-public operation declares
security(and it’s a non-empty array — rejects thesecurity: []form which would silently neutralize the guard). - The 7 audit-flagged routes are individually pinned by name, so a future regression on those exact surfaces fails with a recognisable message.
- The allowlist stays accurate — if a public route is removed but stays on the list, the test catches the stale entry.
Release-prep checklist updated
docs/release-checklist.md Pre-release section now explicitly lists the regenerate-after-bump step. The version-stamp change means every routine bump must regenerate openapi.json and packages/sdk-typescript/openapi.d.ts — without it, CI’s Verify openapi.json is fresh job goes red on the first PR after the bump. Documented in the checklist so the next release author isn’t surprised.
Why no SDK / wire churn
The seven routes already required auth at runtime — runtime behaviour is byte-identical to v0.78.3. The change is purely additive metadata in the OpenAPI spec:
security: DUAL_SECURITYdeclarations (no behaviour change — the auth plugin was already enforcing these scopes).401+403response shape declarations (these envelopes were always being returned; only the spec is catching up).400/409/429/503declarations on the rewrite SSE route (same story — these JSON envelopes were already being emitted).info.versionreads0.78.4instead of0.0.0-extracted.
Existing TypeScript SDK callers see the regenerated packages/sdk-typescript/openapi.d.ts but the runtime contract is unchanged.
Verification
Local pre-tag verification on the v0.78.4 commit:
pnpm extract-openapi --check— passes.pnpm --filter @pulp-engine/sdk build— clean (codegen + tsc).pnpm typecheck— 46/46.pnpm lint— 22/22.pnpm build— 27/27.- New contract test — 4 specs / 1 file in ~300 ms.
- Snapshot of operations missing
securityinopenapi.json:- Pre-fix: 11 operations (4 public + 7 audit-flagged).
- Post-fix: 4 operations (the curated public-route allowlist).
CI run on the underlying Workstream C commit (c22af0f, the same code as v0.78.4 minus the version bump): 9/9 jobs green including all 3 runners that exercise the new contract test (Linux ci/Test, Linux test-file-mode, Windows ci-windows/file-mode).
Known residual
- HA Check 7 environmental flake carries over from the v0.78.3 release window. Single-replica outage failover under sustained load measured 95.16% < 99% availability threshold on the 2026-05-07 nightly run. Unrelated to v0.78.4 — no API failover or LB code changed. GitHub Actions runner timing under sustained load. Worth tracking if it persists.
- SDK trusted-publisher registration still pending on npm and PyPI. Both publish workflows continue to fail at the registry-auth step on every release; one-time operator-side configuration on npmjs.org and pypi.org. Code is fine; nothing in v0.78.4 regressed the publish path.
- Audit Workstreams D + E deferred to follow-up patches: HA Redis/S3 automated checks (Workstream D), complexity hotspot first-carve (Workstream E). v0.78.4’s scope was the OpenAPI contract-truth slice only.