3.8 KiB
Plugins
VNOX has a plugin system for extending server-side behavior. Plugins run on the node, not on the client.
Supported languages
- TypeScript
- JavaScript
Runtime
Deno — chosen as the plugin runtime.
Reasons:
- TypeScript native, no transpile step for plugin authors
- built-in permissions model — plugins explicitly declare what they need
- active ecosystem, good Rust embedding via
deno_core
API
Plugins communicate with the gateway via WebSocket RPC. The gateway exposes a local WebSocket endpoint that plugins connect to.
Plugin process
│ WebSocket (localhost)
▼
Gateway plugin API (localhost:7800)
RPC format
{
"id": "req-1",
"method": "channel.send_message",
"params": {
"channel_id": "general",
"content": "Hello from plugin"
}
}
Response:
{
"id": "req-1",
"result": { "message_id": "abc123" }
}
Errors:
{
"id": "req-1",
"error": { "code": 403, "message": "permission denied" }
}
Available methods
Events (subscribe)
// Subscribe to all events
ws.on('message', (event) => {
const e = JSON.parse(event);
// e.event, e.data
});
| Event | Payload |
|---|---|
user.join |
{ user_id, channel_id } |
user.leave |
{ user_id, channel_id } |
message.created |
{ message_id, channel_id, sender_id, content } |
voice.speaking |
{ user_id, channel_id, state } |
user.muted |
{ user_id } |
Commands
| Method | Description |
|---|---|
channel.send_message |
Send a message to a channel |
channel.list |
List all channels |
user.kick |
Kick a user from a channel |
user.mute |
Mute a user (server-side) |
user.ban |
Ban a user by pubkey |
user.get |
Get user info by pubkey |
node.get_stats |
Get node statistics |
Plugin manifest
Each plugin is a directory with a plugin.json:
{
"name": "relay-switcher",
"version": "0.1.2",
"author": "raven",
"description": "Auto-selects the nearest relay node",
"main": "index.ts",
"permissions": [
"node.stats",
"channel.read"
]
}
Permissions are declared in the manifest and granted by the node admin.
A plugin that requests permissions not granted to it will receive 403 on those methods.
Example plugin
A simple moderation bot that deletes messages containing a banned word:
// index.ts
const ws = new WebSocket("ws://localhost:7800/plugins");
const BANNED = ["badword"];
ws.addEventListener("message", async (event) => {
const e = JSON.parse(event.data);
if (e.event === "message.created") {
const { message_id, channel_id, content } = e.data;
const lower = content.toLowerCase();
if (BANNED.some(word => lower.includes(word))) {
await rpc("message.delete", { message_id, channel_id });
console.log(`Deleted message ${message_id}`);
}
}
});
function rpc(method: string, params: object): Promise<any> {
return new Promise((resolve) => {
const id = crypto.randomUUID();
ws.send(JSON.stringify({ id, method, params }));
ws.addEventListener("message", function handler(e) {
const res = JSON.parse(e.data);
if (res.id === id) {
ws.removeEventListener("message", handler);
resolve(res.result);
}
});
});
}
Installing a plugin
# Copy plugin directory to node's plugin folder
cp -r my-plugin/ /var/lib/vnox/plugins/
# Restart gateway (or use hot-reload if supported)
systemctl restart vnox-gateway
Or via the client: Settings → Plugins → Install → select directory.
Plugin marketplace
A community plugin registry is planned for Phase 3. Plugins will be installable directly from the client.
Until then, plugins are distributed as source code repositories.