Skip to content

GET /capabilities

GET /capabilities HTTP/1.1
Host: srv.hadronmemory.com
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=60
ETag: "3f7314e610ee311b51e46134b6c0f530"
Content-Type: application/json; charset=utf-8

{"capabilities":[]}

Which deployment switches are on, readable before sign-in. A client reads it to decide whether to select GraphQL fields that exist only while a switch is on. The rule behind it is cor:api:020:01.

Request

Path /capabilities, on the server's base URL (https://srv.hadronmemory.com in production)
Methods GET, and HEAD (answered from the same route with no body). Any other method gets the server's ordinary 404.
Authentication None. A token, cookie or session sent with it is ignored.
Input None. The query string, body and cookies are not read, and nothing is echoed back.

Response

{"capabilities": ["node-signing", "passkey-registration"]}

capabilities is an array of token words, one per switch that is on:

Token On when What it adds
node-signing The server runs with PASSKEY_SIGNING_ENABLED=true Node signing: the signing types (SigningRequest, NodeSignature and their enums), every field that returns one, and ServerInfo.signing
passkey-registration Signing is on and PASSKEY_REGISTRATION_ENABLED=true Passkey registration: ServerInfo.passkeyRegistration
  • An off switch is absent, never false. With nothing on, the answer is {"capabilities":[]}. There is no form that lists a switch as off.
  • passkey-registration never appears without node-signing. The server computes registration as signing on and registration on, so turning on registration alone leaves both off, and the answer stays [].
  • Test for a token, never for the array's length or exact contents. New words may be added.

The words are the same ones hadron_server_info prints on its capabilities: line (see MCP tools: Server identity). Both are read from one function on the server, so the two cannot disagree.

Headers

Header Value Why
Access-Control-Allow-Origin * Any origin can read it, without credentials. The route answers before the server's credentialed CORS policy, which only allows the portal's origin.
Cache-Control public, max-age=60 Shared caches, the CDN included, may keep it for a minute.
ETag A strong ETag of the body If-None-Match with it gets 304 Not Modified and no body.

The answer changes only when the server restarts with different switches, so the body and its ETag stay fixed for the life of the process.

Status codes

Status When
200 The answer.
304 If-None-Match matched the current ETag.
404 A method other than GET or HEAD.
429 More than 60 requests a minute from one client IP. The body is empty.

Today the 429 carries no Retry-After and no RateLimit headers. It also carries no Access-Control-Allow-Origin, so a browser reports it as a CORS failure, not a 429. Adding both headers to the 429 is an open follow-up (review note on hadron-server#1930).

Any failed read means every switch is off: a network error, a CORS failure, a 429, or a body that isn't {"capabilities": [...]}.

Using it to pick GraphQL fields

A switch doesn't make a field return null or an error when it is off: it removes the field and its types from the schema. Selecting a removed field fails GraphQL validation, and the whole operation fails with it.

  1. Read /capabilities once, when the client starts or the page loads.
  2. Select a switch-gated field only when its token is in the array: ServerInfo.signing and anything that returns a signing type need node-signing; ServerInfo.passkeyRegistration needs passkey-registration.
  3. If the read fails in any way, select none of them.

There is no GraphQL capabilities field: this endpoint is the discovery path. The GraphQL reference shows the schema production serves, so a field behind a switch that is off in production is not listed there.

curl -s https://srv.hadronmemory.com/capabilities
# {"capabilities":[]}