Skip to content

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)

  1. plugin.json exists, is well-formed JSON.
  2. All required manifest fields are present and have the right type.
  3. The author hasn't included source_url or sha256.
  4. id matches the parent directory name.
  5. id is filesystem-safe ([A-Za-z0-9_-]+, starts with alphanumeric, ≤64 chars).
  6. version and cleat_min_version (if present) match strict MAJOR.MINOR.PATCH.
  7. plugin.lua exists at the root of the entry directory.
  8. The id field inside plugin.lua matches the manifest's id.
  9. The version field inside plugin.lua matches the manifest's version.
  10. The id is 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.