Plugins
OxideTerm ships with a runtime plugin system that lets third-party extensions add tabs, sidebar panels, terminal hooks, commands, SFTP workflows, and lightweight UI integrations without patching the core application.
This page covers both the user-facing plugin model and the developer-facing runtime API.
Official Plugins
Section titled “Official Plugins”OxideTerm keeps the core app local-first and optional by design. Official plugins extend that baseline with focused workflows:
- Cloud Sync — encrypted self-hosted sync and backup for
.oxidesnapshots via WebDAV, HTTP JSON, Dropbox, Git, or S3 - Telnet Client — native Telnet client for routers, switches, and legacy devices
Official plugins are optional. You do not need an account to use OxideTerm, and cloud-backed workflows stay opt-in.
Installing Plugins
Section titled “Installing Plugins”A plugin becomes available when its manifest and entry file are present under ~/.oxideterm/plugins/{plugin-id}/.
- Download or clone the plugin into the plugins directory
- Keep the
plugin.jsonmanifest and ESM entry point together - If a plugin does not appear immediately, restart OxideTerm after adding it
Runtime Model
Section titled “Runtime Model”Plugins are loaded from ~/.oxideterm/plugins/{plugin-id}/ and activated dynamically at runtime.
~/.oxideterm/plugins/my-plugin/├── plugin.json├── main.js├── locales/└── assets/The host validates the manifest, builds a frozen PluginContext, then calls activate(ctx) from the plugin’s ESM entry.
Manifest Shape
Section titled “Manifest Shape”{ "id": "my-plugin", "name": "My Plugin", "version": "1.0.0", "main": "./main.js", "engines": { "oxideterm": ">=1.6.0" }, "contributes": { "tabs": [ { "id": "dashboard", "title": "Dashboard", "icon": "LayoutDashboard" } ], "sidebarPanels": [ { "id": "overview", "title": "Overview", "icon": "PanelLeft", "position": "top" } ], "apiCommands": ["list_connections"] }}There is no standalone permissions field in the current plugin runtime. Backend command access is declared through contributes.apiCommands, while higher-level capabilities are exposed through dedicated namespaces like ctx.sftp, ctx.forward, and ctx.terminal.
PluginContext API
Section titled “PluginContext API”Plugins receive a frozen PluginContext with a top-level pluginId field plus 18 API namespaces:
connectionseventsuiterminalsettingsi18nstorageapiassetssftpforwardsessionstransfersprofilereventLogideaiapp
The UI namespace now includes fully host-wired extension points:
ctx.ui.registerContextMenu()forterminal,sftp,tab, andsidebarctx.ui.registerStatusBarItem()for the main bottom status barctx.ui.registerKeybinding()for global shortcutsctx.ui.showProgress()for a top-right progress HUD
Shared Modules
Section titled “Shared Modules”Plugins use host-provided shared modules through window.__OXIDE__:
ReactReactDOMzustandlucideIconslucideReactuiversionpluginApiVersion
This keeps plugins small and avoids duplicate React instances.
Security Model
Section titled “Security Model”The current plugin runtime uses layered containment rather than a true browser sandbox:
- Frozen membrane:
PluginContextobjects are built withObject.freeze()to prevent mutation of host API objects. - Manifest validation: tabs, sidebar panels, terminal hooks, and backend command access must be declared before use.
- Advisory backend whitelist:
ctx.api.invoke()can only call commands listed incontributes.apiCommands. - Path safety: plugin file loading rejects traversal outside the plugin directory.
- Circuit breaker: plugins that throw repeatedly are auto-disabled.
- Terminal fail-open: input/output hooks fall back to the original data when a plugin throws.
Minimal Example
Section titled “Minimal Example”const { React } = window.__OXIDE__;const { createElement: h } = React;
function DashboardTab({ pluginId, tabId }) { return h('div', { className: 'p-6' }, `Hello from ${pluginId}:${tabId}`);}
export function activate(ctx) { ctx.ui.registerTabView('dashboard', DashboardTab);
ctx.ui.registerCommand( 'hello.open-dashboard', { label: 'Open Dashboard', icon: 'LayoutDashboard' }, () => ctx.ui.openTab('dashboard'), );
ctx.events.onConnect((connection) => { ctx.ui.showNotification({ title: 'Connection active', body: `${connection.username}@${connection.host}`, severity: 'info', }); });}Next Step
Section titled “Next Step”For the full API reference, manifest fields, shared module details, and TypeScript definitions, continue to the plugin development guide.