> For the complete documentation index, see [llms.txt](https://docs.getvenora.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.getvenora.tech/plugin-api/plugin-manifest.md).

# Plugin manifest

Part of the [Community Plugin API specification](/plugin-api.md). The key words MUST, SHOULD, and MAY are used as in RFC 2119.

Every plugin has a `plugin.json` file: a JSON object describing the plugin.

* Bundled plugins live in `venora-chrome/plugins/<id>/`.
* Imported plugins are installed from a ZIP file (see [Packaging](#packaging)).

Both kinds follow the same rules.

Imported manifests are **untrusted**. Venora validates them before storing anything, and an invalid manifest means the plugin is not installed.

## Example

```json
{
  "id": "mention-avatars",
  "name": "Mention avatars",
  "version": "1.2.0",
  "author": "Belligerently",
  "description": "Shows the mentioned user's avatar beside their name.",
  "main": "index.js",
  "css": "style.css",
  "permissions": ["account", "network", "storage", "ui"],
  "compatibility": {"apiVersion": 1, "minVenoraVersion": "0.1.0"},
  "settings": [
    {"key": "size", "name": "Avatar size", "type": "number", "default": 16, "min": 12, "max": 32}
  ],
  "changelog": [
    {"version": "1.2.0", "changed": ["Shows avatars one at a time, so pages with many mentions stay within Venora's request limits."]},
    {"version": "1.1.0", "changed": ["Runs in the sandboxed plugin runtime."]},
    {"version": "1.0.0", "added": ["Shows the mentioned user's avatar beside their name."]}
  ]
}
```

## Fields

"Enforced" means an imported plugin with an invalid value is rejected at install. Every rule below is enforced.

| Field           | Required             | Type                        | Valid values                                                                                                                          | Purpose                                                                                                                                                                                                                                                                                                                                                                                                          |
| --------------- | -------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | **Yes**              | string                      | `^[a-z0-9][a-z0-9-]*$` (Enforced)                                                                                                     | Permanent identity. Updates are matched by `id`, never by name. Settings and storage are keyed by it.                                                                                                                                                                                                                                                                                                            |
| `name`          | **Yes**              | string                      | Not blank; at most 64 characters; no control characters or text-direction characters such as U+202E (Enforced)                        | Display name on the plugin card and in dialogs. Also the label on imported plugins' notices, so it must read exactly as written. The length and character rules were added in Phase 3.3, under the security exception in [plugin-api.md §9.4](/plugin-api.md#94-breaking-changes-which-need-a-new-api-version). Plugins installed earlier keep their names; Venora removes unsafe characters when it shows them. |
| `version`       | **Yes**              | string                      | [Semantic Versioning 2.0.0](https://semver.org), for example `1.4.2` or `2.0.0-beta.1` (Enforced)                                     | The plugin's release. Updates must have a higher version.                                                                                                                                                                                                                                                                                                                                                        |
| `author`        | No                   | string, or array of strings | A non-empty string, or a list of non-empty strings (Enforced)                                                                         | Creator or maintainers, shown as "By …" on the card                                                                                                                                                                                                                                                                                                                                                              |
| `description`   | No                   | string                      | Must be a string when present (Enforced)                                                                                              | One-line summary shown on the card. The card shows the `id` if it is missing.                                                                                                                                                                                                                                                                                                                                    |
| `main`          | No                   | string                      | Relative path to a file in the plugin; default `index.js`. MUST exist, and be at most 2,000,000 characters (Enforced).                | The entry script: one ES module whose default export is the plugin function. See [plugin-lifecycle.md](/plugin-api/plugin-lifecycle.md).                                                                                                                                                                                                                                                                         |
| `css`           | No                   | string                      | Relative path; the file MUST exist (Enforced)                                                                                         | CSS applied while the plugin runs, with the `ui` permission and the restrictions in [plugin-api.md §5.5](/plugin-api.md#55-plugin-css): no resource loads, no quoted text, and at most 131,072 characters. CSS that breaks them is not applied.                                                                                                                                                                  |
| `permissions`   | No                   | array of strings            | IDs from the [permission registry](/plugin-api/plugin-permissions.md); duplicates are ignored (Enforced). Missing or `[]` means none. | What the plugin may do. Sensitive permissions are confirmed by the user at install and update.                                                                                                                                                                                                                                                                                                                   |
| `compatibility` | No                   | object                      | See [below](#compatibility) (Enforced)                                                                                                | Requirements on Venora                                                                                                                                                                                                                                                                                                                                                                                           |
| `settings`      | No                   | array of objects            | See [plugin-settings-storage.md](/plugin-api/plugin-settings-storage.md#declaring-settings) (Enforced)                                | User-configurable options shown in the plugin's settings dialog                                                                                                                                                                                                                                                                                                                                                  |
| `changelog`     | No                   | array of objects            | See [below](#changelog) (Enforced)                                                                                                    | Release notes, shown in update prompts and the info dialog                                                                                                                                                                                                                                                                                                                                                       |
| `required`      | Bundled plugins only | boolean                     | `true` makes the plugin always enabled                                                                                                | Ignored in imported manifests, so a third-party plugin cannot make itself impossible to disable                                                                                                                                                                                                                                                                                                                  |

### Reserved fields

Venora sets these fields itself. In an imported `plugin.json` they are removed at install and never trusted:

* `required`
* `imported`
* `enabled`
* `script`
* `style`
* `moduleUrl`

### Unknown fields

Unknown top-level fields are stored but **ignored**. They MUST NOT change behaviour, which lets newer plugins add informational fields without breaking older Venora releases.

A new top-level field that older Venora releases must not ignore is not allowed. Any such requirement MUST be added under `compatibility` instead, where older releases reject what they do not recognize.

**`apiVersion` at the top level is a mistake.** It would be ignored, so the API version would silently go unchecked. Venora therefore rejects a top-level `apiVersion`, with a message pointing to `compatibility.apiVersion`.

## Compatibility

```json
"compatibility": {"apiVersion": 1, "minVenoraVersion": "0.2.0"}
```

| Field              | Type             | Meaning                                                                                                                     | Status                                                                           |
| ------------------ | ---------------- | --------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `apiVersion`       | positive integer | The major Plugin API version the plugin targets; see [plugin-api.md §9](/plugin-api.md#9-api-versioning). Missing means 1.  | Enforced at install and at load: it must be a version Venora supports (today, 1) |
| `minVenoraVersion` | semver string    | The oldest Venora release with every API the plugin uses. It is compared with the `version` in Venora's extension manifest. | Enforced at install and at load                                                  |

Validation rules:

* An empty or missing `compatibility` object means no requirements.
* `compatibility` MUST be an object.
* An **unknown field inside `compatibility` is rejected**, so that an older Venora never ignores a requirement it does not understand.
* If Venora's own version cannot be compared, the plugin is treated as incompatible (fail closed).

`apiVersion` lives here rather than at the top level for exactly that reason. Venora 0.1.0 already rejects malformed `compatibility.apiVersion` values. The supported fields are defined in `venora-chrome/plugin-compatibility.js`.

When a plugin is not compatible:

* at install or update, it is rejected before anything is stored;
* at load, it is skipped and its card says "Not compatible: …".

## Changelog

```json
"changelog": [
  {"version": "1.3.0", "added": ["Custom themes."], "fixed": ["A crash when opening threads."]},
  {"version": "1.2.0", "added": ["Plugin settings."]}
]
```

* Each entry has a semver `version` that is unique within the changelog.
* Each entry has at least one of four note lists:
  * `breaking`, shown as "Breaking changes";
  * `added`, shown as "New features";
  * `changed`, shown as "Improvements";
  * `fixed`, shown as "Bug fixes".
* Notes are non-empty plain-text strings of at most 500 characters. HTML and Markdown are shown literally, not rendered.
* An entry with no notes or with an unrecognized field is rejected.

Where the notes are shown:

* The update prompt shows the notes for the new version only.
* The info dialog shows the whole history, newest first.

## Packaging

Plugins are imported as ZIP files with **Settings → Plugins → Add plugin**.

* `plugin.json` must be at the root of the ZIP, or inside a single top-level folder.
* `__MACOSX/` folders and `.DS_Store` files are ignored.
* Absolute paths and paths containing `..` are rejected.
* The entry script is a **single ES module**. It is loaded from a `data:` URL, so relative imports do not work; bundle all dependencies into it.
* No other files are read.

**Size limits:**

* the ZIP file is at most 16 MiB;
* `plugin.json` and the CSS file are at most 131,072 characters each;
* the entry script is at most 2,000,000 characters.

Each file is read through a stream that stops at its limit. A ZIP that expands to far more than it claims (a "zip bomb") is therefore refused without filling memory.

The ZIP, `plugin.json`, and CSS limits were added in Phase 3.4, under the security exception in [plugin-api.md §9.4](/plugin-api.md#94-breaking-changes-which-need-a-new-api-version). They stop a plugin file from exhausting memory. Existing plugins are not affected, because they are already installed.

Venora checks the ZIP in this order and stops at the first failure. Nothing is stored unless every step succeeds.

1. ZIP structure and paths
2. `plugin.json` is at most 131,072 characters and parses as a JSON object
3. `id`, a non-blank `name`, and `version`
4. The `name` length and character rules
5. `permissions`
6. `compatibility`
7. `changelog`
8. `settings`
9. `description`, `author`, `main` and `css` types, and no top-level `apiVersion`
10. The entry script exists, the declared CSS exists, the entry script is at most 2,000,000 characters, and the CSS file is at most 131,072 characters
11. If a plugin with the same `id` is installed, the update rules in [plugin-lifecycle.md](/plugin-api/plugin-lifecycle.md#install-update-uninstall)
12. If the plugin declares sensitive permissions, the user confirms them
13. The plugin is stored

Bundled plugins MUST satisfy the same rules. The test suite installs every bundled plugin through this pipeline.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.getvenora.tech/plugin-api/plugin-manifest.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
