ward

Star0
View all files

Activity

Contributors

README

Ward, a small friendly ghost

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.

Try Ward in your browser

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.

Download for Android Desktop installers

v0.1.0 is a preview. Read the safeguarding policy below before giving it to a child.

Checking an example group chat: pick who you are, and Ward says this is serious, then lists what it noticed   Picking a night light: Lantern, Moonlit, Firefly, Ember, Aurora   A branching story: a group chat about Saturday bowling where one classmate is left out

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:

  1. An MVP that works: a child-owned app for Android, desktop and the web, tested on the device and in CI.
  2. 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.
  3. 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

HomeWho are you?The verdictWhat do I do?
Home at night: Ward says late messages feel worse, with today's lesson and helplinesChoosing which person in the chat is you, or checking for a friendThis is serious, with Talk to someone now firstThe Coach: steps to save, block and report, and tell an adult
Resilience Pathاردو
The Resilience Path with lessons and stories per unitHome in Urdu, right to left in Nastaliq

Three rings

The child

Share to WardIn 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 phoneWard 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 patternOne 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 HauntingThe 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).
CoachDon'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 nowAnything 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 vaultSaved chats are encrypted (AES-GCM, key from a PIN via PBKDF2), each message SHA-256 fingerprinted.
Resilience PathDuolingo-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.
StoriesShort 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 lightSix 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 aloudA 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 wrongA 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 friendOn 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 phonesEach 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 toldA 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 modeA 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 kitA 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 WardRight-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 evidenceWard 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 modeOpt-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 reportThe 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 viewerviewer/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 modeA 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 onlyShieldGemma 2BA phone: word lists + Ward LiteGemma 4 E2B (phone size)*A computer: Gemma 4 E4B*
Roman Urdu59% (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-31Word lists onlyA phone: word lists + Ward LiteA computer: word lists + Gemma 4 E4B
Right level48 (38%)50 (39%)92 (72%)
Urgent chats missed (of 28)23143
Called urgent wrongly (of 100)52111
Friendly chats not shown as calm (of 40)121724

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 dist builds an installer for your own computer into release/. 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 apk builds a lite debug APK (apk:gemma for the other; needs the Android SDK and JDK 21). Debug builds install as a separate app, org.ward.app.debug. android.yml builds 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 runs release.yml, which publishes signed APKs, the installers and SHA256SUMS as 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 put http://<computer's address>:11434 in 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.0 lets 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.app in 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 runsHow it's builtWho reads the chat
Android phoneCapacitor (mobile/), lite APK for any phone, Gemma APK for arm64Word lists + Ward Lite; Gemma 4 E2B helps write on 6 GB+ phones
ComputerElectron (desktop/), macOS, Windows, LinuxGemma 4 E4B through Ollama or built in
Browserapps/web on Render and GitHub PagesWord lists + Ward Lite in the page; hosted Gemma (Google AI Studio, through the relay) only if you press the demo button
Discordapps/discord on RenderThe same engine and Ward Lite in Node; no database
Open model or toolWhat it does in Ward
Gemma 4 E4B / E2BReads 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 2BThe baseline on the scorecard
Ollama / LiteRT-LMRun Gemma locally on computers and phones
RenderHosts the browser demo, the Discord bot and the demo's Gemma relay
Tiger DataKeeps every eval run; CI fails a push that makes a score worse (eval/history)
Browserbase + SerpApipnpm helpcheck checks the Coach's block and report steps against each app's help pages
ElevenLabs107 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.