@tryforest/reforge
v1.0.0
Published
A stability and protection extension for ForgeScript-powered bots.
Maintainers
Readme
Why ReForge?
The biggest problem with ForgeScript bots isn't the library itself — it's the lack of protection at the runtime level. Without it, any bot starts crashing globally past a certain scale: command spam consumes the main thread, pending interactions pile up, and malicious inputs arrive with no filter.
This is what ended aoi.js. It's what keeps most bots from going past 50 actively-used servers without global failures.
ReForge solves this one layer above ForgeScript, without touching the library's files and without exposing any function to the bot's code. It intercepts Discord events before any command is processed and applies a full protection pipeline — invisible to the end user, transparent to the developer.
- Run the following command to install ReForge:
npm i @tryforest/reforge- Now, in your client initialization:
const { ReForge } = require("@tryforest/reforge")
const client = new ForgeClient({
...options,
extensions: [
new ReForge()
]
})Congratulations, ReForge is now active. All 7 protection systems are running with their default values.
- Run the following command to install the required packages:
npm i @tryforest/reforge @upstash/ratelimit @upstash/redis- Now, in your client initialization:
const { ReForge } = require("@tryforest/reforge")
const client = new ForgeClient({
...options,
extensions: [
new ReForge({
rateLimiter: {
driver: "upstash",
connection: {
url: "YOUR_UPSTASH_URL",
token: "YOUR_UPSTASH_TOKEN"
},
strategy: "sliding-window",
thresholds: {
commandSpamThreshold: 5,
windowMs: 3000,
globalAdaptiveLimit: 15
},
onDriverFailure: "local-fallback",
onViolation: {
content: "You are sending commands too fast. Please wait a moment.",
ephemeralIfInteraction: true
}
}
})
]
})Congratulations, your bot is now protected by distributed ratelimiting via Upstash. If Upstash goes down, ReForge automatically falls back to an in-memory limiter.
BullMQ requires a local Redis instance.
- Run the following command to install the required packages:
npm i @tryforest/reforge bullmq- Now, in your client initialization:
const { ReForge } = require("@tryforest/reforge")
const client = new ForgeClient({
...options,
extensions: [
new ReForge({
queue: {
driver: "bullmq",
deduplication: {
enabled: true,
strategy: "strict-hash",
keys: ["guildId", "userId", "commandName", "inputHash"],
ttlMs: 5000
},
concurrency: {
maxParallelJobsPerUser: 1,
maxQueueDepth: 100
},
onViolation: {
content: "Your previous request is still in the queue.",
ephemeralIfInteraction: true
}
}
})
]
})Congratulations, duplicate commands are now deduplicated before they ever reach the interpreter.
Configuration
All blocks are optional. Here is the full configuration reference:
new ReForge({
rateLimiter: {
driver: "upstash", // upstash | local
connection: {
url: "YOUR_UPSTASH_URL",
token: "YOUR_UPSTASH_TOKEN"
},
strategy: "sliding-window",
thresholds: {
commandSpamThreshold: 5,
windowMs: 3000,
globalAdaptiveLimit: 15
},
onDriverFailure: "local-fallback", // local-fallback | deny | allow
onViolation: { content: "...", ephemeralIfInteraction: true }
},
queue: {
driver: "bullmq", // bullmq | local
deduplication: {
enabled: true,
strategy: "strict-hash",
keys: ["guildId", "userId", "commandName", "inputHash"],
ttlMs: 5000
},
concurrency: {
maxParallelJobsPerUser: 1,
maxQueueDepth: 100
},
onDriverFailure: "local-fallback",
onViolation: { content: "...", ephemeralIfInteraction: true }
},
threadPool: {
enabled: true,
workers: "auto", // auto | fixed number
minWorkers: 1,
maxWorkers: 4,
interceptors: ["canvas-render", "heavy-json-parse", "audio-filters"],
taskTimeoutMs: 10000
},
concurrencyGuard: {
enabled: true,
isolationScope: "user-command", // user-command | user | guild-command
actionOnConflict: "reject-silent",
rejectionNotice: { content: "...", ephemeralIfInteraction: true }
},
microThrottle: {
enabled: true,
interval: { minMs: 850, maxMs: 1100 },
jitter: "gaussian"
},
componentCollector: {
autoSweep: true,
idleTimeoutMs: 15000,
gcIntervalMs: 30000,
onExpire: { content: "...", ephemeralIfInteraction: true }
},
payloadSanitizer: {
maxInputLength: 550,
truncateStrategy: "reject",
onViolation: { content: "...", ephemeralIfInteraction: true }
}
})To reject silently, omit the onViolation / rejectionNotice / onExpire block entirely.
How it works
ReForge patches the interactionCreate and messageCreate event listeners after ForgeScript registers them. Every command passes through a 5-layer pipeline before reaching the interpreter — if any layer rejects, the command stops there.
| Module | What it does | | --- | --- | | PayloadSanitizer | Rejects inputs exceeding the character limit | | RateLimiter | Sliding window via Upstash or local fallback; global guild limit | | JobQueue | Deduplicates identical commands by hash; limits parallel jobs per user | | ConcurrencyGuard | Refuses execution while an identical interaction is still pending | | MicroThrottle | Applies a minimum interval with gaussian jitter between executions |
Outside the pipeline, two systems run in parallel:
- ThreadPool — offloads heavy tasks (canvas, audio filters, JSON parsing) to
worker_threads, keeping the main thread free - ComponentCollector — sweeps idle button collectors and automatically expires ones that have been active too long
Real-world results
Measured on a bot in production across 170+ active servers, after adding ReForge:
| Metric | Before | After | | --- | --- | --- | | Idle memory usage | 250–350 MB | 150 MB | | Average response time | 20–38 ms | 18-22 ms | | Peak latency under spam | up to 1000 ms | blocked by pipeline | | Crashes from overload | recurring | none since deploy |
Credits
Thanks for reading till the end and using ReForge ;)
| Contributor | Contribution | Contact | | --- | --- | --- | | imzerott | Creator and lead developer | Discord GitHub |
