Host-mediated outbound HTTP for wasm plugins: guest http.RoundTripper over the http.request capability; the host performs the request under policy. New egress package defines the host-pattern grammar (exact or multi-label wildcard, public-suffix rejected), matching, resource bounds, and the ErrDenied sentinel (ABI_ERROR_CODE_EGRESS_DENIED). plugin.mod gains first-class AllowedHosts + MaxResponseMB; manifest gains allowed_hosts=34, max_response_mb=35. CoreServices.OutboundHTTP carries the transport. Golden roundtrip + denial-mapping tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
889 lines
22 KiB
Protocol Buffer
889 lines
22 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/http.proto";
|
|
import "v1/invoke.proto";
|
|
|
|
option go_package = "git.dev.alexdunmow.com/block/pluginsdk/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;
|
|
}
|
|
|
|
// ContentPublishedBlockConfigsRequest asks the host for the stored content
|
|
// (config JSON) of every block with the given key inside a CURRENTLY
|
|
// published page snapshot. Lets a guest resolve server-authoritative
|
|
// per-block settings (e.g. a captcha requirement) it cannot query itself.
|
|
message ContentPublishedBlockConfigsRequest {
|
|
string block_key = 1;
|
|
}
|
|
|
|
message ContentPublishedBlockConfigsResponse {
|
|
repeated bytes configs = 1; // each entry: one block's content as JSON
|
|
}
|
|
|
|
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
|
|
string body = 7; // rendered HTML; set by get_post / list_posts(include_body)
|
|
string author_name = 8;
|
|
string author_slug = 9;
|
|
google.protobuf.Timestamp published_at = 10;
|
|
}
|
|
|
|
message ContentListPostsRequest {
|
|
int32 limit = 1;
|
|
int32 offset = 2;
|
|
string category = 3; // optional category slug filter ("" = all)
|
|
bool published_only = 4;
|
|
bool include_body = 5;
|
|
bool include_excerpt = 6;
|
|
}
|
|
|
|
message ContentListPostsResponse {
|
|
repeated PostInfo posts = 1;
|
|
}
|
|
|
|
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 {}
|
|
|
|
// --- content.* writes (content.Author, WO-WZ-019) ---
|
|
//
|
|
// Imperative page/post authoring. The idempotent seed-time counterparts live
|
|
// in the provisioner.* family below; these are for runtime authoring and for
|
|
// the WO-WZ-020 codeless seed runner.
|
|
|
|
message ContentCreatePageRequest {
|
|
string slug = 1;
|
|
string parent_slug = 2; // "" = root
|
|
string title = 3;
|
|
string template_key = 4;
|
|
string master_page_key = 5; // "" = none
|
|
}
|
|
|
|
message ContentCreatePageResponse {
|
|
string page_id = 1; // UUID
|
|
// False when a page with this slug already existed (the existing ID is
|
|
// returned and nothing is modified).
|
|
bool created = 2;
|
|
}
|
|
|
|
// PageBlock is one block instance placed on a page (mirrors
|
|
// plugin.PageBlockConfig / MasterPageBlock).
|
|
message PageBlock {
|
|
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;
|
|
}
|
|
|
|
message ContentSetPageBlocksRequest {
|
|
string page_id = 1; // UUID
|
|
// Full replacement set for the page's draft document blocks.
|
|
repeated PageBlock blocks = 2;
|
|
}
|
|
|
|
message ContentSetPageBlocksResponse {}
|
|
|
|
message ContentPublishPageRequest {
|
|
string page_id = 1; // UUID
|
|
}
|
|
|
|
message ContentPublishPageResponse {}
|
|
|
|
// ContentSetPageSeoRequest mirrors the CMS UpdatePageSEO surface. Unset
|
|
// optionals leave the existing value untouched.
|
|
message ContentSetPageSeoRequest {
|
|
string page_id = 1; // UUID
|
|
optional string meta_title = 2;
|
|
optional string meta_description = 3;
|
|
optional string og_title = 4;
|
|
optional string og_description = 5;
|
|
optional string og_image = 6;
|
|
optional string focus_keyphrase = 7;
|
|
optional string canonical_url = 8;
|
|
optional string robots_directive = 9;
|
|
optional string twitter_title = 10;
|
|
optional string twitter_description = 11;
|
|
optional string twitter_image = 12;
|
|
}
|
|
|
|
message ContentSetPageSeoResponse {}
|
|
|
|
// ContentUpsertPostRequest creates or updates a blog post by slug (posts live
|
|
// in the dedicated blog_posts table).
|
|
message ContentUpsertPostRequest {
|
|
string slug = 1;
|
|
string title = 2;
|
|
// JSON encoding of the BlockNote document.
|
|
bytes document_json = 3;
|
|
optional string excerpt = 4;
|
|
optional string author_profile_id = 5; // UUID
|
|
optional string featured_image_id = 6; // UUID (e.g. from media.deposit)
|
|
// Publish immediately after the upsert.
|
|
bool publish = 7;
|
|
bool is_featured = 8;
|
|
}
|
|
|
|
message ContentUpsertPostResponse {
|
|
string post_id = 1; // UUID
|
|
bool created = 2;
|
|
}
|
|
|
|
// --- provisioner.* (plugin.Provisioner, WO-WZ-019) ---
|
|
//
|
|
// Idempotent seed/ensure operations, 1:1 with the plugin.Provisioner Go
|
|
// interface. In the wasm world these are LOAD-TIME capabilities: plugins call
|
|
// deps.Provisioner from their Load hook (RegisterWithProvisioner cannot cross
|
|
// the DESCRIBE boundary — host functions are stubbed there). All methods
|
|
// check-then-create; re-running them is safe.
|
|
//
|
|
// EmbedConfig.RenderFunc deliberately does not cross: an ABI-provisioned
|
|
// embed is template-rendered host-side. A compute-rendered embed needs a
|
|
// block + hook instead.
|
|
|
|
message ProvisionerEnsureDataTableRequest {
|
|
string key = 1;
|
|
string name = 2;
|
|
string description = 3;
|
|
// JSON schema of the table (json.RawMessage in DataTableConfig).
|
|
bytes schema_json = 4;
|
|
string primary_key = 5;
|
|
}
|
|
|
|
message ProvisionerEnsureDataTableResponse {}
|
|
|
|
message ProvisionerMergeSiteSettingsRequest {
|
|
// JSON encoding of the defaults map; existing keys win.
|
|
bytes defaults_json = 1;
|
|
}
|
|
|
|
message ProvisionerMergeSiteSettingsResponse {}
|
|
|
|
message ProvisionerEnsureSettingRequest {
|
|
string key = 1;
|
|
// JSON encoding of the default value.
|
|
bytes default_value_json = 2;
|
|
}
|
|
|
|
message ProvisionerEnsureSettingResponse {}
|
|
|
|
// PageSeed mirrors plugin.PageConfig.
|
|
message PageSeed {
|
|
string slug = 1;
|
|
string parent_slug = 2;
|
|
string title = 3;
|
|
string template_key = 4;
|
|
repeated PageBlock blocks = 5;
|
|
string detail_source_type = 6;
|
|
string detail_source_key = 7;
|
|
string detail_slug_field = 8;
|
|
bool reconcile_blocks = 9;
|
|
bool reconcile_template = 10;
|
|
}
|
|
|
|
message ProvisionerEnsurePageRequest {
|
|
PageSeed page = 1;
|
|
}
|
|
|
|
message ProvisionerEnsurePageResponse {}
|
|
|
|
message ProvisionerOverrideSiteSettingsRequest {
|
|
// JSON encoding of the overrides map; plugin values win.
|
|
bytes overrides_json = 1;
|
|
}
|
|
|
|
message ProvisionerOverrideSiteSettingsResponse {}
|
|
|
|
// ProvisionerEnsureMenuItemRequest mirrors plugin.MenuItemConfig under a
|
|
// named menu.
|
|
message ProvisionerEnsureMenuItemRequest {
|
|
string menu_name = 1;
|
|
string label = 2;
|
|
string url = 3;
|
|
string page_slug = 4;
|
|
int32 sort_order = 5;
|
|
}
|
|
|
|
message ProvisionerEnsureMenuItemResponse {}
|
|
|
|
// ProvisionerRegisterEmbeddingConfigRequest mirrors plugin.EmbeddingConfigDef.
|
|
message ProvisionerRegisterEmbeddingConfigRequest {
|
|
string table_key = 1;
|
|
string text_template = 2;
|
|
bool enabled = 3;
|
|
}
|
|
|
|
message ProvisionerRegisterEmbeddingConfigResponse {}
|
|
|
|
// ProvisionerEnsureEmbedRequest mirrors plugin.EmbedConfig minus RenderFunc
|
|
// (function values cannot cross; the Template renders host-side).
|
|
message ProvisionerEnsureEmbedRequest {
|
|
string key = 1;
|
|
string title = 2;
|
|
string description = 3;
|
|
string icon = 4;
|
|
string label_field = 5;
|
|
string template = 6;
|
|
string data_source_type = 7; // EmbedDataSource.Type
|
|
string data_source_table_key = 8; // EmbedDataSource.TableKey
|
|
}
|
|
|
|
message ProvisionerEnsureEmbedResponse {}
|
|
|
|
// ProvisionerEnsureJobScheduleRequest mirrors plugin.JobScheduleConfig.
|
|
message ProvisionerEnsureJobScheduleRequest {
|
|
string job_type = 1;
|
|
string cron_expression = 2;
|
|
// JSON job configuration.
|
|
bytes config_json = 3;
|
|
}
|
|
|
|
message ProvisionerEnsureJobScheduleResponse {}
|
|
|
|
message ProvisionerUpdateDataTableRowFieldRequest {
|
|
string row_id = 1; // UUID
|
|
string field_key = 2;
|
|
// JSON encoding of the value.
|
|
bytes value_json = 3;
|
|
}
|
|
|
|
message ProvisionerUpdateDataTableRowFieldResponse {}
|
|
|
|
message ProvisionerDisableOrphanedJobSchedulesRequest {
|
|
repeated string registered_types = 1;
|
|
}
|
|
|
|
message ProvisionerDisableOrphanedJobSchedulesResponse {}
|
|
|
|
message ProvisionerEnsurePluginRequest {
|
|
string name = 1;
|
|
}
|
|
|
|
message ProvisionerEnsurePluginResponse {}
|
|
|
|
// ProvisionerEnsureCustomColorRequest mirrors plugin.CustomColorConfig.
|
|
message ProvisionerEnsureCustomColorRequest {
|
|
string name = 1;
|
|
string light_value = 2;
|
|
string dark_value = 3;
|
|
string source = 4;
|
|
}
|
|
|
|
message ProvisionerEnsureCustomColorResponse {}
|
|
|
|
// ProvisionerEnsureMediaRequest mirrors plugin.MediaDeposit with a REQUIRED
|
|
// deterministic id (the template-referable key). Idempotent: an existing id
|
|
// is a no-op; changed bytes warn rather than overwrite.
|
|
message ProvisionerEnsureMediaRequest {
|
|
string id = 1; // UUID, required
|
|
string filename = 2;
|
|
bytes data = 3;
|
|
string alt_text = 4;
|
|
string folder = 5;
|
|
string source = 6;
|
|
}
|
|
|
|
message ProvisionerEnsureMediaResponse {}
|
|
|
|
// --- settings.update_plugin_settings (settings.Updater, WO-WZ-019) ---
|
|
|
|
// SettingsUpdatePluginSettingsRequest replaces the calling plugin's own
|
|
// settings map. The host binds the target plugin from the caller's identity;
|
|
// plugin_name is advisory and MUST match it (PERMISSION_DENIED otherwise).
|
|
message SettingsUpdatePluginSettingsRequest {
|
|
string plugin_name = 1;
|
|
// JSON encoding of the full settings map.
|
|
bytes settings_json = 2;
|
|
}
|
|
|
|
message SettingsUpdatePluginSettingsResponse {}
|
|
|
|
// --- jobs.progress (plugin.JobHandlerFunc progress callback, WO-WZ-019) ---
|
|
|
|
// JobsProgressRequest reports progress for the CURRENTLY EXECUTING job. The
|
|
// host correlates it to the job via the invoking HOOK_JOB call's context, so
|
|
// it is only meaningful while a JOB hook is on the stack; outside one it is
|
|
// accepted and discarded.
|
|
message JobsProgressRequest {
|
|
int32 current = 1;
|
|
int32 total = 2;
|
|
string message = 3;
|
|
}
|
|
|
|
message JobsProgressResponse {}
|
|
|
|
// --- bridge.invoke (plugin.PluginBridge cross-plugin calls, WO-WZ-019) ---
|
|
|
|
// BridgeInvokeRequest calls a method on another plugin's registered bridge
|
|
// service. The provider answers via HOOK_BRIDGE_CALL (wasm) or an in-process
|
|
// plugin.BridgeInvokable (bundled). Payloads are opaque bytes — the two
|
|
// plugins agree on the encoding (JSON by convention).
|
|
message BridgeInvokeRequest {
|
|
string plugin_name = 1;
|
|
string service_name = 2;
|
|
string method = 3;
|
|
bytes payload = 4;
|
|
}
|
|
|
|
message BridgeInvokeResponse {
|
|
bytes payload = 1;
|
|
}
|
|
|
|
// --- http.request (egress.Client host-mediated outbound HTTP, ADR 0023) ---
|
|
|
|
// HttpRequestRequest asks the host to perform one outbound HTTPS request on
|
|
// the plugin's behalf. The host enforces the plugin's egress grant (host
|
|
// patterns + response cap), the platform denylist, the SSRF guard, and the
|
|
// redirect policy before any bytes leave — the guest never touches a socket.
|
|
// Names differ from the HANDLE_HTTP hook pair (HttpRequest/HttpResponse in
|
|
// http.proto), which is inbound traffic INTO the guest mux.
|
|
message HttpRequestRequest {
|
|
string method = 1;
|
|
// Absolute https URL (scheme required; http is refused by policy).
|
|
string url = 2;
|
|
map<string, HeaderValues> headers = 3;
|
|
bytes body = 4;
|
|
// Optional guest-side deadline. It may only LOWER the host's per-fetch
|
|
// ceiling, never raise it; zero means the host ceiling applies.
|
|
uint32 timeout_ms = 5;
|
|
}
|
|
|
|
// HttpRequestResponse is the fully buffered upstream response. Bodies larger
|
|
// than the granted cap fail the call (EGRESS_DENIED is policy; oversize is
|
|
// INTERNAL with a too-large message) — never a silent truncation.
|
|
message HttpRequestResponse {
|
|
int32 status = 1;
|
|
map<string, HeaderValues> headers = 2;
|
|
bytes body = 3;
|
|
// URL that produced the response after any (allowlist-validated)
|
|
// redirects.
|
|
string final_url = 4;
|
|
}
|