The manifest
How a device describes itself. Every capability can read the world or can do something in it.
A manifest is the device's description of itself: who it is, and a list of capabilities. The person reads it at pairing. The agent reads it as tool descriptions. A manifest that arrives from a device is untrusted input, so the vessel validates it before anyone sees it.
{
id: "desk-lamp",
name: "Desk lamp",
kind: "lamp",
version: "0.1.0",
capabilities: [
{ name: "read_light", kind: "sensor", description: "Is the lamp on, and how bright." },
{
name: "set_light",
kind: "actuator",
description: "Turn the lamp on or off.",
params: { on: { type: "boolean", required: true }, brightness: { type: "number" } },
},
{ name: "strobe", kind: "actuator", description: "Flash the lamp.", confirm: true },
],
}Can read and Can do
Every capability has a kind, and there are two:
| In these docs | kind | Means | Example |
|---|---|---|---|
| Can read | sensor | Reads the world and changes nothing | read_light |
| Can do | actuator | Changes something in the world | set_light, strobe |
The person sees the kind of every capability before allowing it, and so does the agent: a tool description starts with [DEVICE Desk lamp (lamp) sensor] or ... actuator]. Policy rules can match on it too, for example "ask me before any Can do capability" (Capabilities as agent tools).
The kind is what the device declares. 8DK cannot check that a capability marked Can read really changes nothing. See Security model.
Ask every time: confirm
A capability with confirm: true asks the person on every call, even after it is allowed. Use it for anything you would not want done twice by accident: a door lock, a payment, the strobe above.
Fields
| Field | Rule |
|---|---|
id | Lower case letters and digits in words joined by -, for example desk-lamp. At most 24 characters. Stable for the device. |
name | Shown to the person at pairing. At most 80 characters. |
kind | Free text, for example lamp, glasses, phone. At most 40 characters. |
version | Free text. At most 40 characters. |
capabilities | At least 1, at most 32. Names unique. |
Each capability:
| Field | Rule |
|---|---|
name | Lower snake case, starting with a letter, never containing __. At most 30 characters. |
kind | sensor or actuator. |
description | Shown to the person and to the agent. At most 500 characters. |
params | Optional. At most 16. Each name is lower snake case (at most 30 characters), each type is string, number or boolean, with an optional description (at most 200 characters) and required. |
confirm | Optional. true asks the person on every call. |
Text fields are refused, never cleaned up, if they contain any control or invisible formatting character: newline, tab and escape, zero-width characters, direction-changing characters, Unicode tag characters and variation selectors. That also refuses an emoji written with a variation selector. A consent prompt has to show exactly what a person would read.
Checking a manifest
| Function | Does |
|---|---|
validateManifest(input) | Checks untrusted input and returns a frozen copy, or throws ManifestError. |
defineDevice(manifest, handlers) | Validates, then requires exactly one handler per capability: none missing, none extra. |
manifestDigest(manifest) | A SHA-256 of the validated manifest. authenticate compares it, so a changed manifest fails and the device must pair again. |
validateInput(capability, input) | Checks a call's input: an object, no unknown keys, required keys present, each value the declared type (numbers must be finite). Returns an error message or null. |
validateManifest({ id: "Desk Lamp", name: "x", kind: "x", version: "1", capabilities: [] });
// throws ManifestError: manifest id "Desk Lamp" must be lower case words joined by "-"Next: Pairing.