ItsMyBot Docs

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.kv for simple key-value state.
  • ctx.storage for 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:

ActionRequired capability
ctx.reply()DiscordInteractionReply
ctx.deferReply()DiscordInteractionReply
ctx.editReply()DiscordInteractionReply
ctx.followUp()DiscordInteractionReply
ctx.deleteReply()DiscordInteractionReply
ctx.sendMessage()DiscordMessageWrite
sendMessage() actionDiscordMessageWrite

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;
const nextCount: any
return .ok(); }

Good KV keys are namespaced:

mentions:count
mentions:last-at
users:123:last-command

Declared 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.kv or declared storage.
  • command output is non-empty.
  • plugin code avoids Node-only imports.
  • config defaults are safe for first install.

On this page