Home/Docs/Creating Plugins

Creating Plugins

Build a plugin in JavaScript, package it as a zip, install it through the Manage Plugins modal. No build chain required for simple plugins; no restart needed at install time.

Plugin architecture

A Binderus plugin is a folder with a manifest.json and a main.js entry point. At install time the folder lives at:

<vault>/.binderus/plugins/<manifest.id>/
├── manifest.json
├── main.js
└── (any assets your plugin needs)

The plugin-loader reads main.js through a vault-aware Tauri command (read_plugin_file) so plugins work in both filesystem and encrypted-libsql storage modes.

manifest.json

{
  "id":            "my-plugin",
  "name":          "My Plugin",
  "version":       "0.1.0",
  "minAppVersion": "0.9.0",
  "description":   "What my plugin does, in one sentence.",
  "author":        "Your Name"
}
FieldRequiredNotes
idyesLowercase, hyphenated, unique. Becomes the install folder name.
nameyesHuman-readable name shown in Manage Plugins.
versionyesSemver. Bumping version on update lets the loader replace the running copy on hot-load.
minAppVersionyesBinderus refuses to load plugins targeting a newer version than the installed app.
descriptionrecommendedOne-line summary. Shown in the plugin row.
authorrecommendedDisplay only. No verification.

main.js — lifecycle

main.js is a JavaScript module that exports a default object with lifecycle hooks. The plugin manager calls onLoad(ctx) when activating the plugin and onUnload() on deactivate or uninstall — this is where you wire up commands and tear them down cleanly.

// main.js
export default {
  onLoad(ctx) {
    // Register a command — appears in the command palette + bindable to a shortcut
    const dispose = ctx.commands.register({
      id:   'my-plugin.hello',
      name: 'My Plugin: Say Hello',
      run:  () => ctx.editor.insertText('Hello from my plugin!')
    });

    // Persistent settings — keyed by plugin id under vault settings JSON
    const settings = ctx.settings.get();
    if (!settings.greeting) ctx.settings.set({ greeting: 'Hello' });

    // Subscribe to setting changes (for live UI updates)
    const unsubscribe = ctx.settings.onChange((s) => {
      console.log('settings updated', s);
    });

    // Return a teardown function or store disposers — onUnload calls them
    this._dispose = () => { dispose(); unsubscribe(); };
  },
  onUnload() {
    this._dispose?.();
  }
};

The PluginContext (ctx)

SurfaceUse for
ctx.commandsRegister commands that show up in Cmd P and can be bound to shortcuts.
ctx.editorRead selection, insert text, run editor commands.
ctx.settingsPer-plugin settings store, persisted to vault settings JSON under plugins.<id>. get(), set(partial), replace(full), onChange(cb).
ctx.panelMount a side-panel UI (HTML / DOM nodes). Hosts in the plugin panel area.
ctx.statusBarAdd a status-bar entry — text + click handler.
ctx.notifyShow a toast (info / success / error).

Packaging

Zip up the folder. From your plugin's parent dir:

cd path/to/my-plugin
zip -r my-plugin.zip manifest.json main.js  # add any other assets

The zip should contain manifest.json and main.js at the root, not inside a subfolder.

Install

In Binderus: Manage Plugins → Install (subtitle "from zip file") → pick the zip. Binderus calls the Rust install_plugin_from_zip command which:

  1. Validates the zip (size, entry count, manifest required)
  2. Sanitizes paths against zip-slip attacks
  3. Reads manifest.json and validates the id
  4. Wipes any prior install of the same id
  5. Unpacks into <vault>/.binderus/plugins/<id>/
  6. Hot-loads the plugin without restarting the app

Uninstall is one-click per plugin row in the same modal.

Examples

The bindeck-mirror repo includes working example plugins under examples/plugins/:

  • ollama-chat — chat with a local Ollama model using current editor / selection / files / folder as context
  • ai-chat — same UX, generalized to any OpenAI-compatible endpoint (Ollama, LM Studio, OpenAI, DeepInfra, Groq, Together, OpenRouter)

Both ship as folders; the repo's tooling builds them into installable zips.

Security model

  • Plugins run as JavaScript inside the Binderus renderer with a curated ctx API — no raw filesystem, no fetch-anywhere by default
  • Network access goes through plugin-aware permissions (request, user prompt, allowlist)
  • Plugin-scoped settings live under plugins.<id> in the vault settings JSON — one plugin can't read another's settings
  • Disable / uninstall cleanly tears down every contribution; no leaked listeners, no dangling commands

Distribution

For now: share your .zip directly (Discord, GitHub Releases, Gumroad).

Have a plugin you'd like featured? Open a PR on GitHub or post in the Discord.