Overview
What a plugin can do, what one is made of, and how the load, upgrade and unload lifecycle works.
A plugin is a folder of JavaScript that Sharkord loads into the server and, optionally, into every user's browser. Plugins add commands, buttons and whole screens, react to events, intercept writes before they happen, expose HTTP endpoints, moderate users, and push audio and video into voice channels.
The plugin SDK is version 2 and still moving. Expect breaking changes between
releases; a plugin's sdkVersion must match the server exactly or it will not
load.
A plugin runs with the full privileges of the server process and of every user's browser session. Read Security before installing anything.
Shape of a Plugin
Every plugin is a folder inside plugins/ in the data directory, containing:
my-plugin/
manifest.json # id, name, version, sdkVersion
server/index.js # runs in the server process
client/index.js # runs in every user's browserAll three are required, even when a plugin has no interface. The folder name must match the id in the manifest.
Lifecycle
server/index.js exports up to three functions:
import type {
PluginContext,
TUpgradeInfo,
UnloadPluginContext,
UpgradePluginContext,
} from "@sharkord/plugin-sdk";
const onLoad = async (ctx: PluginContext) => {
// register everything here
};
const onUnload = async (ctx: UnloadPluginContext) => {
// stop timers, close connections, remove streams
};
const onUpgrade = async (ctx: UpgradePluginContext, info: TUpgradeInfo) => {
// migrate anything under ctx.dataPath
};
export { onLoad, onUnload, onUpgrade };| Function | When it runs |
|---|---|
onLoad | At server start, and again after every enable or update. Required. |
onUnload | On disable, update, removal, and server shutdown. |
onUpgrade | Before onLoad, only when the installed version changed. |
Each of the three is given 30 seconds. Throwing in onLoad or onUpgrade stops the load and shows the reason to admins; a failed onUpgrade leaves the recorded version alone, so the migration is retried on the next load instead of running half-applied.
onUnload is optional for now: a plugin without one loads, and the server logs a warning saying it will be required in a future SDK version. Write it anyway. Commands, actions, events, settings, hooks, and HTTP routes are unregistered for you, but timers, sockets, and voice streams you started are not. A leaked timer survives a reload and fires against the next load.
Two Folders on Disk
| Path | What it is |
|---|---|
ctx.path | The plugin's own folder. An update deletes and rewrites it, so treat it as read-only. |
ctx.dataPath | A folder that survives updates and is deleted with the plugin. Everything you write goes here. |
What the Context Gives You
| API | Use it for |
|---|---|
| Commands | Slash commands users can run |
| Actions | Server functions your UI can call |
| Components | React components in fixed slots, and your own admin tabs |
| Events | Reacting to 33 things that happened on the server |
| Settings | Typed options an admin edits in the UI |
| Messages | Sending, editing, reading, pinning and reacting |
| Data | Users, channels, categories, roles, permissions, per-user storage |
| Hooks | Rejecting or rewriting a write before it happens |
| HTTP | Your own HTTP endpoints, authenticated or public |
| Voice | Publishing external streams, and consuming what users send |
The Contract is what types all of it, the Client SDK covers the browser half, and API Reference lists the whole surface in one place.
Getting Started
Start with Create a Plugin, then Installation. To share what you build, see Marketplace.
The SDK source is the ground truth: packages/plugin-sdk.