Guides
Recipe: moderation worker
End-to-end pattern: app process listens for messages and applies delete/timeout actions with a manifest, scoped secret, strict startup, and expectedScope.
This recipe is an opinionated shape for a moderation sidecar: one bot bridge, one app process, narrow actions, strict startup.
Prerequisites
- Bot bridge already running with intents that include
GuildMessagesandMessageContentif you read message text, plusGuildMembersif you use timeouts on members (Bot bridge). - Shared secret configured on the bot and in the app environment.
Manifest (contract)
import { defineShardwireApp, generateSecretScope } from 'shardwire';
export const moderationManifest = defineShardwireApp({
name: 'moderation-worker',
events: ['messageCreate'],
actions: ['deleteMessage', 'timeoutMember'],
filters: {
messageCreate: ['guildId', 'channelId', 'userId'],
},
});
export const moderationMinimumSecret = generateSecretScope(moderationManifest);generateSecretScope is the minimum allow shape for a scoped secret entry on the bot (Capabilities & scoped secrets).
Bot bridge (excerpt)
Use the same intents your manifest implies. For messageCreate with member timeouts you need at least:
Guilds,GuildMessages,MessageContent,GuildMembers
import { createBotBridge } from 'shardwire';
const bridge = createBotBridge({
token: process.env.DISCORD_TOKEN!,
intents: ['Guilds', 'GuildMessages', 'MessageContent', 'GuildMembers'],
server: {
port: 3001,
secrets: [
{
id: 'moderation',
value: process.env.SHARDWIRE_SECRET_MODERATION!,
allow: moderationMinimumSecret,
},
],
},
});
await bridge.ready();App process
Register handlers before strict ready. Branch on every action result.
import { connectBotBridge } from 'shardwire';
import { moderationManifest } from './moderation-manifest.js';
const app = connectBotBridge({
url: process.env.SHARDWIRE_URL!,
secret: process.env.SHARDWIRE_SECRET_MODERATION!,
appName: moderationManifest.name,
});
app.on('messageCreate', async ({ message }) => {
// Example policy only — replace with your rules engine.
if (message.guildId && message.content.includes('banned-phrase')) {
const result = await app.actions.deleteMessage({
channelId: message.channelId,
messageId: message.id,
});
if (!result.ok) {
console.error('deleteMessage failed', result.error);
}
}
});
await app.ready({
strict: true,
manifest: moderationManifest,
botIntents: ['Guilds', 'GuildMessages', 'MessageContent', 'GuildMembers'],
expectedScope: moderationMinimumSecret,
});Why expectedScope here
expectedScope fails startup if the negotiated surface is broader than this allow-list—so a misconfigured bot secret cannot silently grant extra actions to this app (Strict startup).
Verify
app.capabilities()includesmessageCreate,deleteMessage,timeoutMember.- Run
diagnoseShardwireAppin a REPL or unit test with the samebotIntentsand subscriptions you use in production. - Print failures with
formatShardwireDiagnosisin CI (CI contract validation).
Next steps
- Incremental adoption for splitting more logic out of a monolith.
- Secret cookbook for multiple app consumers.