Browse the user guide

Plugins

Install trusted plugins and configure their permissions and isolation.

Canopus supports two plugin APIs. Existing Ruby plugins use Workspace#plugins and remain available for compatibility. New plugins use the isolated Gienah host and a plugin.json manifest.

{
  "id": "word-count",
  "name": "Word Count",
  "version": "0.1.0",
  "api_version": 2,
  "entry": "plugin.rb",
  "activation": ["onCommand:word-count.refresh"],
  "capabilities": ["buffer.read", "ui.panel"],
  "contributes": {
    "commands": [{"id": "word-count.refresh", "title": "Refresh word count"}],
    "panels": [{"id": "word-count", "title": "Word Count", "dock": "right"}]
  }
}

The host discovers manifests without starting processes. A workspace must be trusted before a manifest is activated. Capabilities are denied by default; process.exec, filesystem, and network access additionally require Saiph.

Plugin entrypoints use Gienah’s SDK and communicate with Canopus through the declared host methods:

require "gienah"

Gienah::Plugin.on("ui/event") { |event| Gienah::Plugin.call("workspace/notify", "text" => event["id"]) }
Gienah::Plugin.export("word-count.refresh") do
  text = Gienah::Plugin.call("buffer/text")
  Gienah::Plugin.call("ui/render", "panel" => "word-count", "tree" => {
    "type" => "text", "props" => {"value" => "#{text.split.size} words"}, "children" => []
  })
  nil
end
Gienah::Plugin.run

Panels use the allow-listed declarative vocabulary (column, row, spacer, scroll, divider, text, icon, badge, progress, code, button, checkbox, text_field, select, list, tree, and section). Raw colors, images, fonts, and coordinates are not accepted. UI rendering is cached in the editor, so a slow plugin cannot block a frame. Completion providers have a 200 ms response budget.

The host also exposes storage/get, storage/set, process/exec, net/fetch, ui/status, ui/quick_pick, ui/input, and ui/confirm. Storage is scoped to the workspace and plugin ID. Network requests must match the manifest’s net: host pattern, and dangerous operations remain gated by the manifest capability and workspace trust.

buffer/text limits an unbounded request to 1 MiB; larger documents must be read with a byte range. Calling buffer/subscribe with the active buffer URI starts diff notifications for that plugin only. The host sends buffer/didChange, buffer/didOpen, buffer/didSave, buffer/didClose, and throttled selection/didChange notifications for the subscribed buffer, plus workspace/didChangeFiles and settings/didChange to subscribed plugins.