OpenVCS plugins run as long-lived Node.js processes and are authored in TypeScript.
Client (Frontend) -> Client (Backend host) <-> Plugin (Node.js process)
- The frontend talks to the backend via Tauri commands and events.
- The backend starts each plugin module as a persistent Node.js process.
- Host and plugin communicate through JSON-RPC 2.0 over stdio with
Content-Lengthframing. - Plugin authors can mirror that contract through
@openvcs/sdk/runtimeand@openvcs/sdk/types.
- Method names and framing live in
Client/Backend/src/plugin_runtime/protocol.rs. - Runtime process implementation lives in:
Client/Backend/src/plugin_runtime/node_instance.rsClient/Backend/src/plugin_runtime/runtime_select.rs
- Plugin modules contribute
plugin.*,vcs.*, and runtime options through the exportedPluginDefinitionobject consumed by the generated bootstrap.
Core host-to-plugin method groups:
plugin.*: lifecycle, menus, and settings hooksvcs.*: backend operations for repository workflows
Repository identity is resolved from Git config, not from an OpenVCS-side cache.
vcs.get_identity reads the repository's configured user.name and
user.email, and commit flows fail when Git has no usable identity instead of
inventing an OpenVCS author.
plugin.handle_action requests carry the selected action as action_id and an
optional payload object. Plugin handlers may return a modal definition object
to reopen a plugin modal after the action completes.
Plugin modal definitions support nested layout containers as content items:
horizontal-boxvertical-boxgrid
The host renders these containers recursively, so plugin authors can group fields and buttons into horizontal rows, vertical stacks, and multi-column grids.
When a plugin runtime is active, plugin-contributed menu definitions whose ids
match built-in top-level menus such as repository are projected into the main
menubar. For VCS backend plugins, these items therefore appear only after the
repository-scoped runtime has started.
Menu surfaces can be explicitly targeted using the surface option:
getOrCreateMenu('repository', 'Repository', { surface: 'menubar' })- renders in the top menubargetOrCreateMenu('my-settings', 'My Settings', { surface: 'settings' })- renders in the Settings modal
The surface option is required. Plugin authors must explicitly specify where their menus should appear.
Core plugin-to-host notifications:
host.loghost.ui_notifyhost.status_sethost.event_emitvcs.event
Selected-file commit flows stage repository-relative paths into the index with
vcs.stage_paths before issuing vcs.commit. Plugins implementing selected-path
commits should therefore support both RPCs consistently.
Clone flows resolve the final target directory in the host and then invoke the
selected backend plugin's vcs.clone_repo method with { url, dest }, where
dest is the full repository destination path.
Plugin runtime requires the app-bundled Node binary; there is no fallback to a
system node executable.
Instead, OpenVCS resolves config-declared plugin sources into the writable local plugin store before discovery runs:
- Built-in source list:
Client/openvcs.plugins.json(channel-first) - User source list: top-level
plugin = [...]inopenvcs.conf
The built-in config uses a channel-first schema:
{
"stable": ["@openvcs/git-plugin@latest", "@openvcs/official-themes@latest"],
"beta": ["@openvcs/git-plugin@beta", "@openvcs/official-themes@beta"],
"dev": ["@openvcs/git-plugin@edge", "@openvcs/official-themes@nightly"]
}The active channel is determined by OPENVCS_UPDATE_CHANNEL:
stable- production releasesbeta- beta buildsdev- development buildsnightly- alias fordev- unset/unknown values default to
stable
For local development, create openvcs.plugins.local.json in the Client directory
to override the channel list (gitignored). If the file defines the active
channel key, that list fully replaces the committed channel list, including an
empty array.
The resolver accepts:
- npm package specifiers such as
@scope/nameorname@version - local paths to npm plugin folders such as
../Git
For source resolution, OpenVCS uses npm pack to materialize the plugin package
contents and then installs runtime dependencies into the local plugin root when
needed. Local path plugins therefore behave like npm packages and should define
their published files and prepack behavior accordingly.
After sync, the backend operates only on local installed plugin directories:
plugins/
<plugin-id>/
package.json
source.json
index.json
current.json
bin/
themes/
node_modules/
Built-in plugins are first materialized into the app resource directory under
built-in-plugins/<plugin-id>/ during the client build. On startup, the backend
synchronizes those built-in directories into the same writable installed store
used for user plugins.
The host currently consumes these manifest fields from package.json.openvcs:
id(required)name,version(optional but recommended)default_enabled(optional)module.exec(optional Node entry filename underbin/)module.vcs_backends(optional VCS backend ids or backend objects the module provides)
Backend objects may include a namespaced action-label map such as VCS.Push
→ Push, VCS.Pull → Pull, and VCS.Commit → Commit. The client falls
back to generic VCS text when a label is missing.
module.exec must resolve to a .js, .mjs, or .cjs file inside bin/.
- Configured plugin sources are synchronized on startup.
openvcs.confchanges are watched and re-synchronized while the app is running.- The Settings > Plugins pane can also reload config and re-run source sync.
- Non-VCS module runtimes are started and stopped according to enabled state.
- VCS backend plugin runtimes are repo-scoped and start when opening a repository through that backend.
- Saving global settings preserves active repo-scoped VCS backend runtimes when the backend plugin remains enabled, so the current repository stays usable.
- Closing the main window tears down config watchers and active plugin
runtimes so
cargo tauri devexits promptly instead of leaving the backend process alive.
- No per-capability permission prompts.
- Plugins have full system access within their own Node process.
- Config-managed plugins are auto-approved because declaring them in config is the trust action.
Install only plugins from authors you trust.