Release 0.31.0 - #230
Merged
Merged
Release 0.31.0#230
Conversation
A browsable API, modelled on AWX's /api/v2/: the same routes answer with a page when the caller sends Accept: text/html, and every response names the routes next to it so the API can be followed rather than memorised. Off by default behind server.ui.enabled. With the flag unset the router is exactly the one that shipped in 0.30.0 — no login form, no cookie credential, no HTML renderer — so a machine-to-machine deployment gains nothing it did not ask for. The handlers were not touched. A middleware renders what they already return, and the help panel, the Allow header and the action buttons are all read from the OpenAPI document in memory, so a route added later is browsable and self-documenting the day it exists. Two breaking changes on GET /api/v1/sources, both documented in the CHANGELOG: the list is now every configured source rather than only the cached ones, and age_seconds/total_hosts are null when an id holds no data. ?cached=true restores the previous membership for a consumer that wants it.
The advisory (TLS 1.3 handshake messages accepted across encryption level boundaries) landed three days ago and already had main failing audit and deny. Lockfile only: rustls reaches the build through reqwest's rustls-tls and through russh, so it covers federated reads and the SSH connector.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A browsable API, modelled on AWX's
/api/v2/. The same routes answer with apage when the caller sends
Accept: text/html, and every response names theroutes next to it so the API can be followed rather than memorised.
What it adds
GET /api/v1/— the index: every collection this key may read, as pathsrelatedon the metadata responses, so a consumer can follow the graph incurl | jqwith no HTML anywhereGET /api/v1/sources/{id}— a source or view as a resource. The id answeredonly
DELETE, so the obvious click from the list landed on a 405breadcrumbs, the route's own documentation, the
Allowheader, six themesOff by default
server.ui.enabledgates all of it, and it isfalseunless asked for. Withthe flag unset the router is exactly the one that shipped in 0.30.0: the
middlewares are not layered and the login routes are not registered. A
machine-to-machine deployment gains only the JSON half.
The handlers were not touched
A middleware renders what they already return, and the help panel, the
Allowline and the action buttons are all read from the OpenAPI document in memory —
the same text that feeds Swagger. So a route added later is browsable and
self-documenting the day it exists, with no second place to register it.
Breaking, both on
GET /api/v1/sourcesthat never completed a sync did not appear at all, and that is precisely the
one an operator goes looking for. It can now contain an id whose
/datasetanswers 404 —
?cached=truerestores the previous membership exactly.age_secondsandtotal_hostsarenullwhen an id holds no data, beside anew
cachedboolean. A zero there read as "synced just now, and empty".Safety
The pages render data that came from connectors, so it is all treated as
hostile. An
Htmlnewtype that can only be built by escaping (a&strcannotreach the page without going through it), linkification restricted to our own
route templates, and a CSP of
default-src 'none'withframe-ancestors 'none'— that last one is what keeps another site from framing a page and collecting a
click on its Evict button with the victim's own CSRF token inside.
Writes from a browser carry a CSRF token; requests presenting an API key
directly do not need one and are unchanged. The
_method=DELETEoverride ishonoured only for a request that carries a session cookie, so a cross-site form
cannot reach a DELETE route. A configuration reload that revokes a key now ends
that key's browser sessions in the same breath.
Sessions are a handle on an existing API key, validated by the same
constant-time compare a header goes through — there is no second credential
store, so the UI inherits the permission model: a restricted key browsing the
console sees exactly the sources its token allows.
Verification
661 tests,
clippy -D warningsandcargo fmt --checkclean. Reviewed forcorrectness, for regressions against existing JSON consumers, and adversarially
for security; every finding from those passes is either fixed here or noted in
docs/browsable-api.md.