github oriolrius/pki-manager-web v3.9.6
v3.9.6 — SSH REST/OpenAPI parity, dark-mode Swagger

3 hours ago

SSH: the REST API now covers the whole CA lifecycle

The SSH service layer was always meant to be reachable from both APIs — typed tRPC for the UI, REST/OpenAPI for automation — but it had quietly drifted. Only 30 of the 52 tRPC procedures had a REST twin.

The gaps were not peripheral. Everything after create a CA (get, import, revoke, rotate, retire) and the entire bulk router were UI-only, so a script, pki-manager-cli or an Ansible play could stand a CA up and then had nowhere to go.

All 22 missing operations are now exposed, each delegating to the same service singleton its tRPC twin calls:

Area New endpoints
CA GET /cas/:caId · POST /cas/import · POST /cas/:caId/{revoke,rotate,retire}
Host GET /hosts/:id · GET /hosts/:id/deploy-bundle · POST /hosts/:id/{revoke,ecies-key,offboard}
Identity GET /identities · GET /users/certificates · POST /identities/:id/{disable,offboard}
Principal GET /principals/mappings · GET /principals/stale-hosts · DELETE /principals/:id
Bulk GET /bulk/expiring · POST /bulk/{renew,revoke}
KRL POST /cas/:caId/revoke-serial · POST /cas/:caId/revoke-key

Bug fixes found while wiring them up

Two services had the same silent no-op that markPushed had in 3.9.5:

  • deletePrincipal() never checked existence. Deleting an unknown id removed zero rows and returned success. Now returns 404.
  • revokeCurrent() conflated two different failures. "No such host" and "host exists but has no live certificate" shared one message, so a typo'd id surfaced as a state error. Now 404 vs 400 respectively.

Two REST-layer defects the new tests caught:

  • /principals/mappings returned 400 — the service yields a Record, not an array, against an array response schema.
  • Fastify validates a declared body schema even when no body is sent, so endpoints whose body is nothing but an optional reason rejected a bare curl -X POST with body must be object. The body now defaults to {}. This also fixes the pre-existing POST /certs/:id/revoke.

The guard against future drift

ssh-rest-parity.test.ts enumerates the live tRPC router (sshRouter._def.procedures) rather than a hand-written list, and asserts every procedure maps to a registered REST operation. Adding a tRPC-only procedure now fails CI with instructions to add the route or record a deliberate exemption. The exemption list is empty, and a test keeps it that way.

UI

/api-docs is readable in dark mode. swagger-ui ships a hardcoded light palette, which left the embedded docs white-on-white against the app theme.


Upgrade notes: no migration, no configuration change, no breaking change — every route is additive, and the two service fixes turn silent successes into correct errors.

Backend: 63 files / 702 tests (was 61/625). Frontend: 52 tests.

Don't miss a new pki-manager-web release

NewReleases is sending notifications on new releases.