Plugins can be enabled and disabled at runtime without restarting the server. Two ways to do it:
Edit [plugin] enabled while the server is running. The config watcher detects the change, diffs the enabled list against currently loaded plugins, and starts or stops plugins accordingly.
[plugin]
enabled = [
"reduce",
"spindle",
#"voor", # comment out to disable
]
POST /api/plugins/{name}/enable
POST /api/plugins/{name}/disable
Both return JSON with the plugin's new state:
{"action": "enable", "name": "voor", "state": "running"}
{"action": "disable", "name": "voor", "state": "stopped"}
See API reference for all plugin endpoints.
Enable: discovered from search paths, loaded, gRPC connected, registered, initialized, provider services wired, async handlers and watchers registered. Same sequence as boot, but for one plugin.
Disable: gRPC shutdown sent, process killed, unregistered from domain registry, watchers pruned, async handlers removed, HTTP mux cleared.
Plugins not mentioned in the change are untouched. Both transitions emit a colored banner in the log.
Plugins can advertise their HTTP endpoints by setting http_routes in InitializeResponse. These show up in the plugin banner at startup and are queryable via GET /api/plugins/routes. This is optional — plugins that don't set it get a nudge in the banner.
GET /api/plugins/routes also maps provider roles to core invocation endpoints (e.g. an llm-provider plugin includes POST /api/prompt/direct with the provider name).