[{"data":1,"prerenderedAt":696},["ShallowReactive",2],{"search-en":3,"content-en-developer\u002Fplugin-platform":4,"surround-en-\u002Fdeveloper\u002Fplugin-platform":689},[],{"id":5,"title":6,"body":7,"description":682,"extension":683,"meta":684,"navigation":282,"path":685,"seo":686,"stem":687,"__hash__":688},"content_en\u002F3.developer\u002F40.plugin-platform.md","Plugin Platform",{"type":8,"value":9,"toc":675},"minimark",[10,24,27,62,76,81,172,175,234,241,245,373,486,490,524,534,538,618,622,668,671],[11,12,13,14,18,19,23],"p",{},"Owlat's variation points — send transports, agent steps, draft strategies, autonomy gates, automations, webhook events, import providers, crons, navigation, settings — are all reachable from ",[15,16,17],"strong",{},"plugins",". A plugin is an npm package that exports one validated manifest and, optionally, static modules behind the contracts in ",[20,21,22],"code",{},"@owlat\u002Fplugin-kit",".",[11,25,26],{},"The platform has three deliberate properties:",[28,29,30,41,56],"ol",{},[31,32,33,36,37,40],"li",{},[15,34,35],{},"One manifest."," Everything the host needs to compose, permission, flag, budget, and render a plugin is data in ",[20,38,39],{},"definePlugin(...)",". Validation never invokes plugin code.",[31,42,43,46,47,51,52,55],{},[15,44,45],{},"Declaration is not authority."," A manifest ",[48,49,50],"em",{},"requests"," capabilities. An operator ",[48,53,54],{},"grants"," them. Both are rechecked at the moment of every hosted operation.",[31,57,58,61],{},[15,59,60],{},"No arbitrary runtime code."," Nothing loads JavaScript into Convex or the browser at runtime. Bundled code is composed at build time; untrusted code runs out of process (a connected app) or behind an OS boundary (a sandboxed worker job).",[63,64,67],"callout",{"title":65,"type":66},"Where the contracts live","info",[11,68,69,71,72,75],{},[20,70,22],{}," is the public compatibility boundary and the only package a plugin author imports. Its ",[15,73,74],{},"major version"," is the compatibility line — individual contribution interfaces do not carry their own versions. Pre-1.0 it tracks the Owlat repository version and contracts may break with the application.",[77,78,80],"h2",{"id":79},"the-three-tiers","The three tiers",[82,83,84,103],"table",{},[85,86,87],"thead",{},[88,89,90,94,97,100],"tr",{},[91,92,93],"th",{},"Tier",[91,95,96],{},"What it is",[91,98,99],{},"Installation",[91,101,102],{},"Trust",[104,105,106,131,152],"tbody",{},[88,107,108,114,121,124],{},[109,110,111],"td",{},[15,112,113],{},"1 — Bundled plugin",[109,115,116,117,120],{},"An npm package listed in ",[20,118,119],{},"plugins.config.ts",", composed into the Convex and Nuxt builds",[109,122,123],{},"Edit config → codegen → rebuild → deploy",[109,125,126,127,130],{},"Same trust as the operator's own deployed code. Capabilities and flags ",[48,128,129],{},"reduce"," authority; they are not a sandbox",[88,132,133,138,146,149],{},[109,134,135],{},[15,136,137],{},"2 — Connected app",[109,139,140,141],{},"An external HTTPS service with plugin-bound API keys and webhooks, plus a specified signed synchronous hook protocol whose runtime adapter is not shipped — see ",[142,143,145],"a",{"href":144},"\u002Fdeveloper\u002Fplugin-connected-apps","Connected Apps",[109,147,148],{},"Register in the dashboard; instant, no rebuild",[109,150,151],{},"Untrusted network peer. Every response is strictly validated, scrubbed, clamped, and bounded",[88,153,154,159,166,169],{},[109,155,156],{},[15,157,158],{},"3 — Sandboxed worker job",[109,160,161,162,165],{},"A specified worker protocol behind the ",[20,163,164],{},"code-worker"," OS boundary; no host enqueue adapter is shipped",[109,167,168],{},"Reserved",[109,170,171],{},"Untrusted process. No ambient credentials, hard CPU\u002Fmemory\u002Ftime ceilings, host-controlled command",[11,173,174],{},"One maintained reference plugin covers each tier:",[82,176,177,189],{},[85,178,179],{},[88,180,181,183,186],{},[91,182,93],{},[91,184,185],{},"Reference",[91,187,188],{},"What it contributes",[104,190,191,204,217],{},[88,192,193,196,201],{},[109,194,195],{},"1",[109,197,198],{},[20,199,200],{},"examples\u002Fplugins\u002Fescalation-guard",[109,202,203],{},"An agent step, a draft strategy, all three automation registries, a webhook event, and nav\u002Fsettings entries — all in-process",[88,205,206,209,214],{},[109,207,208],{},"2",[109,210,211],{},[20,212,213],{},"examples\u002Fplugins\u002Fslack-approvals",[109,215,216],{},"A restrict-only hold gate served out of process over the signed hook protocol; no in-process contributions at all",[88,218,219,222,227],{},[109,220,221],{},"3",[109,223,224],{},[20,225,226],{},"examples\u002Fplugins\u002Fdeliverability-lab",[109,228,229,230,233],{},"A seed-list job on the sandboxed queue, plus a Tier-1 send gate, cron and UI entries and ",[20,231,232],{},"llm:invoke"," for host-mediated generation",[11,235,236,237,240],{},"One plugin may use several tiers at once, as the Deliverability Lab does. ",[20,238,239],{},"examples\u002Fconformance"," drives all three through the real host, codegen and CLI on every test run.",[77,242,244],{"id":243},"anatomy-of-a-plugin","Anatomy of a plugin",[246,247,252],"pre",{"className":248,"code":249,"language":250,"meta":251,"style":251},"language-ts shiki shiki-themes github-light github-dark-dimmed","import { definePlugin } from '@owlat\u002Fplugin-kit';\n\nexport const helloPlugin = definePlugin({\n    id: 'hello-owlat',\n    version: '0.1.0',\n    capabilities: ['plugin-storage:read', 'plugin-storage:write'],\n    \u002F\u002F Storage capabilities REQUIRE a flag: access must be revocable at runtime.\n    flag: { default: false },\n});\n","ts","",[20,253,254,277,284,307,319,330,348,355,367],{"__ignoreMap":251},[255,256,259,263,267,270,274],"span",{"class":257,"line":258},"line",1,[255,260,262],{"class":261},"s7YZ4","import",[255,264,266],{"class":265},"sYgZi"," { definePlugin } ",[255,268,269],{"class":261},"from",[255,271,273],{"class":272},"s-HuK"," '@owlat\u002Fplugin-kit'",[255,275,276],{"class":265},";\n",[255,278,280],{"class":257,"line":279},2,[255,281,283],{"emptyLinePlaceholder":282},true,"\n",[255,285,287,290,293,297,300,304],{"class":257,"line":286},3,[255,288,289],{"class":261},"export",[255,291,292],{"class":261}," const",[255,294,296],{"class":295},"sviXB"," helloPlugin",[255,298,299],{"class":261}," =",[255,301,303],{"class":302},"sPO5f"," definePlugin",[255,305,306],{"class":265},"({\n",[255,308,310,313,316],{"class":257,"line":309},4,[255,311,312],{"class":265},"    id: ",[255,314,315],{"class":272},"'hello-owlat'",[255,317,318],{"class":265},",\n",[255,320,322,325,328],{"class":257,"line":321},5,[255,323,324],{"class":265},"    version: ",[255,326,327],{"class":272},"'0.1.0'",[255,329,318],{"class":265},[255,331,333,336,339,342,345],{"class":257,"line":332},6,[255,334,335],{"class":265},"    capabilities: [",[255,337,338],{"class":272},"'plugin-storage:read'",[255,340,341],{"class":265},", ",[255,343,344],{"class":272},"'plugin-storage:write'",[255,346,347],{"class":265},"],\n",[255,349,351],{"class":257,"line":350},7,[255,352,354],{"class":353},"sDN9O","    \u002F\u002F Storage capabilities REQUIRE a flag: access must be revocable at runtime.\n",[255,356,358,361,364],{"class":257,"line":357},8,[255,359,360],{"class":265},"    flag: { default: ",[255,362,363],{"class":295},"false",[255,365,366],{"class":265}," },\n",[255,368,370],{"class":257,"line":369},9,[255,371,372],{"class":265},"});\n",[82,374,375,385],{},[85,376,377],{},[88,378,379,382],{},[91,380,381],{},"Field",[91,383,384],{},"Meaning",[104,386,387,397,407,421,431,446,462,476],{},[88,388,389,394],{},[109,390,391],{},[20,392,393],{},"id",[109,395,396],{},"Lowercase kebab-case, ≤ 64 characters. Namespaces flags, storage, spend, audit attribution, and every contributed kind",[88,398,399,404],{},[109,400,401],{},[20,402,403],{},"version",[109,405,406],{},"Semantic version",[88,408,409,414],{},[109,410,411],{},[20,412,413],{},"capabilities",[109,415,416,417,420],{},"Requested host operations, ",[20,418,419],{},"domain:action",", exact match, at most 64",[88,422,423,428],{},[109,424,425],{},[20,426,427],{},"contributes",[109,429,430],{},"Data-only descriptors per contribution bucket, at most 256 per bucket",[88,432,433,438],{},[109,434,435],{},[20,436,437],{},"flag",[109,439,440,443,444],{},[20,441,442],{},"{ default, requiredEnvVars? }",". Required whenever the plugin contributes anything, requests plugin storage, or requests ",[20,445,232],{},[88,447,448,453],{},[109,449,450],{},[20,451,452],{},"llmBudget",[109,454,455,458,459,461],{},[20,456,457],{},"{ dailyUsd }",". Required with ",[20,460,232],{},"; a hard per-UTC-day ceiling",[88,463,464,469],{},[109,465,466],{},[20,467,468],{},"component",[109,470,471,472,475],{},"One condition-independent Convex component export, installed under an injective ",[20,473,474],{},"plugin_\u003Cid>"," namespace",[88,477,478,483],{},[109,479,480],{},[20,481,482],{},"settingsSchema",[109,484,485],{},"Declarative operator form; the host renders, validates, and redacts it",[77,487,489],{"id":488},"how-a-bundled-plugin-reaches-production","How a bundled plugin reaches production",[28,491,492,497,511,518],{},[31,493,494,495,23],{},"The package name is added to the checked-in root ",[20,496,119],{},[31,498,499,502,503,506,507,510],{},[20,500,501],{},"bun run plugins:codegen"," (or ",[20,504,505],{},"owlat-plugins codegen",") verifies the installed package's identity, lockfile integrity and realpath containment, imports ",[15,508,509],{},"only"," its manifest entry, validates one immutable snapshot, and writes the checked-in Convex and Nuxt composition files ordered by manifest id.",[31,512,513,514,517],{},"Builds and CI run ",[20,515,516],{},"bun run plugins:check",", so stale or invalid composition fails the build closed.",[31,519,520,521,523],{},"The deployment rebuilds. The plugin's feature flag starts at its declared default — normally ",[20,522,363],{}," — so nothing runs until an operator enables it and grants capabilities.",[63,525,528],{"title":526,"type":527},"A manifest is not a sandbox","warning",[11,529,530,531,533],{},"Bundled plugin code runs in Owlat's own processes. Capability declarations, grants, flags and host-mediated services ",[48,532,129],{}," what a bundled plugin can reach and make its actions attributable — they do not contain hostile code. Anything you do not fully trust belongs in a connected app (Tier 2) or a sandboxed job (Tier 3).",[77,535,537],{"id":536},"package-map","Package map",[82,539,540,550],{},[85,541,542],{},[88,543,544,547],{},[91,545,546],{},"Package",[91,548,549],{},"Role",[104,551,552,566,576,586,599,609],{},[88,553,554,559],{},[109,555,556],{},[20,557,558],{},"packages\u002Fplugin-kit",[109,560,561,562,565],{},"Public contracts: ",[20,563,564],{},"definePlugin",", capability constants, every contribution type. The only package a plugin imports",[88,567,568,573],{},[109,569,570],{},[20,571,572],{},"packages\u002Fplugin-host",[109,574,575],{},"Private, runtime-neutral enforcement kernel shared by Convex and Nuxt: capability checks, flags, ordering, gate results, untrusted-text policy",[88,577,578,583],{},[109,579,580],{},[20,581,582],{},"packages\u002Fplugin-codegen",[109,584,585],{},"Build tool that owns the single composition point and the generated catalogs\u002Fregistries",[88,587,588,593],{},[109,589,590],{},[20,591,592],{},"packages\u002Fplugin-cli",[109,594,595,598],{},[20,596,597],{},"owlat-plugins"," — create, add, remove, codegen, dev",[88,600,601,606],{},[109,602,603],{},[20,604,605],{},"examples\u002Fplugins\u002F*",[109,607,608],{},"Maintained reference plugins exercising the real contribution points",[88,610,611,615],{},[109,612,613],{},[20,614,239],{},[109,616,617],{},"The conformance gallery: lifecycle (install\u002Fadd\u002Fremove\u002Fdisable\u002Fupgrade) and full-pipeline replay suites over those references, plus the plugin-provider parity proof — a fixture ESP bundle driven through the shipped routing, dispatch, measurement, return-path and credential-form modules",[77,619,621],{"id":620},"read-next","Read next",[623,624,625,632,639,646,653,662],"ul",{},[31,626,627,631],{},[142,628,630],{"href":629},"\u002Fdeveloper\u002Fplugin-authoring","Building a Plugin"," — scaffold, manifest, modules, tests, ship",[31,633,634,638],{},[142,635,637],{"href":636},"\u002Fdeveloper\u002Fplugin-contributions","Contribution Reference"," — every bucket, its capability, and its host semantics",[31,640,641,645],{},[142,642,644],{"href":643},"\u002Fdeveloper\u002Fplugin-capabilities","Capabilities & Trust"," — the permission model and the invariants a plugin can never weaken",[31,647,648,652],{},[142,649,651],{"href":650},"\u002Fdeveloper\u002Fplugin-operations","Installing & Operating"," — the operator lifecycle",[31,654,655,657,658],{},[142,656,145],{"href":144}," and ",[142,659,661],{"href":660},"\u002Fdeveloper\u002Fplugin-sandboxed-jobs","Sandboxed Jobs",[31,663,664],{},[142,665,667],{"href":666},"\u002Fdeveloper\u002Fplugin-troubleshooting","Troubleshooting",[11,669,670],{},"Architecture decisions: ADR-0049 (platform contract), ADR-0050 (draft strategies), ADR-0051 (ordered autonomy gates), ADR-0052 (connected apps and signed hooks), ADR-0053 (sandboxed plugin jobs).",[672,673,674],"style",{},"html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html pre.shiki code .sPO5f, html code.shiki .sPO5f{--shiki-default:#6F42C1;--shiki-dark:#DCBDFB}html pre.shiki code .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":251,"searchDepth":279,"depth":279,"links":676},[677,678,679,680,681],{"id":79,"depth":279,"text":80},{"id":243,"depth":279,"text":244},{"id":488,"depth":279,"text":489},{"id":536,"depth":279,"text":537},{"id":620,"depth":279,"text":621},"How Owlat is extended: one validated manifest, a capability model, and three execution tiers.","md",{},"\u002Fdeveloper\u002Fplugin-platform",{"title":6,"description":682},"3.developer\u002F40.plugin-platform","VaJE6jQErHg84z1uQbOb-5hfDok19M8zZpHg5s4Vj0c",[690,694],{"title":691,"path":692,"stem":693,"children":-1},"Convex Backend","\u002Fdeveloper\u002Fconvex","3.developer\u002F4.convex",{"title":630,"path":629,"stem":695,"children":-1},"3.developer\u002F41.plugin-authoring",1786915092233]