Plugins overview
A Kryton plugin is a folder containing a manifest.json plus a client
entry, a server entry, or both. Plugins extend the editor (UI slots,
code-fence renderers, commands, editor extensions) and the server
(HTTP routes, event hooks, scoped storage).
File layout
Section titled “File layout”The minimum is a manifest plus at least one entry:
my-plugin/ manifest.json client/ index.js # optional — runs in the browser server/ index.js # optional — runs in Node, loaded by the server __tests__/ *.test.js # optional — vitestThis mirrors the layout in every plugin under
kryton-plugins/plugins/ —
client-only (recent-files), server-only (sample-wordcount), or both
(kanban, git-backup).
The manifest
Section titled “The manifest”Required fields (validated by
kryton-plugins/scripts/validate-registry.js and the server’s
PluginManifest type in
packages/server/src/modules/plugins/services/types.ts):
| Field | Type | Notes |
|---|---|---|
id | string | Must equal the directory name. |
name | string | Human-readable label. |
version | string | Semver. |
description | string | One-line summary. |
author | string | |
minKrytonVersion | string | Earliest Kryton release this plugin supports. |
Optional:
server— path to the server entry file, relative to the plugin dir.client— path to the client entry file, relative to the plugin dir.settings[]— declarative settings rendered by the host. Each entry haskey,type(string|boolean|number),default,label, andperUser(when true, each user gets their own value).
Example (kryton-plugins/plugins/recent-files/manifest.json):
{ "id": "recent-files", "name": "Recent Files", "version": "1.0.0", "description": "Display recently opened files for quick navigation", "author": "Kryton", "minKrytonVersion": "2.0.0", "tags": ["navigation", "productivity"], "icon": "clock", "client": "client/index.js", "settings": [ { "key": "maxItems", "type": "number", "default": 20, "label": "Maximum recent files to show", "perUser": true } ]}Lifecycle
Section titled “Lifecycle”Server-side, every plugin moves through these states
(PluginManager.loadPlugin in services/manager.ts):
- installed — manifest read from disk.
- loaded — server entry
require()d. - active — module’s exported
activate(api)ran successfully (with a timeout). Client-only plugins skip straight from installed to active. - error — load or activation threw. The error message is persisted
on the
installedPluginrow.
The enabled flag (also on installedPlugin) is independent of state:
disabling a plugin from the admin panel deactivates it, persisting
enabled = false, and the next boot skips loading it.
Client-side, activate(api) runs after the host injects
window.__krytonPluginDeps (React + ReactDOM). Registrations made
during activate — sidebar panels, fence renderers, commands —
persist for the session. deactivate(), if exported, runs on plugin
disable.
Registry vs local install
Section titled “Registry vs local install”Two install paths, both handled by PluginManager:
- Registry — the official catalogue lives at
kryton-plugins/registry.json, regenerated from the manifests bykryton-plugins/scripts/generate-registry.js. The admin panel’s Plugin Manager browses this list and installs by id. - Local — drop a folder into the configured plugins directory and the manager picks it up on boot (or via the Reload plugins action). Use this for development.
Both end up as rows in installedPlugin; the lifecycle is the same
from there.
What’s next
Section titled “What’s next”- Quickstart — a 30-line sidebar plugin you can clone and edit.
- Client API — autogenerated
TypeDoc reference for everything on
ClientPluginAPI. - Server API — autogenerated
TypeDoc reference for the Node-side
PluginAPI. - UI slots — every
api.ui.register*method with a real-world example. - Code-fence renderers —
how the Kanban plugin replaces a
kanban ``` block with an interactive board. - Testing and publishing — vitest, manifest validation, and how to land a plugin in the registry.