Download the leaf certificate this listener currently serves (PEM).
const url = 'http://localhost:8080/v1/tls/certificate.pem';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}use reqwest;
#[tokio::main]pub async fn main() { let url = "http://localhost:8080/v1/tls/certificate.pem";
let client = reqwest::Client::new(); let response = client.get(url) .send() .await;
let results = response.unwrap() .json::<serde_json::Value>() .await .unwrap();
dbg!(results);}curl --request GET \ --url http://localhost:8080/v1/tls/certificate.pem§S6a. Returns the PUBLIC half of the certificate in force, so an operator can add it to their OS trust store and reach the instance over a trusted HTTPS origin.
DELIBERATELY UNAUTHENTICATED, and that is correct rather than lax: a client that does not yet trust the certificate frequently cannot complete a login in order to fetch it, so requiring a bearer token would make this route useless in exactly the situation it exists for. It discloses SANs (internal hostnames, LAN IPs) to anyone who can reach the port - so does the TLS handshake itself. Equivalent exposure, not new exposure.
NEVER THE PRIVATE KEY. The server builds this response by re-encoding PEM from the PARSED LEAF’S DER with a compile-time “CERTIFICATE” block type; it does not read the key path, and no key bytes are in scope on this path. An explicit test asserts no PRIVATE KEY block can appear in the response.
THE HONEST CAVEAT: downloading a certificate over a connection you do not yet trust is trust-on-first-use, and a MITM can serve their own. The mitigation is the fingerprint, and it only works OUT OF BAND - the control plane logs the SHA-256 fingerprint at startup, and it is also returned here as X-Quasar-Certificate-Fingerprint. A client MUST tell the operator to compare the two before trusting. Without that instruction the download button is security theatre; with it, it is a real verification step.
Under topology C the certificate returned here is Quasar’s INTERNAL one and is not what the operator’s browser validates. GET /v1/admin/access-check says so.
Responses
Section titled “Responses”The leaf certificate, PEM. Sent as an attachment with Cache-Control: no-store (the certificate can be replaced at any moment by an upload, and a cached stale one is precisely what would make an operator trust the wrong fingerprint).
Headers
Section titled “Headers”SHA-256 over the DER, uppercase colon-separated hex - the same form the startup log and a browser’s certificate viewer show, so the comparison is literal.
TLS is disabled on this control plane (QUASAR_TLS=off), so it serves no certificate.
object
object
E.g. validation_failed, unauthorized, forbidden, not_found, conflict, session_quota_exceeded, home_in_use, home_not_provisioned, parent_app_disabled, profile_ineligible, profile_not_launchable_for_app, no_host_available, capacity_exhausted, restart_required, rate_limited, internal. Open string, not an enum: new codes are additive and an unknown one falls through to a client’s generic per-status branch.
Present on restart_required.
Steam library discovery Phase 3, ADDITIVE: present on home_in_use when the guard could name the CONFLICTING live session - the one already holding the home. It is here so the client can offer “go to your running session” with a link instead of a dead-end toast. OMITTED rather than empty when the conflict is known but the session is not, so a client branches on presence and never renders a link to nowhere. Load-bearing once derived tiles exist: the lock is held by the PARENT’s home, so a user who clicks a game tile can be refused because a DIFFERENT app (the Steam launcher, or another game from the same install) is running, and without the session id the refusal reads as a bug. See control-api.md §Derived tiles.
Steam library discovery Phase 3, ADDITIVE: present on the 409 conflict from DELETE /v1/apps/{id} when the app has derived tiles and ?delete_derived=true was not sent. A LIST, not a count - the point of the confirmation is that the admin sees what they are about to destroy. Capped; an empty array means the tiles could not be listed, never that there are none.
object
Examplegenerated
{ "error": { "code": "example", "message": "example", "live_sessions": 1, "session_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "derived_tiles": [ { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "example" } ] }}