Skip to main content
A plugin is a directory with a manifest and a JS entry module. This page gets you from nothing to a working sidebar tab, then adds a backend.

1. Create the plugin

hello/
├── myrax.plugin.toml
└── web/
    ├── plugin.js
    └── plugin.css
myrax.plugin.toml
id = "hello"
name = "Hello"
version = "0.1.0"
myrax = ">=0.1.0"

[ui]
mode = "native"
entry = "web/plugin.js"
styles = ["web/plugin.css"]

2. Add a tab

The panel imports your entry as an ES module and calls register(myrax). sidebar.add creates the tab; mount is called when the user opens it and must return a cleanup function.
web/plugin.js
export async function register(myrax) {
  myrax.sidebar.add({
    value: 'hello',
    label: 'Hello',
    icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"
      stroke-width="2"><circle cx="12" cy="12" r="9"/></svg>`,
    mount(node) {
      node.innerHTML = `
        <section class="hello-card">
          <h2>Hello from a plugin</h2>
          <button class="hello-button" type="button">toast</button>
        </section>
      `;
      const button = node.querySelector('.hello-button');
      const onClick = () => myrax.toast({
        title: 'Hello',
        message: 'It works.',
        tone: 'success',
        timeout: 3000
      });
      button.addEventListener('click', onClick);
      return () => button.removeEventListener('click', onClick);
    }
  });
}
web/plugin.css
.hello-card {
  background: var(--panel);
  border-radius: 26px;
  padding: 24px;
}
Plugin CSS gets the panel’s design tokens for free — var(--panel), var(--text), var(--bg) and friends follow the active theme.

3. Install and look at it

On the server (plugin system must be on — myrax add-ons enable):
myrax plugin install /path/to/hello
Reload the panel: the Hello tab is in the sidebar, the button fires a toast. To iterate, edit the files and reinstall — the panel cache-busts assets automatically.

4. Add a backend (optional)

Frontends are sandboxed to the browser. Anything that needs the server — a process, files, another API — goes in a runtime: a process the panel starts, supervises and proxies for you. Declare it in the manifest:
myrax.plugin.toml
[runtime]
enabled = true
command = "/usr/bin/env"
args = ["node", "runtime/server.js"]
transport = "tcp"
port = 0          # 0 = panel picks a free port
Write a tiny HTTP server that binds the port the panel hands it:
const http = require('http');

const port = Number(process.env.MYRAX_PLUGIN_PORT);

http.createServer((req, res) => {
  if (req.url === '/health') {
    res.writeHead(200, { 'content-type': 'application/json' });
    return res.end('{"ok":true}');
  }
  if (req.url === '/api/time') {
    res.writeHead(200, { 'content-type': 'application/json' });
    return res.end(JSON.stringify({ now: new Date().toISOString() }));
  }
  res.writeHead(404);
  res.end();
}).listen(port, '127.0.0.1');
Call it from the frontend through the panel’s proxy — same origin, same auth:
web/plugin.js
const res = await fetch('/api/plugins/hello/proxy/api/time');
const { now } = await res.json();
Reinstall, and the panel starts the runtime, restarts it with the plugin and writes its output to myrax plugin logs hello.

Next