{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://termix.site/schemas/plugin-manifest.schema.json", "title": "Termix Plugin Manifest v2", "type": "object", "additionalProperties": false, "required": [ "id", "name", "version", "description", "author", "license", "engine", "category", "capabilities" ], "properties": { "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$", "description": "Unique plugin id. Lowercase, starts with a letter, 2-40 characters. Must equal the plugin's directory name." }, "name": { "type": "string", "minLength": 1 }, "version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z-.]+)?(\\+[0-9A-Za-z-.]+)?$", "description": "Semver version string." }, "description": { "type": "string", "minLength": 1 }, "author": { "type": "object", "additionalProperties": false, "required": ["name"], "properties": { "name": { "type": "string", "minLength": 1 }, "url": { "type": "string", "minLength": 1 } } }, "license": { "type": "string", "minLength": 1 }, "repository": { "type": "string", "minLength": 1 }, "category": { "type": "string", "enum": [ "Terminal", "Files & Transfer", "Infrastructure", "Monitoring", "Networking", "Access & Security", "Productivity" ] }, "icon": { "type": "string", "minLength": 1, "description": "A Lucide icon name, like \"Play\"." }, "video": { "type": "string", "pattern": "^https://(www\\.|m\\.)?(youtube\\.com|youtu\\.be|youtube-nocookie\\.com)/", "description": "A YouTube link shown at the top of the plugin's page." }, "features": { "type": "array", "maxItems": 20, "items": { "type": "string", "minLength": 1, "maxLength": 160 }, "description": "Short lines listing what the plugin does, shown on its page." }, "docs": { "type": "string", "pattern": "^https://", "description": "Where the plugin's docs live. Shown as a Documentation link in Termix." }, "env": { "type": "array", "description": "Environment variables the plugin reads, listed in its docs.", "items": { "type": "object", "additionalProperties": false, "required": ["name", "description"], "properties": { "name": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]{0,63}$" }, "description": { "type": "string", "minLength": 1, "maxLength": 300 }, "default": { "type": "string" }, "required": { "type": "boolean" }, "secret": { "type": "boolean" } } } }, "engine": { "type": "object", "additionalProperties": false, "required": ["termix", "api"], "properties": { "termix": { "type": "string", "minLength": 1, "description": "Semver range of Termix releases the plugin runs on, e.g. \">=2.9.0\"." }, "api": { "type": "string", "minLength": 1, "description": "Semver range of the plugin API it needs, e.g. \"1\" or \"^1.2\"." } } }, "capabilities": { "type": "array", "description": "Catalog capability ids this plugin's code needs. See the SDK capability catalog.", "items": { "type": "string", "enum": [ "credentials:read", "ssh:connect", "process:spawn", "users:write", "auth:provide", "auth:policy", "sync:provide", "ssh:transport", "system:tls", "device:serial", "hosts:write", "hosts:export", "credentials:use", "credentials:write", "network:outbound", "network:serve", "network:broadcast", "users:read", "events:core", "notify:send", "audit:read", "network:probe", "hosts:status-provide", "hosts:activity-provide", "hosts:lock", "desktop:window", "desktop:local", "users:impersonate", "db:core-refs", "hosts:read", "db:own", "kv:own", "files:own", "secrets:own", "settings:read-core", "ui:surface", "notify:hub", "plugins:read", "ui:layout", "ui:theme" ] } }, "dependencies": { "type": "object", "description": "Plugin ids this plugin cannot run without, mapped to a semver range.", "additionalProperties": { "type": "string", "minLength": 1 }, "propertyNames": { "pattern": "^[a-z][a-z0-9-]{1,39}$" } }, "optionalDependencies": { "type": "object", "description": "Plugin ids this plugin uses when present. It must keep working without them.", "additionalProperties": { "type": "string", "minLength": 1 }, "propertyNames": { "pattern": "^[a-z][a-z0-9-]{1,39}$" } }, "provides": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["service", "version", "permission"], "properties": { "service": { "type": "string", "pattern": "^[a-z0-9-]+(\\.[a-z0-9-]+)+$" }, "version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+(-[0-9A-Za-z-.]+)?(\\+[0-9A-Za-z-.]+)?$" }, "permission": { "type": "string", "minLength": 1 }, "names": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" } } } } }, "requires": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["service", "versionRange"], "properties": { "service": { "type": "string", "pattern": "^[a-z0-9-]+(\\.[a-z0-9-]+)+$" }, "versionRange": { "type": "string", "minLength": 1 }, "optional": { "type": "boolean" } } } }, "providesSecret": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["key", "permission"], "properties": { "key": { "type": "string", "pattern": "^[a-z0-9-]+$" }, "permission": { "type": "string", "minLength": 1 }, "descriptionKey": { "type": "string", "minLength": 1 } } } }, "requiresSecret": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["plugin", "key"], "properties": { "plugin": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" }, "key": { "type": "string", "pattern": "^[a-z0-9-]+$" }, "optional": { "type": "boolean" } } } }, "contributes": { "type": "object", "additionalProperties": false, "properties": { "tabs": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "titleKey", "icon", "openFrom"], "properties": { "id": { "type": "string", "minLength": 1 }, "titleKey": { "type": "string", "minLength": 1 }, "icon": { "type": "string", "minLength": 1 }, "openFrom": { "type": "array", "minItems": 1, "items": { "type": "string", "enum": ["rail", "host-context-menu", "palette"] } } } } }, "guest": { "type": "boolean" }, "protocols": { "type": "array", "description": "Connection protocols whose per-host login core stores, encrypts, shares and syncs for the plugin.", "items": { "type": "object", "additionalProperties": false, "required": ["id"], "properties": { "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,31}$", "not": { "enum": ["ssh"] } }, "credentialFields": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["key"], "properties": { "key": { "type": "string", "pattern": "^[a-zA-Z][a-zA-Z0-9]{0,31}$", "not": { "enum": [ "username", "password", "authType", "credentialId" ] } }, "secret": { "type": "boolean" } } } }, "defaultPort": { "type": "integer", "minimum": 1, "maximum": 65535 }, "hostLoginFallback": { "type": "array", "uniqueItems": true, "items": { "enum": ["username", "password"] } } } } }, "keybindingActions": { "type": "array", "description": "Keybinding actions the plugin adds, with the parameters a saved binding carries.", "items": { "type": "object", "additionalProperties": false, "required": ["id"], "properties": { "id": { "type": "string", "pattern": "^[a-zA-Z][a-zA-Z0-9.-]{0,63}$", "not": { "enum": [ "nextTab", "previousTab", "openCommandPalette", "reconnectSession" ] } }, "params": { "type": "object", "propertyNames": { "pattern": "^[a-zA-Z][a-zA-Z0-9]{0,31}$", "not": { "const": "type" } }, "additionalProperties": { "type": "object", "additionalProperties": false, "required": ["type"], "properties": { "type": { "enum": ["string", "boolean"] }, "required": { "type": "boolean" }, "pattern": { "type": "string" }, "maxLength": { "type": "integer", "minimum": 1 }, "syncEntity": { "type": "string", "pattern": "^[a-zA-Z][a-zA-Z0-9]*$" } } } } } } }, "syncEntities": { "type": "array", "minItems": 1, "uniqueItems": true, "description": "Wire names of the sync entities this plugin registers.", "items": { "type": "string", "pattern": "^[a-zA-Z][a-zA-Z0-9]*$" } }, "guestViews": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]*$" } }, "auth": { "type": "object", "additionalProperties": false, "properties": { "sshAuthTypes": { "type": "array", "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" } }, "loginMethods": { "type": "array", "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" } }, "secondFactors": { "type": "array", "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" } }, "secretSchemes": { "type": "array", "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" } }, "keyboardInteractive": { "type": "array", "items": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" } } } }, "panels": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "titleKey"], "properties": { "id": { "type": "string", "minLength": 1 }, "titleKey": { "type": "string", "minLength": 1 }, "icon": { "type": "string", "minLength": 1 } } } }, "dashboardCards": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "titleKey"], "properties": { "id": { "type": "string", "minLength": 1 }, "titleKey": { "type": "string", "minLength": 1 }, "icon": { "type": "string", "minLength": 1 } } } }, "actions": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "titleKey", "handler"], "properties": { "id": { "type": "string", "pattern": "^[a-z0-9-]+(\\.[a-zA-Z0-9-]+)+$" }, "titleKey": { "type": "string", "minLength": 1 }, "handler": { "type": "string", "pattern": "^[A-Za-z_$][A-Za-z0-9_$]*$" }, "icon": { "type": "string", "minLength": 1 }, "permission": { "type": "string", "minLength": 1 }, "slot": { "type": "string", "minLength": 1 }, "kind": { "type": "string", "enum": ["button", "component"] } } } }, "actionSlots": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "accepts"], "properties": { "id": { "type": "string", "pattern": "^[a-z0-9-]+(\\.[a-zA-Z0-9-]+)+$" }, "accepts": { "type": "array", "minItems": 1, "items": { "type": "string", "enum": ["button", "component"] } }, "descriptionKey": { "type": "string", "minLength": 1 } } } }, "permissions": { "type": "array", "description": "Role permissions this plugin contributes. Core registers each as ..", "items": { "type": "object", "additionalProperties": false, "required": ["name", "titleKey", "descriptionKey"], "properties": { "name": { "type": "string", "description": "Plugin-relative name. May contain dots. Must not start with a core group or this plugin's id.", "pattern": "^[a-z0-9_-]+(\\.[a-z0-9_-]+)*$" }, "titleKey": { "type": "string", "minLength": 1 }, "descriptionKey": { "type": "string", "minLength": 1 }, "defaultRoles": { "type": "array", "description": "System roles that hold this the first time it is seen.", "items": { "type": "string", "enum": ["admin", "user"] } } } } }, "settings": { "type": "object", "description": "Settings fields core renders on the plugin's settings page and in the host editor.", "additionalProperties": false, "properties": { "admin": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["key", "type"], "properties": { "key": { "type": "string", "description": "Storage key, unique within its scope.", "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$" }, "type": { "type": "string", "enum": [ "boolean", "string", "number", "select", "multiselect", "secret", "textarea", "json", "custom" ] }, "labelKey": { "type": "string", "minLength": 1, "description": "Required for every type except \"custom\"." }, "descriptionKey": { "type": "string", "minLength": 1 }, "placeholderKey": { "type": "string", "minLength": 1 }, "default": { "description": "Value used until one is written." }, "options": { "type": "array", "description": "select and multiselect only.", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["value", "labelKey"], "properties": { "value": { "type": "string", "minLength": 1 }, "labelKey": { "type": "string", "minLength": 1 } } } }, "min": { "type": "number", "description": "number fields only." }, "max": { "type": "number", "description": "number fields only." }, "requires": { "type": "string", "description": "Key of a boolean field in the same scope that must be on." }, "permission": { "type": "string", "minLength": 1, "description": "Permission needed to write it. Admin fields default to admin.plugins.manage." }, "group": { "type": "string", "minLength": 1, "description": "Section heading this field sits under." }, "component": { "type": "string", "minLength": 1, "description": "Registered component id. Required when type is \"custom\"." }, "hidden": { "type": "boolean", "description": "Stored and validated, but not drawn by the generic settings form." } } } }, "user": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["key", "type"], "properties": { "key": { "type": "string", "description": "Storage key, unique within its scope.", "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$" }, "type": { "type": "string", "enum": [ "boolean", "string", "number", "select", "multiselect", "secret", "textarea", "json", "custom" ] }, "labelKey": { "type": "string", "minLength": 1, "description": "Required for every type except \"custom\"." }, "descriptionKey": { "type": "string", "minLength": 1 }, "placeholderKey": { "type": "string", "minLength": 1 }, "default": { "description": "Value used until one is written." }, "options": { "type": "array", "description": "select and multiselect only.", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["value", "labelKey"], "properties": { "value": { "type": "string", "minLength": 1 }, "labelKey": { "type": "string", "minLength": 1 } } } }, "min": { "type": "number", "description": "number fields only." }, "max": { "type": "number", "description": "number fields only." }, "requires": { "type": "string", "description": "Key of a boolean field in the same scope that must be on." }, "permission": { "type": "string", "minLength": 1, "description": "Permission needed to write it. Admin fields default to admin.plugins.manage." }, "group": { "type": "string", "minLength": 1, "description": "Section heading this field sits under." }, "component": { "type": "string", "minLength": 1, "description": "Registered component id. Required when type is \"custom\"." }, "hidden": { "type": "boolean", "description": "Stored and validated, but not drawn by the generic settings form." } } } }, "host": { "type": "object", "additionalProperties": false, "required": ["fields"], "properties": { "enableKey": { "type": "string", "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$", "description": "Boolean rendered first, gating the rest of the section." }, "enableLabelKey": { "type": "string", "minLength": 1 }, "enableDescriptionKey": { "type": "string", "minLength": 1 }, "fields": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["key", "type"], "properties": { "key": { "type": "string", "description": "Storage key, unique within its scope.", "pattern": "^[a-zA-Z][a-zA-Z0-9_-]{0,63}$" }, "type": { "type": "string", "enum": [ "boolean", "string", "number", "select", "multiselect", "secret", "textarea", "json", "custom" ] }, "labelKey": { "type": "string", "minLength": 1, "description": "Required for every type except \"custom\"." }, "descriptionKey": { "type": "string", "minLength": 1 }, "placeholderKey": { "type": "string", "minLength": 1 }, "default": { "description": "Value used until one is written." }, "options": { "type": "array", "description": "select and multiselect only.", "minItems": 1, "items": { "type": "object", "additionalProperties": false, "required": ["value", "labelKey"], "properties": { "value": { "type": "string", "minLength": 1 }, "labelKey": { "type": "string", "minLength": 1 } } } }, "min": { "type": "number", "description": "number fields only." }, "max": { "type": "number", "description": "number fields only." }, "requires": { "type": "string", "description": "Key of a boolean field in the same scope that must be on." }, "permission": { "type": "string", "minLength": 1, "description": "Permission needed to write it. Admin fields default to admin.plugins.manage." }, "group": { "type": "string", "minLength": 1, "description": "Section heading this field sits under." }, "component": { "type": "string", "minLength": 1, "description": "Registered component id. Required when type is \"custom\"." }, "hidden": { "type": "boolean", "description": "Stored and validated, but not drawn by the generic settings form." }, "defaultFrom": { "type": "string", "minLength": 1, "description": "Deprecated and ignored. Host defaults cover every host field." }, "defaultable": { "type": "boolean", "description": "Whether the field can have a host default. On by default for non-secret fields." }, "defaultLevels": { "type": "array", "minItems": 1, "items": { "enum": ["admin", "user", "folder"] }, "description": "Levels that may set a default for the field. All three by default." }, "personal": { "type": "boolean", "description": "A look-and-feel value: a user a host is shared with gets their own default for it." }, "shareRead": { "type": "string", "enum": ["connect", "view", "edit", "manage"], "description": "Lowest share level whose recipients see the value in the host payload." }, "ownerOnly": { "type": "boolean", "description": "Only the host's owner may change it." }, "secretKeys": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "description": "JSON fields only: keys inside the stored object that hold secrets." } } } }, "enableDefault": { "type": "boolean", "description": "What the enable switch reads before a host saves it. Off by default." }, "editorGroup": { "enum": ["top", "ssh"], "description": "Host editor strip the generated tab sits in. Defaults to top." }, "editorOrder": { "type": "number", "description": "Position among that strip's tabs, core ones included." } } } } }, "hostCapability": { "description": "A host-editor checkbox backed by a boolean column on the host record. Reshaped by a later step.", "oneOf": [ { "$ref": "#/$defs/hostCapability" }, { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/hostCapability" } } ] }, "http": { "type": "object", "additionalProperties": false, "description": "HTTP options for the plugin's /plugin-api// router.", "properties": { "adminImpersonation": { "type": "boolean", "description": "An admin may call the plugin's routes on behalf of another user with the X-Admin-Target-User header." }, "legacyPaths": { "type": "array", "description": "Old URLs under // served by the plugin's router with that prefix removed.", "items": { "type": "string", "pattern": "^/[a-z][a-z0-9-]*(/[A-Za-z0-9_.-]+)+$" } }, "legacyRedirects": { "type": "array", "description": "Old URLs outside the plugin's namespace, redirected to a path in its router with the rest of the path and the query kept.", "items": { "type": "object", "additionalProperties": false, "required": ["from", "to"], "properties": { "from": { "type": "string", "pattern": "^(/[A-Za-z0-9_.-]+)+$" }, "to": { "type": "string", "pattern": "^(/[A-Za-z0-9_.-]+)+$" }, "status": { "enum": [307, 308] } } } } } }, "uiPresets": { "type": "object", "additionalProperties": false, "required": ["simple", "balanced", "advanced"], "description": "The plugin's Appearance defaults for each interface preset, read with usePluginUiPreferences.", "properties": { "simple": { "type": "object", "additionalProperties": { "anyOf": [ { "type": "boolean" }, { "type": "number" }, { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] } }, "balanced": { "type": "object", "additionalProperties": { "anyOf": [ { "type": "boolean" }, { "type": "number" }, { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] } }, "advanced": { "type": "object", "additionalProperties": { "anyOf": [ { "type": "boolean" }, { "type": "number" }, { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] } } } }, "uses": { "type": "array", "minItems": 1, "uniqueItems": true, "description": "Other plugins' action, slot and extension point ids this plugin calls, fills or extends.", "items": { "type": "string", "pattern": "^[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)+$" } }, "layouts": { "type": "array", "description": "Whole-app layouts the frontend registers with app.registerLayout. Needs the ui:layout capability.", "items": { "type": "object", "additionalProperties": false, "required": ["id", "label"], "properties": { "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" }, "label": { "type": "string", "minLength": 1, "description": "i18n key in the plugin namespace, or plain text." }, "description": { "type": "string", "minLength": 1 }, "defaultTheme": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$", "description": "Theme picked with this layout when nobody chose one." }, "default": { "type": "boolean", "description": "The layout a default-look plugin starts with. At most one." } } } }, "themes": { "type": "array", "description": "Themes: one CSS file each with token rules scoped under .. Needs the ui:theme capability.", "items": { "type": "object", "additionalProperties": false, "required": ["id", "label", "mode", "css"], "properties": { "id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$" }, "label": { "type": "string", "minLength": 1 }, "mode": { "type": "string", "enum": ["light", "dark"] }, "css": { "type": "string", "pattern": ".css$", "description": "Path of the CSS file inside the plugin." }, "swatch": { "type": "object", "additionalProperties": false, "required": ["background", "panel", "text"], "properties": { "background": { "type": "string", "pattern": "^#[0-9a-fA-F]{3,8}$" }, "panel": { "type": "string", "pattern": "^#[0-9a-fA-F]{3,8}$" }, "text": { "type": "string", "pattern": "^#[0-9a-fA-F]{3,8}$" } } } } } }, "mobile": { "type": "object", "description": "How the plugin shows in the Termix mobile app. Needs plugin API 1.13 (Termix 26.10.6).", "additionalProperties": false, "required": ["mode"], "properties": { "mode": { "type": "string", "enum": ["webview", "native", "none"], "description": "webview: the app shows the plugin's UI from the server. native: the app has its own screens. none: nothing to show in the app." }, "entry": { "type": "string", "pattern": "\\.js$", "description": "webview only. A bundle built for the app, in the same folder as the frontend bundle. Unset means the normal frontend runs in a phone sized frame." }, "sizes": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "enum": ["phone", "tablet"] }, "description": "webview only. Screen sizes the UI works at. Defaults to both." }, "views": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "type": "string", "minLength": 1 }, "description": "webview only. Tab, panel or card ids the app offers. Defaults to every tab." }, "feature": { "type": "string", "pattern": "^[a-z][a-z0-9-]{1,39}$", "description": "native only. The app feature that stands in for this plugin. Defaults to the plugin id." }, "note": { "type": "string", "minLength": 1, "description": "i18n key in the plugin's namespace, or plain text, shown in the app." } } } } }, "backend": { "type": "string", "minLength": 1, "description": "Built backend entry, relative to the plugin root. Defaults to dist/backend.js." }, "frontend": { "type": "string", "minLength": 1, "description": "Built frontend entry, relative to the plugin root. Defaults to dist/frontend.js." }, "locales": { "type": "string", "minLength": 1, "description": "Locales directory, relative to the plugin root. Defaults to locales." }, "platforms": { "type": "array", "minItems": 1, "items": { "type": "string", "enum": ["linux", "win32", "darwin"] } }, "desktop": { "type": "string", "enum": ["mirror", "local", "server"], "description": "How a desktop linked to a server treats this plugin: mirror follows the server, local is decided on each desktop, server never runs on a linked desktop. Defaults to mirror." }, "runtime": { "type": "string", "enum": ["desktop"], "description": "desktop: the plugin only loads on a desktop app's own embedded backend, never on a server. It implies desktop local and may declare desktop:local." }, "nativeDependencies": { "type": "array", "minItems": 1, "description": "Bare npm package names this plugin depends on that carry a native (.node) binding. The build never bundles them: they stay a real dependency in the plugin's own package.json and are resolved from node_modules at runtime, the same as a host-provided package, so the compiled native binary is never copied into the JS bundle.", "items": { "type": "string", "minLength": 1 } } }, "$defs": { "hostCapability": { "type": "object", "additionalProperties": false, "required": ["key", "labelKey", "editorTab"], "properties": { "key": { "type": "string", "minLength": 1 }, "labelKey": { "type": "string", "minLength": 1 }, "editorTab": { "type": "string", "minLength": 1 } } } } }