Defines the host<->guest wire contract for the .so -> wazero migration as a repo-local buf module (abi/proto/v1, generated Go in abi/v1): - manifest.proto: PluginManifest mirroring every static field of plugin.PluginRegistration (abi_version, deps, block/template registrations, admin pages, settings schema, theme presets, fonts, master pages, AI actions, RBAC method roles, CSS manifest, icon packs, directory extensions, job types, RAG fetcher types, settings panel, hook flags, core service bindings) - invoke.proto: bn_invoke envelope, Hook catalog (RENDER_BLOCK, RENDER_TEMPLATE, HANDLE_HTTP, JOB, LOAD, UNLOAD, RAG_FETCH, MEDIA_HOOK, DESCRIBE), AbiError, and job/lifecycle/rag/media/describe payloads - render.proto: RenderBlock/RenderTemplate payloads + RenderContext enumerating every ctx value from blocks/context.go (incl. BlockContext 1:1) - http.proto: buffered HttpRequest/HttpResponse for HANDLE_HTTP - db.proto: guest sql-driver messages (DbValue oneof over pgx-mappable types, query/exec/tx with host-side tx handles, SQLSTATE-carrying DbError) - capability.proto: HostCall envelope + one request/response pair per CoreServices interface method (content, settings+update, gating, crypto, menus, datasources, users, subscriptions, media.deposit, email.send, ai.text_call, ai.tools.register, bridge, jobs.submit, embeddings, rag, reviews, badges.refresh) The abi/ buf module is deliberately separate from the repo-root config: proto/ is the shared block/proto submodule (service API contracts); the ABI is SDK-internal and versions in lockstep with the guest shim. New `make abi` target runs buf lint + generate. docs/wasm-abi.md documents the ptr+len calling convention (bn_alloc, bn_invoke, packed u64), hook catalog, error semantics, abi_version evolution rules, and the full registration/ CoreServices coverage tables. Verified: `cd abi && buf lint` exit 0; `go build ./...` exit 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
542 lines
12 KiB
Protocol Buffer
542 lines
12 KiB
Protocol Buffer
// capability.proto — guest→host capability calls (WO-WZ-001).
|
|
//
|
|
// Each CoreServices interface (core/plugin/deps.go) becomes a host-function
|
|
// family; every interface method gets one request/response message pair
|
|
// here, mirroring the Go signature 1:1. UUIDs travel as canonical strings;
|
|
// map[string]any / []byte-JSON values travel as JSON bytes.
|
|
//
|
|
// Calls cross via the generic HostCallRequest/HostCallResponse envelope; the
|
|
// method string selects the pair (e.g. "content.get_author_profile" →
|
|
// ContentGetAuthorProfileRequest/Response). Full method table in
|
|
// core/docs/wasm-abi.md.
|
|
//
|
|
// Not represented here by design:
|
|
// - CoreServices.Pool → db.proto (the DB driver messages)
|
|
// - CoreServices.Interceptors → host-side only (never crosses)
|
|
// - CoreServices.AppURL/MediaPath → LoadRequest.host_config (invoke.proto)
|
|
// - CoreServices.CoreServiceBindings → manifest core_service_bindings
|
|
// - RAGService.RegisterContentFetcher → manifest rag_content_fetcher_types
|
|
// + the RAG_FETCH hook (callback inversion)
|
|
// - ai.ToolDefinition.Handler → host→guest tool execution is a runtime-WO
|
|
// concern (flagged in core/docs/wasm-abi.md)
|
|
|
|
syntax = "proto3";
|
|
|
|
package abi.v1;
|
|
|
|
import "google/protobuf/timestamp.proto";
|
|
import "v1/invoke.proto";
|
|
|
|
option go_package = "git.dev.alexdunmow.com/block/core/abi/v1;abiv1";
|
|
|
|
// HostCallRequest is the generic guest→host capability envelope.
|
|
message HostCallRequest {
|
|
// Capability method, "<family>.<method>" (e.g. "crypto.encrypt_secret").
|
|
string method = 1;
|
|
// Serialized family request message.
|
|
bytes payload = 2;
|
|
}
|
|
|
|
// HostCallResponse is the generic host→guest capability return envelope.
|
|
message HostCallResponse {
|
|
// Serialized family response message; empty when error is set.
|
|
bytes payload = 1;
|
|
// Set when the capability call failed; the guest SDK surfaces it as a
|
|
// normal Go error.
|
|
AbiError error = 2;
|
|
}
|
|
|
|
// --- content.* (content.Content) ---
|
|
|
|
message ContentGetAuthorProfileRequest {
|
|
string id = 1; // UUID
|
|
}
|
|
|
|
message ContentGetAuthorProfileResponse {
|
|
AuthorProfile author = 1;
|
|
}
|
|
|
|
// AuthorProfile mirrors content.AuthorProfile.
|
|
message AuthorProfile {
|
|
string id = 1; // UUID
|
|
string name = 2;
|
|
string slug = 3;
|
|
string bio = 4;
|
|
string avatar_url = 5;
|
|
string website = 6;
|
|
map<string, string> social_links = 7;
|
|
}
|
|
|
|
message ContentGetPageRequest {
|
|
string slug = 1;
|
|
}
|
|
|
|
message ContentGetPageResponse {
|
|
PageInfo page = 1;
|
|
}
|
|
|
|
// PageInfo mirrors content.PageInfo.
|
|
message PageInfo {
|
|
string id = 1; // UUID
|
|
string slug = 2;
|
|
string title = 3;
|
|
}
|
|
|
|
message ContentGetPostRequest {
|
|
string slug = 1;
|
|
}
|
|
|
|
message ContentGetPostResponse {
|
|
PostInfo post = 1;
|
|
}
|
|
|
|
// PostInfo mirrors content.PostInfo.
|
|
message PostInfo {
|
|
string id = 1; // UUID
|
|
string slug = 2;
|
|
string title = 3;
|
|
string excerpt = 4;
|
|
string featured_image_url = 5;
|
|
string author_id = 6; // UUID
|
|
}
|
|
|
|
message ContentSlugifyRequest {
|
|
string text = 1;
|
|
}
|
|
|
|
message ContentSlugifyResponse {
|
|
string slug = 1;
|
|
}
|
|
|
|
message ContentBlockNoteToHtmlRequest {
|
|
// JSON encoding of the BlockNote document map.
|
|
bytes doc_json = 1;
|
|
}
|
|
|
|
message ContentBlockNoteToHtmlResponse {
|
|
string html = 1;
|
|
}
|
|
|
|
message ContentGenerateExcerptRequest {
|
|
string html = 1;
|
|
int32 max_len = 2;
|
|
}
|
|
|
|
message ContentGenerateExcerptResponse {
|
|
string excerpt = 1;
|
|
}
|
|
|
|
message ContentStripHtmlRequest {
|
|
string html = 1;
|
|
}
|
|
|
|
message ContentStripHtmlResponse {
|
|
string text = 1;
|
|
}
|
|
|
|
// --- settings.* (settings.Settings) + settings.update (settings.Updater) ---
|
|
|
|
message SettingsGetSiteSettingsRequest {}
|
|
|
|
message SettingsGetSiteSettingsResponse {
|
|
// JSON encoding of the site settings map.
|
|
bytes settings_json = 1;
|
|
}
|
|
|
|
message SettingsGetPluginSettingsRequest {
|
|
string plugin_name = 1;
|
|
}
|
|
|
|
message SettingsGetPluginSettingsResponse {
|
|
// JSON encoding of the plugin settings map.
|
|
bytes settings_json = 1;
|
|
}
|
|
|
|
message SettingsUpdateSiteSettingRequest {
|
|
string key = 1;
|
|
// JSON encoding of the value.
|
|
bytes value_json = 2;
|
|
}
|
|
|
|
message SettingsUpdateSiteSettingResponse {}
|
|
|
|
// --- gating.* (gating.Gating) ---
|
|
|
|
message GatingGetSubscriberTierLevelRequest {
|
|
string user_id = 1; // UUID
|
|
}
|
|
|
|
message GatingGetSubscriberTierLevelResponse {
|
|
int32 level = 1;
|
|
}
|
|
|
|
message GatingEvaluateAccessRequest {
|
|
int32 user_tier_level = 1;
|
|
// Absent rule means "no rule" (access granted).
|
|
AccessRule rule = 2;
|
|
}
|
|
|
|
message GatingEvaluateAccessResponse {
|
|
AccessResult result = 1;
|
|
}
|
|
|
|
// AccessRule mirrors gating.AccessRule.
|
|
message AccessRule {
|
|
int32 min_tier_level = 1;
|
|
string override_tier_id = 2;
|
|
string teaser_mode = 3; // "hard", "soft", "none"
|
|
int32 teaser_percent = 4;
|
|
}
|
|
|
|
// AccessResult mirrors gating.AccessResult.
|
|
message AccessResult {
|
|
bool has_access = 1;
|
|
string teaser_mode = 2;
|
|
int32 teaser_percent = 3;
|
|
int32 required_level = 4;
|
|
}
|
|
|
|
// --- crypto.* (crypto.Crypto) ---
|
|
|
|
message CryptoEncryptSecretRequest {
|
|
string plaintext = 1;
|
|
}
|
|
|
|
message CryptoEncryptSecretResponse {
|
|
string ciphertext = 1;
|
|
}
|
|
|
|
message CryptoDecryptSecretRequest {
|
|
string ciphertext = 1;
|
|
}
|
|
|
|
message CryptoDecryptSecretResponse {
|
|
string plaintext = 1;
|
|
}
|
|
|
|
// --- menus.* (menus.Menus) ---
|
|
|
|
message MenusGetMenuByNameRequest {
|
|
string name = 1;
|
|
}
|
|
|
|
message MenusGetMenuByNameResponse {
|
|
Menu menu = 1;
|
|
}
|
|
|
|
// Menu mirrors menus.Menu.
|
|
message Menu {
|
|
string id = 1; // UUID
|
|
string name = 2;
|
|
}
|
|
|
|
message MenusGetMenuItemsRequest {
|
|
string menu_id = 1; // UUID
|
|
}
|
|
|
|
message MenusGetMenuItemsResponse {
|
|
repeated MenuItem items = 1;
|
|
}
|
|
|
|
// MenuItem mirrors menus.MenuItem.
|
|
message MenuItem {
|
|
string id = 1; // UUID
|
|
string menu_id = 2; // UUID
|
|
string label = 3;
|
|
string url = 4;
|
|
string page_slug = 5;
|
|
optional string parent_id = 6; // UUID; unset mirrors nil *uuid.UUID
|
|
int32 sort_order = 7;
|
|
bool open_in_new_tab = 8;
|
|
string css_class = 9;
|
|
string item_type = 10;
|
|
string icon = 11;
|
|
}
|
|
|
|
// --- datasources.* (datasources.Datasources) ---
|
|
|
|
message DatasourcesResolveBucketRequest {
|
|
string bucket_id = 1; // UUID
|
|
}
|
|
|
|
message DatasourcesResolveBucketResponse {
|
|
DatasourceResult result = 1;
|
|
}
|
|
|
|
message DatasourcesResolveBucketByKeyRequest {
|
|
string bucket_key = 1;
|
|
}
|
|
|
|
message DatasourcesResolveBucketByKeyResponse {
|
|
DatasourceResult result = 1;
|
|
}
|
|
|
|
// DatasourceResult mirrors datasources.Result ([]any / map[string]any as
|
|
// JSON bytes).
|
|
message DatasourceResult {
|
|
// JSON array of items.
|
|
bytes items_json = 1;
|
|
int32 total = 2;
|
|
// JSON object; empty when absent.
|
|
bytes meta_json = 3;
|
|
}
|
|
|
|
// --- users.* (auth.PublicUsers) ---
|
|
|
|
message UsersGetByUsernameRequest {
|
|
string username = 1;
|
|
}
|
|
|
|
message UsersGetByUsernameResponse {
|
|
PublicUserProfile user = 1;
|
|
}
|
|
|
|
message UsersGetByIdRequest {
|
|
string id = 1; // UUID
|
|
}
|
|
|
|
message UsersGetByIdResponse {
|
|
PublicUserProfile user = 1;
|
|
}
|
|
|
|
// PublicUserProfile mirrors auth.PublicUserProfile.
|
|
message PublicUserProfile {
|
|
string id = 1; // UUID
|
|
string email = 2;
|
|
string username = 3;
|
|
string display_name = 4;
|
|
string avatar_url = 5;
|
|
string bio = 6;
|
|
bool email_verified = 7;
|
|
string role = 8;
|
|
}
|
|
|
|
// --- subscriptions.* (subscriptions.Subscriptions) ---
|
|
|
|
message SubscriptionsGetUserTierLevelRequest {
|
|
string user_id = 1; // UUID
|
|
}
|
|
|
|
message SubscriptionsGetUserTierLevelResponse {
|
|
TierLevel tier_level = 1;
|
|
}
|
|
|
|
// TierLevel mirrors subscriptions.TierLevel.
|
|
message TierLevel {
|
|
int32 level = 1;
|
|
// JSON feature payload.
|
|
bytes features = 2;
|
|
}
|
|
|
|
message SubscriptionsGetTierBySlugRequest {
|
|
string slug = 1;
|
|
}
|
|
|
|
message SubscriptionsGetTierBySlugResponse {
|
|
Tier tier = 1;
|
|
}
|
|
|
|
// Tier mirrors subscriptions.Tier.
|
|
message Tier {
|
|
string id = 1; // UUID
|
|
string name = 2;
|
|
string slug = 3;
|
|
int32 level = 4;
|
|
string description = 5;
|
|
// JSON feature payload.
|
|
bytes features = 6;
|
|
bool is_default = 7;
|
|
int32 position = 8;
|
|
}
|
|
|
|
message SubscriptionsListTiersRequest {}
|
|
|
|
message SubscriptionsListTiersResponse {
|
|
repeated Tier tiers = 1;
|
|
}
|
|
|
|
message SubscriptionsListActivePlansRequest {
|
|
string tier_id = 1; // UUID
|
|
}
|
|
|
|
message SubscriptionsListActivePlansResponse {
|
|
repeated Plan plans = 1;
|
|
}
|
|
|
|
// Plan mirrors subscriptions.Plan.
|
|
message Plan {
|
|
string id = 1; // UUID
|
|
string tier_id = 2; // UUID
|
|
string billing_interval = 3;
|
|
int32 amount = 4;
|
|
string currency = 5;
|
|
bool is_active = 6;
|
|
google.protobuf.Timestamp created_at = 7;
|
|
}
|
|
|
|
// --- media.deposit (plugin.Media) ---
|
|
|
|
// MediaDepositRequest mirrors plugin.MediaDeposit.
|
|
message MediaDepositRequest {
|
|
// Optional deterministic media row ID (UUID); empty = host generates one.
|
|
string id = 1;
|
|
string filename = 2;
|
|
bytes data = 3;
|
|
string alt_text = 4;
|
|
string folder = 5;
|
|
string source = 6;
|
|
}
|
|
|
|
// MediaDepositResponse mirrors plugin.MediaResult.
|
|
message MediaDepositResponse {
|
|
string id = 1; // UUID
|
|
// Ready-to-use reference ("media:<uuid>").
|
|
string ref = 2;
|
|
bool created = 3;
|
|
}
|
|
|
|
// --- email.send (plugin.EmailSender) ---
|
|
|
|
message EmailSendRequest {
|
|
string to = 1;
|
|
string subject = 2;
|
|
string body = 3;
|
|
}
|
|
|
|
message EmailSendResponse {}
|
|
|
|
// --- ai.text_call (CoreServices.AITextCall) ---
|
|
|
|
message AiTextCallRequest {
|
|
string task_key = 1;
|
|
string system_prompt = 2;
|
|
string user_message = 3;
|
|
}
|
|
|
|
message AiTextCallResponse {
|
|
string text = 1;
|
|
}
|
|
|
|
// --- ai.tools.* (ai.ToolRegistry) ---
|
|
|
|
// AiToolRegisterRequest mirrors ai.ToolDefinition (minus Handler, which
|
|
// stays guest-side; execution direction is a runtime-WO concern).
|
|
message AiToolRegisterRequest {
|
|
string slug = 1;
|
|
string name = 2;
|
|
string description = 3;
|
|
// JSON encoding of the parameter schema map.
|
|
bytes parameter_schema_json = 4;
|
|
}
|
|
|
|
message AiToolRegisterResponse {}
|
|
|
|
// --- bridge.* (plugin.PluginBridge) ---
|
|
//
|
|
// The bridge shares in-process Go values today; across sandboxes only the
|
|
// registration/lookup surface serializes. Typed cross-plugin calls need a
|
|
// runtime design (see core/docs/wasm-abi.md, open items).
|
|
|
|
message BridgeRegisterServiceRequest {
|
|
string plugin_name = 1;
|
|
string service_name = 2;
|
|
}
|
|
|
|
message BridgeRegisterServiceResponse {}
|
|
|
|
message BridgeGetServiceRequest {
|
|
string plugin_name = 1;
|
|
string service_name = 2;
|
|
}
|
|
|
|
message BridgeGetServiceResponse {
|
|
bool available = 1;
|
|
}
|
|
|
|
// --- jobs.submit (plugin.JobRunner) ---
|
|
|
|
message JobsSubmitRequest {
|
|
string job_type = 1;
|
|
// JSON job configuration.
|
|
bytes config_json = 2;
|
|
}
|
|
|
|
message JobsSubmitResponse {}
|
|
|
|
// --- embeddings.* (plugin.EmbeddingService) ---
|
|
|
|
message EmbeddingsGenerateEmbeddingRequest {
|
|
string text = 1;
|
|
}
|
|
|
|
message EmbeddingsGenerateEmbeddingResponse {
|
|
repeated float embedding = 1;
|
|
}
|
|
|
|
message EmbeddingsEmbedContentRequest {
|
|
string source_type = 1;
|
|
string source_id = 2; // UUID
|
|
string text = 3;
|
|
}
|
|
|
|
message EmbeddingsEmbedContentResponse {
|
|
bool embedded = 1;
|
|
}
|
|
|
|
message EmbeddingsIsAvailableRequest {}
|
|
|
|
message EmbeddingsIsAvailableResponse {
|
|
bool available = 1;
|
|
}
|
|
|
|
// --- rag.* (plugin.RAGService) ---
|
|
|
|
message RagQueryRequest {
|
|
string query = 1;
|
|
int32 limit = 2;
|
|
}
|
|
|
|
message RagQueryResponse {
|
|
repeated RagResult results = 1;
|
|
}
|
|
|
|
// RagResult mirrors plugin.RAGResult.
|
|
message RagResult {
|
|
string content = 1;
|
|
double score = 2;
|
|
map<string, string> metadata = 3;
|
|
}
|
|
|
|
message RagOnContentChangedRequest {
|
|
string content_type = 1;
|
|
string content_id = 2; // UUID
|
|
}
|
|
|
|
message RagOnContentChangedResponse {}
|
|
|
|
// --- reviews.* (plugin.ReviewSubmitter) ---
|
|
|
|
// ReviewsSubmitReviewRequest mirrors plugin.SubmitReviewParams.
|
|
message ReviewsSubmitReviewRequest {
|
|
string table_id = 1; // UUID
|
|
string row_id = 2; // UUID
|
|
int32 overall_rating = 3;
|
|
string review_text = 4;
|
|
// JSON encoding of the per-criterion ratings map.
|
|
bytes ratings_json = 5;
|
|
repeated string photos = 6;
|
|
}
|
|
|
|
message ReviewsSubmitReviewResponse {
|
|
string review_id = 1;
|
|
}
|
|
|
|
// --- badges.refresh (plugin.BadgeRefresher) ---
|
|
|
|
message BadgesRefreshBadgesRequest {
|
|
string table_id = 1; // UUID
|
|
string row_id = 2; // UUID
|
|
}
|
|
|
|
message BadgesRefreshBadgesResponse {}
|