plugin.json schema¶
Each plugin lives in its own directory under plugins/:
plugins/
└── <plugin-id>/
├── plugin.lua # the plugin source code
└── plugin.json # author-facing manifest (metadata only)
Authors only provide source files + a small metadata file. CI takes care of zipping, hashing, and publishing — there's no separate "package and host the artifact" step.
Author-facing manifest¶
| Field | Type | Required | Notes |
|---|---|---|---|
id |
string | yes | Plugin id. Must match the parent directory name and the id field inside plugin.lua. Filesystem-safe ([A-Za-z0-9_-], must start with alphanumeric, ≤64 chars). Globally unique across the registry. |
name |
string | yes | Human-readable name shown in cleat's Browse list. |
description |
string | yes | One sentence. The Browse list truncates long descriptions in the row preview. |
author |
string | yes | Author handle / name as you want it shown. |
category |
string | yes | Free-form category label. Used for grouping in the Browse list. Conventional values: System, Network, Logs, Docker, Status Bar, Custom. |
version |
string | yes | Strict MAJOR.MINOR.PATCH semver. Must match the version field inside plugin.lua. Bumping the version requires bumping both places. |
cleat_min_version |
string | no | Minimum cleat version required to run this plugin. Same compat gate as cleat issue #103. Omit if your plugin runs on the v0.1.0 baseline. |
api_version |
string | no | Plugin host-API token (see cleat issue #103). Currently "1" is the only valid value. Omit when targeting the v1 baseline. |
homepage |
string | no | URL the user can click to read about the plugin (typically your repo). |
Author-facing fields that must not appear¶
source_url— generated by CI.sha256— generated by CI.
The validator rejects manifests that include either field. The registry's CI is the only thing that produces zips, so accepting author-supplied hashes would weaken the trust signal that the published bytes match what the maintainers reviewed.
Example¶
{
"id": "process-list",
"name": "Process List",
"description": "Top processes by CPU/memory with sort + filter",
"author": "alice",
"category": "System",
"version": "1.2.0",
"cleat_min_version": "0.2.0",
"api_version": "1",
"homepage": "https://github.com/alice/cleat-process-list"
}
What the published index.json looks like¶
After your PR merges, CI builds a zip of your plugins/<id>/ directory
and adds an entry like this to the public index.json:
{
"id": "process-list",
"name": "Process List",
"description": "Top processes by CPU/memory with sort + filter",
"author": "alice",
"category": "System",
"version": "1.2.0",
"cleat_min_version": "0.2.0",
"api_version": "1",
"homepage": "https://github.com/alice/cleat-process-list",
"source_url": "https://zimventures.github.io/cleat-plugins/zips/process-list-v1.2.0.zip",
"sha256": "abc123def456...64 hex chars..."
}
The first eight fields above came from the author's plugin.json
verbatim; CI appended source_url and sha256. That's the shape
cleat's Browse Marketplace tab sees and consumes. The full
published-side schema (top-level document + entry fields) is documented
in cleat's docs/marketplace-index-schema.md — but as a plugin author
you don't need to think about it.
Validation rules (what CI checks)¶
plugin.jsonexists, is well-formed JSON.- All required manifest fields are present and have the right type.
- The author hasn't included
source_urlorsha256. idmatches the parent directory name.idis filesystem-safe ([A-Za-z0-9_-]+, starts with alphanumeric, ≤64 chars).versionandcleat_min_version(if present) match strictMAJOR.MINOR.PATCH.plugin.luaexists at the root of the entry directory.- The
idfield insideplugin.luamatches the manifest'sid. - The
versionfield insideplugin.luamatches the manifest'sversion. - The
idis unique across all entries in the registry.
A PR that fails any check gets a red ✗ on the validate workflow. Fix the manifest (or the .lua source), push again, and the run reruns automatically.
Trust model¶
A plugin's presence in the registry means a maintainer reviewed the PR that introduced it. The review is the trust signal — there is no separate "verified" flag.
Because the registry's CI is the only thing that produces zips, the
sha256 published in index.json is by construction the hash of the
exact bytes that were reviewed at merge time. Cleat's client verifies
that hash on every install: if the published bytes ever change without
a new merge (which would be a CI / Pages compromise), users get an
explicit error instead of silently installing tampered code.
Updates¶
A new release is just a follow-up PR to your own plugins/<id>/
bumping both plugin.json's version and the version = field in
plugin.lua. Same validation, same review. Once merged, CI republishes
the index and cleat clients with the previous version installed see an
Update prompt on their next update-check tick.