Plugins & Extensions

Zync has a decentralized extension system. Plugins run in sandboxed Web Workers or iframe panels and communicate with the app through a clean zync API — no native code required.

Extension types

Beyond panel and background plugins, Zync supports theme packs (UI + terminal colors), icon themes (file-type icons in the file manager and connection lists), and editor providers (alternative in-app editors). Icon themes install from the marketplace — defaults include VSCode Icons and Lucide (Settings → Appearance → Icon theme).

How It Works

Every plugin is a .zip archive containing at minimum:

  • manifest.json — declares the plugin's identity, version, and capabilities.
  • main.js — your plugin logic, executed inside a Web Worker with access to the zync global API.

Plugins come in two flavours:

  • Background plugins — run in a Web Worker, no visible UI. Good for commands, status bar widgets, SSH automation.
  • Panel plugins — register a full HTML/CSS/JS interface rendered in a sandboxed iframe inside Zync's tab bar. Good for dashboards, monitoring tools, custom file managers.

manifest.json

Every plugin must have a manifest.json at the root of its .zip. The id must follow reverse-DNS format and be globally unique.

JSONmanifest.json
{  "id": "com.yourname.plugin.my-plugin",  "name": "My Plugin",  "version": "1.0.0",  "description": "A short description shown in the marketplace.",  "author": "Your Name",  "main": "main.js",  "permissions": ["terminal", "statusBar", "ui", "panel", "ssh"]}

For themes, add the theme-specific fields instead of main:

JSONmanifest.json (theme)
{  "id": "com.yourname.theme.my-theme",  "name": "My Theme",  "version": "1.0.0",  "description": "A beautiful dark theme.",  "author": "Your Name",  "style": "theme.css",  "mode": "dark",  "preview_bg": "#0f111a",  "preview_accent": "#6366f1"}

The zync API

When your main.js runs, the global zync object is available. All async methods return Promises.

Lifecycle

JavaScriptmain.js
zync.on('ready', function() {  // Called once when the plugin is fully initialized.  // Register commands and panels here.  zync.logger.log('[my-plugin] ready');});

Command Palette

Register commands that appear in Zync's command palette (Ctrl+P / ⌘P by default).

JavaScriptmain.js
zync.commands.register('my-plugin.ping', 'My Plugin: Ping Server', async function() {  const output  await zync.ssh.exec('ping -c 4 8.8.8.8');  zync.ui.notify({ type 'info', body output });});

Terminal

JavaScriptmain.js
// Write text to the active terminalzync.terminal.send(ls la);// Open a new terminal tab and run a commandzync.terminal.newTab({ command htop });

SSH Exec

Run commands on the active SSH connection and get back the output as a string.

JavaScriptmain.js
const uptime  await zync.ssh.exec('uptime -p');zync.ui.notify({ type 'success', body uptime });

Status Bar

JavaScriptmain.js
// Set a persistent status bar widgetzync.statusBar.set('my-plugin', '⚡ 12% CPU');// Update it on an intervalsetInterval(async ()  {  const stat  await zync.ssh.exec("top -bn1 | grep 'Cpu' | awk '{print $2}'");  zync.statusBar.set('my-plugin', stat.trim()  '% CPU');}, 5000);

Notifications & Dialogs

JavaScriptmain.js
// Toast notifications: success | error | info | warningzync.ui.notify({ type 'success', body 'Deployment complete!' });// Confirm dialog — returns Promise<boolean>const ok  await zync.ui.confirm({  title 'Restart Nginx?',  message 'This will briefly interrupt traffic.',  variant 'danger',});if (ok) await zync.ssh.exec('sudo systemctl restart nginx');

Panel Plugins (Full UI)

Call zync.panel.register to mount a full HTML interface as a tab inside Zync. The panel runs in a sandboxed iframe with a postMessage bridge back to the zync API.

JavaScriptmain.js
zync.on('ready', function() {  const html  DOCTYPE htmlhtml  head    style      body { fontfamily sansserif; background ; color fff; padding ; }      button { background ; color fff; border none; padding  ; borderradius ; cursor pointer; }    style  head  body    h2My Panelh2    button onclick"runCommand()"Run uptimebutton    pre id"out"pre    script      async function runCommand() {        const result  await window.zync.ssh.exec('uptime');        document.getElementById('out').textContent  result;      }    script  bodyhtml;  zync.panel.register('my-plugin.panel', 'My Panel', html);});

For complex panels, compile a full React or Vue app to a single HTML string (e.g. with Webpack html-webpack-plugin + inline-source). See the open-source pm2-monitor plugin in the zync-extensions repo for a complete example.

Packaging Your Plugin

Zip the plugin directory so that manifest.json is at the root of the archive (not inside a subfolder):

BashTerminal
 # From inside your plugin directory:$zip -r my-plugin.zip manifest.json main.js  # Or if you have extra assets:$zip -r my-plugin.zip manifest.json main.js assets/

The zip must be hosted at a publicly accessible URL so Zync can download and install it.

Publishing to the Official Marketplace

The official marketplace is the zync-extensions GitHub repository. To list your plugin:

2
Add your plugin files under plugins/your-plugin-id/.
3
Upload the .zip to a permanent public URL (GitHub Releases on your own repo works well).
4
Add your entry to marketplace.json:
JSONmarketplace.json entry
{  "id": "com.yourname.plugin.my-plugin",  "name": "My Plugin",  "version": "1.0.0",  "description": "What it does in one sentence.",  "author": "Your Name",  "type": "plugin",  "downloadUrl": "https://github.com/yourname/my-plugin/releases/latest/download/my-plugin.zip"}
1
Open a Pull Request. Once merged your plugin appears in the in-app marketplace for all users.

Self-Hosted Registry

You don't have to use the official repository. Anyone can host their own plugin registry — a plain JSON file served over HTTPS — and users can point Zync at it from Settings. This is useful for private company plugins, beta channels, or curated plugin sets.

1. Create your marketplace.json

Your registry file must be a JSON array (or an object with a plugins key). Host it anywhere that returns the raw JSON over HTTPS — a GitHub repo, a CDN, or your own server.

JSONmarketplace.json
[  {    "id": "com.yourcompany.plugin.deploy",    "name": "One-Click Deploy",    "version": "2.1.0",    "description": "Deploy to staging with a single command.",    "author": "Acme Corp",    "type": "plugin",    "downloadUrl": "https://cdn.yourcompany.com/plugins/deploy-2.1.0.zip"  }]

2. Host it at a public URL

The simplest approach is a public GitHub repository — your raw URL will look like:

Bash
https://raw.githubusercontent.com/yourname/your-repo/main/marketplace.json

3. Add the registry URL in Zync

In Zync, open Settings → Extensions and paste your registry URL. Zync will fetch it alongside the official marketplace and merge the results. Users on your team only need to add the URL once.

Format requirements

Zync accepts two formats for the registry file:

  • Flat array[ { ...plugin }, ... ]
  • Object with keys{ "plugins": [...], "themes": [...] }

Each entry must have at minimum: id, name, version, and downloadUrl.