Plugin manifest and modules
Every plugin contains a plugin.yaml manifest. Kumbuka strictly decodes one YAML document: unknown fields, unsupported module types, invalid permissions, and incompatible API versions are rejected.
Manifest
A minimal executable plugin manifest looks like:
api_version: 1
provider: Example
id: com.example.greeting
name: Greeting
icon: message-circle-lucide
version: 1.0.0
default_enabled: true
modules:
- type: macro
id: greeting
name: greeting
permissions: []
Top-level fields are:
| Field | Purpose |
|---|---|
api_version |
Plugin API version. API v1 currently uses 1. |
provider |
Optional author/provider label. It is metadata, not a trust grant. |
id |
Globally unique plugin identifier. |
name |
Human-readable plugin name. |
icon |
Optional host icon used for plugin identity in administration. |
version |
Plugin version in MAJOR.MINOR.PATCH form. |
description |
Optional plugin description. |
default_enabled |
Whether the plugin starts enabled by default. |
requires |
Plugin IDs that must be enabled first. |
modules |
One or more contributions supplied by the package. |
permissions |
Host capabilities the package may request. |
A manifest can declare up to 32 modules. Plugin and module identifiers are lowercase identifiers using letters, digits, ., _, and - within the public format limits.
The optional top-level icon uses the same host icon identifiers exposed by Kumbuka’s icon catalog. Kumbuka uses it for plugin identity in administration, including the Plugin settings navigation. If the declared icon is unavailable, the host falls back to its generic plugin icon. Module-level icons remain separate and describe individual actions such as editor inserts or administrator actions.
Module types
markdown-syntax
Enables a standard host-provided Markdown grammar. Supported syntax identifiers are tables, strikethrough, task-list, definition-list, footnote, and linkify.
renderer-extension
Runs executable WASM during preprocess, content-preprocess, or postprocess. content-preprocess modules can declare a bounded priority.
macro
Declares a named macro parsed and rendered by the WASM guest. The optional capability field can require one host operation in the active render scope.
code-highlighter
Provides the exclusive fenced-code highlighter. The guest receives the source and language with stage highlight. An optional packaged CSS asset is filtered and scoped by Kumbuka.
widget
Runs executable WASM to render a bounded widget on a host-owned surface. Supported surfaces are home, page.details, page.after-content, page.aside, and sidebar. width can be omitted or set to normal or wide; order controls deterministic placement within the surface.
Widget HTML is sanitized by Kumbuka. A widget can also return bounded host-rendered link or dialog actions with local application URLs, or a command action handled through Kumbuka’s host-owned POST endpoint. Command actions do not expose arbitrary plugin routes. They may include optional confirmation text and are executed only after the host revalidates the current page context. Widgets cannot return recursive Markdown fragments.
content-change
Runs executable WASM after a page save commits changed canonical Markdown. The guest receives public metadata for the committed page plus the previous and current source. The hook returns no render output and is not invoked for metadata-only saves.
This module is the generic mutation boundary for plugins that must compare old and new content. Mutation-only capabilities such as notifications.send can be supplied in this context; they remain unavailable during rendering.
page-action
Adds a declarative action to the current page without requiring WASM. name, optional description and icon, order, kind, and a bounded local url describe the host-rendered control. kind can be link or dialog and defaults to link. URL templates may use ${slug} and ${id}; Kumbuka expands both with path-safe current-page values.
Use page-action for navigation or host dialogs. Fundamental page operations such as edit, move, and delete remain core application actions rather than plugin contributions.
exporter
Runs executable WASM to produce one bounded downloadable file for the current authorized page. The exporter receives public page metadata plus the stored Markdown source and returns a base filename, media type, and file bytes. Kumbuka owns the POST route, page authorization, response headers, and filename/media-type validation.
settings
Declares administrator-managed plugin settings. A settings module has two forms:
- without
fields, it is a boolean feature toggle;requirescan reference other modules in the same package; - with
fields, it is a typed singleton settings group rendered on the plugin’s page under Administration → Plugin settings.
Typed settings use bounded scalar configuration fields: text, textarea, url, secret, boolean, select, and color. Typed settings groups can contain up to 16 fields and cannot use key: true, list fields, or requires. Color values use canonical six-digit CSS hexadecimal form such as #64748b.
For example:
modules:
- type: settings
id: appearance
name: Appearance
description: Default presentation for embeds.
fields:
- id: reference_position
name: Reference position
type: select
required: true
default: right
options:
- right
- left
- id: highlight_referenced_lines
name: Highlight referenced lines
type: boolean
default: "true"
An executable plugin with settings:read reads a typed setting through Settings().Get("<module>.<field>"). When the administrator has not saved an explicit value, the host returns the manifest default. Manifest-declared settings are administrator managed; guest settings:write calls cannot overwrite them.
content-style
Adds a packaged stylesheet for rendered page typography. Kumbuka filters selectors, properties, and values before applying it.
render-policy
Declares a bounded semantic rendering-policy marker. Active policies become request-scoped feature flags named render-policy.<policy> that other modules can observe; core does not attach feature-specific behavior to individual policy names.
browser-module
Adds isolated browser-side rendering from packaged JavaScript and optional CSS. It requires the browser:render permission. See Browser modules.
admin-resource
Declares a bounded host-rendered record schema owned by the plugin. Kumbuka owns forms, generic type validation, authorization, CSRF protection, namespaced persistence, and encryption of secret fields. Plugins that declare settings, admin-resource, or admin-action modules appear under Administration → Plugin settings rather than placing their configuration in the Plugins lifecycle page.
Each resource has exactly one required text field marked key: true. Scalar fields use the same types as typed settings: text, textarea, url, secret, boolean, select, and color. Admin resources can additionally use bounded list fields for structured rows. required and max_bytes apply generically. boolean fields may use default: "true" or "false"; select fields declare an options list and may choose one option as default; options must fit the field’s byte limit. url defaults must be absolute HTTP(S) URLs. color values are canonical #RRGGBB values. Secret fields cannot have defaults and are masked in the browser after saving.
A list field declares one to eight typed columns and can set max_items up to 64. List columns can use text, url, select, or color; nested lists, secrets, textareas, booleans, and key columns are not supported. A configuration group or resource may contain at most 16 fields, and a select may contain at most 32 unique options.
For example:
modules:
- type: admin-resource
id: sources
name: Sources
fields:
- id: name
name: Name
type: text
required: true
key: true
- id: endpoint
name: API endpoint
type: url
required: true
- id: token
name: Access token
type: secret
- id: enabled
name: Enabled
type: boolean
default: "true"
- id: provider
name: Provider
type: select
options: [github, gitlab]
default: github
Executable plugins with settings:read can read their own structured records through the SDK Resources() client. Secret fields are decrypted only for the owning plugin.
admin-action
Declares one explicit administrator-triggered executable operation. Kumbuka renders the action under Administration → Plugin settings and owns authentication, authorization, CSRF handling, and the POST route. The action is disabled while the plugin is disabled; plugins do not receive an arbitrary administration HTTP route.
name is required. description and icon are optional; icons use the normal identifier syntax. The module ID identifies the action dispatched to the guest.
modules:
- type: admin-action
id: refresh-cache
name: Refresh cache
description: Mark all cached files stale.
icon: refresh-cw-lucide
Executable plugins register the matching module ID with sdk.RegisterAdminAction. The callback returns an error when the operation fails; successful callbacks return no content because Kumbuka owns the administration UI.
content-substitution
Binds an admin-resource to inline {{prefix:name}} substitutions. Values are restored immediately before Markdown parsing so inserted content is not recursively reinterpreted as new substitutions.
editor-completion
Exposes an admin-resource through a bounded editor-completion trigger and replacement template.
editor-insert
Adds a static insertion action to both editor modes without loading plugin code into Kumbuka’s editor DOM. group is the preferred host-owned toolbar slot and defaults to insert. The stable groups are text, blocks, insert, tools, and plugins. allowed_groups is the bounded set an administrator may choose; when omitted, the contribution stays in its preferred group. order is a hint from -1000 to 1000; Kumbuka applies administrator ordering first and uses the stable plugin-id:module-id contribution ID to break ties.
A single action remains a direct toolbar button:
- type: editor-insert
id: strikethrough
name: Strikethrough
markdown: "~~"
suffix: "~~"
placeholder: strikethrough text
mode: wrap
group: text
allowed_groups: [text]
order: 20
icon: strikethrough-lucide
editor-menu
Groups closely related editor-insert modules behind one host-rendered toolbar icon. The menu owns its stable ID, label, icon, preferred and allowed groups, and default order. children contains two to sixteen unique editor-insert IDs in plugin-owned order. Administrators move, hide, or order the menu as a unit; they cannot reorder its children.
modules:
- type: editor-insert
id: note
name: Note
markdown: "!!! note\nImportant information."
icon: message-square-lucide
- type: editor-insert
id: info
name: Info
markdown: "!!! info\nInformation."
icon: info-lucide
- type: editor-insert
id: tip
name: Tip
markdown: "!!! tip\nHelpful tip."
icon: lightbulb-lucide
- type: editor-insert
id: success
name: Success
markdown: "!!! success\nCompleted successfully."
icon: circle-check-lucide
- type: editor-insert
id: warning
name: Warning
markdown: "!!! warning\nImportant warning."
icon: triangle-alert-lucide
- type: editor-insert
id: danger
name: Danger
markdown: "!!! danger\nDangerous operation."
icon: octagon-alert-lucide
- type: editor-menu
id: callouts
name: Callouts
description: Insert a callout block.
icon: message-square-warning-lucide
group: insert
allowed_groups: [insert, blocks, plugins]
order: 30
children: [note, info, tip, success, warning, danger]
Toolbar metadata is declarative. Plugins cannot provide toolbar HTML, CSS, JavaScript, pixel positions, DOM locations, or relative placement such as “after button X”. Icons are names from Kumbuka’s validated host icon catalog; an omitted icon uses the host fallback.
icon-resource
Adds a declarative icon set from a packaged JSON asset. Kumbuka validates the resource and emits the SVG wrapper itself; plugin-provided SVG markup is not trusted directly. Icon names join the shared icon catalog and disappear when the owning plugin is disabled or removed.
Usage selectors
Executable source-aware modules can declare usage rules so Kumbuka can avoid invoking a plugin for pages that cannot use it:
modules:
- type: renderer-extension
id: diagrams
stage: postprocess
usage:
- fence: mermaid
Each rule contains exactly one selector:
contains— literal Markdown text;macro— a standalone macro name;substitution— an inline substitution prefix;fence— a fenced-code language, or*for any fenced block.
Selectors are performance hints and must not create false negatives. Modules without selectors remain active for all candidate renders. Macro modules can infer their selector from the declared macro name.
Permissions required by executable modules are described in Capabilities.