PKU harness Cloud
PKU harness Cloud is an optional backend standard for plugins, covering storage, relay channels, push, files and server functions. The official instance, a self-hosted server or any conforming server can provide it. Data is end-to-end encrypted by default, and the cloud holds no campus identity.
Most plugins run only on the phone, but a few features need a server. For example, a plugin may share a campus-card payment code live with a friend who doesn't have the app, or keep a small piece of data in sync across devices. PKU harness Cloud ("the cloud" below) is the backend standard for these features. It defines which endpoints a server provides and how the app and plugins use them.
We designed the cloud so that you can use a server without having to trust it. The sections below explain how.
Design principles
Data is encrypted before it leaves the phone. This is called end-to-end (E2E) encryption: only the sender and the receiver hold the keys, and the server forwards and stores ciphertext that it can't decrypt.
The cloud holds no campus identity. The server knows a device only by a keypair that the device generates. It never learns your student ID or name, and it never receives your campus password or login sessions.
Several providers can serve the same protocol. You can use the official instance, a reference server you deploy yourself, or any server URL that implements the protocol. Plugins call the same interface in every case and can't tell which provider they are talking to.
The app keeps plugins apart. We call the app the host, because it runs the plugins. The host gives each plugin a cloud client bound to that plugin alone, with its own tokens, its own storage space and its own consent from you.
The AI assistant uses the cloud only through the tools that plugins provide. It never sees cloud credentials, tokens or share links.
Status (2026-10-24)
The official cloud instance is not deployed yet. Until it is, enter the URL of a self-hosted server in Settings → Cloud if you want to use the cloud.
These parts are done: protocol v1 (@pku-harness/cloud-protocol), the SDK types, the
reference server (packages/cloud-server) and the cloud client in the app. The client handles
device identity, per-plugin binding and consent, end-to-end encrypted store and channels, and
a connection test in Settings → Cloud. The first plugin that uses the cloud is card-share,
which shares a campus-card payment code live, end-to-end encrypted. The push service (push)
isn't finished, and the reference server answers 501 unsupported for it.
When the official instance launches, it will stay small on purpose. It will offer the relay for live shares (channels and mailbox, where the server only forwards ciphertext it can't read), a small end-to-end encrypted store and the static viewer pages. Plugin server functions exist in the reference server, but they are off on the official server.
The three parts of the cloud
The cloud has three parts. The service definition consists of SDK types and a versioned HTTP protocol. Service providers are whoever runs a server. Consumers are the plugins that call the cloud.
| Part | What | Where |
|---|---|---|
| Service definition | SDK types (CloudManifest, PluginCloud) and the HTTP protocol (/v1) | @pku-harness/sdk (cloud.ts), @pku-harness/cloud-protocol, API reference |
| Service providers | The official instance, the open-source reference server (self-hosted), any conforming URL | Providers and self-hosting |
| Consumers | Plugins, through ctx.cloud or props.cloud | Plugin guide |
Services the cloud offers
The protocol defines five services. A provider may offer only some of them.
store is a key-value store. Each plugin on each device gets its own space, keyed by
(device, plugin). It supports versions (If-Match) and expiry times (time to live, TTL). When
a plugin declares e2e, values are encrypted on the phone before they are uploaded.
channels are relay channels for live sharing. A device opens a channel, viewers join with a
share link, and the server relays data frames between them. Channels are always end-to-end
encrypted, and each viewer's key is derived from the secret in the share link.
push handles push tokens and scheduled pushes. It is still planned, and the official instance
won't offer it. Push payloads are encrypted, so the server only learns when to send.
files is file storage. It is also planned.
functions are server functions that a plugin declares in the server: section of its
PLUGIN.md (see below). They are off on the official server. A server
function must be declared in advance and approved by you, and it is the one place in the cloud
where plaintext may be processed.
The app asks a provider which services it offers with GET /v1/meta. If a plugin lists a
service in uses that the provider doesn't offer, the app marks the plugin unavailable and says
why.
How a request flows
Take a plugin writing one value to the store as an example. The plugin calls
ctx.cloud.store.put. The host first checks consent and the uses and e2e fields of the
plugin's manifest. If the plugin declared e2e, the host encrypts the value with
XChaCha20-Poly1305. The host then signs a challenge with the device's Ed25519 key to get a token
valid for 10 minutes, and sends the request with that token to the provider. The provider reads
the device and plugin from the token, picks the storage space, and stores the ciphertext.
plugin ──ctx.cloud.store.put(key, value)──► host (bound client for this plugin)
│ checks consent, manifest `uses`, `e2e`
│ seals value (XChaCha20-Poly1305) when e2e
│ mints/refreshes a 10-min token (Ed25519-signed challenge)
▼
provider PUT /v1/store/<key> Authorization: Bearer <token>
│ namespace = (deviceId, plugin) from the token
▼
stores ciphertextWhere server code runs
When a plugin needs a server, there are three options. We suggest considering them in this order.
- Standard services: store, channels, mailbox, push and files. Every conforming server implements them, and no plugin code runs on the server. Use this option whenever it can do the job.
- Plugin server functions, written in the
server:section of aPLUGIN.md. The server runs them with the same interpreter as the app. They are off on the official server. - External backends: any URL that implements the protocol. The plugin declares it in the
manifest (
backend: { kind: 'external', url }), and you must approve it before it is used.
Server functions
Off on the official server
Keep your plugin's work on the phone, or run your own backend. The design document,
docs/CLOUD-FUNCTIONS.md,
is on hold. What already exists stays, but collections, schedules, push, groups and webhooks
are not being built.
A plugin document can declare server functions. They use the same steps and expressions as the plugin's tools, but they run on the server. Here is an example:
cloud:
disclosure: { zh: ['你输入的名字会发到云端服务器。'], en: ['The name you enter is sent to the cloud server.'] }
server:
network: [api.example.org] # hosts server steps may call (HTTPS)
secrets: [API_KEY] # held by the operator; usable only in request templates
functions:
stats:
effect: read
access: public # user (default) | public served today; peer, group, webhook are not
cache: 30s
steps:
- let: c
request: { url: 'https://api.example.org/v1/count', headers: { authorization: 'Bearer ${secrets.API_KEY}' } }
as: json
return: "{ count: c.count }"Keep these rules in mind:
- A
server:section impliescloud.uses: [functions], and it requirescloud.disclosure. - Server steps have their own set of host abilities: a per-user server key-value store
(
kv.get/set/delete), pluscrypto.sha256,crypto.hmacSha256(which takes a secret by name) andcrypto.randomId. Server steps can't reach campus services.caller.idis a pseudonym of the device, different for each plugin. - The phone calls a function through the
cloud.call { fn, input }ability. A widget can bind{ fn }as well as{ tool }. - The token records the plugin document version you consented to (link plus content hash), and
the server runs only that version. Whether it runs at all also depends on the operator's
policy,
CLOUD_FN_POLICY, which isverified,allowlistoropen. The reference server's README lists the environment variables.
Next
- Plugin guide: declare
cloud, get consent, usestoreandchannels. - Security: device keys, proof of work, tokens, envelopes and share links.
- Providers and self-hosting: choose a provider and run the reference server.
@pku-harness/cloud-protocol: schemas and crypto for servers and viewer pages.- API reference: every endpoint, generated from the OpenAPI 3.1 spec.
pointer
setPointer writes a value into an object at a JSON Pointer path. The dashboard uses it to put binding results into widget state.
Plugin guide
Use PKU harness Cloud from a bundled plugin. Declare the cloud manifest and disclosure, handle consent and availability, use the store and relay channels, and show share links safely.