Skip to content

Peers

Calls one server makes to another when an answer to a request lives on the responder's own server. The responder's server holds the values and hands the requester's server a grant id and a token; the requester's server reads the grant with that token and is told by signed events when it changes. Every endpoint here is called by a server, not by a person, and every outbound call goes through the client that refuses redirects and private addresses.

POST /api/public/requests/{slug}/remote

Accept an answer that lives on another server

Unauthenticated

Callable with no credential.

Called by the responder's server on behalf of a person signed in there. Every gate a local answer passes applies: status, password, identifier, and a proven identity when the request requires one.

Instead of a session, the call carries the grant: its id on the responder's server and the token to read it with. This server reads it once before accepting, which proves the responder's server is reachable at the domain it claims and recognises the token, and yields the values the template is checked against. The values are passed to the request's callback URL and not stored.

An identity is accepted only when it was issued by the relaying server: its fingerprint matches its certificate, that server's root key (tied to its domain by the DNS pin) signed the fingerprint, that server reports it active, and it signed this request's challenge.

Parameters

Name In Required Notes
slug path yes The capability. Unguessable by construction and never derived from anything user-visible — whoever holds it holds the access.

Request body

application/json

Field Type Notes
server string The responder's server's domain.
grant object
password string
identifier string
senderName string
identity object
challengeNonce string
challengeSignature string

Responses

Status Meaning
200 The answer was recorded.
400 Refused, with a named reason.
401 Refused, with a named reason.
403 Refused, with a named reason.
404 No such resource, or the caller may not see it.
429 Too many attempts. Password gates, probes and challenges are limited per client, so a slug cannot be brute-forced.
502 remote_unreachable: the responder's server could not be asked for the grant.

Response fields

Field Type Notes
ok boolean
responseId string
completed boolean
updated boolean

POST /api/grants/resolve

Read grants in bulk

Unauthenticated

Callable with no credential.

Reads up to 100 grants in one call, each with its own token: grants held by another server, and grants a service received at sign-in (revoked_links). A grant that does not exist, was not handed out with a token, or is presented with the wrong token answers not_found; the three cannot be told apart.

Each answer carries an etag. Sending it back with the next read yields notModified: true and no values when nothing changed. Each answer also carries maxAge, the number of seconds the reader may keep the values before asking again (GRANT_MAX_AGE on this server, 60 by default). Values are present only while the grant is active.

Request body

application/json

Field Type Notes
grants array of object

Responses

Status Meaning
200 One answer per grant asked for, in the same order.
400 No grants, or more than 100.
429 Too many attempts. Password gates, probes and challenges are limited per client, so a slug cannot be brute-forced.

Response fields

Field Type Notes
items array of object

POST /api/grants/events

Receive grant events

Unauthenticated

Callable with no credential.

Receives a batch of events from a server holding grants this server reads. An event names the grant, what happened (grant.revoked, grant.changed, grant.expired) and the grant's status now. It never carries a value; the receiver reads the grant again if it needs one.

The body is the exact signed bytes (payload, base64url) and the sender's root-key signature over them (signature, hex). The batch names its sender and its audience; it is accepted only when the sender's root key, tied to its domain by the DNS pin, verifies the signature, the audience is this server, and the batch is under five minutes old. Events older than the status already recorded are ignored. Each applied event is passed on to the request's callback URL.

A sender whose key cannot be fetched gets 503 and retries; one whose signature does not verify gets 401 and does not.

Request body

application/json

Field Type Notes
payload string
signature string

Responses

Status Meaning
200 The batch verified.
401 Refused, with a named reason.
429 Too many attempts. Password gates, probes and challenges are limited per client, so a slug cannot be brute-forced.
503 peer_key_unavailable: the sender's key could not be checked. Retry later.

Response fields

Field Type Notes
applied integer Events about grants this server holds.