PKU harness
CloudAPI reference

Channels (peer)

What a viewer (a web page or another app) calls after opening a share link `https://<provider>/s/<id>#k=<secret>`. The fragment `k` never reaches the server; the viewer derives `authKey` from it to join.

Public channel info

GET
/v1/c/{id}

What a viewer reads before joining. meta is sealed with encKey, so only someone holding the link can read it. Public and rate-limited (20 requests per IP per minute by default).

Path Parameters

id*string

Server-generated, base64url (16 random bytes → 22 characters).

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

Response Body

Channel status, sealed meta and the channel public key.

application/json
  1. response
status*ChannelStatus
Value in"open""closed""expired"
meta*

XChaCha20-Poly1305 sealed JSON. n = 24-byte random nonce, c = ciphertext ‖ 16-byte tag. Open it with open() from @pku-harness/cloud-protocol and validate the result.

chanPub*Key32

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

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

Unix time in milliseconds.

Range0 <= value
ownerOnline*boolean
Example Request
curl -X GET 'https://cloud.example.org/v1/c/string'
Example Response
{  "status": "open",  "meta": {    "v": 1,    "n": "string",    "c": "stringstringstringstri"  },  "chanPub": "string",  "expiresAt": 0,  "ownerOnline": true}

Join a channel

POST
/v1/c/{id}/join

Claims a seat. The server checks sha256(authKey) against the channel's joinHash and enforces maxPeers: when the channel is full the answer is 409 conflict, so a forwarded link is useless once the first viewer claimed it. peerPub is the viewer's ephemeral X25519 key.

Path Parameters

id*string

Server-generated, base64url (16 random bytes → 22 characters).

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

Request Body

application/json
  1. body
authKey*Key32

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

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

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

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

Response Body

The peer id and a peer token for the socket, SSE stream, send and mailbox endpoints.

application/json
  1. response
peerId*PeerId
Match^[A-Za-z0-9_-]{4,64}$
peerToken*string
Length16 <= length
expiresAt*Ms

Unix time in milliseconds.

Range0 <= value
Example Request
curl -X POST 'https://cloud.example.org/v1/c/string/join' \  -H 'Content-Type: application/json' \  -d '{  "authKey": "string",  "peerPub": "string"}'
Example Response
{  "peerId": "string",  "peerToken": "stringstringstri",  "expiresAt": 0}

Peer WebSocket

GET
/v1/c/{id}/ws

Upgrades to a WebSocket for a joined peer.

  • Server → peer: PeerInFrame (hello, owner online state, extended, msg, closed, error, pong).
  • Peer → server: PeerOutFrame (msg, ping).

Path Parameters

id*string

Server-generated, base64url (16 random bytes → 22 characters).

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

Query Parameters

t*string

The peerToken from POST /v1/c/{id}/join.

Length16 <= length

Response Body

Switching to the WebSocket protocol. Frames are described by the PeerInFrame and PeerOutFrame schemas.

Example Request
curl -X GET 'https://cloud.example.org/v1/c/string/ws?t=stringstringstri'
Example Response
Empty

Peer event stream (SSE)

GET
/v1/c/{id}/events

Fallback for webviews without WebSocket (WeChat). Each data: line is one PeerInFrame as JSON. Send with POST /v1/c/{id}/send.

Path Parameters

id*string

Server-generated, base64url (16 random bytes → 22 characters).

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

Query Parameters

t*string

The peerToken from POST /v1/c/{id}/join.

Length16 <= length

Response Body

Server-sent events.

text/event-stream
  1. response

Server → peer frame (WebSocket message or SSE data: line).

t*string
peerId*PeerId
Match^[A-Za-z0-9_-]{4,64}$
ownerOnline*boolean
expiresAt*Ms

Unix time in milliseconds.

Range0 <= value
Example Request
curl -X GET 'https://cloud.example.org/v1/c/string/events?t=stringstringstri'
Example Response
{  "t": "hello",  "peerId": "string",  "ownerOnline": true,  "expiresAt": 0}
POST
/v1/c/{id}/send

The body is one PeerOutFrame, as it would be sent on the WebSocket.

Authorization

peerToken
headerAuthorizationBearer <token>

peerToken from POST /v1/c/{id}/join, as Authorization: Bearer. The WebSocket and SSE endpoints take it as the t query parameter (any token may be passed that way).

Path Parameters

id*string

Server-generated, base64url (16 random bytes → 22 characters).

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

Request Body

application/json
  1. body

Peer → server frame (WebSocket message, or the body of POST /v1/c/{id}/send).

t*string
env*

XChaCha20-Poly1305 sealed JSON. n = 24-byte random nonce, c = ciphertext ‖ 16-byte tag. Open it with open() from @pku-harness/cloud-protocol and validate the result.

Response Body

Accepted.

application/json
  1. response
ok*boolean
Example Request
curl -X POST 'https://cloud.example.org/v1/c/string/send' \  -H 'Content-Type: application/json' \  -d '{  "t": "msg",  "env": {    "v": 1,    "n": "string",    "c": "stringstringstringstri"  }}'
Example Response
{  "ok": true}

Read the mailbox

GET
/v1/c/{id}/mailbox

Batch mode. The sealed blobs the owner left in the channel's mailbox.

Authorization

peerToken
headerAuthorizationBearer <token>

peerToken from POST /v1/c/{id}/join, as Authorization: Bearer. The WebSocket and SSE endpoints take it as the t query parameter (any token may be passed that way).

Path Parameters

id*string

Server-generated, base64url (16 random bytes → 22 characters).

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

Response Body

Mailbox slots.

application/json
  1. response
items*array<>
Example Request
curl -X GET 'https://cloud.example.org/v1/c/string/mailbox'
Example Response
{  "items": [    {      "slot": "string",      "env": {        "v": 1,        "n": "string",        "c": "stringstringstringstri"      },      "expiresAt": 0    }  ]}