Plugin capabilities
WASM plugins do not receive Kumbuka Go pointers, database clients, filesystem handles, raw sockets, or process access. Host operations are exposed through a small versioned capability API and are checked against both the plugin manifest and the active runtime/render policy. Plugins can own credentials in manifest-declared secret settings; Kumbuka encrypts those values at rest and returns them only to the owning plugin.
The Go SDK provides typed clients, so plugins normally do not call the low-level transport directly.
Operations
| Operation | Manifest permission | Purpose |
|---|---|---|
pages.get |
pages:read |
Read authorized public page metadata. |
pages.search |
pages:read |
Search the authorized page catalog with a bounded limit. |
pages.navigation |
pages:read |
Read the prepared navigation tree for the render target. |
pages.links |
pages:read |
Read authorized backlinks and outgoing wiki-link metadata. |
pages.revisions |
pages:read |
Read bounded revision metadata without stored page bodies. |
pages.recent |
pages:read |
Read the newest authorized pages. |
pages.popular |
pages:read |
Read the most-viewed authorized pages. |
pages.recent-viewed |
activity:read |
Read pages recently viewed by the current viewer. |
pages.favorites |
activity:read |
Read pages favorited by the current viewer. |
pages.recent-edits |
activity:read |
Read pages recently edited by the current viewer. |
drafts.list |
drafts:read |
Read bounded private draft metadata for the current viewer. |
pages.content |
pages:content |
Read authorized Markdown source. |
attachments.read |
attachments:read |
Read a bounded authorized attachment byte range. |
plugin.settings.read |
settings:read |
Read this plugin’s settings, including declared typed fields. |
plugin.settings.write |
settings:write |
Write ordinary plugin-owned settings keys. |
plugin.resources.get |
settings:read |
Read one manifest-declared structured setting record. |
plugin.resources.list |
settings:read |
List one manifest-declared structured setting collection. |
plugin.storage.read |
storage:read |
Read this plugin’s opaque data namespace. |
plugin.storage.write |
storage:write |
Write this plugin’s opaque data namespace. |
http.do |
network:http |
Perform one bounded host-mediated HTTP(S) request. |
users.search |
users:read |
Search enabled users by mention or display name. |
users.resolve-mention |
users:read |
Resolve a canonical Kumbuka mention. |
notifications.send |
notifications:send |
Create an attributed in-app notification during a mutation. |
icons.render |
none | Render an icon from the active host icon catalog. |
log |
none | Write a bounded plugin log message. |
A permission must be declared by the package and granted by Kumbuka’s runtime policy. An undeclared call is denied even if another plugin is allowed to use that capability. Missing grants prevent the plugin from loading in that scope.
icons.render and log are explicitly public host operations and therefore need no manifest permission. Unknown methods are rejected rather than treated as unprivileged calls.
Request scope
Page operations use the current viewer’s authorized catalog rather than unrestricted persistence. Link, revision, recent-page, popularity, and search results therefore stay inside the same page-access boundary as the surrounding render.
Viewer-specific activity operations (pages.recent-viewed, pages.favorites, pages.recent-edits, and drafts.list) are available only when the current request supplies that viewer context. Public-share rendering is restricted to the shared page. Static builds can provide prepared navigation without exposing database-backed catalog, activity, draft, or persistent-storage capabilities.
Attachment reads are available only when the current render scope explicitly supplies an authorized range reader. Missing context capabilities return an error.
User-directory results contain only stable user ID, canonical mention, and display name. Email addresses, roles, authentication state, and external identities are not exposed to plugins. users:read makes executable render output dynamic because user names can change.
Notifications().Send is available only in host-recognized mutation contexts such as widget commands and committed content-change hooks. Kumbuka supplies the authenticated actor and calling plugin identity, persists the inbox item, and emits notification.created; rendering a page can never create a notification. Plugins must provide an idempotency key so retried mutations do not produce duplicate inbox items or webhook events.
Plugin-owned storage
Settings and data are separate namespaces owned by the executing plugin. Keys are 1–256 bytes and values are at most 64 KiB. PostgreSQL-backed storage enforces up to 1,024 keys and 16 MiB per plugin across both namespaces. Reads distinguish an absent value from an empty value.
Plugin data survives renderer/runtime restarts and disabling a plugin does not delete it. Manifest-declared typed settings groups and structured admin-resource records are administrator managed and stored in the owning plugin’s settings namespace. Typed settings are read through Settings().Get("<module>.<field>"); when no explicit value has been saved, Kumbuka returns the manifest default. Repeatable records are read through Resources(). Fields declared as secret are encrypted with KUMBUKA__ENCRYPTION_KEY, masked in administration, and decrypted only for the owning plugin.
The raw settings API remains available for plugin-owned keys, but manifest-declared configuration and Kumbuka’s reserved settings keys cannot be overwritten through plugin.settings.write. This keeps administrator-managed configuration authoritative while still allowing plugins to persist unrelated lightweight settings when they declare settings:write.
Outbound HTTP
HTTP().Do lets an executable plugin define an HTTP(S) request while Kumbuka performs the network I/O. Core understands generic HTTP method, URL, headers, body, TLS policy, and destination policy; provider-specific endpoints, authentication headers, response formats, and setting names stay in the plugin.
The base operation requires network:http. A request that supplies exact RFC1918 or IPv6 ULA destination exceptions additionally requires network:private. Setting InsecureSkipVerify additionally requires network:insecure-tls. These permissions must be present both in the package manifest and Kumbuka’s runtime policy.
The host applies bounded request and response bodies, bounded headers, an HTTP(S)-only URL policy, no redirects, timeouts, DNS validation with numeric-address pinning, and SSRF filtering. Private destinations are denied unless the plugin supplies an exact permitted private address and has network:private; loopback, link-local, shared-address space, metadata, documentation, and other special-use destinations remain blocked. The generic client honors Kumbuka’s process HTTP_PROXY, HTTPS_PROXY, NO_PROXY, SSL_CERT_FILE, and SSL_CERT_DIR environment settings.
The server enables outbound plugin HTTP only for authenticated invocation contexts. Public-share rendering cannot use it. Static builds do not grant network permissions.
Limits
Host calls during plugin initialization are denied. One invocation can make at most 512 host calls and those calls share the invocation deadline. Plugin identity is never supplied by guest input; Kumbuka derives it from the executing plugin instance.
See Wire protocol for the low-level kumbuka_v1.call ABI.