> 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-status.md).

# Implementation status

This page is for people working on Venora. It compares the [Community Plugin API specification](/plugin-api.md) with the code.

`tests/plugin-api-spec.test.js` keeps the specification's capability, event, error, and permission tables, and the sandbox's method list, in line with the code.

## Implemented

API version 1 is complete. Every item in the specification is implemented and tested.

### From Phase 2.5

| Area                       | What exists                                                                                                                                        |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Isolation                  | One sandboxed frame per plugin: opaque origin, no extension APIs, no network, no `eval`. Bundled plugins run the same way.                         |
| Bridge                     | Protocol 1 over a private `MessagePort`, with nonce handshake, plain-data validation, and size limits                                              |
| Capabilities               | `ui.toast`, `settings.get`, `mentions.showAvatar`, `mentions.setAvatarSize`, `events.subscribe`, `events.unsubscribe`, and the diagnostic `test.*` |
| Permission enforcement     | All of a capability's permissions are required. Permissions are bound at start.                                                                    |
| Events                     | `settings.changed` (to the owning plugin only), `mentions.added` (each mention replayed once to a new subscriber)                                  |
| Lifecycle                  | `starting`, `running`, `stopping`, `stopped`, `failed`; 10 s / 2 s / 30 s limits; cleanup handlers                                                 |
| Session token              | Central guard on every response and event. Fails closed.                                                                                           |
| Install, update, uninstall | Validation before storage, the sensitive-permission warning, semver update prompts                                                                 |

### Added in Phase 3.2

| Area                                                                                | Implementation                                                                                                                                                                                                                                                                                                               | Tests                                                                                                                                            |
| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| Namespaced API: `venora.ui`, `venora.settings`, `venora.storage`, `venora.mentions` | `API_METHODS` and `createVenoraApi` in `runtime/plugin-runtime.js`. Each method only calls `request("<ns>.<method>", payload)`, so the bridge and the host's capability and permission checks are unchanged. Everything is frozen.                                                                                           | `tests/plugin-api.test.js` (`api:`, `permissions:`); browser test                                                                                |
| `venora.apiVersion`                                                                 | `PLUGIN_API_VERSION = 1` in the sandbox runtime. The plugin cannot set or change it.                                                                                                                                                                                                                                         | `api:` tests; `plugin-compatibility.test.js`                                                                                                     |
| API-version enforcement                                                             | `SUPPORTED_API_VERSIONS = [1]` in `plugin-compatibility.js`, in the existing compatibility check. A missing `apiVersion` means 1. Unsupported versions are rejected at install and skipped at load. A top-level `apiVersion` is rejected.                                                                                    | `plugin-compatibility.test.js`, `plugin-install.test.js`; browser test                                                                           |
| Plugin storage                                                                      | <p><code>storage.get</code>, <code>storage.set</code>, <code>storage.remove</code>, <code>storage.clear</code> in <code>runtime/capabilities.js</code>, backed by <code>runtime/plugin-storage.js</code>:<br>• one entry per plugin<br>• a queue per plugin<br>• quotas checked before writing<br>• deleted on uninstall</p> | `storage:` and `hostile:` tests; browser test (persistence across reload, disable/enable, update; deletion on uninstall; cross-plugin isolation) |
| `quota_exceeded`                                                                    | Added to `ERROR_CODES`                                                                                                                                                                                                                                                                                                       | `storage:` tests                                                                                                                                 |
| Sandbox-side error codes                                                            | `invalid_payload` for non-plain or non-object arguments; `invalid_message` for a non-string capability; `unknown_event` for any unrecognized event name                                                                                                                                                                      | `errors:` tests                                                                                                                                  |
| Settings validation                                                                 | `plugin-settings.js`: definitions are validated at install, and a stored value that no longer fits its definition is replaced by the default when read and in the dialog                                                                                                                                                     | `tests/plugin-settings.test.js`, `plugin-install.test.js`                                                                                        |
| Manifest validation                                                                 | `description`, `author`, `main`, `css` types; `settings`; entry-script size; top-level `apiVersion`                                                                                                                                                                                                                          | `plugin-install.test.js`; `plugin-api-spec.test.js` installs bundled plugins exactly as shipped                                                  |
| Deprecation                                                                         | An optional `deprecated: {since, replacement?}` on capability and event entries, checked at load. Venora logs one warning per plugin run (`warnIfDeprecated`). Nothing is deprecated.                                                                                                                                        | `deprecation:` tests                                                                                                                             |

The security tests were checked by disabling each protection in turn; each change made tests fail:

* trusting a plugin-supplied ID;
* sharing one storage entry between plugins;
* removing the storage queue;
* removing each quota check;
* removing the permission check;
* removing the event-permission check;
* disabling the token guard;
* not freezing `venora`;
* not enforcing `apiVersion`;
* not validating settings.

### Added in Phase 3.3: the UI API and its security boundary

Version 1's UI surface is unchanged:

* `venora.ui.toast`;
* `venora.mentions.showAvatar` and `venora.mentions.setAvatarSize`;
* the `mentions.added` and `settings.changed` events;
* plugin CSS.

No UI methods were added. Phase 3.3 secured the boundary around that surface.

| Area                    | Implementation                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | Tests                                                                                                                                                              |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Plugin code in a worker | <p>The sandbox page performs the handshake and hands the port to a dedicated <code>data:</code> worker built by <code>createWorkerSource</code> from the exact <code>bridge.js</code> and <code>plugin-runtime.js</code> source.<br>• Plugin code has no <code>window</code>, <code>document</code>, <code>parent</code>, or assignable <code>location</code>.<br>• This removes the frame-navigation way out.<br>• The worker inherits the sandbox's no-network CSP; verified in Chromium.<br>• If the worker cannot be created, the plugin fails rather than running in the page.</p> | `plugin-ui.test.js` (`worker:`, `sandbox page:`); browser test (realm, location, exit attempts, no request reaches another server)                                 |
| Trusted page context    | `content.js` runs only in Fermo's top-level page on its origin. The host's `isContextValid` is checked before every start, request, and event. It is false after leaving `/channels/` or when Fermo's document is replaced; requests then get `runtime_stopped` and plugins are stopped. The avatar lookup checks again before changing the page.                                                                                                                                                                                                                                       | `plugin-ui.test.js` (`context:`), `extension-scripts.test.js`; browser test (Fermo framed by Fermo and by another site, a look-alike origin, document replacement) |
| Venora's own UI         | Dialogs, notices, and the Plugins page are in open shadow roots, on hosts with inline `!important` styles, with no page CSS variables. Dialogs use `showModal()` and notices use the popover top layer. The uninstall confirmation is a Venora dialog, not `confirm()`.                                                                                                                                                                                                                                                                                                                 | `plugin-ui.test.js` (`trusted UI:`); browser test (an injector plugin's CSS targets all of Venora's UI and changes nothing)                                        |
| Plugin notices          | Imported plugins' notices have a fixed "Plugin" badge, the cleaned and shortened name, and a non-Venora style. Bundled plugins' notices are Venora's own. All text is set as text.                                                                                                                                                                                                                                                                                                                                                                                                      | `plugin-ui.test.js` (`ui.toast:`); browser test (a plugin named "Venora" with HTML in its message)                                                                 |
| Plugin text             | Every plugin string is rendered as text: names, descriptions, authors, setting labels and descriptions, changelogs, notices. Imported plugins' cards say "Imported".                                                                                                                                                                                                                                                                                                                                                                                                                    | Browser test (injector plugin: HTML in every field, nothing runs)                                                                                                  |
| Names                   | At install, a name must be at most 64 characters with no control or text-direction characters. When shown, any name is cleaned by `pluginLabel` (`plugin-ui-safety.js`).                                                                                                                                                                                                                                                                                                                                                                                                                | `plugin-ui.test.js` (`names:`), `plugin-install.test.js`                                                                                                           |
| Plugin CSS              | `checkPluginCss` (`plugin-ui-safety.js`) also rejects quoted strings other than empty ones, and `@counter-style`, so CSS cannot write text onto the page.                                                                                                                                                                                                                                                                                                                                                                                                                               | `plugin-ui.test.js` (`css:`); browser test                                                                                                                         |
| Input validation        | All UI capability inputs are validated (types, lengths, ranges, nested values, arrays, `null`). Unknown fields are ignored, including any `pluginId`.                                                                                                                                                                                                                                                                                                                                                                                                                                   | `plugin-ui.test.js` (`ui.toast:`, `mentions:`)                                                                                                                     |

**Resource limits.** No new limits were needed:

* a plugin shows at most 3 notices at once, each at most 200 characters;
* event subscriptions are a set per event;
* avatar lookups are cached per user, and mention IDs are validated before any lookup.

General CPU, memory, and rate limits remain future work.

**Changes made under the security exception** ([plugin-api.md §9.4](/plugin-api.md#94-breaking-changes-which-need-a-new-api-version)). These belong in the release notes of the next Venora release:

1. Plugin CSS with quoted text (other than `""`) or `@counter-style` is no longer applied.
2. Plugin names over 64 characters, or with control or text-direction characters, are no longer accepted at install.
3. Plugin code has no `window` or `document` of its own any more. Before, it had its sandbox page's. Plugins that used them for anything worked only inside their own blank sandbox page.

Each change was checked by disabling the protection and confirming that tests fail. The checks were:

* the page-context check;
* the frame and origin guard;
* replaced-document detection;
* `ui.toast`'s permission;
* identity for notices;
* sanitised avatar results;
* plain-data results;
* the CSS text rule;
* the name rule;
* running plugins in a worker;
* rendering notices as text;
* badging imported notices;
* protected host styles.

Removing the shadow root was also caught, but only because the browser test's own lookup helper failed, not by a dedicated check.

### Added in Phase 3.4: runtime hardening and resource controls

No public API was added. One error code was added: `rate_limited`.

| Area                        | Implementation                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Tests                                                                                                                            |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Per-plugin resource account | <p><code>runtime/resource-governor.js</code>, one account per running instance, held by the host and bound to the instance's port:<br>• at most 64 requests waiting for an answer<br>• 120 requests at once, then 60 per second<br>• each costly capability's own <code>rateLimit</code> (in <code>capabilities.js</code>, documented in the capability index), checked after permissions and validation<br>• 100 events at once, then 50 per second; events over that are dropped</p> | `plugin-resources.test.js` (`governor:`, `requests:`, `events:`)                                                                 |
| Message size                | Every message is bounded as a whole: 50,000 values and 1,000,000 characters of strings and keys. A plugin that sends a larger one is stopped at once.                                                                                                                                                                                                                                                                                                                                  | `plugin-resources.test.js` (`bridge:`, `oversized:`)                                                                             |
| Capability timeout          | A capability that has not answered in 30 s is answered with `timeout`, and its slot is freed                                                                                                                                                                                                                                                                                                                                                                                           | `requests:` tests                                                                                                                |
| Watchdog                    | The host pings each running plugin every 2 s and stops one that has not answered within 5 s. Its frame and worker are removed.                                                                                                                                                                                                                                                                                                                                                         | `watchdog:` tests; browser test (a plugin in an endless loop)                                                                    |
| Repeated problems           | More than 200 `rate_limited` rejections in 10 s stops a plugin. After 3 stops for misbehaviour on one page, a plugin is not restarted until reload or uninstall.                                                                                                                                                                                                                                                                                                                       | `requests:`, `repeated problems:` tests; browser test (a flooding plugin)                                                        |
| Install size limits         | ZIP at most 16 MiB; `plugin.json` and CSS at most 131,072 characters; entry script at most 2,000,000. Each file is read through a stream that stops at its limit.                                                                                                                                                                                                                                                                                                                      | `install:` tests (a real zip bomb); browser test (a 17 MB ZIP)                                                                   |
| CSS limit                   | Plugin CSS over 131,072 characters is not applied                                                                                                                                                                                                                                                                                                                                                                                                                                      | `css:` tests                                                                                                                     |
| Recovery                    | The toolbar popup (`manage/popup.html`, an extension page that plugin CSS cannot reach) offers safe mode, enable and disable, and uninstall. Its rules are shared with the Plugins page through `plugin-management.js`. Open Fermo tabs apply changes at once (`chrome.storage.onChanged`). The Plugins page has a safe-mode switch too.                                                                                                                                               | `management:`, `popup:` tests; browser test (hostile CSS hides Fermo's settings; safe mode and uninstall from the popup recover) |
| Bundled plugin              | `mention-avatars` 1.2.0 shows avatars one at a time, well within the limits                                                                                                                                                                                                                                                                                                                                                                                                            | Browser test                                                                                                                     |
| Shadow-root boundary        | A dedicated browser check: Venora's UI hosts have shadow roots holding the UI, and none of it is in the page's DOM. A control element proves the hostile CSS is active in the page. The CSS checks no longer depend on a helper not crashing.                                                                                                                                                                                                                                          | Browser test                                                                                                                     |

**Checked by breaking each protection.** Each of these, disabled on its own, made the unit tests fail:

* the in-flight cap;
* the general rate;
* capabilities' own rates;
* per-plugin accounts (one shared account);
* the event budget;
* stopping on repeated violations;
* the capability timeout;
* the watchdog;
* the repeated-stop refusal;
* stopping on oversized messages;
* the total value and character counts;
* bounded ZIP reads;
* the CSS size limit;
* uninstall's storage cleanup;
* safe mode's pausing of imported plugins.

In the browser test:

* **removing the shadow root** fails the dedicated assertion "Venora's own UI is inside shadow roots…" and five other assertions, not a helper crash;
* **removing the watchdog** fails the stuck-plugin checks;
* **not applying popup changes live** fails the safe-mode recovery checks;
* **ignoring safe mode at start** fails the hung and flooding plugin checks.

**Changes made under the security exception** ([plugin-api.md §9.4](/plugin-api.md#94-breaking-changes-which-need-a-new-api-version)), for the next release's notes:

1. Requests over the new limits are refused with `rate_limited`.
2. Over-sized messages, unresponsive workers, and sustained limit violations stop the plugin.
3. ZIP, `plugin.json`, and CSS size limits.

### Added in Phase 3.6: commands

Venora 0.2.0. Additions only, within API version 1 ([plugin-api.md §9.3](/plugin-api.md#93-changes-that-do-not-need-a-new-api-version)).

**Versioning.** The extension's version went from 0.1.0 to 0.2.0 because this release adds API:

* A plugin that uses `venora.commands` declares `"minVenoraVersion": "0.2.0"`. Venora 0.1.0 then refuses to install it, with a clear message, instead of installing it and failing at runtime with `unknown_capability`. That only works if the first release with commands has its own version.
* It is a minor bump because the change is additive; the Plugin API stays at version 1 (`compatibility.apiVersion` is unchanged).
* Everything that existed before keeps "Since Venora 0.1.0", and plugins that do not use commands, including the bundled ones, keep `"minVenoraVersion": "0.1.0"`.
* `tests/plugin-api-spec.test.js` checks that every documented "Since" version is valid and no newer than `manifest.json`'s version. The browser test installs a plugin that requires 0.2.0.

| Area                                      | Implementation                                                                                                                                                                                                                                                                                                                                                                                                                 | Tests                                                                                            |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| `venora.commands.register` / `unregister` | `commands.register` and `commands.unregister` in `runtime/capabilities.js`, under the existing `ui` permission, with their own rate limit (20 at once, 2 per second). Payloads are checked by `runtime/plugin-commands.js`: ids are lowercase words joined by hyphens, labels and descriptions follow the plugin-name character rules. At most 20 commands per plugin (`quota_exceeded`); a duplicate id is `invalid_payload`. | `tests/plugin-commands.test.js` (validation, duplicates, limit, rate limit, permissions)         |
| `commands.invoked` event                  | Sent only to the plugin that registered the command, through the host's `emitToPlugin`, so the subscription check, page-context check, event budget, and session-token guard all apply.                                                                                                                                                                                                                                        | `plugin-commands.test.js` (isolation, context, token guard); browser test                        |
| Plugins page                              | The running plugin's commands appear as buttons on its own card, under "Commands", in the existing button style. A stale button (the plugin stopped since the page opened) does nothing and says so. A plugin that is stopping shows no commands, and pressing one of its buttons is refused, as its requests are.                                                                                                             | `plugin-commands.test.js` (stopping); browser test (safe mode, disable, uninstall, stale button) |
| Cleanup                                   | The registry is per plugin and cleared by the host's stop hook, so stop, disable, uninstall, safe mode, a failed start, and termination all remove a plugin's commands. A restarted plugin starts with none.                                                                                                                                                                                                                   | `plugin-commands.test.js` (stop, restart, flood termination, failed start)                       |

No other API was added. Considered and deliberately left out:

* **Network and account APIs:** reviewed in [plugin-network-account-review.md](/plugin-api/plugin-network-account-review.md); not added, because they would remove the "nothing leaves the device" property, and no plugin needs them.
* **A navigation or channel-change event:** it would tell plugins which channels the user views. That needs its own permission decision, so it is left for later.
* **A "ready" lifecycle event:** redundant; the default export runs at start, and `venora.on` resolves once a subscription is active.
* **Context-menu contributions and other new UI surfaces:** they would need hooks into Fermo's own DOM. Commands cover user-triggered actions without that.
* **A command palette and commands in the toolbar popup:** the registry can list every running plugin's commands, so either can be added later without changing the API. The popup would need extension messaging to reach the page's runtime, which is out of scope here.

## Not yet implemented

These are known gaps within the current design. They are not missing API.

* **`unlimitedStorage` decision.** Venora does not request the permission.
  * Per-plugin quotas are enforced.
  * A full browser store is reported as `quota_exceeded`.
  * With many imported plugins near their quota, Chrome's 10 MB default could still be reached.
* **Writes from several tabs.** Each tab runs its own plugin instance. Writes made at the same moment from two tabs can lose one tab's change (see [plugin-settings-storage.md](/plugin-api/plugin-settings-storage.md#storage-semantics)).
* **Firefox.** The runtime has not been run in Firefox; see [runtime-architecture.md](/runtime-architecture.md#chrome-and-firefox). The API itself has nothing browser-specific.
* **Known runtime limitations:** browsers give no per-worker CPU or memory quota.
  * The watchdog stops unresponsive plugins, and the resource account bounds what a plugin can make Venora do or hold.
  * A responsive plugin can still use CPU and memory in its own worker.
  * Plugin CSS can hide parts of Fermo's own page, but not Venora's dialogs, notices, or the toolbar popup, whose safe mode removes it at once (see [plugin-security.md](/plugin-api/plugin-security.md#known-limitations)).

## Future phases (not version 1)

These are out of scope unless the specification is changed first:

* notifications other than toasts, account data, and general network access (see [plugin-network-account-review.md](/plugin-api/plugin-network-account-review.md));
* DOM or arbitrary-element APIs;
* plugin-to-plugin messaging;
* a plugin registry, publishing, hosted discovery, automatic or hosted updates, maintainer accounts and badges, telemetry, a developer portal, or a TypeScript SDK.


---

# 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-status.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.
