Release: authenticated API docs + route discovery, secret-key diagnostics #13

Merged
gsadmin merged 2 commits from development into main 2026-09-03 15:32:34 +00:00
Owner

Summary

Two changes since #12.

API docs are no longer public

The OpenAPI spec and Swagger UI were served without authentication. They now require the same auth as the rest of the API, and gained route discovery so clients don't need dev tools to find endpoints.

  • /api/openapi.json and /api/routes require a bearer token or X-API-Key (401 anonymous).
  • /api/docs additionally accepts a session cookie set at login, so a signed-in operator can open it in a new tab. Anonymous browsers are redirected to /login?redirect=... and returned afterwards.
  • The docs cookie is HttpOnly and path-scoped to /api/docs, so it is never sent to /api/v1/* and cannot authenticate an API call — no CSRF surface. Verified: cookie-only request to /api/v1/rules returns 401.

Discovery — both the spec and GET /api/routes accept ?method=get,post and ?path=<substring> (comma-separated, case-insensitive). The route list returns method, path, summary, tag, public, and allowed (false when a read-scoped key can't invoke it). /api/docs passes the same query through to the spec it loads.

UI — a </> icon in the header (both layouts) and an Administration → API Docs menu entry, opened in a new tab via a new external nav-item flag.

Secret-key decryption failures now diagnose themselves

A changed ORCHESTRAD_SECRET_KEY left stored credentials intact but unreadable, surfacing only as an opaque decryption failed inside an unrelated operation.

  • The error now states that the data was encrypted under a different ORCHESTRAD_SECRET_KEY, and names the affected credential.
  • New services.CheckSecretKey verifies every stored secret against the current key at startup and in orchestrad doctor — the latter previously only checked the key's length, which a correctly-sized but wrong key passed while still breaking every bind. Non-fatal; the server still starts.
  • README documents that the key must persist for the life of the install, plus ORCHESTRAD_SECRET_KEY_FILE.

Verification

Full backend suite green. Both changes verified live against the demo instance and the lab DC: anonymous/cookie/API-key access paths, all filter combinations, and reproducing the decryption failure with a wrong key (clear startup error naming the credential) then confirming recovery (connection test passes, rule preview returns 4 matched / 1 planned).

Merging this cuts a release (binaries, container image, MSI).

## Summary Two changes since #12. ### API docs are no longer public The OpenAPI spec and Swagger UI were served without authentication. They now require the same auth as the rest of the API, and gained route discovery so clients don't need dev tools to find endpoints. - `/api/openapi.json` and `/api/routes` require a bearer token or `X-API-Key` (401 anonymous). - `/api/docs` additionally accepts a session cookie set at login, so a signed-in operator can open it in a new tab. Anonymous browsers are redirected to `/login?redirect=...` and returned afterwards. - The docs cookie is **HttpOnly and path-scoped to `/api/docs`**, so it is never sent to `/api/v1/*` and cannot authenticate an API call — no CSRF surface. Verified: cookie-only request to `/api/v1/rules` returns 401. **Discovery** — both the spec and `GET /api/routes` accept `?method=get,post` and `?path=<substring>` (comma-separated, case-insensitive). The route list returns method, path, summary, tag, `public`, and `allowed` (false when a read-scoped key can't invoke it). `/api/docs` passes the same query through to the spec it loads. **UI** — a `</>` icon in the header (both layouts) and an **Administration → API Docs** menu entry, opened in a new tab via a new `external` nav-item flag. ### Secret-key decryption failures now diagnose themselves A changed `ORCHESTRAD_SECRET_KEY` left stored credentials intact but unreadable, surfacing only as an opaque `decryption failed` inside an unrelated operation. - The error now states that the data was encrypted under a different `ORCHESTRAD_SECRET_KEY`, and names the affected credential. - New `services.CheckSecretKey` verifies every stored secret against the current key at startup and in `orchestrad doctor` — the latter previously only checked the key's *length*, which a correctly-sized but wrong key passed while still breaking every bind. Non-fatal; the server still starts. - README documents that the key must persist for the life of the install, plus `ORCHESTRAD_SECRET_KEY_FILE`. ### Verification Full backend suite green. Both changes verified live against the demo instance and the lab DC: anonymous/cookie/API-key access paths, all filter combinations, and reproducing the decryption failure with a wrong key (clear startup error naming the credential) then confirming recovery (connection test passes, rule preview returns 4 matched / 1 planned). ### Merging this cuts a release (binaries, container image, MSI).
gsadmin added 2 commits 2026-09-03 15:32:24 +00:00
The OpenAPI spec and Swagger UI were public. Put them behind the same
authentication as the rest of the API, and add a compact route list so a
client can ask "what can I call?" without opening dev tools.

Access:
- /api/openapi.json and /api/routes require a bearer token or API key.
- /api/docs additionally accepts a session cookie set at login, so a
  signed-in operator can open the docs in a new tab; an anonymous browser
  is redirected to /login?redirect=... and returned afterwards.
- The cookie is HttpOnly and path-scoped to /api/docs, so it is never sent
  to /api/v1/* and cannot authenticate an API call (no CSRF surface).
  Verified: cookie-only request to /api/v1/rules returns 401.

Discovery: both the spec and GET /api/routes accept ?method=get,post and
?path=<substring> (comma-separated, case-insensitive). The route list
returns method, path, summary, tag, public, and `allowed` — false when a
read-scoped API key cannot invoke that route. /api/docs passes the same
query through to the spec it loads.

UI: a </> icon in the header (both layouts) and an Administration → API
Docs menu entry, opened in a new tab via a new `external` nav-item flag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stored credential secrets are encrypted with a key derived from
ORCHESTRAD_SECRET_KEY. When that value changes, the secrets are intact but
unreadable, and the only symptom was an opaque "decryption failed" surfacing
deep inside an unrelated operation:

  "preview failed: building LDAP client: failed to decrypt credential:
   decryption failed"

Nothing pointed at the real cause, so the error is now self-diagnosing:

- ErrDecryptionFailed states that the data was encrypted under a different
  ORCHESTRAD_SECRET_KEY (or is corrupted). GCM auth failure on a well-formed
  ciphertext is overwhelmingly a wrong-key case.
- The three credential decrypt sites name the credential, so the operator
  knows which password to restore or re-enter.
- New services.CheckSecretKey verifies every stored secret against the
  current key. It runs at startup (LogSecretKeyCheck) and in `doctor`, so a
  mismatched key is reported once, loudly, at the moment it is first used
  rather than during the next rule run. A correctly-sized but *different*
  key passed doctor's existing length check and still broke every bind.

Not fatal: the server still starts, since an operator may be mid-migration
or may intend to re-enter the secrets.

Verified on the demo instance: starting with a wrong key logs
"1 of 1 stored credential secret(s) CANNOT be decrypted ... [OrchestrAD]",
and the rule preview error now names both the credential and the key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
gsadmin merged commit d0437df8e6 into main 2026-09-03 15:32:34 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gsadmin/OrchestrAD#13