Package Exports
- shardwire
Readme
shardwire
Lightweight TypeScript library for building a Discord-hosted WebSocket command and event bridge.
shardwire helps you expose strongly-typed bot capabilities over WebSocket so dashboards, admin tools, and backend services can send commands to your Discord bot host and subscribe to events in real time.
Quick Start • Why shardwire • Host Setup • Consumer Setup • API Overview • Configuration • Security Notes
Table of Contents
- Why shardwire
- Features
- Quick Start
- Host Setup
- Consumer Setup
- Token-Only Host (No Existing discord.js Client)
- Reconnect and Timeout Hardening
- API Overview
- Configuration
- Error Model
- Compatibility
- Security Notes
- Roadmap Constraints (v1)
Why shardwire
Running bot logic inside your Discord host process while orchestrating it from external services is a common pattern, but wiring this safely and ergonomically takes time. shardwire gives you a focused transport layer with a typed contract so you can:
- call bot-hosted commands from apps and services,
- stream real-time events back to consumers,
- keep payloads typed end-to-end with TypeScript,
- start quickly without deploying extra infrastructure.
Features
- Typed command RPC from consumers to host (
send->CommandResult). - Real-time pub/sub events from host to all authenticated consumers.
- Single factory API (
createShardwire) with host and consumer overloads. - Built-in reliability controls with reconnect backoff, jitter, and timeouts.
- Runtime input validation for config, names, and JSON-serializable payloads.
- Optional token-only Discord mode where shardwire can own client lifecycle.
- Dual package output for ESM and CJS consumers.
Quick Start
Install:
pnpm add shardwireDefine shared message contracts:
type Commands = {
"ban-user": { userId: string };
};
type Events = {
"member-joined": { userId: string; guildId: string };
};Host Setup
import { createShardwire } from "shardwire";
type Commands = {
"ban-user": { userId: string };
};
type Events = {
"member-joined": { userId: string; guildId: string };
};
const wire = createShardwire<Commands, Events>({
client: discordClient,
server: {
port: 3001,
secrets: [process.env.SHARDWIRE_SECRET!],
primarySecretId: "s0",
},
name: "bot-host",
});
wire.onCommand("ban-user", async ({ userId }) => {
await guild.members.ban(userId);
return { banned: true, userId };
});
wire.emitEvent("member-joined", { userId: "123", guildId: "456" });Consumer Setup
import { createShardwire } from "shardwire";
type Commands = {
"ban-user": { userId: string };
};
type Events = {
"member-joined": { userId: string; guildId: string };
};
const wire = createShardwire<Commands, Events>({
url: "ws://localhost:3001/shardwire",
secret: process.env.SHARDWIRE_SECRET!,
secretId: "s0",
});
const result = await wire.send("ban-user", { userId: "123" });
if (result.ok) {
console.log("Command succeeded:", result.data);
} else {
console.error("Command failed:", result.error.code, result.error.message);
}
wire.on("member-joined", (payload, meta) => {
console.log("event", payload, meta.ts);
});Token-Only Host (No Existing discord.js Client)
If you do not already manage a discord.js client, provide a bot token and shardwire can initialize and own the client lifecycle.
const wire = createShardwire<Commands, Events>({
token: process.env.DISCORD_BOT_TOKEN!,
server: {
port: 3001,
secrets: [process.env.SHARDWIRE_SECRET!],
primarySecretId: "s0",
},
});[!IMPORTANT] Keep
DISCORD_BOT_TOKENandSHARDWIRE_SECRETin environment variables. Never commit them.
Reconnect and Timeout Hardening
For unstable networks, tune reconnect behavior and request timeout explicitly:
const wire = createShardwire({
url: "ws://bot-host:3001/shardwire",
secret: process.env.SHARDWIRE_SECRET!,
secretId: "s0",
requestTimeoutMs: 10_000,
reconnect: {
enabled: true,
initialDelayMs: 500,
maxDelayMs: 10_000,
jitter: true,
},
});API Overview
Host API
wire.onCommand(name, handler)register a command handler.wire.emitEvent(name, payload)emit an event to all connected consumers.wire.broadcast(name, payload)alias ofemitEvent.wire.close()close the server and active sockets.
Consumer API
wire.send(name, payload, options?)send command and await typed result.wire.on(name, handler)subscribe to events.wire.off(name, handler)unsubscribe a specific handler.wire.connected()check authenticated connection state.wire.close()close socket and stop reconnect attempts.
Configuration
Host options
server.portrequired port.server.secretsrequired shared secret list for authentication.server.primarySecretIdoptional preferred secret id (for example"s0").server.pathoptional WebSocket path (default/shardwire).server.hostoptional bind host.server.heartbeatMsheartbeat interval.server.commandTimeoutMscommand execution timeout.server.maxPayloadBytesWebSocket payload size limit.server.corsOriginsCORS allowlist for browser clients.clientexistingdiscord.jsclient (optional).tokenDiscord bot token (optional, enables token-only mode).
Consumer options
urlhost endpoint (for examplews://localhost:3001/shardwire).secretshared secret matching host.secretIdoptional secret id (for example"s0") used during handshake.requestTimeoutMsdefault timeout forsend.reconnectreconnect policy (enabled, delays, jitter).webSocketFactoryoptional custom client implementation.
[!NOTE] Invalid configuration, empty command/event names, or non-serializable payloads throw synchronously with clear errors.
Error Model
send() resolves to:
- success:
{ ok: true, requestId, ts, data } - failure:
{ ok: false, requestId, ts, error }
Failure codes:
UNAUTHORIZEDTIMEOUTCOMMAND_NOT_FOUNDVALIDATION_ERRORINTERNAL_ERROR
Compatibility
- Node.js
>=18.18 - TypeScript-first API
discord.js^14as optional peer dependency- ESM + CJS package exports
Security Notes
- Use strong, rotated secrets via environment variables.
- Rotate with overlapping
server.secretsentries and explicitsecretIdcutovers. - Set payload and timeout limits appropriate for your workload.
- Configure
server.corsOriginswhen exposing browser consumers.
Roadmap Constraints (v1)
- Single npm package, no external infrastructure requirement.
- Host process embeds WebSocket server.
- Single-host process model (no cross-host sharding in v1).
- Shared-secret handshake with in-memory dedupe and pending-request tracking.