Vol. I · Special Feature · Sunday Edition
Stories behind the product · Safety · Code · Intent
He wanted a dating app that did not treat people like inventory
How Wasl began, why safety sits above growth, and what actually runs under the pink buttons
Most dating apps sell speed. Swipe faster. Pay for boosts. Hope a human being survives the feed. This is the story of why I refused that script, what I built instead, and how the machines behind Wasl are wired to protect people before they try to impress them.
The idea did not arrive as a pitch deck. It arrived as irritation. Night after night, friends described the same comedy of errors: empty profiles, bots that smiled too perfectly, chats that jumped from hello to something ugly in three messages, and platforms that treated reporting like a suggestion box nobody emptied. I kept thinking the same quiet sentence. Connection should not feel like a casino.
Wasl, which also ships under the Huzz name in store listings, is named for a simple Arabic word that means joining, reaching, arriving. Not optimizing. Not farming attention. Arriving. That word became the brief. Build a mobile first place where people can discover each other with clearer intent, where mutual interest unlocks a private thread, and where optional Live sessions stay short, timed, and easy to leave.
I am making this app because I want a product that feels culturally awake without becoming a lecture. Features like Mehram, supervised lanes for conversation, clubs with live audio, and a real admin queue exist because I do not believe one Western template of romance fits every user who still wants honesty and dignity online. I want people to feel safe enough to be specific about who they are and what they want.
Pretty screens are easy to fake. Trust is not. Early on I decided the serious work would live on the server side, where the client cannot casually rewrite the rules. Accounts and most live application data sit on Firebase. Authentication holds identity. Firestore stores profiles, matches, reports, and session documents, with realtime listeners so both people see the same truth without stabbing refresh. Cloud Functions handle privileged work: age related gates, sensitive writes, token minting, and moderation side effects that should never be decided by a lonely phone alone.
Live video and voice run through LiveKit. That choice matters. Media credentials are short lived and only minted after the backend confirms you belong to an active Live session. In ordinary language, a stranger should not walk into your call by guessing a room name. The clock, the pair, the skip, the leave: those live in server backed state so both sides share one timeline.
Playful rooms and heavier realtime games lean on dedicated session servers where we ship them. Push rides the usual platform channels. Admin tools sit on a separate path, because moderation power should never look like a normal dating profile. The stack is boring on purpose. Boring infrastructure is how you keep romance from becoming chaos.
If you only remember one line from this paper, remember this. Safety is the design constraint, not the brochure chapter we paste on after launch week. Reporting has somewhere to land. Blocking cuts a path. Message scanning watches for sexual and harassment patterns. Flagged traffic can auto route into an admin queue. Repeat harm can escalate. In serious cases we can ban a physical device so a ban is not just a new email address with the same old habits.
Age assurance workflows gate dating surfaces until required steps are done. Child sexual abuse and exploitation get a zero tolerance posture. There is no soft landing for that category. Read the child safety and community pages if you want the operational detail. Mehram exists because some families and cultures want supervised conversation, and I refuse to pretend privacy means abandoning care.
Growth still matters. Downloads still matter. But I would rather ship slower and keep the room clean than race a chart while people get hurt inside the product. That is the trade I am making in public.
I want Wasl to be a place where people can meet without feeling hunted. I want the backend to keep score when someone misbehaves. I want Live to feel like a doorway you can close. I want matches to feel like consent, not a prize wheel. I want readers, parents, reviewers, and future teammates to understand that the pink gradient on the homepage is decoration. The real color of the product is the discipline underneath it.
This dispatch will grow as the product grows. If you are still here, thank you for reading past the buttons. The app is the invitation. This page is the reason.
Four panels. Zero mystery. The journey of a naughty upload that thought it was slick.
Readers always ask the spicy ones. Here they are, answered in public, without soft lighting.
We do not rely on an on-device NSFWJS model anymore. That path is gone. Visual enforcement now runs through Google Cloud Vision Safe Search Detection, the same family of Vision API calls we use when a photo hits Firebase Storage.
Technically: image bytes are passed to ImageAnnotatorClient.safeSearchDetection.
Google returns a SafeSearchAnnotation object. Inside it, each risk category is a
likelihood enum, not a free floating score. Categories include adult,
racy, violence, medical, and spoof. The
enum values are UNKNOWN, VERY_UNLIKELY, UNLIKELY,
POSSIBLE, LIKELY, and VERY_LIKELY.
Our Cloud Function layer, in visionModeration.js, maps those enums into a hard
gate. If adult is in {POSSIBLE, LIKELY, VERY_LIKELY}, block. If
racy is in the same set, block. That means bikini soft porn framing can fail,
not only full nudity. The callable moderateUploadedImage downloads the Storage
object server side, runs Safe Search, and if blocked deletes or rejects the object before it
becomes a published profile or story image. The client cannot override that decision. Logs
keep adult, racy, and violence labels for audit.
For Live, the same Safe Search philosophy is the visual brain. Session media still rides LiveKit with short lived tokens. If a session turns explicit, skip and leave cut the media path, and staff get the report trail. Safe Search is the classifier. The server is the judge. The admin queue is the appeal court.
Text is a different weapon, so it gets a different pipeline. Before send, a Cloud Function
toxicity check hits a bad words style blocklist. Blocked text never reaches the other person.
The attempt is stored in vulgarAttempts. If something slips through,
scanMessageText still scans sexual and harassment keyword lists, stamps
moderation metadata on the message, and auto opens a report for admins. Club chat uses the
same idea. Sexting is not a secret between two keyboards. It becomes structured evidence.
A Firestore report document is born: reporter, target, reason, categories, status, timestamps, optional match and message pointers. Auto flags create reports too. In the admin app, staff are routed away from the dating home into a console with reports, safety, verifications, users, and vulgar attempt views. They can open a case, read context, pull evidence CSV rows that stitch reports with message moderation flags, then warn, suspend, or device ban. Device bans fingerprint the hardware so "new Gmail, same phone, same habits" dies on launch with an automatic sign out.
Same Safe Search gate. Upload lands in Storage, moderateUploadedImage runs Vision,
adult and racy thresholds fire, object rejected. No avatar, no story
tile, no soft launch of soft porn.
End it. Skip. Leave. Tokens die with the session. Reports hit the queue. Admins can escalate using the same warn, suspend, and device ban tools. Safe Search remains the visual policy brain for image content in the product. LiveKit remains the transport, not the moral system.
That is solicitation and off platform grooming adjacent behavior. Keyword scanning can flag it. Users can report it. Admins see opener spam and vulgar attempt history. Repeated bait gets treated like abuse, not like marketing.
Supervisors can report. They can block on behalf of the person they protect. The chat stays labeled so nobody confuses a guardian with a flirt. Cases still land in the same admin pipeline.
Account bans stop a login. Device bans stop the handset. Fingerprint match on open means forced sign out. That is the answer to the eternal "I will just make another email" trick.
Then they want a different product. Mutual agreement to break Wasl rules is still a break. Filters and reports still apply. This newspaper is not a porn brochure.
Classifiers miss and overfire. That is why we keep likelihood labels in logs, keep thresholds in server code we can tighten, and keep humans in the admin console. Automation is the smoke alarm. Staff are the fire crew.