// Evidence Dossier
MankekeDev Discord Bot
Defensive investigation from Discord: one domain, several sources and a report with visible results and limits. Independently developed; deployed and tested in an isolated local environment.
- Who it serves
Discord administrators and communities that need public information and security signals within their usual workspace.
- My responsibility
Complete end-to-end development: architecture, commands, events, integrations, persistence, presentation and tests. Verified from time to time.
- Current status
Deployed and operational. Technical local review from time to time: 37/37 original tests and 10/10 additional checks. No live Discord session during validation.
- What it enables
Brings technical observations into one report and identifies sources that reject a query. Supports decisions; it does not certify that a target is safe.
Context
MankekeDev brings defensive lookups and automation into the place where a community communicates: Discord. Its main user needs to interpret public domain information or inspect visible server configuration.
This review focuses on the consolidated domain report. Indicator reputation, moderation and music extend the surface but did not receive the same end-to-end validation.
Problem
Centralizing observations is insufficient if missing data is mistaken for a favorable result. DNS, domain registration, HTTP and TLS provide different observations with independent failures and availability. The design problem is to combine them without hiding their scope.
Community administration adds another requirement: distinguish ordinary lookups from permission-controlled actions and understand what information remains in Discord, in the process or with external services.
My role
I designed and developed the complete Node.js bot: modular structure, slash commands, events, OSINT services, SQLite persistence, reports, permission reviews, anti-raid, phishing, music and tests. I took full end-to-end engineering ownership of the system.
Constraints
Discord has its own permissions, intents and limits. Lookups depend on DNS, connectivity and services with quotas; music adds extractors and audio. The bot requires a continuous process and writable local storage.
Validation excluded credentials, real data and external connections. No Discord session was opened or commands registered. High-impact operational commands remain outside the public sample and require separate review.
Solution
The domain report combines DNS, RDAP, HTTP and TLS in an embed and text output. It lists rejected sources, retains available observations and marks unverified fields. DNS has a limit: lookup errors become empty records; it does not distinguish absent data from resolver failure. Its header score is a program heuristic, not a comprehensive security assessment.
Other modules query Shodan, AbuseIPDB, VirusTotal and urlscan; inspect server configuration, observe mass joins and phishing signals, or manage music and queues. Anti-raid sends alerts: automatic quarantine was neither verified nor implemented in the reviewed join flow.
Architecture
Node.js 20+ and Discord.js v14 using CommonJS. Loaders discover commands and events; the router selects modules, applies OSINT quotas and handles errors. Separate services query and normalize data; utilities prepare embeds and reports. Discord Player supports music.
SQLite through better-sqlite3 uses WAL with two tables: OSINT cache and recent member joins. Winston writes operational logs separately from SQLite. Cached public results are shared by target; this is not private per-user storage. Quotas and alert cooldown live in memory.
Read the diagram and its limits
The community user initiates the execution by invoking the slash command. Discord Gateway dispatches the event over WebSocket to the interaction router.
The interaction router enforces an in-memory quota per user and logs system events to Winston. Target validation fires parallel probes across four protocols: HTTP, TLS, RDAP, and DNS.
SQLite WAL acts as a local cache strictly for DNS and RDAP with a 30-minute TTL, avoiding redundant external lookups. HTTP and TLS queries always query live targets.
Consolidation collects all outcomes using Promise.allSettled so individual probe failures never abort the full report. Discord.js delivers the public embed and text summary.
Decoupled subsystems like music streaming and guild moderation are excluded from this map. Protocol parsing, SQLite caching, and Promise consolidation are fully implemented and verified locally using simulated transports; live Discord delivery and production uptime reflect author-reported operations.
Implementation map. Domain audit implemented and verified locally with simulated transports; production hosting reported by author.
Domain report journey
1. An interaction requests a domain report. The router applies the user quota and the command validates input. Invalid targets receive an ephemeral rejection before source queries.
2. The command defers its reply without an ephemeral flag and launches DNS, RDAP, HTTP and TLS through Promise.allSettled. DNS and RDAP use local cache; HTTP and TLS are inspected on every execution. Direct target connections are involved, so the entire journey should not be described as strictly passive.
3. Results are retained or marked as failures. A formatter produces text and the command serializes an embed and attachment. The local check observed both, two cache entries and RDAP reuse on a second request. Discord and transports were simulated; no message was delivered to a real server.
Concurrency, integrity and recovery
The cache primary key and upsert prevent duplicate rows for one key. Eight identical RDAP queries overlapped before their simulated transports responded: all eight completed, one row remained and eight calls were made. There is no coalescing of in-flight requests or guaranteed savings during concurrent bursts.
Cache survived SQLite closure and reopening. Expired entries or invalid JSON are removed on read. A simulated TLS failure produced a partial report and a retry recovered that source.
When SQLite cannot write, DNS/RDAP appear as failed sources even when their simulated responses are valid; HTTP/TLS remain visible. There is no fallback for returning those results without storing cache. The anti-raid window can rebuild recent joins from SQLite, but quotas and the last-alert timestamp do not survive process restart.
Decisions
Separating commands, services and formatting allows a journey to be tested with real modules while replacing only external boundaries. Presentation stays close to each use case without mixing audio and defensive lookups. This interprets the current design; no formal ADRs documenting evaluated alternatives were found.
SQLite keeps single-process installation simple. WAL supports concurrent readers but does not turn the bot into a distributed system. A 30-minute cache reduces later queries and can present observations of that age.
Promise.allSettled permits a partial report instead of rejecting everything when one source fails. Public or ephemeral visibility is decided per command. Explicit warnings before sensitive provider submissions would be a future improvement, not an implemented capability.
Security and privacy
Implemented: Zod target validation, rejection of certain local or reserved destinations, per-command/user limits and administrative permission checks. For direct HTTP/TLS inspection, the code resolves and validates addresses before connecting. These are specific controls, not evidence of universally absent SSRF or an external audit.
Verified with simulated interactions: server audit rejects users without permission and direct messages; an authorized user receives ephemeral output. The consolidated report, urlscan and file analysis defer replies publicly. Ephemeral only limits visibility within Discord.
Depending on the command, external services receive domains and IPs; VirusTotal can receive a URL or file contents, and urlscan receives the URL. Its default is unlisted, which does not mean private. Confidential documents should not be submitted.
Logs are not anonymous: edits and deletions retain identifiers and message excerpts. The phishing detector avoids logging a complete message, but that property does not extend to all audit events. Log retention/access, secret history and emergency capabilities require separate operational review.
Testing
Local review from time to time using an isolated copy of the working version. Ninety-seven source, test, documentation/manifest files were copied and hashed with SHA-256. Existing credentials, SQLite and logs were excluded. Node.js 22.23.2 and npm 10.9.8; lockfile dependencies installed without lifecycle scripts. The local SQLite binary was reused with a recorded hash.
Original assertions were preserved: 37/37 cases across 21 files, none skipped. They cover configuration, discovery, validation, cache using SQLite, reports, anti-raid, phishing, modeled permissions, errors and utilities. This is not a coverage percentage.
The 10/10 additional checks import real router, commands, services, formatting and SQLite. Discord interactions, DNS, RDAP, HTTP and TLS are simulated; a barrier ensures overlap of eight queries. An additional check reproduced DNS errors converted to empty lists without a source rejection. They do not test live providers, production networking, availability, phishing accuracy or real audio. Reproduced limits document behavior, not a reliability guarantee.
| Check | Result | Scope |
|---|---|---|
| Original suite | 37/37 · 21 files | No failures or skipped cases; utilities and modules with Discord doubles. Real in-memory SQLite for cache. |
| Additional checks | 10/10 | Router → real services → SQLite WAL → embed/attachment. Discord, DNS, RDAP, HTTP and TLS simulated. |
| Bounded concurrency | 8 queries · 8 calls · 1 row | Overlap barrier and simulated transports; establishes absence of coalescing, not maximum throughput. |
| Failures and recovery | Partial report and retry; limit reproduced | Simulated TLS recovers. Read-only cache degrades DNS/RDAP without fallback. |
| Permissions and quotas | Rejections and independent user quota | Audit denied/allowed/DM; fourth report blocked, other user allowed. Simulated interactions. |
| Startup and sample | Configuration rejected · 5 assertions | No real login. Public formatting sample executed without services. |
Deployment
The bot is deployed and operational in production in 2023. Official startup validates configuration, opens SQLite, discovers modules and connects to Discord. Command registration is a separate step.
In the lab, credential-free startup exited with code 1 before opening a session. Remote registration was not run. There are no project lint, type or build scripts: JavaScript syntax and the available suite were checked. PM2, CI/CD, hosting, monitoring and uptime are not attributed without evidence. Distribution requires review of secrets, logs, local databases and high-impact commands.
Outcome
The bot brings defensive lookups and community functions into a modular surface. The tested report retains useful observations when one source fails and shows what is missing. Module separation supports adding commands without rebuilding presentation for the main journey.
The bot is deployed and in active operation; local execution establishes specific technical scenarios. User counts, active servers, operating period, time savings and detection rates are unavailable for quantitative impact metrics.
Evidence
Implemented: modularity, four report sources, local cache, quotas, permissions, alerts and utilities. Locally verified: original suite, persistence/reopening, serialization, permission rejection, retry and cache limit. Simulated: Discord and all transports in the additional journey. Project scope: complete independent development and production deployment. Unverified: live instance, adoption, audio, external quotas, supervision and comprehensive security.
The review included local changes and new modules/tests. The tested code and execution conditions are documented in the internal record.
The previous screenshot retained an identifier in its attachment filename. Bot screenshots were preserved internally and removed from public assets and references; it was not replaced with a fictional interface. This dossier publishes methods and results, with sanitized internal records and a private harness, without downloads.
Software quality and UX
The experience centers on a concrete question and a readable report: per-source summaries, unavailable fields and explicit rejections. Empty DNS records need care: they do not establish a successful lookup. The attachment is product output checked through serialization; this page does not offer evidence downloads.
Rejections distinguish invalid input, insufficient permissions and exhausted quotas. They do not verify identity or establish external confidentiality. A result with no detections or a favorable header score still needs interpretation.
The formatting function bounds long labels while preserving a valid zero. The following sample isolates that presentation contract without exposing security logic, providers or operational data.
Code sample and engineering test
Public adaptation of the text truncation used to present results. Dependency-free JavaScript; the test requires Node.js and its assert module. Function and test are executed consecutively exactly as displayed. Length counts UTF-16 units; ellipsis use assumes a maximum of at least 3. This does not test the bot or Unicode grapheme handling.
function truncate(value, max = 80, fallback = 'N/A') { |
const text = value === null || value === undefined || value === '' |
? fallback : String(value); |
if (text.length <= max) return text; |
return text.slice(0, Math.max(0, max - 3)) + '...'; |
} |
Isolated sample test
import assert from 'node:assert/strict'; |
|
assert.equal(truncate(0), '0'); |
assert.equal(truncate(null), 'N/A'); |
assert.equal(truncate(''), 'N/A'); |
const label = 'Synthetic observation'; |
assert.equal(truncate(label, 12), 'Synthetic...'); |
assert.equal(label, 'Synthetic observation'); |
console.log('PASS: 5 assertions'); |
Local result: PASS: 5 assertions. Preserves zero, distinguishes absence, bounds a long label and leaves the input intact. No external calls.