← Back to Plugins

Plugin Developer Guide

Knotbook plugins are tiny JavaScript modules that run sandboxed inside the desktop app. This guide walks you from an empty folder to a published .knotplug file on the gallery.

1. Overview

A plugin is a folder with a manifest.json and a single JavaScript entry file (plus any extra assets you want to bundle). Knotbook copies the folder into its app data directory, then loads the entry inside a Web Worker on demand.

2. Scaffold a plugin folder

Anywhere on disk, create a folder:

my-plugin/
├── manifest.json
└── main.js

3. The manifest

manifest.json is a small JSON object that tells Knotbook what your plugin is called and how to load it.

{
  "name": "Word Counter",
  "version": "1.0.0",
  "description": "Shows live word and character counts for the active note.",
  "entry": "main.js",
  "author": "Your Name",
  "permissions": ["editor.read", "ui.statusBar"]
}

Fields:

4. The runtime API

Knotbook injects a global knotbook object into your worker and calls two well-known exports:

// Knotbook loads this file inside a Web Worker. The 'knotbook' global is
// injected by the host and gives you a small, async API surface.
//
// Lifecycle: onActivate() runs once when the user enables the plugin.
// onDeactivate() runs when they disable it.
export async function onActivate(ctx) {
  ctx.log("Word Counter activated");

  ctx.onEditorChange(async ({ noteId, body }) => {
    const words = body.trim().split(/\s+/).filter(Boolean).length;
    const chars = body.length;
    ctx.statusBar.set(`${words} words · ${chars} chars`);
  });
}

export async function onDeactivate(ctx) {
  ctx.statusBar.clear();
}

The ctx object passed in is your scoped handle — keep a reference to it so cleanups in onDeactivate can undo what you did in onActivate. Available surface (today):

All methods return Promises. Don't try to import npm packages — workers run plain ES modules with no bundler. If you need a library, paste it into your entry file or ship it as an extra file referenced via import from main.js.

5. Install and iterate locally

  1. Open Knotbook. The Plugins section lives in the left sidebar, just under Templates.
  2. Click the + next to Plugins and pick your .knotplug file (or call install_plugin_from_folder via the CLI for a raw dev folder).
  3. Click the plugin row to toggle it Enabled. Your plugin's onActivate fires.
  4. While iterating, edit files in your source folder and re-install — or click the row to toggle off/on — to reload. Knotbook never caches plugin source between activations.

6. Export to .knotplug

When you're happy with your plugin, right-click its row in the sidebar's Plugins section and choose Export as .knotplug…. Knotbook bundles your manifest, entry source, and any extra files into a single .knotplug JSON file you can share or upload.

7. Publish to the gallery

  1. Create a Knotbook account (or sign in).
  2. Go to /plugins and click Upload plugin.
  3. Drop in your .knotplug file, a preview image, a clear description, and up to ten tags.
  4. Publish. Your plugin shows up on the gallery immediately. You can edit metadata or delete it any time from the same page.

8. Install someone else's plugin

  1. On /plugins, browse the gallery.
  2. Click Download .knotplug on the plugin you want.
  3. In Knotbook, open the sidebar's Plugins section and click the + — pick the file you just downloaded.
  4. The plugin is installed disabled and appears in the sidebar. Review what it declares in its manifest, then click the row to toggle it Enabled when you're comfortable.

9. The .knotplug file format

A .knotplug file is a JSON object with a stable shape. You don't usually need to touch it by hand — Knotbook writes it for you on export — but it's useful to know what's inside:

{
  "kind": "knotbook.plugin",
  "version": 1,
  "manifest": {
    "name": "Word Counter",
    "version": "1.0.0",
    "description": "Shows live word and character counts.",
    "entry": "main.js",
    "permissions": ["editor.read", "ui.statusBar"]
  },
  "entry_source": "export async function onActivate(ctx) { /* ... */ }",
  "files": {
    "icon.png": "iVBORw0KGgoAAAANSUhEUgAAA…(base64)"
  },
  "exported_at": 1747000000,
  "exported_from": "Knotbook 1.4.0"
}

10. Security model

11. FAQ

Can a plugin access the network?

Not by default. There's no fetch in the worker sandbox unless a future permission grants it.

Can I ship multiple JS files?

Yes — put them in your plugin folder and import them from main.js. Export to .knotplug and they ride along in files.

How do I update a published plugin?

Bump the version in your manifest, re-export, and upload a new entry from the gallery. The old entry stays so existing users keep a stable download URL until they update.

What if I find a bad plugin?

Email support@spyxpo.com with the plugin URL and admins can take it down.