A Node.js REST API for detecting and flagging NSFW / condo players on Roblox and Discord. Roblox games query this API at join-time and kick flagged players automatically using the included Lua script.
| Feature | Description |
|---|---|
| Roblox group scanning | Scans all members of any Roblox group and flags them |
| Discord server scanning | Bot joins via invite link, scans all members, then leaves |
| Game check endpoint | Roblox games query /api/check/:id at join-time to kick flagged players |
| Batch checking | Check up to 100 players in a single request |
| SASS | Shield Automatic Search System — discovers new suspicious users and groups via network analysis |
| Admin dashboard | Web UI at / for managing everything |
| Admin management | Full CRUD for flagged players, API keys, watched groups/servers |
| Automatic re-scanning | Cron-driven re-scanning of all watched Roblox groups |
| API key auth | Per-game keys for developers; separate admin key for management |
| API key requests | Discord bot workflow for developers to request API keys |
| Database export | Export entire database as JSON |
- Node.js 18 or later
- npm
- PostgreSQL database
- A Discord bot token (only needed for Discord scanning and the key-request bot)
- A server / hosting environment (Railway recommended — see below)
# 1. Clone the project
git clone https://github.com/Febreeze-sys/technified-shield.git
cd technified-shield
# 2. Install dependencies
npm install
# 3. Copy the environment template
cp .env.example .env
# 4. Edit .env — set DATABASE_URL, ADMIN_KEY, and DISCORD_BOT_TOKEN
# 5. Run the interactive setup script (creates all tables)
npm run setup
# 6. Start
npm start
# Dev mode (auto-restarts)
npm run dev| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 3000 |
HTTP port |
NODE_ENV |
No | development |
production enables PostgreSQL SSL |
DATABASE_URL |
Yes | — | PostgreSQL connection string |
ADMIN_KEY |
Yes | — | Master admin key — use a long random string |
DISCORD_BOT_TOKEN |
For Discord features | — | Discord bot token |
RATE_LIMIT_WINDOW_MS |
No | 60000 |
Rate-limit window in ms |
RATE_LIMIT_MAX |
No | 100 |
Max requests per window per key |
RESCAN_CRON |
No | 0 */6 * * * |
Cron for automatic group rescans |
LOG_LEVEL |
No | info |
error / warn / info / debug |
- Push to GitHub
- railway.app → New Project → Deploy from GitHub repo
- Add a PostgreSQL service to your project — Railway injects
DATABASE_URLautomatically - Set environment variables in the Variables tab:
ADMIN_KEY— strong random stringDISCORD_BOT_TOKEN— your bot tokenNODE_ENV—production
- Your app URL (e.g.
https://your-app.up.railway.app) goes intoShieldScript.lua
- Go to https://discord.com/developers/applications and create an application.
- Click Bot → Add Bot.
- Under Privileged Gateway Intents, enable Server Members Intent.
- Copy the token → set
DISCORD_BOT_TOKENin.env. - The bot does not need to be pre-invited — it joins via invite code and leaves after scanning.
| Endpoint type | Header |
|---|---|
Game developer (/api/*) |
X-API-Key: ts_... |
Admin (/admin/*) |
X-Admin-Key: your-admin-key |
Check whether a single Roblox player is flagged.
Response — clean:
{ "flagged": false, "robloxId": "123456789" }Response — flagged:
{
"flagged": true,
"robloxId": "123456789",
"username": "BadActor",
"reason": "Member of flagged Roblox group: NSFW Condos Hub",
"flaggedAt": "2024-06-01T12:00:00",
"source": "roblox_group"
}Check up to 100 players at once.
{ "ids": ["111", "222", "333"] }All require X-Admin-Key header.
| Method | Path | Description |
|---|---|---|
GET |
/admin/flagged |
List flagged Roblox players (paginated, filterable) |
POST |
/admin/flagged |
Manually flag a single player |
POST |
/admin/flagged/mass |
Flag up to 500 players at once |
POST |
/admin/flagged/deduplicate |
Remove duplicate entries |
DELETE |
/admin/flagged/:robloxId |
Unflag a player |
GET |
/admin/discord-flagged |
List flagged Discord members |
POST |
/admin/discord-flagged |
Flag a Discord user |
DELETE |
/admin/discord-flagged/:discordId |
Unflag a Discord user |
| Method | Path | Description |
|---|---|---|
POST |
/admin/scan/group |
Scan a Roblox group { "groupId": "..." } |
POST |
/admin/scan/rescan-all |
Rescan all watched groups immediately |
POST |
/admin/scan/discord-server |
Scan a Discord server { "invite": "..." } |
POST |
/admin/scan/discord-roblox-links |
Scan Discord members for linked Roblox accounts |
| Method | Path | Description |
|---|---|---|
GET |
/admin/groups |
List watched groups |
PATCH |
/admin/groups/:groupId |
Toggle auto-rescan |
POST |
/admin/groups/:groupId/rescan |
Manually rescan a group |
DELETE |
/admin/groups/:groupId |
Stop watching a group |
GET |
/admin/servers |
List watched Discord servers |
DELETE |
/admin/servers/:serverId |
Stop watching a server |
| Method | Path | Description |
|---|---|---|
GET |
/admin/sass/stats |
SASS finding statistics |
GET |
/admin/sass/findings |
List findings by status |
GET |
/admin/sass/scan/status |
Current SASS scan state |
POST |
/admin/sass/scan |
Trigger a SASS scan |
POST |
/admin/sass/findings/:id/approve |
Approve a finding |
POST |
/admin/sass/findings/:id/reject |
Reject a finding |
POST |
/admin/sass/findings/bulk-approve |
Approve multiple findings |
POST |
/admin/sass/findings/bulk-reject |
Reject multiple findings |
POST |
/admin/sass/cleanup |
Delete low-confidence pending findings |
| Method | Path | Description |
|---|---|---|
GET |
/admin/apikeys |
List API keys |
POST |
/admin/apikeys |
Create API key |
DELETE |
/admin/apikeys/:id |
Deactivate API key |
GET |
/admin/requests |
List API key requests |
GET |
/admin/requests/stats |
Pending/approved/rejected counts |
POST |
/admin/requests/:id/approve |
Approve a request |
POST |
/admin/requests/:id/reject |
Reject a request |
DELETE |
/admin/requests/:id |
Delete a request |
| Method | Path | Description |
|---|---|---|
GET |
/admin/stats |
Dashboard statistics |
GET |
/admin/scans |
Scan history |
GET |
/admin/roblox-cookie/status |
Check if Roblox cookie is configured |
POST |
/admin/roblox-cookie |
Set or clear the Roblox cookie |
GET |
/admin/export |
Export entire database as JSON |
GET |
/health |
Health check |
- Open Roblox Studio
- In Explorer, right-click
ServerScriptService→ Insert Object → Script - Rename it to
ShieldScript - Paste the contents of
ShieldScript.lua
At the top of the script, set your values:
local API_KEY = "YOUR_API_KEY_HERE"
local SHIELD_URL = "https://your-app.up.railway.app/api/check"
local WEBHOOK_URL = "" -- Optional Discord webhook for kick notificationsGame Settings → Security → Allow HTTP Requests ✓
# Scan a Roblox group
curl -X POST https://your-app.up.railway.app/admin/scan/group \
-H "X-Admin-Key: your-admin-key" \
-H "Content-Type: application/json" \
-d '{"groupId": "12345678"}'
# Scan a Discord server
curl -X POST https://your-app.up.railway.app/admin/scan/discord-server \
-H "X-Admin-Key: your-admin-key" \
-H "Content-Type: application/json" \
-d '{"invite": "https://discord.gg/abcdefg"}'
# Check a player
curl https://your-app.up.railway.app/api/check/123456789 \
-H "X-API-Key: ts_your_key"
# Manually flag a player
curl -X POST https://your-app.up.railway.app/admin/flagged \
-H "X-Admin-Key: your-admin-key" \
-H "Content-Type: application/json" \
-d '{"robloxId": "123456789", "username": "BadActor", "reason": "Known condo host"}'
# Create a game API key
curl -X POST https://your-app.up.railway.app/admin/apikeys \
-H "X-Admin-Key: your-admin-key" \
-H "Content-Type: application/json" \
-d '{"name": "Sword Fight Simulator", "owner": "MyUsername"}'- Use HTTPS in production (Railway provides this automatically)
- Set a strong
ADMIN_KEY— at least 32 random characters - Rotate keys instantly via
DELETE /admin/apikeys/:id - Never commit
.env(it's in.gitignore)
MIT