MeshChatX/docs/en/plugins.md
2026-08-03 20:53:12 -05:00

282 lines
9.2 KiB
Markdown

# Plugins
Plugins extend MeshChatX with extra tools, nav items, and background behaviour. They are capability-gated: a plugin only gets what you grant at install time.
Manage them from **Settings → Plugins**. Disable every packaged plugin at startup with `--disable-plugins` or `MESHCHAT_DISABLE_PLUGINS=true`.
## What plugins can do
- Add a row on the **Tools** page
- Add an item in the main **Navigation** sidebar
- React to mesh events (announces, RNS link traffic)
- Call narrowly declared backend managers (path table, debug log, bug reports, RNS links)
- Keep a private key-value store (`storage: isolated`)
- Optionally fetch clearnet HTTP (`network: fetch`), still subject to **Privacy mode**
Plugins cannot rewrite core MeshChatX. They do not get open-ended filesystem or process control unless you opt into Sideband Python plugins (see below).
## Runtimes
| Runtime | Where it runs | Trust level |
| --------------- | ------------------------- | ----------------------------------------------- |
| Frontend JS | Browser Web Worker | Medium. Sandboxed worker, capability grants |
| Backend WASM | `wasmtime` on the server | Medium. Fuel-metered, capability-gated host |
| Backend Python | In-process with MeshChatX | High. Permission-checked in-process host |
| Sideband `*.py` | In-process, flat files | Highest. Opt-in danger switch, full host access |
A packaged plugin can ship frontend only, backend only, or both.
## Install flow
```
Pick ZIP or .wasm file in Settings → Plugins
|
--> Preview (permissions, URLs, signature, findings)
|
--> You grant or deny each capability
|
--> Optional: trust a valid signer
|
--> Install + integrity hash stored
|
--> Enable
|
--> Frontend Worker loads (if present)
--> Backend WASM / Python activates (if present)
```
Invalid signatures hard-block install. Unsigned packages are allowed. Present-but-broken signatures are not.
After install, MeshChatX hashes the on-disk tree. If files change outside the app, the plugin is auto-disabled as tampered.
## Bundled example: Bug Reports
`com.meshchatx.mcx-bugs` ships with MeshChatX. It adds a **Bug Reports** tool for sending redacted debug logs to an `mcx-bugs-v1` collector, or running a collector yourself.
Layout:
```
mcx-bugs/
plugin.json
frontend/main.js
backend/main.py
locales/en.json
```
Use it as the reference package when building your own.
## Manifest (`plugin.json`)
Every packaged plugin needs a root `plugin.json`.
```json
{
"id": "com.example.my-plugin",
"version": "1.0.0",
"apiVersion": 1,
"name": "My Plugin",
"description": "Adds a custom tool.",
"frontend": {
"entry": "frontend/main.js",
"type": "js"
},
"backend": {
"entry": "backend/main.py",
"type": "python"
},
"i18n": {
"directory": "locales",
"defaultLocale": "en"
},
"contributes": {
"navItems": [
{
"id": "my-plugin",
"route": { "name": "plugin-my-plugin" },
"icon": "puzzle",
"labelKey": "nav"
}
],
"toolsPageEntries": [
{
"name": "my-plugin",
"route": { "name": "plugin-my-plugin" },
"icon": "puzzle",
"titleKey": "title",
"descriptionKey": "description"
}
]
},
"permissions": {
"hooks": ["announce.received"],
"managers": ["destinationPath.read"],
"storage": "isolated",
"network": "none"
}
}
```
Notes:
- `id` is reverse-DNS style and must stay stable across versions
- `apiVersion` is currently `1`
- Plugin strings live in the plugin bundle (`locales/{locale}.json`), not core `en.json`
- `contributes` wires UI slots through the frontend registries
## Permissions
Nothing is available unless it is declared in the manifest and granted in the install dialog.
### Hooks
| Hook | When it fires |
| ------------------- | ----------------------------------------------------------- |
| `announce.received` | A Reticulum announce arrives |
| `rns.link.event` | Generic RNS Link traffic (`packet_received`, `link_closed`) |
Hook events reach the UI as WebSocket `plugin.event` frames, then into the plugin Worker.
### Managers
| Manager | Purpose |
| ---------------------- | ----------------------------- |
| `destinationPath.read` | Read the Reticulum path table |
| `debugLog.read` | Read redacted debug logs |
| `bugReport.*` | Bug report / collector APIs |
| `rnsLink.open` | Open or reuse an RNS link |
| `rnsLink.identify` | Identify on a cached link |
| `rnsLink.request` | Request/response on a link |
| `rnsLink.send` | Send a raw link packet |
| `rnsLink.close` | Tear down a cached link |
Call managers from a plugin with `POST /api/v1/plugins/{id}/invoke` and `method: "callManager"`. Details for the link transport are in [RNS Link API](rns-link-api.md).
### Storage and network
| Permission | Effect |
| ------------------- | --------------------------------------------------------------------- |
| `storage: isolated` | Private key-value store in the MeshChatX database |
| `storage: none` | No plugin storage |
| `network: fetch` | Outbound HTTP allowed (still blocked by Privacy mode when that is on) |
| `network: none` | No clearnet fetch |
Install preview also scans plugin files for external `http://` / `https://` URLs and shows them before you grant network access.
## How a frontend plugin runs
```
Settings enable plugin
|
--> PluginHost loads /api/v1/plugins
|
--> Fetch frontend entry as text
|
--> Spawn pluginWorker.js (module Worker)
|
--> Register nav / tools contributions
|
--> Subscribe to plugin.event on /ws (if hooks granted)
|
--> Worker may invoke backend via /api/v1/plugins/{id}/invoke
```
The Worker talks to the host with typed messages (`init`, `event`, `request`). The host never gives the Worker a raw privileged API.
## How a backend plugin runs
```
Enable plugin
|
+--> type: wasm --> load into wasmtime, fuel + host caps
|
+--> type: python --> import entry, call activate(host)
|
--> Hooks fan out from PluginManager
|
--> invoke(method, args) for RPC from the UI Worker
```
Python host surface (permission-checked):
- `host.log(message)`
- `host.call_manager(capability, args)`
- `host.storage_get(key)` / `host.storage_set(key, value)`
- `host.network_fetch_allowed()`
## Packaging and signing
Distribute as:
1. **ZIP** with `plugin.json` and assets
2. **WASM bundle** (single `.wasm` with embedded manifest / files / optional signature)
Signature file for ZIP/dir packages: `meshchatx.plugin.rsg`
WASM custom sections:
```
meshchatx.plugin --> embedded plugin.json
meshchatx.files --> embedded text assets
meshchatx.signature --> RSG over payload without this section
```
Canonical ZIP signing uses sorted paths and fixed 1980-01-01 mtimes. The signature file itself is excluded from the signed payload.
Sign and verify with:
```bash
python3 scripts/sign-plugin.py sign-dir ./my-plugin --identity <rnid>
python3 scripts/sign-plugin.py verify-dir ./my-plugin
python3 scripts/sign-plugin.py sign-zip ./my-plugin.zip --identity <rnid>
python3 scripts/sign-plugin.py sign-wasm ./plugin.wasm --identity <rnid>
python3 scripts/sign-plugin.py sign-py ./legacy_plugin.py --identity <rnid>
```
Trust status in the UI:
```
No .rsg present
--> Unsigned (install allowed)
Valid .rsg, signer unknown
--> Signed (you can add to Trusted Publishers)
Valid .rsg, signer in Trusted Publishers
--> Trusted
Broken / mismatched .rsg
--> Invalid (install blocked)
```
## Sideband-compatible plugins
Legacy Sideband-style flat `*.py` files are separate from packaged ZIP/WASM plugins.
```
Settings → Plugins → Sideband
|
--> Confirm danger prompt
|
--> Set directory of *.py files
|
--> Optional filename.py.rsg next to each script
|
--> Reload
```
These run in-process with full host access. They are not ZIP-permission gated. Keep the master switch off unless you trust every file in that directory.
## Operator tips
- Prefer signed packages from publishers you added yourself
- Deny `network: fetch` unless the plugin needs clearnet HTTP
- Prefer WASM backends over Python when you can
- Use `--disable-plugins` when diagnosing weird UI or backend behaviour
- Treat Sideband plugins like running arbitrary local scripts
## See also
- [Tools and utilities](tools.md) for the Tools page and contribution overview
- [RNS Link API](rns-link-api.md) for `rnsLink.*` and `rns.link.event`
- [Architecture and design](architecture.md) for the plugin runtime overview
- [Identities, privacy, and security](identity-and-security.md) for signing and Privacy mode