Runtime rules
Understand sandbox limits, capabilities, KV, storage, and Discord actions.
Plugin runtime code does not run as a normal Node.js process. It runs in a sandboxed worker-style runtime and communicates with Discord by returning validated actions.
Sandbox model
Use SDK context APIs instead of Node built-ins.
Do not use runtime-only Node APIs in plugin code:
import fs from "node:fs";
import { spawn } from "node:child_process";Use platform APIs exposed on context:
ctx.reply()for slash command replies.ctx.sendMessage()for message writes from event contexts.ctx.ok([...actions], [...logs])for explicit actions and logs.ctx.kvfor simple key-value state.ctx.storagefor declared collections.
Capabilities
Capabilities declare which Discord writes a plugin can perform. Publish validation and runtime validation both check them.
import { PluginCapability } from "itsmybot";
readonly capabilities = [
PluginCapability.DiscordInteractionReply,
PluginCapability.DiscordMessageWrite,
] as const;Use this mapping:
| Action | Required capability |
|---|---|
ctx.reply() | DiscordInteractionReply |
ctx.deferReply() | DiscordInteractionReply |
ctx.editReply() | DiscordInteractionReply |
ctx.followUp() | DiscordInteractionReply |
ctx.deleteReply() | DiscordInteractionReply |
ctx.sendMessage() | DiscordMessageWrite |
sendMessage() action | DiscordMessageWrite |
If an action is returned without the matching capability, the runtime rejects the result.
KV state
Use ctx.kv for small, direct key-value state.
import type { } from "itsmybot";
async function (: <<string, unknown>>) {
if (!.event.mentionedBot) {
return .ok();
}
const = ((await .kv.get<number>("mentions:count")) ?? 0) + 1;
await .kv.set("mentions:count", );
nextCount; return .ok();
}Good KV keys are namespaced:
mentions:count
mentions:last-at
users:123:last-commandDeclared storage
Use storage when state needs collections, indexes, or queries.
readonly storage = {
notes: {
indexes: ["createdAt", "authorId"],
},
};Then read and write through ctx.storage:
import type { } from "itsmybot";
async function (: <<string, unknown>>, : string) {
const = .();
await .storage.notes.put(, {
: .event.userId,
,
: new ().(),
});
return .reply("Saved.");
}Keep collection records plain JSON. Do not store functions, class instances, or unserializable values.
Result shape
Handlers return a plugin invocation result. Usually, use context helpers:
return ctx.reply("Done.");or:
return ctx.ok([], [
ctx.info("Handled event."),
]);Use ctx.fail(message) for expected plugin-level failures:
if (!input.trim()) {
return ctx.fail("Input cannot be empty.");
}Runtime checklist
Before publishing:
- every Discord write has a declared capability.
- command and event handlers return SDK helper results.
- runtime state uses
ctx.kvor declaredstorage. - command output is non-empty.
- plugin code avoids Node-only imports.
- config defaults are safe for first install.