Activity
Contributors
README
![]()
Ward
A guardian that lives on a child's own device. It spots cyberbullying in English, Roman Urdu,
Hinglish and Bangla, keeps the evidence, coaches the next step, and trains kids to bounce back.
Open-weight Gemma, running locally. No server and no account: a child's messages never go to us or to any AI company.
The demo runs entirely in your browser: Ward Lite and the word lists load from the page, and the chat you paste is never sent anywhere.
v0.1.0 is a preview. Read the safeguarding policy below before giving it to a child.
Check a chat · Pick your night light · Live a story. Recorded from the browser demo.
The bully is the ghost haunting the chat. Ward is the ghost on your side.
Before you put Ward in front of a child, read the draft safeguarding policy: what Ward will never do, what hasn't been reviewed by a child-protection professional yet, and how to raise a concern.
Raise a concern: found a security flaw? Use the repo's Security tab → Report a vulnerability (SECURITY.md). Worried about a child's safety or someone in the project? Write to hello@muhammadmurtuza.com. Never paste a real child's chat in a public issue.
What this is, and what it isn't
Ward is a response to World Solve problem WS05937: Building effective interventions for cyberbullying harming children by 2030, built for the DEV Hacktoberfest Open-Source AI Challenge, Week 4 (Ghost in the Machine).
It doesn't solve cyberbullying. Nothing built in a week could. What's here is:
- An MVP that works: a child-owned app for Android, desktop and the web, tested on the device and in CI.
- An approach with numbers behind it: a scorecard showing open-weight Gemma reads abuse in the languages these children type, where an English-first safety model goes blind.
- A path to an intervention that helps children at scale, including what's missing and how we'd measure whether children are actually better off: docs/ROADMAP.md.
The evidence is clear that apps alone don't fix bullying. School-wide programs that involve peers and teachers do best, and even they cut cyberbullying modestly (g = -0.13 to -0.18 across 50 studies). So Ward is built as the child's front door into the people who can help: the adult they tell, their class, and helplines.
A look around
| Home | Who are you? | The verdict | What do I do? |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
| Resilience Path | اردو | ||
![]() | ![]() |
Three rings
The child
| Share to Ward | In WhatsApp, Export chat and pick Ward from the share list (Android). Or paste messages, or drop screenshots on a computer running Gemma through Ollama. Say which person is you. |
| Reads on the phone | Ward Lite, a 38 MB model inside the app, reads every message offline. On a computer, Gemma 4 reads with context and can tell banter from bullying. On 64-bit Android phones with 6 GB+, the Gemma build of the app can download Gemma 4 E2B to help write. |
| See the pattern | One mean message is rude; bullying is a pattern. Ward finds the same person again and again, pile-ons, accounts added to a group just to attack ("ghost accounts"), exclusion, attacks after 11pm, and dangerous messages (threats, telling someone to hurt themselves, sexual pressure, threats to share photos). |
| The Haunting | The chat with the attacks lit up and every ordinary stretch folded away, a verdict in plain words for the child's age, and who is doing it (and who isn't). |
| Coach | Don't reply, save it, block/report/mute with steps for WhatsApp, Instagram, TikTok, Snapchat and Discord, and a message to a trusted adult that Gemma drafts in a kid's voice. Ward never sends anything itself. |
| Get help now | Anything dangerous puts helplines for the child's country first (including Pakistan's Madadgaar 1098 and DRF Cyber Harassment Helpline 0800-39393), plus Take It Down for images. |
| Evidence vault | Saved chats are encrypted (AES-GCM, key from a PIN via PBKDF2), each message SHA-256 fingerprinted. |
| Resilience Path | Duolingo-style daily lessons with streaks and XP: Spot it, Shut it down, Save it, Say it, Back them up, Lock it down, Bounce back. Content per age band (8 to 10, 11 to 13, 14 to 17). No hearts to lose. |
| Stories | Short branching chats where you choose: laugh along, stay quiet, speak up, tell someone. Every ending says what that choice did and what else could work, and you can go back and try another. One for every unit in every age band (21), in English and Urdu. |
| Your night light | Six looks the child picks from a gallery, no gender question. The age band changes how playful or grown-up it feels. |
| English and اردو | The whole app, lessons included, in English or Urdu (right to left, Nastaliq script). Switch any time in Me. |
| Read aloud | A Listen button on lessons, onboarding, verdicts and coach steps, on by default for the youngest children. English is narrated with ElevenLabs and bundled, so it works offline. |
| This is wrong | A correction under every flagged message. Kept on the phone (no words), never hides the help for a dangerous message, and shared only if the child chooses. |
| Give Ward to a friend | On Android, Ward sends itself to another phone over Bluetooth or Quick Share, no internet, with a fingerprint to check. On desktop and web, a QR code to the download (once a release is published). |
| Shared phones | Each person gets their own Ward behind an optional PIN. Quick exit (a button, or Escape twice) locks it instantly and hides it from the recents screen. |
The child's circle
| For the adult they told | A page the child hands over, in English and Urdu: what to do in the first minutes, what not to do (take the phone away, confront the other family), and next steps with school, the app and a helpline. |
| Class mode | A teacher projects any lesson for a whole room: one question at a time, the class votes or talks, then the answer. Ground rules up front. Nothing recorded, no accounts. |
| School kit | A term plan, a lesson plan per unit, a letter home (English and Urdu) and an anonymous before and after survey, all printable. The teacher types in the totals and sees what changed. Nothing about any child is collected. |
Discord
| Check with Ward | Right-click any message → Apps. Is it bullying, what kind, and what to do on Discord. Only the teen sees it. Works in any server or DM, because the teen installs Ward to their own account. |
| Save as evidence | Ward DMs the teen a fingerprinted copy. The DMs are the evidence store: the bot has no database. /ward report turns them into a ward.report/v2 a counsellor can check. |
| Server mode | Opt-in with a private #ward-alerts channel: dangerous messages go to moderators, the sender of a hurtful but not dangerous message gets a private nudge to edit or delete it, the target a kind check-in. Never deletes, mutes or bans. |
Same engine and Ward Lite, on the CPU, no closed AI service. Self-host it in a few steps, or on
Render with render.yaml: apps/discord/README.md.
The community
| A helpline-ready report | The child picks who it's for and what they want to happen; details about them are optional. They get a readable page plus ward-report.json in a documented format (docs/REPORT.md) with a digest a helpline can check. Only hurtful messages go in. The rest of the chat stays private. |
| Case viewer | viewer/ward-viewer.html: one offline file a caseworker double-clicks. It checks the report against its own digest and every message's fingerprint (this catches accidental damage and casual edits, not a determined forger), puts safety and the child's wishes first, and exports a summary, a CSV or a print view. No network, nothing uploaded. |
| Counsellor mode | A counsellor's Ward that takes a child's report by file or by QR codes from the child's screen (no internet needed), checks it, and keeps case notes. Locked with a PIN, on that device. No server. |
Does the approach work? The scorecard
Same balanced sample for every system, 200 messages per language, half abusive by each dataset's
own labels. All run locally through Ollama on 2026-10-31, on the engine as of commit e547279.
Method, 95% intervals and run dates per row in
eval/results/scorecard.md; rerun with pnpm eval.
| Caught (precision) | Word lists only | ShieldGemma 2B | A phone: word lists + Ward Lite | Gemma 4 E2B (phone size)* | A computer: Gemma 4 E4B* |
|---|---|---|---|---|---|
| Roman Urdu | 59% (94%) | 14% (70%) | 83% (86%)† | 30% (71%) | 88% (84%) |
| Bangla (abuse aimed at a person) | 10% (83%) | 14% (93%) | 46% (79%) | 26% (65%) | 67% (77%) |
| English (tweet dataset) | 10% (91%) | 19% (73%) | 85% (67%)† | 31% (69%) | 36% (65%) |
With 100 abusive messages per language, each figure could be about 10 points either way (the 95% intervals are in the method page), so read small gaps as ties.
† In distribution. For Roman Urdu and English, Ward Lite was trained on the train splits of the same two datasets the scorecard tests on (it never sees the test messages themselves). The other systems never saw either dataset. So these two cells show Ward Lite learned those datasets, not that a phone reads new kinds of chat better than Gemma. Bangla is the fair comparison: Ward Lite's Bangla came from other datasets, and there the computer's Gemma 4 E4B leads, 67% (57–75) to 46% (37–56).
* The Gemma columns judge unrelated messages in batches with no chat around them: 20 per request for E4B, 5 for E2B, and Ward asks again about any a model leaves out. Some still got no verdict and count as not caught: for E4B, 1, 18 and 52 of 200 (Roman Urdu, Bangla, English); for E2B, 120, 120 and 40. Ollama gives E2B no structured output on this machine, so it answers in plain JSON, and one malformed entry (an unquoted category) loses its whole request. The E2B column, and English for E4B, undercount what those models can read.
Ward Lite is a small classifier we distilled for phones (train/): 37.7 MB, about 8 ms
per message on one CPU thread of the laptop that ran the scorecard, fully offline. A leak check
dropped 98 training messages that also appear in the scorecard's test sets
(train/DATA.md), and in the app it can only add warnings, never clear one.
Bangla is still the weak spot: the next step is more real, non-abusive Bangla chat to train on.
Catching abuse isn't enough; ordinary chat has to stay calm. A set of 329 everyday messages
("see you tomorrow", "gate pe milo, samosay"), kept out of training, is now part of the check:
Ward Lite flags 5% of them, and marks 2% dangerous (eval/everyday). It was
written while fixing that exact problem, so read it as a development check, not a sealed test. The first release scored
"see you tomorrow" as dangerous; the story is in docs/PHASES.md. The
red-team set tests disguises, sarcasm, coded slang and tricky banter, and
publishes the failures: a phone catches 75 of 142 hurtful messages there, Gemma 4 E4B 114.
What the child actually sees: whole chats
The tables above score messages one at a time. A child sees one verdict for a whole chat (calm,
watch, bullying or urgent), after Ward's context rules and pattern engine. The chat-level
benchmark scores that verdict on 128 hand-written synthetic chats, 32 each in English, Roman Urdu,
Hinglish and Bangla: sweary banter between friends, a child quoting abuse to ask for help,
pile-ons, exclusion, and dangerous messages, including a lone threat in a one-to-one chat with and
without a reply. The labels are one author's judgement, pending review by native speakers.
Details and every failure: eval/results/chats.md; rerun with
pnpm eval:chats.
| 128 chats, 2026-10-31 | Word lists only | A phone: word lists + Ward Lite | A computer: word lists + Gemma 4 E4B |
|---|---|---|---|
| Right level | 48 (38%) | 50 (39%) | 92 (72%) |
| Urgent chats missed (of 28) | 23 | 14 | 3 |
| Called urgent wrongly (of 100) | 5 | 21 | 11 |
| Friendly chats not shown as calm (of 40) | 12 | 17 | 24 |
On a phone, half the dangerous chats don't reach "urgent" (two of those 14 at least show the message as "not sure", with helplines), and a fifth of the others do: Ward Lite's danger output fires on plain put-downs ("honestly go away"), and the word lists on a friend joking "party nahi di toh jaan se maar dungi 😂" one to one. The computer tier finds every pile-on and exclusion and most threats, but shows 24 of the 40 friendly chats as something other than calm (17 "watch", 7 "urgent"). It missed a Bangla "why don't you just die" (read as telling them not to die) and two veiled one-to-one threats ("you'll regret it"), which it rated hurtful but not dangerous, so those chats showed only "watch".
This measures whether Ward reads abuse, not whether children are better off. That second question needs a pilot in schools; the plan is in docs/ROADMAP.md.
What the numbers do and don't show:
- English is low for every system that didn't train on it. That dataset is mostly tweets about gender and other topics rather than aimed at one person: word lists catch 10% and Gemma 4 E4B 36%. Ward Lite's 85% is the in-distribution case above. Whole chats, where Ward's pattern engine works, are the chat-level benchmark above.
- Small generative models are poor judges. Gemma 3 1B cleared real insults as harmless. Gemma 4 E2B (phone-sized; scored here through Ollama, on phones it runs through LiteRT-LM) caught between a quarter and a third, and it loses track of lists longer than 5 messages. So on phones Ward Lite reads the chats, and Gemma 4 E2B, if the child downloads it, does what it's good at: writing the message to a trusted adult and giving feedback on lesson answers.
What could go wrong
docs/HARMS.md covers false negatives, false positives, blaming the wrong
person, parents using it as spyware, bullies testing messages against it, and self-harm, with
what Ward does about each. None of it has been reviewed by a child-protection professional yet;
that comes before any pilot.
Why open
- Privacy is the product. A child's chats can't go to a vendor. Open weights running locally are the only way to read them with a real language model and keep that promise.
- It works where resources are scarcest. No account, no subscription, no data plan after install. Slang packs and patterns work on a cheap Android phone with the internet off.
- Communities own the words. Slang packs are plain JSON (
packages/engine/packs). The Roman Urdu pack was grown from a public dataset's training split and reviewed by hand (eval/mine-ru.ts). - It can't disappear. MIT licensed, and headed for the Digital Public Goods registry.
Run it
pnpm install
node apps/web/scripts/copy-ort.mjs # Ward Lite's offline runtime (not in git); rerun after updating packages
ollama pull gemma4:e4b # the close read; Ward still works without it
pnpm dev # http://localhost:5199
pnpm test # every package, plus the viewer, corrections and red-team leak check
pnpm eval # the scorecard (also needs: ollama pull shieldgemma:2b)
Skip the copy-ort step and the app still runs, but without Ward Lite, in the dev server and in
every build below: where Gemma isn't set up, Ward reads with the word lists alone.
- Desktop:
pnpm --filter @ward/desktop start. No Ollama? Me → "Gemma inside Ward" downloads Gemma 4 once (4.6 GB) and runs it through llama.cpp inside the app.--lab(or the switch in Me) makes Ward forget everything on close, for shared school computers.pnpm --filter @ward/desktop distbuilds an installer for your own computer intorelease/. GitHub Actions (desktop.yml) builds and launch-tests installers for macOS, Windows and Linux when run by hand; none has been published as a release yet. - Android:
mobile/is a Capacitor shell with the app bundled inside and a small native plugin for Share to Ward. There are two flavours: lite, the default, with no native code, so it installs on any phone including 32-bit ones; and gemma, which adds on-phone Gemma 4 E2B and installs only on 64-bit (arm64) phones.pnpm --filter @ward/web build && pnpm --filter @ward/mobile apkbuilds a lite debug APK (apk:gemmafor the other; needs the Android SDK and JDK 21). Debug builds install as a separate app,org.ward.app.debug.android.ymlbuilds both flavours, checks their native libraries, and launch-tests the lite one on an emulator with Wi-Fi off. Cloud backup is off, so the vault never leaves the phone. - Releases: pushing a
v*tag runsrelease.yml, which publishes signed APKs, the installers andSHA256SUMSas a prerelease. Only the owner can, since it needs the signing key: docs/RELEASING.md. - Phone + family computer: start Ollama on the computer with
OLLAMA_HOST=0.0.0.0, then puthttp://<computer's address>:11434in Ward's Me screen on the phone. The phone then sends the chats it checks to that computer over your home network, unencrypted, so only do this on a network you trust.OLLAMA_HOST=0.0.0.0lets anyone on that network use the computer's Ollama: never set it on school or public Wi-Fi.
Opening an unsigned build
Ward's installers aren't signed yet, so the first time you open one your computer warns you.
- macOS 15 and later: open Ward once and click Done on the warning. Then open System
Settings → Privacy & Security, scroll down to Security, and click Open Anyway next to the
line about Ward. Confirm with your password, and Ward opens. (Right-click → Open no longer skips
the check on macOS 15.) If there's no Open Anyway button, run
xattr -dr com.apple.quarantine /Applications/Ward.appin Terminal, then open Ward. - Windows: if SmartScreen says "Windows protected your PC", click More info, then Run anyway.
- Linux: make the AppImage runnable with
chmod +x Ward-linux.AppImage, then open it.
Checking the Coach's help steps
The Coach tells kids where Report and Block are in WhatsApp, Instagram, TikTok, Snapchat and
Discord, and apps move their buttons. pnpm helpcheck (-- --only=TikTok for one app) finds each
app's own help page for reporting, blocking and saving proof (SerpApi), loads it in a real browser
(Browserbase), and checks that the button names in Ward's steps are still on it. The latest run is
in eval/helpcheck/report.md. A drift row means a person should look
at the app and fix en.ts and ur.ts together. It isn't proof the step is wrong. It needs
SERPAPI_KEY, BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID in ward/.env. It only reads
public help pages: no chat ever goes out.
How it's built
flowchart LR
subgraph In["Where chats come from"]
WA["WhatsApp export<br/>(Share to Ward)"]
PASTE["Paste or screenshots"]
DC["Discord message<br/>(right-click → Apps)"]
end
subgraph Engine["packages/engine (same code everywhere)"]
P["Parser"] --> WL["Word lists<br/>slang packs, 4 languages"]
P --> LITE["Ward Lite<br/>38 MB ONNX, on device"]
P --> G["Gemma 4<br/>E4B on computers · E2B on phones"]
WL & LITE & G --> C["combine()<br/>can raise, never lower"]
C --> PAT["Pattern engine<br/>repeat attacker · pile-on · ghost accounts · late night"]
end
subgraph Out["What the child gets"]
V["Verdict + help first"]
CO["Coach"]
VA["Encrypted vault<br/>AES-GCM, PIN"]
R["ward.report/v2"]
end
WA & PASTE & DC --> P
PAT --> V --> CO
V --> VA --> R
R --> VIEW["Offline case viewer<br/>counsellor mode"]
R -. child chooses .-> ADULT["Trusted adult · helpline"]
Everything inside the engine box runs on the child's device. The only network calls are optional and named: Ollama on the family computer, the Discord bot, and the browser demo's Gemma relay.
| Where Ward runs | How it's built | Who reads the chat |
|---|---|---|
| Android phone | Capacitor (mobile/), lite APK for any phone, Gemma APK for arm64 | Word lists + Ward Lite; Gemma 4 E2B helps write on 6 GB+ phones |
| Computer | Electron (desktop/), macOS, Windows, Linux | Gemma 4 E4B through Ollama or built in |
| Browser | apps/web on Render and GitHub Pages | Word lists + Ward Lite in the page; hosted Gemma (Google AI Studio, through the relay) only if you press the demo button |
| Discord | apps/discord on Render | The same engine and Ward Lite in Node; no database |
| Open model or tool | What it does in Ward |
|---|---|
| Gemma 4 E4B / E2B | Reads chats with context; wrote the synthetic training data Ward Lite learned from |
| Ward Lite (e5-small, fine-tuned) | The on-device reader that runs everywhere, offline |
| ShieldGemma 2B | The baseline on the scorecard |
| Ollama / LiteRT-LM | Run Gemma locally on computers and phones |
| Render | Hosts the browser demo, the Discord bot and the demo's Gemma relay |
| Tiger Data | Keeps every eval run; CI fails a push that makes a score worse (eval/history) |
| Browserbase + SerpApi | pnpm helpcheck checks the Coach's block and report steps against each app's help pages |
| ElevenLabs | 107 English narration clips bundled for read-aloud, offline |
packages/engine parser, slang packs, pattern engine, Gemma and ShieldGemma judges,
the ward.report/v2 builder; unit-tested
packages/lessons the Resilience Path as plain data, with content tests
apps/web React 19, Vite, Tailwind 4, Motion, Phosphor icons
desktop Electron, served from app://ward (one of Ollama's allowed origins)
mobile Capacitor Android with the ShareIn and Give Ward plugins
apps/discord the Discord bot (discord.js), same engine and Ward Lite in Node
eval datasets, baselines and the scorecard
docs PLAN, HARMS, SAFEGUARDING, REPORT, REDTEAM, ROADMAP, DPG
The drop zone's travelling rim light is adapted from the idea in cult-ui's Halo Dropzone on 21st.dev. Fonts: Bricolage Grotesque and Atkinson Hyperlegible Next.
Help wanted
If you work at a child helpline, a school, or with young people in Pakistan, India or Bangladesh, we'd like to hear what's wrong with this before anyone builds more of it. Open an issue, or start with docs/ROADMAP.md.
To contribute: CONTRIBUTING.md, LANGUAGES.md and Ward Lite's model card.
Licence
MIT. Helpline numbers were checked on 2026-10-29; please open an issue if one is wrong or missing for your country.






