dumbtv
dumbtv takes the ads, screen tracking and update nags out of your Fire TV over ADB. No root, none of your apps touched, every step can be undone. Free and open source.
Open-source trust & safety for community builders. MIT.
SecurityTypeScript · HTMLGitHub @luishenrichOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗Stop scammers getting into communities while letting real builders in fast. Greenlight scores applications against community rules using AI, maintains a shared blocklist across communities (HMAC-hashed for privacy), and provides a review queue for moderators. Built for hackathon Track 1 (community infrastructure).
Greenlight was built in a single evening at the Grok Bot Community Engineering London Hackathon (22 Oct 2026) by two humans working alongside a team of Grok Bot agents. Each agent had its own chat, memory and job, and they handed work to each other the way a small team would.
Instead of picking a model on vibes, the agents built a labelled test set of 200 applicants (80 good fit, 50 borderline, 50 scammers, 10 bots, 10 cross-community repeats) and ran every candidate scorer on it.
| Model | Correct | False blocks | Scammers accepted |
|---|---|---|---|
| Jev (typesafe/jev-router) | 195/200 (97.5%) | 0 | 0 |
| GPT-6 Luna | 179/200 (89.5%) | 0 | 0 |
Honest misses: Jev sent 4 bots to "ask" rather than declining them, and accepted 1 borderline applicant it should have asked about. Full results are in data/eval_large_results.json.
openai/gpt-6-luna for best accuracy, 29/30 on test set)The shared blocklist lets communities warn each other about scammers without exposing member data:
HMAC-SHA256 hashing – Phone numbers, email addresses, and handles are hashed with a server-only secret (BLOCKLIST_HMAC_SECRET) before storage. The raw values never enter the database.
Handle normalization – Before hashing, handles are normalized (lowercase, strip @._- and trailing digits) so @cryptomax_alpha and @cryptomax_alpha1 produce the same hash and match.
Match-only checks – When another community checks a new applicant, the API returns only { match: true/false, matched_on: ["phone"|"handle"|"email"], status, reported_at }. The reporting community's identity and the evidence are never shared.
RLS (Row Level Security) – Each community sees only its own blocklist entries in full (with evidence). Cross-community matching happens server-side via the service role; moderators never query other communities' entries directly.
Confirmed entries only – The API checks only status='confirmed' entries. Demo mode auto-confirms for speed; production would require a second moderator to confirm before an entry becomes shared.
When a moderator reports a scammer:
HMAC-SHA256(normalize(phone)), HMAC-SHA256(normalize(handle)), HMAC-SHA256(normalize(email))status='confirmed'When another community checks an applicant:
blocklist_entries (service role, bypasses RLS) for matching HMACs where status='confirmed'@cryptomax_alpha to Community A and @cryptomax_alpha1 to Community B, normalization ensures both hash to the same value.audit_log with timestamps and actors.Install dependencies:
npm install
Set up environment variables:
Copy .env.example to .env.local and fill in:
NEXT_PUBLIC_SUPABASE_URL – Your Supabase project URLNEXT_PUBLIC_SUPABASE_ANON_KEY – Supabase anon public keySUPABASE_SERVICE_ROLE_KEY – Supabase service role key (server-only)BLOCKLIST_HMAC_SECRET – Secret for HMAC hashing (generate a random string, keep it safe)BOT_API_TOKEN – Bearer token for bot API accessGATEKEEPER_API_URL – Your deployed API URL (or http://localhost:3000 for dev)USE_MOCKS=true – Server-only flag for the mock routes and the queue page. The page receives it as a prop. Do not set NEXT_PUBLIC_USE_MOCKS.OPENROUTER_API_KEY – OpenRouter API key for LLM scoringSCORER_MODEL – Model to use (recommended: openai/gpt-6-luna for best accuracy; default: typesafe/jev-router)DISCORD_BOT_TOKEN – Discord bot token (optional)DISCORD_MOD_CHANNEL_ID – Discord mod channel ID (optional)COMMUNITY_SLUG – Your community slug (e.g. london-agent-builders)Apply Supabase migrations:
# Install Supabase CLI if needed: npm install -g supabase
supabase db push
# Or manually apply migrations in order: supabase/migrations/*.sql in your Supabase SQL editor
Run development server:
npm run dev
Visit http://localhost:3000
Run evaluation (optional):
npm run eval
# Scores all 30 test applicants, shows accuracy and false-block count
Test WhatsApp webhook (optional):
npx tsx scripts/simulate_whatsapp.ts
# Simulates a WhatsApp message with correct signature, no real phone needed
Greenlight can accept applications via WhatsApp using the official Meta WhatsApp Cloud API (no third-party libraries).
Create a Meta Business Account at business.facebook.com
Create a WhatsApp Business App in the Meta for Developers console
Add WhatsApp product to your app
Get credentials:
WHATSAPP_TOKEN: Permanent access token (Generate in Settings → WhatsApp → API Setup)WHATSAPP_PHONE_NUMBER_ID: Phone number ID from the test number or your own numberWHATSAPP_APP_SECRET: App secret from Settings → BasicWHATSAPP_VERIFY_TOKEN: Any random string you choose (used for webhook verification)WHATSAPP_COMMUNITY_SLUG: Which community receives WhatsApp applicationsConfigure webhook in WhatsApp → Configuration:
https://your-domain.com/api/whatsapp/webhookWHATSAPP_VERIFY_TOKEN abovemessages fieldTest with simulation script:
# Set WHATSAPP_APP_SECRET in .env.local
npx tsx scripts/simulate_whatsapp.ts
# Check /queue to see the scored application
POST /api/whatsapp/webhook with the messageaccept → "Thanks! Your application looks good, and a lead will confirm shortly."ask → Follow-up question from scorer's reason (e.g. "Missing info: which community do you run?")decline or blocklist hit → No auto-reply, goes to moderator queue/queue and makes final decisionAll decisions land in the queue. WhatsApp uses the channel adapter in lib/channels/.
Score an application against community rules and check the shared blocklist.
Request:
{
"community_slug": "london-agent-builders",
"application": {
"name": "Ada Testworth",
"handle": "@ada_builds",
"phone": "+44 7700 900101",
"email": "[email protected]",
"github": "ada-testworth-demo",
"text": "I'm building an open-source eval harness..."
}
}
Response:
{
"application_id": "uuid",
"decision": "accept"|"ask"|"decline",
"score": 95,
"reason": "Builds agents, open-source repo, offers to demo, London-based (rules 1, 2, 3).",
"signals": {
"rules_fit": {
"score": 95,
"rule_refs": ["london-agent-builders#1", "london-agent-builders#2"]
},
"spam_text": {
"score": 0.02,
"matched_patterns": []
},
"stopforumspam": {
"appears": false,
"frequency": 0,
"confidence": 0
},
"github": {
"exists": true,
"account_age_days": 450,
"public_repos": 8
},
"devpost": {
"projects_found": 2
},
"blocklist_hit": false
},
"blocklist_match": {
"matched_on": ["phone", "handle"],
"status": "confirmed",
"reported_at": "2026-10-22T18:00:00Z"
}
}
Signals:
rules_fit: LLM score (0-100) + rule citationsspam_text: Deterministic scam-text classifier (0-1 score, matched patterns)stopforumspam: StopForumSpam.org check (appears, frequency, confidence) or null if skippedgithub: GitHub account exists, age in days, public repo count, or null if skippeddevpost: Number of projects found on Devpost, or null if skippedblocklist_hit: Boolean, true if matched confirmed entry in shared blocklistblocklist_match: Present only when blocklist_hit is true; shows which fields matchedAll external checks (StopForumSpam, GitHub, Devpost) have 2s timeouts and are cached. They can never break scoring.
Record a moderator's final decision and optionally report to the shared blocklist.
Request:
{
"decision": "accept"|"decline",
"report_to_blocklist": true,
"evidence": "Crypto pump scam, identical to A21"
}
Response:
{
"ok": true,
"blocklist_entry_id": "uuid",
"blocklist_status": "confirmed"
}
Notes:
evidence is required when report_to_blocklist is trueconfirmed blocklist entry (demo mode; production would use pending → moderator review → confirmed)Check if identifiers match the shared blocklist.
Headers:
Authorization: Bearer <BOT_API_TOKEN>
Request:
{
"community_slug": "london-agent-builders",
"handle": "@cryptomax_alpha1",
"phone": "+44 7700 900901",
"email": "[email protected]"
}
Response:
{
"match": true,
"matched_on": ["phone", "handle"],
"status": "confirmed",
"reported_at": "2026-10-22T18:00:00Z"
}
Notes:
confirmed entriesA lead can turn Auto mode on per community at /settings (same gk_lead cookie as the queue). It is off by default.
When Auto mode is on, scoring may send a reply with no extra click, and only in these cases:
It never auto-sends:
signals.blocklist_hit or a blocklist_match)WhatsApp uses the Cloud API adapter. Successful sends are written to audit_log with auto=true, action='auto_send', and details {application_id, decision, channel} — no phone number. If the send fails, the failure is logged and the application stays in the queue. The queue header shows whether Auto mode is on, and an auto-sent badge marks items that were sent.
GET and PATCH /api/communities/:slug/settings read and update { auto_mode } with the service-role client. Both require the lead token.
supabase/migrations/*.sql in order.env.local above)INSERT INTO communities (slug, name, rules_md) VALUES ('your-slug', 'Your Community', '...');
-- First, create a moderator account via Supabase Auth (magic link)
-- Then add them to your community:
INSERT INTO moderators (user_id, community_id) VALUES ('<user-uuid>', '<community-uuid>');
USE_MOCKS=true to test with fixture dataUSE_MOCKS=false, share the /queue URL with moderatorsapp/ – Next.js App Router pages and API routes
api/score/ – Score applicant endpoint (real scoring with all signals)api/decisions/ – Moderator decision endpointapi/blocklist/ – Blocklist check endpoint (bot API)api/mock/ – Mock API routes (active when server USE_MOCKS=true; /api/_mock/* rewrites here)lib/ – Shared utilities
scorer.ts – OpenRouter LLM scorer (prompt v3, 29/30 accuracy on test set)spam-classifier.ts – Deterministic scam-text classifiersignals.ts – StopForumSpam, GitHub, Devpost API helpers with caching + timeoutshmac.ts – Handle/phone/email normalization + HMAC-SHA256supabase/ – Server (service role) and browser (anon) clientsscripts/ – Eval and utility scripts
eval.ts – Score all 30 test applicants, show accuracyeval_spam.ts – Evaluate spam_text classifier on SMS datasetfetch_sms_spam.ts – Download SMS spam dataset from HuggingFaceseed-demo.ts – Reset demo tables for clean demo runsupabase/migrations/ – Database schema
0001_init.sql – Tables, RLS policies, seed communities0002_rules.sql – Seed community rules from data/rules.md0004_auto_mode.sql – communities.auto_mode and audit_log.autofixtures/ – Mock data for /api/_mock/* routesdata/ – Fictional demo dataset (30 applicants, 2 communities, rules, expected decisions)docs/ – Build spec and handoff docsThe scorer has been tested with multiple models. For production:
Recommended: openai/gpt-6-luna
Budget-friendly: typesafe/jev-router (default)
Set via SCORER_MODEL environment variable. The code default is typesafe/jev-router.
The 30-applicant demo set is fictional. The 200-applicant eval set adds scrubbed spam texts to fictional applications:
All identities are fictional (Ofcom drama numbers, example.com). Sources, licences, and class counts are in data/DATASETS.md.
MIT – see LICENSE file