返回 CodeWhale
PLUGIN_BUNDLES.md
根目录 / docs / PLUGIN_BUNDLES.md
1 # Plugin bundles
2
3 Codewhale v0.9.1 supports a deliberately small plugin-bundle boundary. A
4 bundle may contribute declarative Skills and MCP server configuration through
5 Codewhale's existing engines. Discovery alone never executes, enables, trusts,
6 downloads, updates, or installs anything. For the `/plugin install` on-ramp
7 that places bundles onto disk, see [PLUGINS.md](PLUGINS.md).
8
9 ## Discovery and precedence
10
11 Codewhale scans only its own roots:
12
13 - User: `~/.codewhale/plugins/<name>/plugin.toml`
14 - Workspace: `<workspace>/.codewhale/plugins/<name>/plugin.toml`
15
16 No built-in bundle ships in v0.9.1. The internal precedence order is built-in,
17 user, then workspace; the first bundle with a given name wins. This prevents a
18 repository from shadowing an explicitly installed user bundle. Symbolic-link
19 roots, manifests, component paths, and nested component files fail closed.
20
21 New user and workspace bundles are always untrusted and disabled. Discovery is
22 read-only and does not inspect any other application's extension or credential
23 directories.
24
25 Pre-v0.9.1 `overrides.json` enablement is intentionally not imported as trust.
26 Existing bundles therefore return to disabled until they receive the v1
27 content and capability review.
28
29 ## Manifest
30
31 Every bundle uses a versioned `plugin.toml` and a semantic version:
32
33 ```toml
34 schema_version = 1
35
36 [plugin]
37 name = "example"
38 version = "0.1.0"
39 description = "Example instruction and MCP bundle"
40 author = "Example Author"
41
42 [skills]
43 path = "skills"
44
45 [mcp_servers.local]
46 command = "node"
47 args = ["server.js"]
48 cwd = "mcp"
49
50 [mcp_servers.remote]
51 url = "https://example.invalid/mcp"
52
53 [capabilities]
54 network_hosts = ["example.invalid"]
55
56 [when]
57 os = ["macos", "linux", "windows"]
58 binaries = ["node"]
59 ```
60
61 Component paths must be relative, contained, present, and free of symbolic
62 links or Windows reparse points (including junctions and mount points). The v1
63 schema rejects unknown MCP fields, ambiguous local/remote
64 transport combinations, unbounded lists/timeouts, and overlapping tool
65 filters.
66
67 Remote MCP URLs must use HTTPS, except for explicit loopback HTTP endpoints.
68 They cannot contain user information, a query, or a fragment. Literal headers
69 are rejected: authentication must name a source environment variable through
70 `env_headers` or `bearer_token_env_var`. A remote bundle must declare exactly
71 the normalized host set used by its endpoints in
72 `capabilities.network_hosts`; endpoint scheme, normalized host, port, and path
73 remain bound to the review. Redirects are limited and must retain that exact
74 normalized origin. Reviewed remote transports use an explicit no-proxy HTTP
75 client: v1 bundles never read or use ambient `HTTP_PROXY`, `HTTPS_PROXY`, or
76 `NO_PROXY` values, because proxy credentials and proxy observation are outside
77 the reviewed authority. User-authored MCP configuration keeps its existing
78 explicit proxy support.
79
80 Local stdio environment entries must use exact `${SOURCE_ENV}` references.
81 The review shows destination and source names, but never reads or prints their
82 values. Plugin children inherit only Codewhale's base secret-scrubbed child
83 environment plus those reviewed mappings; credential-capable proxy variables
84 and the broader compatibility environment used by user-authored MCP
85 configuration are not inherited ambiently. Absolute arguments and parent
86 traversal are rejected; contained bundle entrypoints are frozen to their
87 staged paths before spawn.
88
89 Every stdio argument is shown losslessly as a JSON string during review.
90 Common credential-bearing flags and known literal token shapes are rejected
91 from argv; credentials must instead use a reviewed environment mapping.
92 Plugin-contributed MCP OAuth is disabled for v0.9.1, including discovery,
93 login, refresh, and token storage.
94
95 `[skills]` and `[mcp_servers.*]` are the only active component adapters in
96 v0.9.1. The manifest can inventory the following future surfaces, but a bundle
97 declaring any of them cannot be enabled yet:
98
99 ```toml
100 [commands]
101 path = "commands"
102
103 [agents]
104 path = "agents"
105
106 [hooks]
107 path = "hooks"
108
109 [lsp]
110 path = "lsp"
111
112 [native]
113 path = "native"
114
115 [capabilities]
116 filesystem_roots = ["workspace"]
117 network_hosts = ["api.example.invalid"]
118 lifecycle_mutation = true
119 ```
120
121 Remote MCP endpoint hosts must exactly match the displayed network inventory.
122 A successful environment or health check is never treated as trust.
123
124 ## Review, trust, and enablement
125
126 Use the in-session command surface:
127
128 ```text
129 /plugin list
130 /plugin validate example
131 /plugin show example
132 /plugin enable example
133 ```
134
135 The first `enable` opens a review showing source, component inventory,
136 requested permissions, sanitized MCP endpoints, full content and capability
137 hashes, and inactive declarations. It also prints an exact confirmation:
138
139 ```text
140 /plugin trust example <full-content-sha256>.<full-capability-sha256>
141 ```
142
143 Run that exact command only after reviewing the bundle. The confirmation token
144 uses both complete SHA-256 receipts rather than display prefixes. Trust first
145 copies the complete reviewed tree into a Codewhale-owned, content-addressed
146 runtime snapshot and records the matching receipt; it does not activate
147 anything.
148 Then run `/plugin enable example` again. Trust and enablement are separate:
149
150 - `/plugin disable example` stops contribution while preserving trust.
151 - `/plugin revoke example` removes trust while preserving the enablement bit;
152 the bundle remains inactive until reviewed again.
153 - `/plugin reload` rebuilds the current workspace registry when files have
154 changed on disk.
155
156 Trust, enable, disable, revoke, and reload rebuild the current workspace's
157 Skill catalogue and MCP pool immediately. Each persisted transition advances a
158 per-bundle generation under a stable cross-process lock. A generation change
159 cancels in-flight MCP work, removes cached catalog entries, terminates an idle
160 plugin stdio child, and denies persisted queued Skills carrying the older
161 authority receipt.
162
163 The review distinguishes remote MCP endpoints from local stdio MCP servers.
164 A local stdio server is a child process running with the Codewhale user's host
165 filesystem and network authority; plugin trust is not an OS sandbox. The
166 review therefore shows the command, argument count, working directory,
167 environment-variable names, and this host-authority warning without printing
168 environment or header values. MCP tool approval still applies after the
169 server starts.
170
171 Trust receipts live in `~/.codewhale/plugins/state.json`. Atomic owner-only
172 writes record the full content hash, capability hash, reviewed capability
173 inventory, generation, and review time, with the latest 32 reviews retained as
174 a bounded audit trail. Malformed or unsupported state is not overwritten: all
175 bundles fail closed until the state file is repaired or moved.
176
177 The content hash covers the manifest, complete bundle tree, and executable
178 shape in deterministic path order, including local MCP entrypoints and
179 companion assets. Staging is bounded, rejects symbolic links and unsupported
180 file kinds (plus every Windows reparse point and hard-linked files), uses an
181 atomic destination swap, and applies owner-only runtime permissions or ACLs
182 through validated object handles on Windows. The capability hash covers the
183 normalized component and permission inventory. A source or staged-content
184 edit, capability change, or unsafe runtime-root replacement invalidates the
185 receipt deterministically; an already-enabled bundle becomes inactive until
186 it is reviewed again.
187
188 ## Runtime behavior
189
190 An active bundle must be enabled, trusted for its current hashes, applicable to
191 the host, free of validation errors, and limited to supported component kinds.
192
193 - Skills are exposed only as `<plugin>:<skill>`. The model-facing catalogue and
194 `load_skill` use an in-memory snapshot bound to the reviewed staged tree,
195 rather than reading a mutable source path at execution time. `load_skill`
196 revalidates source, stage, receipt, workspace, and generation immediately
197 before releasing content and fails closed on drift. Queued messages persist
198 the same provenance and repeat that check at dispatch. `/skills inspect`
199 identifies the reviewed bundle without exposing its mutable source path.
200 - MCP server names are exposed as
201 `plugin-<plugin-name-byte-length>-<plugin>-<server>` so hyphens in either
202 component cannot create an authority collision. Disabled or untrusted
203 bundles are denied again at the headless MCP adapter. Authority is checked
204 before connection, immediately before every lazy stdio spawn, after
205 transport construction, and before each tool/resource/prompt operation.
206 Persisted generation/enablement/trust state is also watched while an
207 operation is in flight, so disable, revoke, or another cross-process state
208 transition cancels the operation and terminates a plugin stdio child. Full
209 source and staged-tree hashes are revalidated at dispatch/catalogue
210 boundaries; v0.9.1 does not continuously re-hash those trees during an
211 already-running MCP call. Source or stage drift therefore fails the next
212 boundary and drops the stale connection/catalogue entry, but is not claimed
213 to interrupt a call already executing. Every failure includes instructions
214 to reload, review, trust, and enable the bundle again.
215 - Plain launch, resume, fork, exec, and serve each construct an immutable
216 workspace-scoped registry before constructing their Skill or MCP catalogue.
217 - Constitution, repository instructions, permission rules, sandbox policy,
218 and MCP tool approval continue to outrank plugin instructions.
219
220 `/plugin list`, `show`, and `validate` perform no network requests, process
221 launches, credential reads, or configuration writes. Reviews render structural
222 argv as lossless JSON strings and environment provenance without values.
223 Credential-bearing argv is rejected at manifest validation; plugin-originated
224 errors suppress URL query, authentication, argv, and environment material.
225 Legacy executable tools under `[tools].plugin_dir` remain a distinct system
226 and are listed under `/plugin tools`.
227
228 ## Explicit non-goals for v0.9.1
229
230 There is no remote marketplace, install/update command, ambient compatibility
231 discovery, automatic trust, hook adapter, command adapter, agent adapter, LSP
232 adapter, native extension runtime, MCP subscription adapter, or migration of
233 another application's bundle. These remain later work rather than implied
234 capabilities.
235
235 lines MARKDOWN