[{"data":1,"prerenderedAt":530},["ShallowReactive",2],{"search-en":3,"content-en-developer\u002Fplugin-operations":4,"surround-en-\u002Fdeveloper\u002Fplugin-operations":521},[],{"id":5,"title":6,"body":7,"description":513,"extension":514,"meta":515,"navigation":516,"path":517,"seo":518,"stem":519,"__hash__":520},"content_en\u002F3.developer\u002F44.plugin-operations.md","Installing & Operating Plugins",{"type":8,"value":9,"toc":494},"minimark",[10,19,24,27,142,150,154,157,164,185,193,200,204,211,226,229,233,237,258,262,269,302,314,318,378,381,385,459,465,476,480,490],[11,12,13,14,18],"p",{},"This is the operator's side of the platform. Plugin administration requires the ",[15,16,17],"code",{},"organization:manage"," capability — workspace owners and admins only.",[20,21,23],"h2",{"id":22},"install-a-bundled-plugin-tier-1","Install a bundled plugin (Tier 1)",[11,25,26],{},"Bundled plugins are composed at build time, so installation is a code change, not a dashboard action.",[28,29,30,35,55,59,87,98,102,117,131,135],"steps",{},[31,32,34],"h3",{"id":33},"install-the-package","Install the package",[11,36,37,38,42,43,46,47,50,51,54],{},"Add it as a ",[39,40,41],"strong",{},"direct root dependency"," (",[15,44,45],{},"dependencies"," or ",[15,48,49],{},"optionalDependencies",") from the npm registry. Codegen verifies the installed package identity, the ",[15,52,53],{},"bun.lock"," integrity entry, and realpath containment. Aliases, git\u002FURL\u002Ffile\u002Fworkspace sources, and external symlinks fail closed.",[31,56,58],{"id":57},"register-it","Register it",[60,61,66],"pre",{"className":62,"code":63,"language":64,"meta":65,"style":65},"language-sh shiki shiki-themes github-light github-dark-dimmed","bun packages\u002Fplugin-cli\u002Fsrc\u002Findex.ts add @vendor\u002Fowlat-plugin-example\n","sh","",[15,67,68],{"__ignoreMap":65},[69,70,73,77,81,84],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"sOLd2","bun",[69,78,80],{"class":79},"s-HuK"," packages\u002Fplugin-cli\u002Fsrc\u002Findex.ts",[69,82,83],{"class":79}," add",[69,85,86],{"class":79}," @vendor\u002Fowlat-plugin-example\n",[11,88,89,90,93,94,97],{},"This edits the checked-in ",[15,91,92],{},"plugins.config.ts"," and prints the capability diff the change introduces. Run it with ",[15,95,96],{},"--dry-run"," first to review the diff and the proposed file without writing.",[31,99,101],{"id":100},"regenerate-the-composition","Regenerate the composition",[60,103,105],{"className":62,"code":104,"language":64,"meta":65,"style":65},"bun run plugins:codegen\n",[15,106,107],{"__ignoreMap":65},[69,108,109,111,114],{"class":71,"line":72},[69,110,76],{"class":75},[69,112,113],{"class":79}," run",[69,115,116],{"class":79}," plugins:codegen\n",[11,118,119,120,122,123,126,127,130],{},"Commit ",[15,121,92],{}," ",[39,124,125],{},"and"," the generated composition files in the same change. CI runs ",[15,128,129],{},"bun run plugins:check",", which fails when generated output is stale or invalid.",[31,132,134],{"id":133},"rebuild-and-deploy","Rebuild and deploy",[11,136,137,138,141],{},"The plugin now exists in the deployment. Its feature flag sits at the manifest's declared default — normally ",[15,139,140],{},"false"," — and no capability is granted, so nothing runs yet.",[143,144,147],"callout",{"title":145,"type":146},"Review the capability diff before you deploy","warning",[11,148,149],{},"The diff is the security review surface for a bundled plugin: it is the complete list of host operations the package can ever request. Bundled code runs with the same trust as your own deployed code — read the diff, not just the README.",[20,151,153],{"id":152},"enable-and-grant","Enable and grant",[11,155,156],{},"Settings → Plugins lists every registered plugin with its state, capabilities, and required environment variables.",[11,158,159,160,163],{},"Enabling a bundled plugin is an ",[39,161,162],{},"explicit consent action",":",[165,166,167,175,182],"ul",{},[168,169,170,171,174],"li",{},"The deployment must have every ",[15,172,173],{},"flag.requiredEnvVars"," value present. Enabling fails with the list of missing variables otherwise.",[168,176,177,178,181],{},"The enable request must approve ",[39,179,180],{},"every"," capability the manifest declares — no more, no fewer, no duplicates. A partial or unknown approval is rejected.",[168,183,184],{},"Disabling the plugin deletes its grant record entirely.",[11,186,187,188,192],{},"That is how \"grants can only restrict the manifest\" appears in practice for Tier 1: the operator's choice is to consent to the full declared set or to leave the plugin off. Connected apps (Tier 2) are the tier where a ",[189,190,191],"em",{},"subset"," of the manifest is granted per app.",[11,194,195,196,199],{},"Both the flag state and the grant record are read fresh inside each caller's transaction, so ",[39,197,198],{},"disabling a plugin or revoking a grant takes effect on the very next operation"," — no redeploy and no cache to wait out.",[20,201,203],{"id":202},"configure","Configure",[11,205,206,207,210],{},"If the plugin declares a ",[15,208,209],{},"settingsSchema",", its detail page renders a generic form: string, number, boolean, and select fields with the declared labels, descriptions, defaults, and bounds. The host validates input server-side against the schema.",[11,212,213,214,217,218,221,222,225],{},"A ",[15,215,216],{},"secret"," field is ",[39,219,220],{},"not"," part of the form. Owlat stores no plugin credentials: the field names a ",[15,223,224],{},"PLUGIN_","-prefixed environment variable you set on the deployment, and the page shows only that variable's name and whether it is present. A save that carries a value for a secret key is rejected. \"Reset to defaults\" clears stored values back to the schema defaults.",[11,227,228],{},"If a plugin is removed from the build while settings rows remain, the index page offers \"Clear plugin settings\" for that orphaned id.",[20,230,232],{"id":231},"operate-the-tiers","Operate the tiers",[31,234,236],{"id":235},"tier-2-connected-apps","Tier 2 — connected apps",[11,238,239,240,243,244,247,248,251,252,257],{},"Registered from the dashboard; no rebuild. Each app is bound to a plugin id, requests a subset of that plugin's declared capabilities, and holds an encrypted shared secret used to sign hooks. Statuses are ",[15,241,242],{},"enabled",", ",[15,245,246],{},"disabled",", and ",[15,249,250],{},"revoked"," (terminal). See ",[253,254,256],"a",{"href":255},"\u002Fdeveloper\u002Fplugin-connected-apps","Connected Apps",".",[31,259,261],{"id":260},"tier-3-sandboxed-jobs","Tier 3 — sandboxed jobs",[11,263,264,265,268],{},"The code-worker retains the dormant plugin-job polling protocol, but no host enqueue adapter or operator job UI is shipped. The ",[15,266,267],{},"plugin-tasks"," profile is therefore not a usable plugin surface today:",[60,270,272],{"className":62,"code":271,"language":64,"meta":65,"style":65},"COMPOSE_PROFILES=plugin-tasks docker compose up -d code-worker\n",[15,273,274],{"__ignoreMap":65},[69,275,276,280,284,286,289,292,295,299],{"class":71,"line":72},[69,277,279],{"class":278},"sYgZi","COMPOSE_PROFILES",[69,281,283],{"class":282},"s7YZ4","=",[69,285,267],{"class":79},[69,287,288],{"class":75}," docker",[69,290,291],{"class":79}," compose",[69,293,294],{"class":79}," up",[69,296,298],{"class":297},"sviXB"," -d",[69,300,301],{"class":79}," code-worker\n",[11,303,304,305,308,309,313],{},"The same container actively serves the coding-agent queue under the ",[15,306,307],{},"inbox-codetasks"," profile. See ",[253,310,312],{"href":311},"\u002Fdeveloper\u002Fplugin-sandboxed-jobs","Sandboxed Jobs"," for the reserved Tier-3 contract.",[20,315,317],{"id":316},"monitor","Monitor",[319,320,321,334],"table",{},[322,323,324],"thead",{},[325,326,327,331],"tr",{},[328,329,330],"th",{},"Surface",[328,332,333],{},"What it shows",[335,336,337,354,362,370],"tbody",{},[325,338,339,343],{},[340,341,342],"td",{},"Audit log",[340,344,345,346,349,350,353],{},"Every hosted plugin operation, attributed with ",[15,347,348],{},"organizationId"," and ",[15,351,352],{},"pluginId",", recording fixed operation and outcome codes only",[325,355,356,359],{},[340,357,358],{},"Hook delivery logs",[340,360,361],{},"Per-app, per-kind signed-hook resolutions: attempted or not, which side won, the fixed fallback reason, and network duration. Retained for 30 days",[325,363,364,367],{},[340,365,366],{},"Webhook delivery logs",[340,368,369],{},"Outbound webhook deliveries, including plugin-published events",[325,371,372,375],{},[340,373,374],{},"Plugin task list",[340,376,377],{},"Recent Tier-3 jobs with status, attempts, and timing",[11,379,380],{},"None of these surfaces contain payloads, prompts, generated text, mail content, secrets, or signatures — the redaction is structural, so no read path can leak them.",[20,382,384],{"id":383},"revoke-and-remove","Revoke and remove",[319,386,387,400],{},[322,388,389],{},[325,390,391,394,397],{},[328,392,393],{},"Goal",[328,395,396],{},"Action",[328,398,399],{},"Takes effect",[335,401,402,413,424,434,445],{},[325,403,404,407,410],{},[340,405,406],{},"Stop a bundled plugin and withdraw its authority",[340,408,409],{},"Disable its feature flag — this also deletes its grant record",[340,411,412],{},"Next operation",[325,414,415,418,421],{},[340,416,417],{},"Stop a connected app now",[340,419,420],{},"Disable it",[340,422,423],{},"Next hook call or API request",[325,425,426,429,432],{},[340,427,428],{},"Permanently kill a connected app",[340,430,431],{},"Revoke it (terminal)",[340,433,423],{},[325,435,436,439,442],{},[340,437,438],{},"Revoke a plugin-bound API key",[340,440,441],{},"Revoke the key, or disable the plugin it is bound to",[340,443,444],{},"Next request — the effective scope set is re-derived per request",[325,446,447,450,456],{},[340,448,449],{},"Remove a bundled plugin",[340,451,452,455],{},[15,453,454],{},"owlat-plugins remove \u003Cpackage>",", regenerate, rebuild, deploy",[340,457,458],{},"After redeploy",[11,460,461,462,464],{},"Removing the package from ",[15,463,92],{}," also removes its Convex component registration. Plugin storage rows and settings survive the removal; use \"Clear plugin settings\" on the plugins index to purge the orphaned settings.",[143,466,469],{"title":467,"type":468},"Incident response order","danger",[11,470,471,472,475],{},"If a plugin is misbehaving, ",[39,473,474],{},"disable the flag first"," — it is instant and needs no deploy. Only then investigate, and remove the package afterwards. Do not start with a rebuild: it is the slowest lever you have.",[20,477,479],{"id":478},"zero-plugin-deployments","Zero-plugin deployments",[11,481,482,483,485,486,489],{},"The zero-plugin composition is a valid no-op deployment. ",[15,484,92],{}," with an empty ",[15,487,488],{},"bundledPluginPackages"," array generates a composition with no plugins, and every registry falls back to its built-in members with their original order, defaults, and error semantics.",[491,492,493],"style",{},"html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}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);}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}",{"title":65,"searchDepth":495,"depth":495,"links":496},2,[497,504,505,506,510,511,512],{"id":22,"depth":495,"text":23,"children":498},[499,501,502,503],{"id":33,"depth":500,"text":34},3,{"id":57,"depth":500,"text":58},{"id":100,"depth":500,"text":101},{"id":133,"depth":500,"text":134},{"id":152,"depth":495,"text":153},{"id":202,"depth":495,"text":203},{"id":231,"depth":495,"text":232,"children":507},[508,509],{"id":235,"depth":500,"text":236},{"id":260,"depth":500,"text":261},{"id":316,"depth":495,"text":317},{"id":383,"depth":495,"text":384},{"id":478,"depth":495,"text":479},"The operator lifecycle: install, enable, grant, configure, monitor, revoke, and remove.","md",{},true,"\u002Fdeveloper\u002Fplugin-operations",{"title":6,"description":513},"3.developer\u002F44.plugin-operations","hb83-4LiZOsoOTjIPNiEbGjkoL4TDLcMUkv48qU3n5Y",[522,526],{"title":523,"path":524,"stem":525,"children":-1},"Capabilities, Grants & Trust","\u002Fdeveloper\u002Fplugin-capabilities","3.developer\u002F43.plugin-capabilities",{"title":527,"path":528,"stem":529,"children":-1},"The owlat plugins CLI","\u002Fdeveloper\u002Fplugin-cli","3.developer\u002F45.plugin-cli",1786915092585]