VoidAccess
VoidAccess는 한 질문으로 보안 피드를 병렬 검색해 지표를 돌려준다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A cyber-safety platform for South African learners, built with C3SA (a non-profit). Learners can take short modules and quizzes, read scam guides, share and read moderated community reports, find emergency contacts, and chat with CyberBot on WhatsApp about cyberbullying and online scams.
flowchart LR
L[Learner on phone/browser] -->|HTTPS| W[Web app<br/>Next.js PWA<br/>Vercel]
L -->|WhatsApp| M[Meta WhatsApp<br/>Cloud API]
M -->|signed webhook| B[CyberBot<br/>Node/Express<br/>Render]
B -->|chat completion| G[Groq API<br/>gpt-oss-120b]
W -->|supabase-js<br/>anon key + user JWT| S[(Supabase<br/>Postgres + Auth + Storage<br/>Row Level Security)]
A[Admin / teacher] --> W
B -. "ask for a person" .-> C3[C3SA staff WhatsApp]
There is no separate application server. The web app talks to Supabase directly, and the database enforces who may do what:
submit_quiz() grades a quiz and is the only way progress is saved.check_quiz_answer() reveals the correct answer for one question after it is answered.admin_save_learning_content() saves a module and its quiz in one transaction.validate_new_post() rejects reports that are too short or too long, or carry oversized images.post_flags records "Report post" flags (one per person per post). Admins see them under the "flagged" filter in /admin/community.set_post_display_name() sets a post's public name from the author's username, so it cannot be faked.auto_hide_reported_post() returns a post to review after 3 reports.post_like_counts is a view that gives public "helpful" counts without revealing who liked what.is_admin() is the shared role check used by policies.learner, teacher, admin) live in profiles and cannot be changed from the browser.posts with status pending. Only the author can see it, marked "Pending review"./admin/community, reads it, and approves or removes it.pending automatically. When an admin re-approves it, only new reports count.The server enforces this, not just the screens: signed-out visitors cannot insert posts, nobody can approve their own post, and the public name is set from the author's username by the database (or "Anonymous" if they ticked that box). Admins can still see who wrote an anonymous post.
Comments work differently from posts, on purpose. A review queue for every reply would make a conversation unusable, so comments appear straight away and safety comes from other controls:
/admin/community, Comments tab. The default view is "Reported". They can restore, hide or remove a comment. Removed comments stay in the database for records.| Path | What it is |
|---|---|
apps/web | Next.js 15 / React 19 web app (installable PWA). Pages are in src/app/*. |
whatsapp-bot | CyberBot: Express webhook + Groq. index.js (server), bot-logic.js (AI + safety), commands.js (typed commands), security.js (webhook signature check), test/ (automated tests). |
supabase | schema.sql (initial schema), migrations/ (dated changes), seed_*.sql (starter content). |
cd apps/web
npm install
cp .env.example .env.local # then fill in the two values below
npm run dev # http://localhost:3000
| Variable | Where to find it |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | Supabase → Project Settings → API |
NEXT_PUBLIC_SUPABASE_ANON_KEY | Supabase → Project Settings → API (the anon/public key only) |
Without these the app still starts in a demo mode so the UI can be worked on.
cd whatsapp-bot
npm install
npm test # 27 automated tests, no API keys needed
npm start
| Variable | Purpose |
|---|---|
GROQ_API_KEY | Groq API key for the AI replies |
WHATSAPP_TOKEN | Meta permanent access token |
PHONE_NUMBER_ID | WhatsApp phone number ID from Meta |
VERIFY_TOKEN | Any secret string; must match what you enter in Meta's webhook setup |
APP_SECRET | Meta app secret. Required: the bot refuses to start without it |
STAFF_WHATSAPP | C3SA staff number (international format, no +) that receives "talk to a person" requests |
STAFF_TEMPLATE_NAME | Recommended. Name of an approved WhatsApp message template with one body variable (the learner's number). WhatsApp only delivers plain text to a number that messaged the bot in the last 24 hours, so without a template the staff message can fail. If it fails, the learner is told honestly and given the helplines. |
STAFF_TEMPLATE_LANG | Language code of that template (default en) |
PORT | Optional, defaults to 3000 |
ALLOW_UNSIGNED_WEBHOOKS | true skips the signature check. Local testing only, never in production |
Webhook URL for Meta: https://<your-render-url>/webhook.
supabase/schema.sql in the SQL Editor, then the files in supabase/migrations/ in filename order, then the seed_*.sql files.update public.profiles set role = 'admin' where id = '<user uuid>';
Known gap in the SQL files. The
"Quizz"table, the"Tier"type, and a few columns onmoduleswere created in the Supabase dashboard and are not inschema.sql, so the SQL files alone cannot rebuild a database from scratch. To fix this, export the real schema from the production project and commit it as the new baseline:supabase db dump --schema public -f supabase/baseline.sql.
| Part | Host | Notes |
|---|---|---|
| Web app | Vercel | Root directory apps/web; set the two NEXT_PUBLIC_* variables. |
| CyberBot | Render | Start command npm start; set all bot variables. A deploy without APP_SECRET fails on purpose and the previous version keeps running. |
| Database | Supabase | Apply new files from supabase/migrations/ in order. |
/admin.STAFF_WHATSAPP points to a staffed number, set up a WhatsApp template and STAFF_TEMPLATE_NAME, test "Talk to a person" once, and agree response times.https://<your-domain>/reset-password to the Redirect URLs. Password reset emails do not work without it.