Disabling, replacing and profiles
Turn system Plugins off per Project, replace a Plugin or one slot entry, and choose the hosts and profiles a Plugin runs on.
Which Plugins run in a window is decided by three things: the Project's
choice (a system Plugin switched off), a trusted Plugin that replaces
another, and the host mode's profile. This page says what each does and
where it is stored. Design: dev_docs/everything-is-a-plugin-plan.md
sections 5.1, 5.7 and 5.8.
Turning a system Plugin off#
The Plugins page has a System tab with every system Plugin (studio.*).
Its switch turns the Plugin off for the open Project, both halves. A system
Plugin cannot be uninstalled.
- The choice is stored per Project in
.witwin/plugins/state.json(thedisabledlist,plugins/state.py), the same ledger as third-party Plugins. Web-only system Plugins (studio.hierarchy,studio.properties, ...) are stored there too. - Plugins that require the one turned off go off with it. The page names
them in a confirmation first; each row then says why it is off, for example
requires studio.runs, which is disabled in this Project. Turning it back on restores all of them. - Plugins with an optional dependency on it stay on. Their entries in its
slots wait and come back when it does (
buildHost.tsx, plan 5.1). - Two system Plugins cannot be turned off:
studio.sceneandstudio.project. The kernel itself serves their RPC domains (scene sync, the Project file layer,ui_state.*). The list iskernelinserver/witwin_server/plugins_builtin/profiles.toml; the page shows them locked, andplugin.system.set_enabledrefuses them. - When it takes effect: the web half and the Python half's RPC methods change
at once. Python Handlers, routes and startup Tools change the next time the
Project opens; the page says so (
restartRequired).
RPC: plugin.system.list (the state the page shows) and
plugin.system.set_enabled { plugin_id, enabled }
(server/witwin_server/features/plugins/rpc.py). The Python side is
BuiltinPluginHost.set_enabled / system_state (plugins/builtin.py); the
web side is studio/src/app/pluginHost/systemComposition.ts, which calls
pluginRuntime.setEnabled(id, enabled, reason).
Replacing a Plugin or one slot entry#
replaces in witwin.toml (and the same field in definePlugin) names what
this Plugin takes the place of.
replaces = ["studio.console"] # the whole Plugin
replaces = ["properties.inspector:component:transform"] # one keyed slot entry: "<slot>:<key>"- Whole Plugin. While the replacer is loaded and active, the replaced
Plugin is off in both halves. The Plugins page shows "Replaced by
<id>". Plugins that require the replaced one count the replacer in its place. When the replacer unloads, the replaced Plugin comes back, unless the Project turned it off. The same happens in the window when the replacer's webactivatethrows: the replacer is marked failed and the replaced Plugin is activated again (pluginRuntime.tsrestoreReplaced). - One entry.
"<slot>:<key>"lets the replacer's registration for that key in that keyed slot win over the owner's, without turning the owner off (replaces: '*'on the registration,@witwin/workbench/slots). A wildcard never displaces an entry the kernel registered. - Only a trusted third-party Plugin replaces anything: a local-path
Plugin, or an installed one the user trusted. The server does not apply
replacesof a system Plugin (plugins/composition.pytrusted_replacementsskips them), and no system Plugin declares one. An untrusted Plugin'sreplacesis ignored; its registrations collide instead. - The security core cannot be replaced: the kernel Plugins above, Tool
permission tiers and the confirmation flow (
tools/invoker.py), and theproject.fspath checks. They are kernel code, not Plugins, so there is nothing to name;replacesof a kernel Plugin or a kernel module is refused (plugins/composition.pyreplace_error). - A replacer keeps its own namespace. It cannot take over the replaced
Plugin's RPC domain (third-party methods live under
<plugin id>.), so it serves its own methods; it may declare the same slots, so other Plugins' entries attach to it.
The regression Plugin is
server/tests/fixtures/plugins/witwin_example_console/: it replaces
studio.console in both halves and declares console.channels. Tests:
server/tests/test_system_plugin_disable.py and
studio/src/app/pluginHost/systemComposition.test.ts.
Profiles and hosts#
A profile says which system Plugins a host mode loads. The file is
server/witwin_server/plugins_builtin/profiles.toml:
| Profile | Used by | System Plugins |
|---|---|---|
desktop | the desktop app's ProjectServer | all (studio.*) |
web | a hosted ProjectServer (WITWIN_HOSTED=1) | all (studio.*) |
headless | witwin CLI and witwin.studio scripts that open a Project (api/runtime.py open_project_scene) | the Python halves a script needs: scene, project, assets, materials, em, parameters, timeline, platform, runs, results, studies, agent, tasks |
WITWIN_PROFILE=desktop|web|headless overrides the choice. Each profile
must include every required dependency of what it lists
(test_profiles_file_names_only_system_plugins_and_is_closed_under_requires).
A system Plugin outside the profile has status excluded.
hosts in witwin.toml (and definePlugin) says where a Plugin may load:
any of desktop, web, headless. Without it a Plugin loads everywhere.
hosts = ["desktop"] # needs this computer: not loaded in Studio WebA Plugin whose hosts lack the current host does not load. The
ProjectServer reports not available on web (hosts: desktop) (or on desktop, on headless), and a web half reports
not available on the web (hosts: desktop) (or on the desktop) on the
Plugins page (pluginRuntime.ts).