manifest.json
{
"id": "com.yourname.myplugin",
"name": "My Plugin",
"version": "1.0.0",
"author": "Your Name",
"summary": "One line describing what this does.",
"entry": "index.html",
"mode": "session",
"permissions": ["voice", "eyes"],
"minAppBuild": 17,
"files": ["index.html", "plugin.js"]
}
| Field | Required | Notes |
|---|---|---|
id |
yes | Reverse-DNS, globally unique. This is your identity — installs, permissions and updates are keyed on it. Changing it makes a different plugin. |
name |
yes | Shown in the gallery and the plugin list. |
version |
yes | Semver. Bump it for every published change. |
author |
no | Shown to the user. |
summary |
no | One line, shown in the gallery. Write it for someone deciding whether to install. |
entry |
no | Defaults to index.html. |
mode |
no | session (default) or ambient. See below. |
permissions |
no | Array of strings — see permissions.md. Empty means your plugin is pure web content. |
minAppBuild |
no | Refuse to install on older builds. Use it when you depend on a newer API. |
files |
yes | Every file the plugin needs, as a path relative to the manifest. |
files
There is no zip anywhere in the pipeline — not in the upload, not on the
device. The app fetches each path in files individually.
So files is the definition of your plugin. A file you upload but don't list is
not installed, and a file you list but don't upload is a blocking error at
upload time. A stylesheet missing from the list is the most common way a plugin
works locally and ships blank.
mode
session — full screen, opened and closed by the user. Your page owns the
display; the robot's face is not visible while you run.
ambient — replaces the idle face on the dock. Good for clocks, ambient
visuals, a custom face.
One thing to know about ambient: it yields the moment the robot leaves idle. If the user speaks to it, your plugin gives up the screen until the conversation ends. Deep idle also always wins, for battery. Do not build an ambient plugin that depends on running continuously.
Unknown fields and permissions
Unknown permission strings are dropped, not rejected — a plugin built
against a newer SDK still runs on an older app, with the subset that build
understands. Combine that with minAppBuild and feature detection to decide
what "still runs" should mean for you.