`ninja plugin build` compiles a plugin to reactor-mode wasip1 wasm (Go >= 1.24 enforced), extracts manifest.pb by driving one HOOK_DESCRIBE over wazero with failing host stubs, and packs a tar.zst .bnp (plugin.wasm, plugin.mod, manifest.pb + migrations/schemas/assets/web-dist when present) with a summary table. A describe-time capability call (e.g. db.* from Register) fails with an actionable error naming the offending method. `ninja plugin verify` re-runs the CMS reader's layout/name/abi/path-safety/size checks standalone (deliberate duplication of cms backend/plugin/bnp/reader.go; kept in lockstep by WO-WZ-010). ABI riders (additive; buf breaking clean): - ABI_ERROR_CODE_TX_EXPIRED enum value + bnwasm guest mapping to a new bnwasm.ErrTxExpired sentinel (retryable tx expiry, distinct from real faults); the cms dbexec side adopts the emit separately. - PluginManifest.data_dir bool + a first-class `data_dir` key on the plugin.mod parser (so writeMod's struct round-trip can't drop it); `plugin build` stamps it from plugin.mod into the manifest. Docs: wasm-abi.md gains a Building & packing section, the error-code table row, the manifest data_dir mapping, and the plugin.mod reference. Tests: CLI e2e builds the WZ-002 fixture → verify + manifest block keys; a capfixture proves the actionable describe-time error; verify rejects each malformed class; bnwasm TX_EXPIRED classification. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
223 lines
7.5 KiB
Protocol Buffer
223 lines
7.5 KiB
Protocol Buffer
// manifest.proto — the static plugin manifest (WO-WZ-001).
|
|
//
|
|
// PluginManifest is the wire form of everything that is *static data* in
|
|
// core/plugin/registration.go (PluginRegistration). It is produced once at
|
|
// publish time by calling the guest's DESCRIBE hook and stored as manifest.pb
|
|
// inside the .bnp artifact; the CMS loader reads it without instantiating the
|
|
// wasm module.
|
|
//
|
|
// Function-valued registration fields cannot cross the wire; they map to
|
|
// either a hook (Load/Unload/JobHandlers/MediaHooks/HTTPHandler), a boolean
|
|
// presence flag here, or an artifact directory (Assets → assets/,
|
|
// Schemas → schemas/, Migrations → migrations/). See core/docs/wasm-abi.md
|
|
// for the full field-by-field mapping.
|
|
|
|
syntax = "proto3";
|
|
|
|
package abi.v1;
|
|
|
|
option go_package = "git.dev.alexdunmow.com/block/core/abi/v1;abiv1";
|
|
|
|
// PluginManifest mirrors the static surface of plugin.PluginRegistration.
|
|
message PluginManifest {
|
|
// ABI major version the plugin was built against. The host rejects
|
|
// manifests whose major version it does not support.
|
|
uint32 abi_version = 1;
|
|
|
|
// PluginRegistration.Name / .Version.
|
|
string name = 2;
|
|
string version = 3;
|
|
|
|
// PluginRegistration.Dependencies.
|
|
repeated Dependency dependencies = 4;
|
|
|
|
// Blocks registered via BlockRegistry.Register (captured by DESCRIBE).
|
|
repeated BlockMeta blocks = 5;
|
|
// Blocks registered via BlockRegistry.RegisterTemplateOverride[WithSource].
|
|
repeated BlockTemplateOverride block_template_overrides = 6;
|
|
|
|
// Templates registered via TemplateRegistry (captured by DESCRIBE).
|
|
repeated string template_keys = 7; // TemplateRegistry.Register
|
|
repeated SystemTemplateMeta system_templates = 8; // RegisterSystemTemplate
|
|
repeated PageTemplateMeta page_templates = 9; // RegisterPageTemplate
|
|
repeated string email_wrapper_system_keys = 10; // RegisterEmailWrapper
|
|
|
|
// PluginRegistration.AdminPages.
|
|
repeated AdminPage admin_pages = 11;
|
|
|
|
// PluginRegistration.SettingsSchema / .ThemePresets / .BundledFonts (JSON).
|
|
bytes settings_schema = 12;
|
|
bytes theme_presets = 13;
|
|
bytes bundled_fonts = 14;
|
|
|
|
// PluginRegistration.MasterPages.
|
|
repeated MasterPageDefinition master_pages = 15;
|
|
|
|
// PluginRegistration.AIActions.
|
|
repeated AiAction ai_actions = 16;
|
|
|
|
// RBAC roles for the plugin's own Connect services, merged from
|
|
// ServiceRegistration (full method name → role, e.g.
|
|
// "/symposium.v1.ForumService/CreateThread" → "admin").
|
|
map<string, string> rbac_method_roles = 17;
|
|
|
|
// PluginRegistration.CSSManifest.
|
|
CssManifest css_manifest = 18;
|
|
|
|
// PluginRegistration.RequiredIconPacks.
|
|
repeated string required_icon_packs = 19;
|
|
|
|
// PluginRegistration.DirectoryExtensions (static fields only; the
|
|
// panel-section / pin-decorator callbacks are counted so the host knows
|
|
// how many guest callbacks exist).
|
|
DirectoryExtensions directory_extensions = 20;
|
|
|
|
// Job types the plugin handles (keys of PluginRegistration.JobHandlers).
|
|
// The host dispatches these via the JOB hook.
|
|
repeated string job_types = 21;
|
|
|
|
// Content types the plugin registered RAG content fetchers for
|
|
// (RAGService.RegisterContentFetcher inverts to a manifest declaration;
|
|
// the host calls back via the RAG_FETCH hook).
|
|
repeated string rag_content_fetcher_types = 22;
|
|
|
|
// PluginRegistration.SettingsPanel — Module Federation path of the
|
|
// settings panel component ("" when the plugin has none).
|
|
string settings_panel = 23;
|
|
|
|
// Presence flags for function-valued registration fields that invert to
|
|
// hooks at runtime.
|
|
bool has_http_handler = 24; // PluginRegistration.HTTPHandler → HANDLE_HTTP
|
|
bool has_load_hook = 25; // PluginRegistration.Load → LOAD
|
|
bool has_unload_hook = 26; // PluginRegistration.Unload → UNLOAD
|
|
bool has_media_hooks = 27; // PluginRegistration.MediaHooks → MEDIA_HOOK
|
|
bool has_provisioner = 28; // PluginRegistration.RegisterWithProvisioner
|
|
|
|
// Core CMS services the plugin mounts with custom RBAC roles
|
|
// (CoreServiceBindings.Bind becomes a static declaration; the host
|
|
// constructs and mounts the handlers).
|
|
repeated CoreServiceBinding core_service_bindings = 29;
|
|
|
|
// data_dir requests a persistent per-plugin /data preopen at load. Its
|
|
// source of truth is the plugin.mod `data_dir = true` key, which lives
|
|
// outside the guest code, so DESCRIBE cannot populate it: the packer
|
|
// (`ninja plugin build`) stamps it into the manifest from plugin.mod so
|
|
// the loader reads one source. OFF by default.
|
|
bool data_dir = 30;
|
|
}
|
|
|
|
// Dependency mirrors plugin.Dependency.
|
|
message Dependency {
|
|
string plugin = 1;
|
|
string min_version = 2;
|
|
bool required = 3;
|
|
}
|
|
|
|
// BlockMeta mirrors blocks.BlockMeta. Source is omitted: the host assigns it
|
|
// from the manifest's plugin name at load time.
|
|
message BlockMeta {
|
|
string key = 1;
|
|
string title = 2;
|
|
string description = 3;
|
|
// blocks.BlockCategory string ("content", "layout", "navigation", "blog",
|
|
// "theme").
|
|
string category = 4;
|
|
bool has_internal_slot = 5;
|
|
bool hidden = 6;
|
|
string editor_js = 7;
|
|
}
|
|
|
|
// BlockTemplateOverride captures BlockRegistry.RegisterTemplateOverride and
|
|
// RegisterTemplateOverrideWithSource registrations.
|
|
message BlockTemplateOverride {
|
|
string template_key = 1;
|
|
string block_key = 2;
|
|
// Override source label; empty for plain RegisterTemplateOverride.
|
|
string source = 3;
|
|
}
|
|
|
|
// SystemTemplateMeta mirrors templates.SystemTemplateMeta.
|
|
message SystemTemplateMeta {
|
|
string key = 1;
|
|
string title = 2;
|
|
string description = 3;
|
|
}
|
|
|
|
// PageTemplateMeta mirrors templates.PageTemplateMeta plus the system
|
|
// template it was registered under.
|
|
message PageTemplateMeta {
|
|
string system_key = 1;
|
|
string key = 2;
|
|
string title = 3;
|
|
string description = 4;
|
|
repeated string slots = 5;
|
|
}
|
|
|
|
// AdminPage mirrors plugin.AdminPage.
|
|
message AdminPage {
|
|
string key = 1;
|
|
string title = 2;
|
|
string icon = 3;
|
|
string route = 4;
|
|
}
|
|
|
|
// AiAction mirrors plugin.AIAction.
|
|
message AiAction {
|
|
string key = 1;
|
|
string title = 2;
|
|
string description = 3;
|
|
string default_provider = 4;
|
|
string default_model = 5;
|
|
}
|
|
|
|
// CssManifest mirrors plugin.CSSManifest.
|
|
message CssManifest {
|
|
map<string, string> npm_packages = 1;
|
|
repeated string css_directives = 2;
|
|
string input_css_append = 3;
|
|
}
|
|
|
|
// DirectoryExtensions mirrors the static fields of plugin.DirectoryExtensions.
|
|
message DirectoryExtensions {
|
|
repeated string boolean_filter_fields = 1;
|
|
repeated string select_filter_fields = 2;
|
|
map<string, BadgeLabel> badge_labels = 3;
|
|
// Counts of the callback slices (PanelSections / PinDecorators) so the host
|
|
// knows how many guest callbacks the plugin registered. Their invocation
|
|
// hook is a runtime-WO concern.
|
|
uint32 panel_section_count = 4;
|
|
uint32 pin_decorator_count = 5;
|
|
}
|
|
|
|
// BadgeLabel mirrors the [2]string value of DirectoryExtensions.BadgeLabels.
|
|
message BadgeLabel {
|
|
string positive = 1;
|
|
string negative = 2;
|
|
}
|
|
|
|
// MasterPageDefinition mirrors plugin.MasterPageDefinition.
|
|
message MasterPageDefinition {
|
|
string key = 1;
|
|
string title = 2;
|
|
repeated string page_templates = 3;
|
|
repeated MasterPageBlock blocks = 4;
|
|
}
|
|
|
|
// MasterPageBlock mirrors plugin.MasterPageBlock.
|
|
message MasterPageBlock {
|
|
string block_key = 1;
|
|
string title = 2;
|
|
// JSON encoding of the block's content map.
|
|
bytes content_json = 3;
|
|
optional string html_content = 4;
|
|
string slot = 5;
|
|
int32 sort_order = 6;
|
|
}
|
|
|
|
// CoreServiceBinding mirrors a plugin.CoreServiceBindings.Bind call: mount
|
|
// the named core-provided Connect service with these RBAC roles.
|
|
message CoreServiceBinding {
|
|
string service_name = 1;
|
|
map<string, string> method_roles = 2;
|
|
}
|