Skip to content

Verify an SSH signature and receive a JWT.

POST
/v1/auth/verify
curl --request POST \
--url https://example.com/v1/auth/verify \
--header 'Content-Type: application/json' \
--data '{ "challengeId": "example", "payload": "example", "publicKeyFingerprint": "example", "signature": "example" }'
  • None

JSON body for POST /auth/verify.

The server decides from payload and signature alone. The origin, the purpose, the nonce and, for a claim, the administrator’s name and publicKey all travel inside the signed payload; there is no unsigned copy of any of them.

Media typeapplication/json

JSON body for POST /auth/verify.

The server decides from payload and signature alone. The origin, the purpose, the nonce and, for a claim, the administrator’s name and publicKey all travel inside the signed payload; there is no unsigned copy of any of them.

object
challengeId
required

The challenge ID from the challenge response. Only selects the nonce; the signature binds the choice.

string
payload
required

The exact UTF-8 text hf signed, sent verbatim (a JSON object; see auth_proto::payload). At most 4096 bytes.

string
publicKeyFingerprint
required

Fingerprint of the public key (as stored in the credential). Only selects the stored credential on a login and is ignored on a claim; it is never trusted, because verification against the selected key binds it.

string
signature
required

PEM-encoded SSHSIG over payload, under namespace henceforth-login-v2 or henceforth-claim-v2, which also selects the login or claim branch.

string
Examplegenerated
{
"challengeId": "example",
"payload": "example",
"publicKeyFingerprint": "example",
"signature": "example"
}

Successful POST /auth/verify response: the same token fields as a login, plus whether this request claimed the instance (#994 D3, #1022).

Media typeapplication/json

Successful POST /auth/verify response: the same token fields as a login, plus whether this request claimed the instance (#994 D3, #1022).

object
claimed
required

true exactly when this very request won the claim latch and created the first administrator; false for an ordinary login. A claim signature never yields a login: one that loses the race to an earlier claim is refused 409 INSTANCE_ALREADY_CLAIMED, and a login signature never claims. A client that read GET /status before verifying reports a claim from this field, not from what it saw earlier.

boolean
expiresIn
required

Token lifetime in seconds.

integer format: int64
token
required

Signed JWT (ES256).

string
Examplegenerated
{
"claimed": true,
"expiresIn": 1,
"token": "example"
}

Failed to parse the request body as JSON

Media typetext/plain
string

Expected request with Content-Type: application/json

Media typetext/plain
string

Failed to deserialize the JSON body into the target type

Media typetext/plain
string

Structured error envelope (SPEC-DATA-0006): {"errors": [{"domain", "scope", "target", "code", "message"}, ...]}. domain (added #950 D9) names the subsystem that produced the entry (data, metadata, apply, git, auth, iam, hfx, or api for a transport-level failure with no domain error behind it) and is always present.

Media typeapplication/json

The top-level response body. Wraps a non-empty list of [ErrorEntry].

object
errors
required

The error entries making up the envelope. Always non-empty.

Array<object>

One entry in the error envelope.

Carries a [Domain] naming the subsystem that produced it, an anchoring [ErrorScope], an optional target that disambiguates within the scope, a stable SCREAMING_SNAKE_CASE code, and a human-readable English message. See SPEC-DATA-0006 for the full envelope contract.

object
code
required

Machine-readable, stable, SCREAMING_SNAKE_CASE identifier.

string
domain
required

Which subsystem produced this entry. Always serialized.

One of:

data::error::DataError – record CRUD, validation, queries.

string
Allowed value: data
location
Any of:

HFX source location for an HFX_PARSE entry.

Emitted as the extra location field on the entry JSON when present. The line/column are 1-based. The frame is the offending source line extracted verbatim from the input.

object
column
required

1-based column within the line, measured in UTF-16 code units of the prefix so browser-side editors can consume it directly.

integer format: uint32
frame

The offending source line text, when available.

string | null
line
required

1-based line number within the source text.

integer format: uint32
message
required

Server-authored English message. Not part of the API contract; may change across releases without notice.

string
rule

The key of the validation rule that failed, when the code is RULE_FAILED. None for all other codes. Skipped during serialization when absent so it does not appear on unrelated entries (same pattern as location).

string | null
scope
required

Anchoring category. Drives client-side rendering.

One of:

Attached to a specific request field. target is the field key.

string
Allowed value: field
target

Field key for field scope, record id for record scope, null otherwise. Always serialized, even when null.

string | null
Example
{
"errors": [
{
"domain": "data",
"scope": "field"
}
]
}