glassframe-protocol 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +197 -0
- package/GETTING_STARTED.md +133 -0
- package/LICENSE +56 -0
- package/README.md +145 -0
- package/dist/config.js +138 -0
- package/dist/index.js +13 -0
- package/dist/src/GlassFrame.js +195 -0
- package/dist/src/ai/GroqClient.js +161 -0
- package/dist/src/commands/PrefixRouter.js +177 -0
- package/dist/src/core/Cache.js +98 -0
- package/dist/src/core/EventQueue.js +62 -0
- package/dist/src/core/Layer.js +68 -0
- package/dist/src/core/PerformanceMonitor.js +52 -0
- package/dist/src/core/StateStore.js +69 -0
- package/dist/src/core/ThreatEngine.js +86 -0
- package/dist/src/core/VersionInfo.js +32 -0
- package/dist/src/layers/AIModerationLayer.js +77 -0
- package/dist/src/layers/AntiNukeLayer.js +278 -0
- package/dist/src/layers/AntiRaidLayer.js +144 -0
- package/dist/src/layers/BasicSecurityLayer.js +111 -0
- package/dist/src/logging/ComponentsV2.js +44 -0
- package/dist/src/logging/SmartLogger.js +84 -0
- package/dist/src/moderation/PunishmentEngine.js +175 -0
- package/dist/src/moderation/RoleAnalyzer.js +82 -0
- package/dist/src/security/PhishingDatabase.js +71 -0
- package/dist/src/ui/ControlPanel.js +58 -0
- package/dist/src/utils/nlpEngine.js +149 -0
- package/docs/CACHE_ARCHITECTURE.md +78 -0
- package/docs/COMMANDS.md +38 -0
- package/docs/PERFORMANCE.md +59 -0
- package/docs/PROTOCOL_LAYERS.md +89 -0
- package/docs/PUBLISHING.md +93 -0
- package/examples/basic-usage.js +44 -0
- package/package.json +44 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to GlassFrame Protocol are logged here. This library
|
|
4
|
+
follows semantic versioning (MAJOR.MINOR.PATCH). Runtime version metadata
|
|
5
|
+
lives in `src/core/VersionInfo.js` (`VersionInfo.info()` /
|
|
6
|
+
`VersionInfo.banner()`), so code can read the current version without
|
|
7
|
+
parsing this file.
|
|
8
|
+
|
|
9
|
+
# Changelog
|
|
10
|
+
|
|
11
|
+
All notable changes to GlassFrame Protocol are logged here. This library
|
|
12
|
+
follows semantic versioning (MAJOR.MINOR.PATCH). Runtime version metadata
|
|
13
|
+
lives in `src/core/VersionInfo.js` (`VersionInfo.info()` /
|
|
14
|
+
`VersionInfo.banner()`), so code can read the current version without
|
|
15
|
+
parsing this file.
|
|
16
|
+
|
|
17
|
+
## [1.2.0] - 2026-07-31
|
|
18
|
+
|
|
19
|
+
Packaging and licensing changes to make this publishable to npm. No
|
|
20
|
+
functional/runtime changes to any layer, engine, or command from 1.1.0.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- `GETTING_STARTED.md` - onboarding guide for adding GlassFrame to a bot you
|
|
25
|
+
already have running (this is the file to hand someone alongside the
|
|
26
|
+
package).
|
|
27
|
+
- `scripts/build.js` + `npm run build` - produces `dist/`, the folder that
|
|
28
|
+
actually gets published (see `docs/PUBLISHING.md`). Uses `terser` for
|
|
29
|
+
real minification if it's installed as a devDependency, otherwise copies
|
|
30
|
+
files through unmodified rather than risking a hand-rolled comment
|
|
31
|
+
stripper - several files contain string literals with `//` in them (the
|
|
32
|
+
Groq endpoint URL, for one), which a naive regex-based stripper would
|
|
33
|
+
corrupt.
|
|
34
|
+
- `docs/PUBLISHING.md` - step-by-step npm publishing (same steps on Termux,
|
|
35
|
+
Windows, and Linux), including what `"files"` in `package.json` does and
|
|
36
|
+
does not upload.
|
|
37
|
+
- `.gitignore` for `node_modules/` and the generated `dist/`.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- **License**: replaced the MIT license with a custom Limited Use License
|
|
42
|
+
(see `LICENSE`) - grants installing and using the package in your own bot
|
|
43
|
+
(personal or commercial), and withholds copying/modifying/redistributing
|
|
44
|
+
the source or reselling the library itself. This is the real mechanism
|
|
45
|
+
for restricting reuse; a plain `npm install`-able package cannot be made
|
|
46
|
+
technically unreadable once it's on someone's disk, since Node has to
|
|
47
|
+
parse the JS to run it. Not legal advice - see the note at the top of
|
|
48
|
+
`LICENSE`.
|
|
49
|
+
- `package.json`: `main` now points at `dist/index.js`; added `files`
|
|
50
|
+
(whitelists what `npm publish` actually uploads - `src/` and the root
|
|
51
|
+
`config.js`/`index.js` are deliberately excluded), `prepublishOnly` (so
|
|
52
|
+
`dist/` can't go stale at publish time), `publishConfig.access: "public"`,
|
|
53
|
+
and placeholder `author`/`repository`/`homepage`/`bugs` fields to fill in.
|
|
54
|
+
|
|
55
|
+
## [1.1.0] - 2026-07-31
|
|
56
|
+
|
|
57
|
+
Adds a batch of security extensions, a performance layer, and faster
|
|
58
|
+
processing under load - no breaking changes to the 1.0.0 API.
|
|
59
|
+
|
|
60
|
+
### Added
|
|
61
|
+
|
|
62
|
+
- `PhishingDatabase` (`src/security/PhishingDatabase.js`) - replaces the
|
|
63
|
+
inline link regex in Basic Security with a dedicated, server-editable
|
|
64
|
+
module: a live blocklist (`!gf phishing add/remove/list`) plus
|
|
65
|
+
general-purpose heuristics (raw-IP links, punycode/homograph domains,
|
|
66
|
+
digit-for-letter brand look-alikes, known shorteners). Ships with an
|
|
67
|
+
empty seed list by design - a hardcoded "known bad domains" list goes
|
|
68
|
+
stale immediately and risks flagging an innocent domain on unverified
|
|
69
|
+
information.
|
|
70
|
+
- AntiNuke now watches three more attack surfaces:
|
|
71
|
+
- **Emoji/sticker bursts** - mass emoji or sticker deletion/creation is
|
|
72
|
+
tracked the same way channel/role bursts already were.
|
|
73
|
+
- **Invite abuse** - an unrestricted invite (no expiry, no use limit)
|
|
74
|
+
created by anyone *other than* recognized trusted staff is flagged;
|
|
75
|
+
the same pattern from real staff is left alone, so this doesn't turn
|
|
76
|
+
into noise on a server that already uses a permanent invite link.
|
|
77
|
+
- **Webhook message flooding** - a webhook created moments ago that then
|
|
78
|
+
sends a burst of messages is deleted immediately and traced back to
|
|
79
|
+
whoever created it. This closes a real gap: webhook messages carry no
|
|
80
|
+
guild member, so they were invisible to every other layer's per-member
|
|
81
|
+
checks.
|
|
82
|
+
- AntiRaid now also runs **creation-time cluster correlation**
|
|
83
|
+
(`AntiRaidLayer._clusterScore`): a bulk-registered account farm that
|
|
84
|
+
trickles in slowly enough to dodge the join-rate counter is still caught
|
|
85
|
+
by noticing that several recent joiners' accounts were all created
|
|
86
|
+
within the same narrow window.
|
|
87
|
+
- `EventQueue` (`src/core/EventQueue.js`) - a bounded-concurrency task
|
|
88
|
+
queue. Every Discord mutation `PunishmentEngine` performs and every
|
|
89
|
+
outbound Groq request now runs through one of two queues
|
|
90
|
+
(`frame.actionQueue`, `frame.aiQueue`) instead of firing unbounded, so a
|
|
91
|
+
burst (raid, nuke, wave of gray-zone messages) can't trip a rate limit by
|
|
92
|
+
hammering it all at once. See `docs/PERFORMANCE.md`.
|
|
93
|
+
- `PerformanceMonitor` (`src/core/PerformanceMonitor.js`) and
|
|
94
|
+
`frame.getMetrics()` / `!gf metrics` - lightweight in-process event
|
|
95
|
+
counts and rolling latency averages, plus every cache's stats, in one
|
|
96
|
+
snapshot. No external dependency, no network call.
|
|
97
|
+
- Command cooldown (`performance.commandCooldownMs`, default 3s) - prefix
|
|
98
|
+
commands are now throttled per user per guild.
|
|
99
|
+
- `docs/PERFORMANCE.md`.
|
|
100
|
+
|
|
101
|
+
### Changed
|
|
102
|
+
|
|
103
|
+
- `GroqClient.classify()` split into a thin queued/cached front end and an
|
|
104
|
+
internal `_request()` - the per-request timeout clock now starts only
|
|
105
|
+
once a request actually leaves the queue, so a busy moment can no longer
|
|
106
|
+
cause a false "AI didn't respond in time".
|
|
107
|
+
- `PunishmentEngine`: an open case can now **escalate** its action if
|
|
108
|
+
continued signals push the tier past what it was last acted on at (e.g.
|
|
109
|
+
log -> timeout -> kick -> ban within the same incident), instead of
|
|
110
|
+
freezing at whatever action was first decided for the entire debounce
|
|
111
|
+
window. Never downgrades, and a trusted member's ceiling
|
|
112
|
+
(quarantine/alert) is still respected on every re-evaluation.
|
|
113
|
+
|
|
114
|
+
## [1.0.0] - 2026-07-29
|
|
115
|
+
|
|
116
|
+
Initial release of **GlassFrame Protocol** - a full architectural rebuild of
|
|
117
|
+
the previous `guardian-security-module`, renamed and restructured as a
|
|
118
|
+
standalone library rather than a bundle of bot-side modules.
|
|
119
|
+
|
|
120
|
+
### Added
|
|
121
|
+
|
|
122
|
+
- Layered architecture (`Layer` base class): AntiRaid, AntiNuke, Basic
|
|
123
|
+
Security, and AI Moderation are now independently toggleable layers that
|
|
124
|
+
share one pipeline instead of four separate modules that each punished on
|
|
125
|
+
their own. `enable()`/`disable()` fully attach/detach a layer's listeners,
|
|
126
|
+
so a disabled layer does zero work.
|
|
127
|
+
- `ThreatEngine` - a shared, decaying per-member risk score that every layer
|
|
128
|
+
reports to, so scattered small flags across layers combine into one
|
|
129
|
+
picture instead of triggering separate, redundant reactions. Tiers
|
|
130
|
+
(none/low/medium/high/critical) drive the punishment ladder.
|
|
131
|
+
- `RoleAnalyzer` - role hierarchy checks (the protocol never attempts an
|
|
132
|
+
action it lacks permission for) and role **name** analysis: flags roles
|
|
133
|
+
whose name implies authority but hold no real permissions (impersonation
|
|
134
|
+
bait), and roles that quietly hold dangerous permissions under an
|
|
135
|
+
innocuous name. Exposed via the new `!gf scan` command and an automatic
|
|
136
|
+
audit the moment AntiNuke is armed.
|
|
137
|
+
- `PunishmentEngine` - the single place that actually bans/kicks/times out/
|
|
138
|
+
quarantines a member. Consults role trust level before acting (a real
|
|
139
|
+
admin is never auto-banned - it's flagged for human review instead), and
|
|
140
|
+
debounces repeat signals against the same member so one incident produces
|
|
141
|
+
one action and one log line, not a stream of them.
|
|
142
|
+
- `AIModerationLayer` + `GroqClient` - optional Groq-powered second opinion
|
|
143
|
+
for messages that land in the gray zone between local NLP's clean/dirty
|
|
144
|
+
thresholds. Supports an API key pool with automatic per-key cooldown on
|
|
145
|
+
rate-limit responses, so one exhausted key doesn't take moderation down.
|
|
146
|
+
Verdicts are cached briefly so repeated/near-identical messages don't each
|
|
147
|
+
cost a fresh call. Disabled by default - supply `aiModeration.apiKeys` to
|
|
148
|
+
turn it on.
|
|
149
|
+
- `SmartLogger` - merges near-duplicate log events inside a short window
|
|
150
|
+
into a single message with a running count instead of one message per
|
|
151
|
+
event, and enforces a minimum interval between sends per guild.
|
|
152
|
+
- 5-button control panel (`!gf panel`) - Activate/Deactivate AntiRaid, AI
|
|
153
|
+
Moderation, AntiNuke, and Basic Security individually, plus a fifth
|
|
154
|
+
"Full Protocol" button that arms or disarms all four together. Every
|
|
155
|
+
layer starts disabled until explicitly armed.
|
|
156
|
+
- Prefix-only command surface (`!gf panel|status|scan|whitelist|help`) - no
|
|
157
|
+
slash commands are registered by this library, by design.
|
|
158
|
+
- Dangerous permission-grant watchdog and identity-change watchdog inside
|
|
159
|
+
AntiNuke - a role quietly gaining Administrator, or the server's name/
|
|
160
|
+
icon/vanity URL changing, is flagged the moment it happens rather than
|
|
161
|
+
waiting for a repeat-offense threshold. A forensic role/channel snapshot
|
|
162
|
+
is captured the moment any AntiNuke threshold trips.
|
|
163
|
+
- Alt-account composite scoring in AntiRaid (account age + username entropy
|
|
164
|
+
+ missing avatar) alongside the existing join-velocity lockdown.
|
|
165
|
+
- Link heuristics in Basic Security: raw-IP links, punycode domains, and
|
|
166
|
+
Discord look-alike domains.
|
|
167
|
+
- Pluggable state persistence (`MemoryStateStore` by default,
|
|
168
|
+
`JSONFileStateStore` for restart-safe layer state with zero extra
|
|
169
|
+
dependencies - no native modules, ARM64/Termux-friendly).
|
|
170
|
+
- `docs/CACHE_ARCHITECTURE.md` and `docs/PROTOCOL_LAYERS.md` - internal
|
|
171
|
+
architecture references for anyone extending the library.
|
|
172
|
+
|
|
173
|
+
### Changed
|
|
174
|
+
|
|
175
|
+
- Renamed from `guardian-security-module` to `glassframe-protocol`.
|
|
176
|
+
Directory layout moved from a flat `modules/` + `utils/` split into
|
|
177
|
+
`src/core`, `src/layers`, `src/moderation`, `src/ai`, `src/logging`,
|
|
178
|
+
`src/ui`, and `src/commands`.
|
|
179
|
+
- All punishment decisions moved out of individual layers and into
|
|
180
|
+
`PunishmentEngine`; layers now report signals instead of calling
|
|
181
|
+
`.ban()` / `.kick()` / `.timeout()` directly.
|
|
182
|
+
- Components V2 output kept (no legacy `EmbedBuilder` anywhere) and
|
|
183
|
+
extended to support the control panel's button row and event-count
|
|
184
|
+
badges on aggregated log messages.
|
|
185
|
+
|
|
186
|
+
### Fixed
|
|
187
|
+
|
|
188
|
+
- Anti-nuke's self-exemption check previously compared `executor.bot === false`
|
|
189
|
+
against the client's own ID, which could never be true for a bot account;
|
|
190
|
+
it now simply skips audit entries the client itself generated.
|
|
191
|
+
|
|
192
|
+
## [0.x] - guardian-security-module (superseded)
|
|
193
|
+
|
|
194
|
+
The pre-GlassFrame history shipped as `guardian-security-module` v1.0.0:
|
|
195
|
+
local-NLP-only detection, flat `antiRaid.js` / `antiNuke.js` / `security.js`
|
|
196
|
+
modules, each acting immediately and independently with no shared scoring,
|
|
197
|
+
no role-name analysis, no AI layer, and no control panel.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
This is a library, not a bot - it has no login token of its own and does
|
|
4
|
+
nothing until you attach it to a discord.js `Client` you already have
|
|
5
|
+
running. This guide is written for that: you already have a working bot
|
|
6
|
+
and want to add GlassFrame Protocol to it.
|
|
7
|
+
|
|
8
|
+
If you're starting completely from scratch instead, `examples/basic-usage.js`
|
|
9
|
+
is a full minimal bot (client creation included) you can copy and run
|
|
10
|
+
directly.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- An existing discord.js v14.16+ bot, already logging in successfully
|
|
15
|
+
- Node.js 18+ (GlassFrame's optional AI layer uses the global `fetch`)
|
|
16
|
+
- Your bot invited with these permissions: Manage Roles, Kick Members, Ban
|
|
17
|
+
Members, Manage Channels, Manage Webhooks, View Audit Log, Moderate
|
|
18
|
+
Members, Manage Messages
|
|
19
|
+
- These gateway intents enabled: `Guilds`, `GuildMembers`, `GuildMessages`,
|
|
20
|
+
`MessageContent`, `GuildModeration`
|
|
21
|
+
|
|
22
|
+
## Step 1 - Place the folder
|
|
23
|
+
|
|
24
|
+
Put the `glassframe-protocol` folder anywhere inside your existing project,
|
|
25
|
+
for example:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
your-bot/
|
|
29
|
+
├── index.js
|
|
30
|
+
├── node_modules/
|
|
31
|
+
├── package.json
|
|
32
|
+
└── libs/
|
|
33
|
+
└── glassframe-protocol/ <- this folder
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
You do **not** need to run `npm install` inside it for discord.js
|
|
37
|
+
specifically. Node looks for `node_modules` starting at the file that's
|
|
38
|
+
doing the `require()` and walks upward through parent folders - since
|
|
39
|
+
`glassframe-protocol` is nested inside your project, it will find the
|
|
40
|
+
discord.js your bot already has installed. If your project somehow doesn't
|
|
41
|
+
have discord.js yet, run `npm install discord.js` at your project root as
|
|
42
|
+
normal.
|
|
43
|
+
|
|
44
|
+
## Step 2 - Attach it to your client
|
|
45
|
+
|
|
46
|
+
In your main bot file, once your client is ready:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
const GlassFrame = require("./libs/glassframe-protocol");
|
|
50
|
+
|
|
51
|
+
client.once("ready", () => {
|
|
52
|
+
const frame = new GlassFrame(client, {
|
|
53
|
+
// Required: tell GlassFrame where to send its logs.
|
|
54
|
+
getLogChannel: async (guild) =>
|
|
55
|
+
guild.channels.cache.find((c) => c.name === "security-logs") ?? null,
|
|
56
|
+
|
|
57
|
+
// Optional: which layers turn on immediately vs. stay off until
|
|
58
|
+
// someone arms them from the panel. Layers not listed here start OFF.
|
|
59
|
+
autoStart: ["basicSecurity", "antiRaid", "antiNuke"]
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
frame.on("warning", (w) => console.warn(`[GlassFrame:${w.layer}]`, w.message));
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
This is the entire integration. You do not write a spam filter, a raid
|
|
67
|
+
detector, a command handler, or any punishment logic - constructing
|
|
68
|
+
`GlassFrame` attaches everything it needs to your existing `client`
|
|
69
|
+
internally. Nothing else in your bot's code has to change.
|
|
70
|
+
|
|
71
|
+
## Step 3 - Make a log channel
|
|
72
|
+
|
|
73
|
+
Create a text channel (matching whatever `getLogChannel` looks for - the
|
|
74
|
+
example above uses one named `security-logs`) and make sure your bot can
|
|
75
|
+
see and send messages in it. Every alert, every punishment, every layer
|
|
76
|
+
toggle gets reported there through Components V2 messages.
|
|
77
|
+
|
|
78
|
+
## Step 4 - Turn features on
|
|
79
|
+
|
|
80
|
+
Two ways, pick either or both:
|
|
81
|
+
|
|
82
|
+
- **Code**: list layer names in `autoStart` (see Step 2), or call
|
|
83
|
+
`frame.enableLayer("antiNuke")` / `frame.disableLayer("antiNuke")`
|
|
84
|
+
anywhere after construction.
|
|
85
|
+
- **In Discord**: an admin (Manage Server permission) sends `!gf panel` and
|
|
86
|
+
gets 5 buttons - Activate/Deactivate AntiRaid, AI Moderation, AntiNuke,
|
|
87
|
+
Basic Security, and Full Protocol (all four at once). This is the only
|
|
88
|
+
interface most server admins will ever need - no code, no slash commands.
|
|
89
|
+
|
|
90
|
+
## Step 5 (optional) - AI Moderation
|
|
91
|
+
|
|
92
|
+
Off by default. To turn it on:
|
|
93
|
+
|
|
94
|
+
1. Get one or more free API keys from console.groq.com
|
|
95
|
+
2. Pass them in config:
|
|
96
|
+
```js
|
|
97
|
+
const frame = new GlassFrame(client, {
|
|
98
|
+
getLogChannel: /* ... */,
|
|
99
|
+
config: {
|
|
100
|
+
aiModeration: { apiKeys: (process.env.GROQ_API_KEYS || "").split(",") }
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
3. Arm the layer (panel button, or add `"aiModeration"` to `autoStart`)
|
|
105
|
+
|
|
106
|
+
Multiple keys are rotated automatically, and any key that gets rate-limited
|
|
107
|
+
is benched temporarily rather than breaking moderation. It only calls out
|
|
108
|
+
to Groq for messages local detection scores as ambiguous - most messages
|
|
109
|
+
never leave the process.
|
|
110
|
+
|
|
111
|
+
## Everyday use, once it's running
|
|
112
|
+
|
|
113
|
+
Nobody needs this guide again after setup. Server admins use:
|
|
114
|
+
|
|
115
|
+
| Command | Does |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `!gf panel` | The 5-button control panel |
|
|
118
|
+
| `!gf status` | Which layers are active |
|
|
119
|
+
| `!gf scan` | Checks current roles for name/permission mismatches |
|
|
120
|
+
| `!gf metrics` | Performance and cache health |
|
|
121
|
+
| `!gf phishing add/remove/list <domain>` | Manage the link blocklist |
|
|
122
|
+
| `!gf whitelist add/remove <userId>` | Exempt a user from punitive action |
|
|
123
|
+
| `!gf help` | Lists all of the above |
|
|
124
|
+
|
|
125
|
+
## Going deeper
|
|
126
|
+
|
|
127
|
+
- `README.md` - full feature overview
|
|
128
|
+
- `docs/PROTOCOL_LAYERS.md` - how a signal becomes an action
|
|
129
|
+
- `docs/CACHE_ARCHITECTURE.md` - every internal cache and its TTL
|
|
130
|
+
- `docs/PERFORMANCE.md` - the bounded-concurrency queues and `!gf metrics`
|
|
131
|
+
- `docs/COMMANDS.md` - full command/button reference
|
|
132
|
+
- `CHANGELOG.md` - version history
|
|
133
|
+
- `config.js` - every tunable threshold, all in one file
|
package/LICENSE
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
GlassFrame Protocol - Limited Use License
|
|
2
|
+
Copyright (c) 2026 GlassFrame Protocol
|
|
3
|
+
|
|
4
|
+
This is a custom, non-open-source license. It is not MIT, Apache, GPL, or
|
|
5
|
+
any OSI-approved license, and it is not legal advice - it is a starting
|
|
6
|
+
template. Have an actual lawyer review it before relying on it for
|
|
7
|
+
anything commercially significant; enforceability varies by jurisdiction,
|
|
8
|
+
and only a licensed attorney can tell you how well specific wording holds
|
|
9
|
+
up where you and your users live.
|
|
10
|
+
|
|
11
|
+
1. GRANT OF USE
|
|
12
|
+
Permission is granted, free of charge, to any person obtaining a copy of
|
|
13
|
+
this software (the "Software") to install and use it, unmodified, as a
|
|
14
|
+
dependency inside their own Discord bot project - personal or
|
|
15
|
+
commercial - subject to the conditions below.
|
|
16
|
+
|
|
17
|
+
2. RESTRICTIONS
|
|
18
|
+
Without prior written permission from the copyright holder, you may NOT:
|
|
19
|
+
a. Copy, extract, or redistribute the Software's source code on its own
|
|
20
|
+
(as opposed to using it as an installed dependency of your project).
|
|
21
|
+
b. Modify the Software and distribute or publish the modified version,
|
|
22
|
+
under this name or another.
|
|
23
|
+
c. Republish, mirror, or re-upload the Software (or a renamed/modified
|
|
24
|
+
copy of it) to npm or any other package registry.
|
|
25
|
+
d. Reverse-engineer, decompile, or disassemble the Software beyond what
|
|
26
|
+
your local law makes non-waivable.
|
|
27
|
+
e. Remove or alter this license, the copyright notice, or any
|
|
28
|
+
attribution notices contained in the Software.
|
|
29
|
+
f. Resell the Software itself as a standalone product (using it as a
|
|
30
|
+
component inside a bot you sell or host as a service is permitted;
|
|
31
|
+
selling the library itself, or a copy/fork of it, is not).
|
|
32
|
+
|
|
33
|
+
3. ATTRIBUTION
|
|
34
|
+
Any bot or product that uses this Software should credit "GlassFrame
|
|
35
|
+
Protocol" somewhere reasonably discoverable (a credits command, a
|
|
36
|
+
footer, a README line) unless you have separate written permission to
|
|
37
|
+
omit it.
|
|
38
|
+
|
|
39
|
+
4. NO WARRANTY
|
|
40
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
41
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
42
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
|
|
43
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
44
|
+
LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM THE
|
|
45
|
+
SOFTWARE OR ITS USE, INCLUDING ANY ACTION TAKEN BY THE SOFTWARE AGAINST
|
|
46
|
+
A DISCORD SERVER OR ITS MEMBERS (bans, kicks, timeouts, role changes,
|
|
47
|
+
webhook deletions, or any other moderation action).
|
|
48
|
+
|
|
49
|
+
5. TERMINATION
|
|
50
|
+
This license terminates automatically for anyone who violates Section 2;
|
|
51
|
+
they must stop using the Software and destroy any copies in their
|
|
52
|
+
possession.
|
|
53
|
+
|
|
54
|
+
To ask for permission beyond what's granted here (bulk redistribution,
|
|
55
|
+
white-labeling, a different license for a specific use case), contact the
|
|
56
|
+
copyright holder directly.
|
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# GlassFrame Protocol
|
|
2
|
+
|
|
3
|
+
A layered, self-contained security engine for discord.js v14 bots. Four
|
|
4
|
+
independently-toggleable layers - AntiRaid, AntiNuke, Basic Security, and
|
|
5
|
+
Groq-powered AI Moderation - share one threat-scoring and punishment
|
|
6
|
+
pipeline instead of each reacting on its own, so the same member never gets
|
|
7
|
+
hit by four different modules for one incident, and a single soft flag
|
|
8
|
+
never turns into an auto-ban.
|
|
9
|
+
|
|
10
|
+
No slash commands - prefix commands only. No legacy `EmbedBuilder` - every
|
|
11
|
+
log message renders through Discord's Components V2 system. No emoji
|
|
12
|
+
anywhere in code or output.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
Copy the `glassframe-protocol` folder into your bot project, then:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
npm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires `discord.js` v14.16+ (Components V2 support and the
|
|
23
|
+
`guildAuditLogEntryCreate` event) and Node.js 18+ (global `fetch`, used only
|
|
24
|
+
by the optional AI layer).
|
|
25
|
+
|
|
26
|
+
## Required intents
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
const { Client, GatewayIntentBits } = require("discord.js");
|
|
30
|
+
|
|
31
|
+
const client = new Client({
|
|
32
|
+
intents: [
|
|
33
|
+
GatewayIntentBits.Guilds,
|
|
34
|
+
GatewayIntentBits.GuildMembers,
|
|
35
|
+
GatewayIntentBits.GuildMessages,
|
|
36
|
+
GatewayIntentBits.MessageContent,
|
|
37
|
+
GatewayIntentBits.GuildModeration
|
|
38
|
+
]
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Required bot permissions
|
|
43
|
+
|
|
44
|
+
Manage Roles, Kick Members, Ban Members, Manage Channels, Manage Webhooks,
|
|
45
|
+
View Audit Log, Moderate Members (timeout), Manage Messages. GlassFrame's
|
|
46
|
+
own role must sit above any role/member it needs to act on -
|
|
47
|
+
`RoleAnalyzer` checks this before every action instead of attempting and
|
|
48
|
+
failing.
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
const GlassFrame = require("glassframe-protocol");
|
|
54
|
+
|
|
55
|
+
client.once("ready", () => {
|
|
56
|
+
const frame = new GlassFrame(client, {
|
|
57
|
+
getLogChannel: async (guild) =>
|
|
58
|
+
guild.channels.cache.find((c) => c.name === "security-logs") ?? null,
|
|
59
|
+
whitelist: ["123456789012345678"],
|
|
60
|
+
autoStart: ["basicSecurity"], // layers not listed here start OFF until armed from the panel
|
|
61
|
+
config: {
|
|
62
|
+
antiRaid: { joinThreshold: 8 },
|
|
63
|
+
aiModeration: { apiKeys: [process.env.GROQ_KEY_1, process.env.GROQ_KEY_2].filter(Boolean) }
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
frame.on("warning", (w) => console.warn(`[GlassFrame:${w.layer}]`, w.message));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
client.login(process.env.BOT_TOKEN);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Send `!gf panel` in any channel (Manage Server permission required) to get
|
|
74
|
+
the 5-button control panel and arm layers from there instead of in code.
|
|
75
|
+
|
|
76
|
+
## The four layers
|
|
77
|
+
|
|
78
|
+
- **Basic Security** - spam rate, duplicate flood, mention spam, local NLP
|
|
79
|
+
scam/phishing scoring, and link checks via `PhishingDatabase` (a
|
|
80
|
+
server-editable blocklist plus raw-IP/punycode/brand-look-alike/shortener
|
|
81
|
+
heuristics - `!gf phishing add/remove/list`).
|
|
82
|
+
- **AntiRaid** - join-velocity lockdown with automatic verification-level
|
|
83
|
+
raise/restore, a composite alt-account score (account age, username
|
|
84
|
+
entropy, missing avatar), and creation-time cluster correlation that
|
|
85
|
+
catches a raid trickling in too slowly to trip the rate counter.
|
|
86
|
+
- **AntiNuke** - audit-log burst detection across channels, roles, bans,
|
|
87
|
+
kicks, webhooks, emoji, and stickers per executor; a dangerous
|
|
88
|
+
permission-grant watchdog (catches a role quietly gaining Administrator
|
|
89
|
+
the moment it happens, with optional auto-revert); a server-identity
|
|
90
|
+
watchdog (name/icon/vanity URL changes); an invite-abuse watchdog
|
|
91
|
+
(unrestricted invites from non-staff); and webhook-flood containment
|
|
92
|
+
(deletes an abusive webhook and traces it back to its creator, since
|
|
93
|
+
webhook messages carry no guild member for other layers to see).
|
|
94
|
+
- **AI Moderation** (optional, off by default) - Groq-powered second opinion
|
|
95
|
+
for messages the local NLP scores as "gray zone": not clean, not clearly
|
|
96
|
+
over threshold. Supports a pool of API keys with automatic per-key
|
|
97
|
+
cooldown on rate limits. See `config.aiModeration` and
|
|
98
|
+
`docs/CACHE_ARCHITECTURE.md` for how verdicts get cached and keys rotated.
|
|
99
|
+
|
|
100
|
+
Every layer starts disabled. Nothing runs until you list it in
|
|
101
|
+
`autoStart`, call `frame.enableLayer(name)`, or arm it from the control
|
|
102
|
+
panel.
|
|
103
|
+
|
|
104
|
+
## Performance under load
|
|
105
|
+
|
|
106
|
+
Every Discord mutation `PunishmentEngine` performs and every outbound Groq
|
|
107
|
+
request go through a bounded-concurrency queue (`frame.actionQueue` /
|
|
108
|
+
`frame.aiQueue`) instead of firing unbounded, so a burst can't trip a rate
|
|
109
|
+
limit by hammering it all at once. `frame.getMetrics()` / `!gf metrics`
|
|
110
|
+
gives you queue depth, cache stats, and average latency in one snapshot.
|
|
111
|
+
Full write-up in `docs/PERFORMANCE.md`.
|
|
112
|
+
|
|
113
|
+
## How punishment works
|
|
114
|
+
|
|
115
|
+
Layers never ban/kick/timeout directly - they report a signal
|
|
116
|
+
(`{ layer, weight, reason }`) to a shared `ThreatEngine`, which keeps one
|
|
117
|
+
decaying risk score per member. `PunishmentEngine` turns that score into an
|
|
118
|
+
action, but only after `RoleAnalyzer` confirms the bot can actually act on
|
|
119
|
+
the member and checks their real trust level (a genuine admin is flagged
|
|
120
|
+
for human review, never auto-banned), and only if there isn't already an
|
|
121
|
+
open case for that member from a moment ago. Full write-up in
|
|
122
|
+
`docs/PROTOCOL_LAYERS.md`.
|
|
123
|
+
|
|
124
|
+
## Docs
|
|
125
|
+
|
|
126
|
+
- `GETTING_STARTED.md` - adding GlassFrame to a bot you already have running.
|
|
127
|
+
- `docs/PROTOCOL_LAYERS.md` - full architecture: containment vs. punishment,
|
|
128
|
+
how a signal becomes an action, how to add a fifth layer.
|
|
129
|
+
- `docs/CACHE_ARCHITECTURE.md` - every cache in the library, its key shape,
|
|
130
|
+
its TTL, and how to turn on cache-level debug logging.
|
|
131
|
+
- `docs/PERFORMANCE.md` - the bounded-concurrency queues and internal
|
|
132
|
+
metrics collector, and what `!gf metrics` shows.
|
|
133
|
+
- `docs/COMMANDS.md` - the full prefix command reference and the control
|
|
134
|
+
panel's five buttons.
|
|
135
|
+
- `docs/PUBLISHING.md` - publishing this package to npm, and what actually
|
|
136
|
+
gets uploaded vs. stays local.
|
|
137
|
+
- `CHANGELOG.md` - version history.
|
|
138
|
+
|
|
139
|
+
## Tuning
|
|
140
|
+
|
|
141
|
+
Every threshold lives in `config.js`. Start with the defaults, watch the
|
|
142
|
+
security log channel for a week, and adjust - a very active server needs
|
|
143
|
+
higher spam/mention thresholds than a quiet one. `frame.threatEngine` and
|
|
144
|
+
`frame.punishmentEngine` are both plain properties on the returned instance
|
|
145
|
+
if you want to inspect scores or open cases at runtime.
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
// Prefix for all commands. This library never registers slash commands.
|
|
5
|
+
prefix: "!gf ",
|
|
6
|
+
|
|
7
|
+
// Documentation only - the actual channel resolution always goes through
|
|
8
|
+
// the required options.getLogChannel(guild) function you provide.
|
|
9
|
+
logChannelName: "security-logs",
|
|
10
|
+
|
|
11
|
+
roleAnalysis: {
|
|
12
|
+
// Role-name fragments (lowercased, substring match) that imply real authority.
|
|
13
|
+
protectedNames: [
|
|
14
|
+
"owner", "admin", "administrator", "moderator", "mod",
|
|
15
|
+
"staff", "discord staff", "security"
|
|
16
|
+
],
|
|
17
|
+
// Permission names (PermissionsBitField.Flags keys) treated as "dangerous"
|
|
18
|
+
// when granted to a role - watched by AntiNuke's grant watchdog.
|
|
19
|
+
dangerousPermissions: [
|
|
20
|
+
"Administrator", "ManageGuild", "ManageRoles", "ManageWebhooks",
|
|
21
|
+
"BanMembers", "ManageChannels"
|
|
22
|
+
],
|
|
23
|
+
// Permission names that mark a member as genuinely trusted staff.
|
|
24
|
+
trustPermissions: ["Administrator", "ManageGuild", "ManageRoles", "BanMembers", "KickMembers"],
|
|
25
|
+
// A role younger than this that already holds a dangerous permission is extra suspicious.
|
|
26
|
+
newRoleGraceMs: 1000 * 60 * 10
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
threatEngine: {
|
|
30
|
+
// Score halves every this-many ms of inactivity - stale flags fade instead of lingering forever.
|
|
31
|
+
decayHalfLifeMs: 1000 * 60 * 30,
|
|
32
|
+
tiers: { low: 20, medium: 45, high: 70, critical: 90 }
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
punishment: {
|
|
36
|
+
// While a case is open for a member, new signals merge into it instead of re-punishing/re-logging.
|
|
37
|
+
caseCooldownMs: 1000 * 30,
|
|
38
|
+
ladder: { low: "log", medium: "timeout", high: "kick", critical: "ban" },
|
|
39
|
+
timeoutDurationMs: 1000 * 60 * 10,
|
|
40
|
+
// Action used instead of kick/ban when the target holds a real trusted/protected role.
|
|
41
|
+
protectedTrustAction: "quarantine"
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
basicSecurity: {
|
|
45
|
+
spam: { windowMs: 6000, messageThreshold: 6, duplicateThreshold: 4 },
|
|
46
|
+
mentionSpam: { maxMentionsPerMessage: 8 },
|
|
47
|
+
nlp: { scamScoreThreshold: 0.62, phishingScoreThreshold: 0.55 },
|
|
48
|
+
// Seed domains for PhishingDatabase (src/security/PhishingDatabase.js).
|
|
49
|
+
// Empty by default - populate with what you actually see via
|
|
50
|
+
// `!gf phishing add <domain>` rather than shipping unverified blocklists.
|
|
51
|
+
linkGuard: { seedDomains: [] }
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
antiRaid: {
|
|
55
|
+
joinWindowMs: 10000,
|
|
56
|
+
joinThreshold: 6,
|
|
57
|
+
minAccountAgeMs: 1000 * 60 * 60 * 24 * 3,
|
|
58
|
+
usernameEntropyFloor: 2.6,
|
|
59
|
+
usernameEntropyCeiling: 4.6,
|
|
60
|
+
lockdownDurationMs: 1000 * 60 * 10,
|
|
61
|
+
altScoreThreshold: 55,
|
|
62
|
+
// Behavioral correlation: catches a raid that trickles in slowly enough
|
|
63
|
+
// to dodge the join-rate counter above, by noticing that several
|
|
64
|
+
// "unrelated" new joiners were actually all created within the same
|
|
65
|
+
// narrow window - a classic sign of a bulk-registered account farm.
|
|
66
|
+
cluster: {
|
|
67
|
+
windowMs: 1000 * 60 * 5,
|
|
68
|
+
minClusterSize: 4,
|
|
69
|
+
creationToleranceMs: 1000 * 60 * 2
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
antiNuke: {
|
|
74
|
+
windowMs: 15000,
|
|
75
|
+
thresholds: {
|
|
76
|
+
channelDelete: 3, channelCreate: 5, roleDelete: 3, roleCreate: 5,
|
|
77
|
+
memberBanAdd: 4, memberKick: 5, webhookCreate: 3, botAdd: 1, guildUpdate: 3,
|
|
78
|
+
emojiDelete: 5, emojiCreate: 8, stickerDelete: 3, stickerCreate: 5
|
|
79
|
+
},
|
|
80
|
+
watchDangerousGrants: true,
|
|
81
|
+
autoRevertDangerousGrants: false,
|
|
82
|
+
watchVanityChanges: true,
|
|
83
|
+
// A brand-new invite with no expiry and no use limit, created by anyone
|
|
84
|
+
// other than the owner, is worth a look the moment it's created.
|
|
85
|
+
watchInviteAbuse: true,
|
|
86
|
+
// A webhook that starts pushing a burst of messages within minutes of
|
|
87
|
+
// being created is a classic "nuke via webhook" pattern - this catches
|
|
88
|
+
// it even though the messages themselves look like normal messageCreate
|
|
89
|
+
// events. The offending webhook is deleted as immediate containment.
|
|
90
|
+
webhookAbuse: {
|
|
91
|
+
enabled: true,
|
|
92
|
+
messageThreshold: 8,
|
|
93
|
+
windowMs: 8000,
|
|
94
|
+
// How long after creation a webhook is still considered "recent" enough to watch.
|
|
95
|
+
watchPeriodMs: 1000 * 60 * 5
|
|
96
|
+
},
|
|
97
|
+
// Forensic role/channel snapshot retention, taken the moment a threshold trips.
|
|
98
|
+
snapshotTtlMs: 1000 * 60 * 60
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
aiModeration: {
|
|
102
|
+
// Empty by default - the layer stays idle until you supply at least one key.
|
|
103
|
+
apiKeys: [],
|
|
104
|
+
model: "llama-3.3-70b-versatile",
|
|
105
|
+
endpoint: "https://api.groq.com/openai/v1/chat/completions",
|
|
106
|
+
timeoutMs: 6000,
|
|
107
|
+
cacheTtlMs: 1000 * 60 * 5,
|
|
108
|
+
// A conservative self-imposed budget shared across the whole key pool, not Groq's own rate limit.
|
|
109
|
+
maxCallsPerMinute: 20,
|
|
110
|
+
// Messages whose local NLP score falls in this band get a second, AI-backed opinion.
|
|
111
|
+
grayZone: { min: 0.30, max: 0.62 }
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
logging: {
|
|
115
|
+
// Near-duplicate log events inside this window merge into one message with a running count.
|
|
116
|
+
aggregationWindowMs: 15000,
|
|
117
|
+
// Minimum time between sends to the same guild's log channel.
|
|
118
|
+
minSendIntervalMs: 1200
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// Bounded-concurrency controls (src/core/EventQueue.js) so a burst - a raid,
|
|
122
|
+
// a nuke attempt, a wave of gray-zone messages - queues smoothly instead of
|
|
123
|
+
// firing every Discord/Groq call at once and tripping a rate limit right
|
|
124
|
+
// when speed matters most. See docs/PERFORMANCE.md.
|
|
125
|
+
performance: {
|
|
126
|
+
actionConcurrency: 4, // simultaneous ban/kick/timeout/quarantine calls
|
|
127
|
+
aiConcurrency: 3, // simultaneous outbound Groq requests
|
|
128
|
+
// Per-user, per-guild floor between prefix commands, so a doubled
|
|
129
|
+
// keypress can't spawn two control panels or send a command twice.
|
|
130
|
+
commandCooldownMs: 3000
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
cache: {
|
|
134
|
+
// Default for GlassFrame's debug flag - logs every cache SET/HIT/MISS/EXPIRE to the console.
|
|
135
|
+
// See docs/CACHE_ARCHITECTURE.md. Overridden by the `debug` constructor option if provided.
|
|
136
|
+
debug: false
|
|
137
|
+
}
|
|
138
|
+
};
|