API Client

link-api is Link's typed client for the account service's API, one function a route, on link-fetch. It holds no credential of its own, and it makes and checks the access tokens the service's routes take.

Its types

types is made at every build from openapi.yaml (build.rs, with typify): every object closed, every pattern and limit checked as an answer is read, every credential a Secret. A change to the API is a change to that file first.

One function a route

Each is named as the spec's operationId, takes the route's request type and answers its answer type. ROUTES lists each method, path under BASE_PATH (/api/v1) and function, and a test holds them to the spec's paths.

The report's answer

The one answer read entry by entry, not closed, is the report's (Asked): an action of a kind this machine doesn't know is refused alone, so a newer service never stops an older machine's actions.

Its calls

Api is the service at one URL, cheap to clone. A call that needs a credential is handed it, a &Secret, by its caller: the accounts plugin, which keeps this machine's key and signs for access tokens with it (Api::sign_for), or a plugin it handed an access token to.

https:// only (or http:// on this machine), no redirects, an answer's size capped, and errors mapped once: a 401 is Error::SignedOut, a 429 Error::Later. What a refusal says, in the spec's words, is refused.

Access tokens (jwt)

Access tokens and the assertions machines sign for them, as the spec has them (MachineAssertionHeader, MachineAssertionClaims, AccessTokenHeader, AccessTokenClaims): compact JWS, Ed25519 alone.

assertion

A machine signs an assertion with its key for each access token it asks for: one request, at most ASSERTION_LIFE, 60 seconds.

check

Whatever an access token is for checks it with check and the account service's public keys alone (Keys), never asking the service. The header is read first: alg EdDSA and typ at+jwt alone, its kid one it holds. Then the signature, then the claims.

The claims

Closed: the issuer it trusts, its own audience, a subject of a kind it takes, a grant of what it's asked to do (Grants::allows), and a life no longer than its own longest, with no allowance for clocks.