PKU harness
CloudAPI reference

Devices and auth

A device's cloud identity is an Ed25519 signing key plus an X25519 box key, nothing else: no student ID, no name. Registration costs a proof of work; plugin tokens are minted from a signed challenge and live 10 minutes.

Register a device

POST
/v1/devices

Registers a device identity. Authenticated by proof of work instead of a token: find a decimal nonce such that SHA-256(prefix ‖ UTF-8(nonce)) starts with at least pow.difficulty zero bits (from GET /v1/meta), where

prefix = UTF-8("pku-harness-cloud/v1/pow\n" + salt + "\n" + signPub + "\n" + boxPub + "\n")

The server accepts its current salt and the previous one. Binding the keys into the prefix means one solution registers exactly one identity. Clients refuse a difficulty above 28 (MAX_POW_DIFFICULTY); the default is 16 (about one second on a phone). Use solvePow from @pku-harness/cloud-protocol.

Request Body

application/json
  1. body
signPub*Key32

32 bytes, base64url (Ed25519 or X25519 public key, hash).

Match^[A-Za-z0-9_-]{43}$
boxPub*Key32

32 bytes, base64url (Ed25519 or X25519 public key, hash).

Match^[A-Za-z0-9_-]{43}$
pow*

Response Body

The server-generated device id.

application/json
  1. response
deviceId*DeviceId

Server-generated, base64url.

Match^[A-Za-z0-9_-]{8,64}$
Example Request
curl -X POST 'https://cloud.example.org/v1/devices' \  -H 'Content-Type: application/json' \  -d '{  "signPub": "string",  "boxPub": "string",  "pow": {    "salt": "string",    "nonce": "string"  }}'
Example Response
{  "deviceId": "string"}

Mint a plugin token

POST
/v1/auth/token

Exchanges an Ed25519-signed challenge for a 10-minute bearer token bound to one device, one plugin and a set of services (claims sub = deviceId, plg = plugin, svc = services). The signed bytes are these lines joined by \n (no trailing newline):

pku-harness-cloud/v1/auth<aud>        provider origin, lowercase scheme://host[:port], no path<deviceId><plugin><services>   deduplicated, sorted, joined by ","<ts>         Unix ms, decimal<nonce>      16 random bytes, base64url

The server compares aud with its own origin, accepts |now − ts| ≤ 5 min and rejects a reused nonce within that window. Use signAuthChallenge from @pku-harness/cloud-protocol.

Request Body

application/json
  1. body
aud*string

The provider origin, lowercase, no path or trailing slash.

Match^https?:\/\/[a-z0-9.\-:[\]]+$
deviceId*DeviceId

Server-generated, base64url.

Match^[A-Za-z0-9_-]{8,64}$
plugin*PluginId

Registry id rule, plus mcp-… host ids for remote plugins.

Match^[a-z0-9][a-z0-9-]{1,63}$
services*array<>
Items1 <= items
ts*Ms

Unix time in milliseconds.

Range0 <= value
nonce*Nonce16

16 bytes, base64url.

Match^[A-Za-z0-9_-]{22}$
sig*Sig64

64-byte Ed25519 signature, base64url.

Match^[A-Za-z0-9_-]{86}$

Response Body

A bearer token for this plugin's requests.

application/json
  1. response
token*string

Opaque to clients.

Length16 <= length
expiresAt*Ms

Unix time in milliseconds.

Range0 <= value
Example Request
curl -X POST 'https://cloud.example.org/v1/auth/token' \  -H 'Content-Type: application/json' \  -d '{  "aud": "string",  "deviceId": "string",  "plugin": "string",  "services": [    "store"  ],  "ts": 0,  "nonce": "string",  "sig": "string"}'
Example Response
{  "token": "stringstringstri",  "expiresAt": 0}
DELETE
/v1/devices/me

Wipes every record of the device (store, channels, push schedules). Any plugin token of the device works. The app calls it from Settings → 删除云端数据 and before a full reset.

Authorization

pluginToken
headerAuthorizationBearer <token>

Token from POST /v1/auth/token (10 minutes, one device + one plugin + services). Channel owner endpoints require the token of the device and plugin that opened the channel.

Response Body

Deleted.

application/json
  1. response
ok*boolean
Example Request
curl -X DELETE 'https://cloud.example.org/v1/devices/me'
Example Response
{  "ok": true}