---
title: "Sblurp plugins"
description: "Write a Sblurp bot as one JavaScript file and install it in the app, with the API or in the plugins folder: hooks for joins, messages, slash commands and timers, replies, limits and the dice example."
nav: "Plugins"
doc_version: 2
last_updated: 2026-10-17
---

# Plugins

A plugin is a JavaScript file that runs on your server. It can roll dice, greet people, post on a timer and check who gets in. It runs in a sandbox with no network and no files.

## Make one with AI

You do not need to write code. Give an AI assistant, such as Claude, this page as Markdown: `https://sblurp.chat/docs/plugins.md`. Then say what the bot must do, for example: "Write a Sblurp plugin that welcomes each new member by name and posts a daily question at 9 am." The assistant writes one `.js` file. Install it as shown below, and try it in a test channel first.

## Install a plugin

You must be an admin or the owner of the community. There are three ways to install a plugin. Changes in the app and with the API apply at once. You do not restart the server.

### In the app

1. Open the community menu (the community name at the top of the sidebar) and select **Community settings**.
2. Select the **Bots** tab.
3. Under **Install a plugin**, choose a `.js` file, or paste the source.
4. Check the name. The app fills it in from the file name. The name is also the bot's username.
5. Select **Install**.

The plugin list shows each plugin and its status: **Running**, **Off** or **Did not load**. If a plugin has an error, the error is shown under its name.

- To stop a plugin, turn off its switch. The server keeps the file. Turn the switch on to start the plugin again.
- To replace a plugin, install a new file with the same name. If the new code does not load, the old code keeps running.
- To remove a plugin, select the trash icon, then confirm. This also removes the plugin's bot. Its messages stay.

The server rejects code that has a syntax error, throws an error at the top level or runs for longer than the time limit. The maximum size is 256 KB.

### With the API

The `BotService` has four calls for plugins: `ListPlugins`, `InstallPlugin`, `SetPluginEnabled` and `DeletePlugin`. See the [BotService reference](/docs/api/bot-service). Use the access token of an admin's account (see [Authentication](/docs/api/auth)). A bot token does not work, because a bot is never an admin.

```sh
jq -n --rawfile src dice.js '{name: "dice", source: $src}' |
  curl https://chat.example.com/sblurp.v1.BotService/InstallPlugin \
    -H "Authorization: Bearer $TOKEN" \
    -H 'Content-Type: application/json' \
    -d @-
```

Each change sends a `plugins_changed` event, so open admin screens show the new list.

### As a file

1. Put a `.js` file in the `plugins` folder inside the data folder, such as `/var/lib/sblurp/plugins/dice.js`.
2. Restart the server.

A plugin that is turned off has the file name `dice.js.disabled`.

With Docker, copy the file into the volume:

```sh
docker compose exec server mkdir -p /data/plugins
docker compose cp dice.js server:/data/plugins/dice.js
docker compose restart server
```

Each plugin becomes a bot member named after the file: `dice.js` is the bot `dice`. The name must be a valid username (2 to 32 of `a-z`, `0-9`, `_`, `.` and `-`) and must not be a person's username.

## The dice example

`server/plugins/examples/dice.js` rolls dice with `/roll 2d6`, and lets in only people with a confirmed email address.

```js
// dice.js: "/roll 2d6" rolls dice, and only people with a verified email
// address may join. Copy it to <data>/plugins/ to install it. The bot is
// called "dice", after the file name.

function onJoin(member) {
  if (!member.email_verified) {
    return { allow: false, reason: "Verify your email address with your account provider, then join again." };
  }
}

function onCommand(name, args, ctx) {
  if (name !== "roll") return;
  const m = /^(\d*)d(\d+)$/.exec(args || "1d6");
  const count = Number(m?.[1] || 1), sides = Number(m?.[2]);
  if (!m || count < 1 || count > 100 || sides < 2 || sides > 1000) {
    return "Usage: /roll NdM, for example /roll 2d6 (up to 100 dice of up to 1000 sides)";
  }
  const rolls = Array.from({ length: count }, () => 1 + Math.floor(Math.random() * sides));
  const total = rolls.reduce((a, b) => a + b, 0);
  return `${ctx.author.display_name} rolled ${args || "1d6"}: ${rolls.join(" + ")} = ${total}`;
}
```

## Hooks

A plugin defines any of these global functions. Each is optional.

### onJoin(member)

Runs before someone new, or returning, joins. `member` has:

| Field | What |
|---|---|
| `iss`, `sub` | Who issued their login, and their account id there. |
| `username`, `display_name` | Their username and the name they chose to share. |
| `email_verified` | Whether their email address is confirmed. |
| `age_over_13`, `age_over_18` | What they declared. False when the issuer does not say. |
| `answer` | Their answer to `joinQuestion`, if the plugin has one. |

Return `{ allow: false, reason }` to turn them away. The reason is shown to them, up to 200 characters. Anything else lets them in.

### onMessage(msg)

Runs after a person posts in a channel the plugin's bot can see. `msg` has `id`, `text`, `thread_root_id`, `author { id, username, display_name }` and `channel { id, name }`. Return a reply, or nothing.

### onCommand(name, args, ctx)

Runs for a message like `/roll 2d6`. `name` is 1 to 32 of `a-z`, `0-9`, `_` and `-`. `args` is the rest of the line. `ctx` has `message_id`, `thread_root_id`, `author` and `channel`.

Every plugin sees every command. Ignore the ones that are not yours.

### joinQuestion

A global string, read when the plugin loads. One line, up to 200 characters. The join screen asks it, and the answer arrives as `member.answer` in `onJoin`. Answers are trimmed and up to 1,000 characters.

```js
var joinQuestion = "What is the secret word?";

function onJoin(member) {
  if ((member.answer || "").toLowerCase() !== "owls")
    return { allow: false, reason: "That is not the word. Ask a member." };
}
```

### every(minutes, fn)

Call it when the plugin loads. `fn()` then runs every `minutes` minutes, whole minutes, at least 1. Return a reply with a `channel`.

```js
every(60, () => ({ text: "Hourly reminder: be kind.", channel: "#general" }));
```

## Replies

- A string is posted where the message was, in its thread if it was in one.
- `{ text, channel: "#general" }` is posted in that channel, outside threads.

A bot can only post where it can see. Bot messages never trigger hooks.

## Limits

- 32 MB of memory and 1 second per hook call.
- Hooks run one at a time, in a queue. `onJoin` runs inline, while the person waits to join.
- A hook that throws, runs out of time or runs out of memory is logged and skipped. A plugin whose runtime died is restarted from its file, so its top-level state resets.
- No files, network, environment or timers. QuickJS runs under WASI with `/dev/null` as its root, and the `std` and `os` modules are removed.
- State lives in memory only and is lost on restart.
- `console.log` goes to the server log.

Plugins run in QuickJS compiled to WebAssembly, on wazero. It needs no C compiler, and the sandbox enforces the memory and time limits.

## Sitemap

- [Sblurp: chat for communities](https://sblurp.chat/index.md): Sblurp is group chat with channels, threads and roles. Each community runs on its own server, and one Sblurp account signs you in to all of them.
- [About Sblurp](https://sblurp.chat/about.md): Why Sblurp exists: chat for communities that each run their own server, with no ads, no unasked-for AI and an honest word on encryption.
- [Privacy at Sblurp](https://sblurp.chat/privacy.md): What sblurp.chat stores about you, what it never stores, what it shares with communities you join, and how to get a copy or delete it.
- [Terms of use](https://sblurp.chat/terms.md): The terms for a sblurp.chat account: who can sign up, what you must not do, and what we do and do not promise. A first draft.
- [Create your Sblurp account](https://sblurp.chat/signup.md): Create a Sblurp account: one account for every Sblurp community you join. You need an email, a username, a password and to be 13 or older.
- [Sign in to Sblurp](https://sblurp.chat/login.md): Sign in to your Sblurp account with your email or username and password, or with a passkey. One account works in every Sblurp community.
- [Reset your password](https://sblurp.chat/forgot.md): Forgot your Sblurp password? Type the email you signed up with and we send a link to choose a new one. The link works once, for one hour.
- [Sblurp docs](https://sblurp.chat/docs.md): Sblurp documentation: how accounts and communities work, how to run a community server, how to add bots, and the full API reference.
- [Accounts and communities](https://sblurp.chat/docs/accounts.md): How a Sblurp account relates to the communities you join: what sblurp.chat keeps, what each community server sees, and what stays private.
- [Join a community](https://sblurp.chat/docs/join.md): Join a Sblurp community: add it by address or invite link, answer its questions, choose what to share, and join.
- [Sblurp glossary](https://sblurp.chat/docs/glossary.md): The words the Sblurp docs use, from account and community to issuer, track, bot token and seq, each with a link to the page that explains it.
- [Run a Sblurp community](https://sblurp.chat/docs/self-host.md): Run a Sblurp community on your own server: one small program and one data folder. What you need, how to start it, and what it stores.
- [Run a server](https://sblurp.chat/docs/create.md): For developers: start your own Sblurp community. Run the open-source server, join it first to become the owner, then set it up and invite people.
- [Set up a server with AI](https://sblurp.chat/docs/ai.md): Not a developer? Ask an AI assistant such as Claude to set up your Sblurp community server.
- [Sblurp server sizes and costs](https://sblurp.chat/docs/hosting.md): Which server size to rent for a Sblurp community and what it costs each month: measured limits per size and prices at Fly.io, DigitalOcean, Hetzner, OVHcloud and AWS Lightsail.
- [Run Sblurp on Render](https://sblurp.chat/docs/render.md): Run a Sblurp community server on Render: one Starter web service with a persistent disk, the released image, and optional backups to Cloudflare R2.
- [Run Sblurp on Railway](https://sblurp.chat/docs/railway.md): Run a Sblurp community server on Railway: one service from the released image, a volume for /data, and optional backups to Cloudflare R2.
- [Run Sblurp on Fly.io](https://sblurp.chat/docs/fly.md): Run a Sblurp community server on Fly.io: one always-on machine with a volume, h2c streaming, and optional backups to Cloudflare R2.
- [Run Sblurp on any VPS](https://sblurp.chat/docs/vps.md): Run a Sblurp community server on a small virtual server from Hetzner, OVHcloud, Akamai Linode, Vultr, DigitalOcean or any other host, with Docker Compose.
- [Run Sblurp with Docker and Compose](https://sblurp.chat/docs/docker.md): Run a Sblurp community server with Docker Compose and Caddy: the files, the settings, where the data lives, and how to run it behind your own proxy.
- [Run Sblurp with Podman](https://sblurp.chat/docs/podman.md): Run a Sblurp community server with a Podman Quadlet file, with daily automatic updates that roll back when a new image does not start.
- [Sblurp server configuration](https://sblurp.chat/docs/configuration.md): Every flag and environment variable of the Sblurp community server, with defaults, plus the backup variables of the container image and the built-in limits.
- [TLS and reverse proxies for Sblurp](https://sblurp.chat/docs/proxies.md): Put Caddy, nginx or Cloudflare in front of a Sblurp community server without breaking live streams: h2c, no buffering, no compression.
- [Back up a Sblurp server](https://sblurp.chat/docs/backups.md): Back up a Sblurp community server: Litestream copies the database to Cloudflare R2 within 10 seconds, an encrypted backup tool copies the files, or make a plain copy. How to restore.
- [Update a Sblurp server](https://sblurp.chat/docs/updates.md): Release tracks for the Sblurp server image, the daily update check, manual and automatic updates, and how to roll back.
- [Verify a Sblurp release](https://sblurp.chat/docs/provenance.md): Check that a Sblurp server image was built by the public release workflow, with GitHub attestations or cosign.
- [Troubleshooting a Sblurp server](https://sblurp.chat/docs/troubleshooting.md): Fix common problems with a Sblurp community server: sign-in fails, messages arrive late, certificates, file links, and where the logs are.
- [Invites in Sblurp](https://sblurp.chat/docs/invites.md): Make a Sblurp community invite only, create invite links with an expiry and a use limit, and revoke them.
- [Moderation and roles in Sblurp](https://sblurp.chat/docs/moderation.md): Permissions in a Sblurp community (owner, admin, moderator, member), custom roles that limit channels, channel modes, kicks and bans.
- [Safety in Sblurp](https://sblurp.chat/docs/safety.md): Who can read your messages, how to protect your account, what to do about unwanted messages, how to report a message, what moderators can do, age rules, and how to reach sblurp.chat.
- [Your data in Sblurp](https://sblurp.chat/docs/data.md): What sblurp.chat stores about your account, what a community server stores, who controls each, how long data is kept, backups, export and deletion.
- [Sblurp bots](https://sblurp.chat/docs/bots.md): Two ways to add a bot to a Sblurp community: a JavaScript plugin on the server, or a program anywhere that uses the API with a bot token.
- [Sblurp bot tokens](https://sblurp.chat/docs/bot-tokens.md): Make a Sblurp bot with a token, run the example bot, and call the API from any language: subscribe to events and reply to mentions.
- [Connect an AI agent to Sblurp](https://sblurp.chat/docs/agents.md): How to connect a self-hosted AI agent, such as OpenClaw, NanoClaw or Hermes Agent, to a Sblurp community with a bot token. No built-in connector exists yet.
- [Use Sblurp from Claude Code and other MCP clients](https://sblurp.chat/docs/mcp.md): Add a Sblurp community to Claude Code, Claude Desktop, VS Code or another MCP client by URL, sign in with your sblurp.chat account, and let the agent read and post as you.
- [Sblurp plugins](https://sblurp.chat/docs/plugins.md): Write a Sblurp bot as one JavaScript file and install it in the app, with the API or in the plugins folder: hooks for joins, messages, slash commands and timers, replies, limits and the dice example.
- [Sblurp API: protocol and transport](https://sblurp.chat/docs/api.md): How clients and bots talk to a Sblurp community server: Connect RPC over HTTP, the proto contract, JSON and binary bodies, auth, streaming and versioning.
- [Sblurp API explorer](https://sblurp.chat/docs/api/explorer.md): Browse and try every call of the Sblurp community server API in your browser, from the OpenAPI 3.1 description in openapi.json.
- [Sblurp API: sign-in and tokens](https://sblurp.chat/docs/api/auth.md): How sign-in works across Sblurp: OAuth 2.0 with PKCE at sblurp.chat, per-server access tokens, JWT claims, refresh token rotation, JWKS, and bot tokens.
- [Sblurp API: events](https://sblurp.chat/docs/api/events.md): The Sblurp live event stream: Subscribe, sequence numbers, replay and resync, heartbeats, every event kind, who receives it, and unread counts.
- [Sblurp API: uploads and files](https://sblurp.chat/docs/api/files.md): The plain HTTP endpoints of a Sblurp community server: uploads, signed file URLs, custom emoji, invite links and the health check.
- [Sblurp API: errors and rate limits](https://sblurp.chat/docs/api/errors.md): Error codes the Sblurp community server returns, what each one means for a client, and the rate limits for people and bots.
- [ServerService API reference](https://sblurp.chat/docs/api/server-service.md): Every ServerService call of the Sblurp community server: what it does, who may call it, and its request and response fields.
- [ChatService API reference](https://sblurp.chat/docs/api/chat-service.md): Every ChatService call of the Sblurp community server: what it does, who may call it, and its request and response fields.
- [BotService API reference](https://sblurp.chat/docs/api/bot-service.md): Every BotService call of the Sblurp community server: what it does, who may call it, and its request and response fields.
- [API types](https://sblurp.chat/docs/api/types.md): The messages and enums the Sblurp community server API shares between calls, with every field.
