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. |