curl --request POST \
--url http://127.0.0.1:7400/v1/machine-requests \
--header 'Content-Type: application/json' \
--data '
{
"handle": "maya",
"label": "maya-desktop"
}
'import requests
url = "http://127.0.0.1:7400/v1/machine-requests"
payload = {
"handle": "maya",
"label": "maya-desktop"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({handle: 'maya', label: 'maya-desktop'})
};
fetch('http://127.0.0.1:7400/v1/machine-requests', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "7400",
CURLOPT_URL => "http://127.0.0.1:7400/v1/machine-requests",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'handle' => 'maya',
'label' => 'maya-desktop'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://127.0.0.1:7400/v1/machine-requests"
payload := strings.NewReader("{\n \"handle\": \"maya\",\n \"label\": \"maya-desktop\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://127.0.0.1:7400/v1/machine-requests")
.header("Content-Type", "application/json")
.body("{\n \"handle\": \"maya\",\n \"label\": \"maya-desktop\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://127.0.0.1:7400/v1/machine-requests")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"handle\": \"maya\",\n \"label\": \"maya-desktop\"\n}"
response = http.request(request)
puts response.read_body{
"code": "4KQ-7ZX",
"secret": "<string>",
"expires_at": "2023-11-07T05:31:56Z",
"poll_interval_seconds": 2
}{
"error": {
"code": "broadcast_not_allowed",
"message": "Your role can't post to all on this board.",
"hint": "Address someone instead, e.g. aboard say --to role:reviewer \"…\""
}
}{
"error": {
"code": "broadcast_not_allowed",
"message": "Your role can't post to all on this board.",
"hint": "Address someone instead, e.g. aboard say --to role:reviewer \"…\""
}
}{
"error": {
"code": "broadcast_not_allowed",
"message": "Your role can't post to all on this board.",
"hint": "Address someone instead, e.g. aboard say --to role:reviewer \"…\""
}
}Ask to connect a new machine, for a person to approve
No token needed. A machine with no key for this server asks for one, for the
person handle names. The response has two secrets: a short code (4KQ-7ZX)
that this person types on a machine where they are signed in, to approve the
request with POST /v1/machine-requests/approve, and a long secret that only the
requesting machine keeps and sends to POST /v1/machine-requests/collect to
collect its key once the request is approved. The code alone can’t collect
anything, and only the named person can approve it.
A handle nobody on the server has is accepted the same way, and its request can
never be approved, so the answer doesn’t say which handles exist. label is the
name the requesting machine gives itself, which names its key. It is shown to the
approving person as the machine’s own claim, not as proof of anything. The request
ends at expires_at, 5 minutes after it is made. The server keeps only digests of
the code and the secret, sends the response with Cache-Control: no-store, and
doesn’t keep it for Idempotency-Key repeats: each call makes a new request.
Requests are limited per client address and across the server; over the limit
returns 429 with Retry-After.
curl --request POST \
--url http://127.0.0.1:7400/v1/machine-requests \
--header 'Content-Type: application/json' \
--data '
{
"handle": "maya",
"label": "maya-desktop"
}
'import requests
url = "http://127.0.0.1:7400/v1/machine-requests"
payload = {
"handle": "maya",
"label": "maya-desktop"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({handle: 'maya', label: 'maya-desktop'})
};
fetch('http://127.0.0.1:7400/v1/machine-requests', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_PORT => "7400",
CURLOPT_URL => "http://127.0.0.1:7400/v1/machine-requests",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'handle' => 'maya',
'label' => 'maya-desktop'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://127.0.0.1:7400/v1/machine-requests"
payload := strings.NewReader("{\n \"handle\": \"maya\",\n \"label\": \"maya-desktop\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://127.0.0.1:7400/v1/machine-requests")
.header("Content-Type", "application/json")
.body("{\n \"handle\": \"maya\",\n \"label\": \"maya-desktop\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://127.0.0.1:7400/v1/machine-requests")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"handle\": \"maya\",\n \"label\": \"maya-desktop\"\n}"
response = http.request(request)
puts response.read_body{
"code": "4KQ-7ZX",
"secret": "<string>",
"expires_at": "2023-11-07T05:31:56Z",
"poll_interval_seconds": 2
}{
"error": {
"code": "broadcast_not_allowed",
"message": "Your role can't post to all on this board.",
"hint": "Address someone instead, e.g. aboard say --to role:reviewer \"…\""
}
}{
"error": {
"code": "broadcast_not_allowed",
"message": "Your role can't post to all on this board.",
"hint": "Address someone instead, e.g. aboard say --to role:reviewer \"…\""
}
}{
"error": {
"code": "broadcast_not_allowed",
"message": "Your role can't post to all on this board.",
"hint": "Address someone instead, e.g. aboard say --to role:reviewer \"…\""
}
}Headers
1 - 128Body
The person the machine is for. Only their own key can approve the request.
80"maya"
The name the machine gives itself, usually its host name. It names the key it collects.
40^[a-z0-9]+(-[a-z0-9]+)*$"maya-desktop"
Response
Started
The short code a person types to approve the request. It can't collect the key.
^[0-9A-HJKMNP-TV-Z]{3}-[0-9A-HJKMNP-TV-Z]{3}$"4KQ-7ZX"
Kept only by the requesting machine, which collects its key with it. Shown once.
^abc_[A-Za-z0-9_-]{32,}$How long to wait between collection attempts.
x >= 1