
Inside NGL's fake-message engine — how a "who likes you?" app generates the very messages it then charges you to unmask.
NGL is an anonymous-question app built for Instagram. You post a link, friends send you anonymous questions, and a paid tier — NGL Pro — promises to reveal who sent them. A user came to a simple suspicion: messages were arriving in the inbox when no link had ever been shared. This report documents a full static reverse-engineering of the Android client, and the suspicion is confirmed by the app's own code. NGL contains a purpose-built engine that generates messages attributed to no real person, schedules them to land when the inbox is quiet, and funnels the resulting curiosity into a weekly subscription.
Every claim below is traceable to a specific artefact and a reproducible step. The client was acquired as a standalone Android package, fingerprinted, unpacked, and decompiled through two independent pipelines — one for the native DEX code, one for the JavaScript bytecode that carries virtually all of NGL's logic.
Artefact : ngl.apk
Size : 64,215,002 bytes
SHA-256 : a9a2da21f19a89a49c3060e11152e5dd4bb8b8cced45f087c2722927672e9fc4
Pipeline 1 (DEX) : jadx → Java sources for the Android/React-Native shell
Pipeline 2 (bytecode) : hermes-dec → assets/index.android.bundle
magic c61f bc03 · Hermes bytecode version 98
output hbc-out.js · 59,337,247 bytes of reconstructed JS
The single most direct piece of evidence is a hard-coded Firebase URL that asks NGL's backend to manufacture engagement messages for the authenticated user's own account. The name is self-describing: generate-additional-ems — "EMS" for engagement messages.
r8 = global;
r8 = r8.HermesInternal;
r9 = r8.concat;
r8 = 'https://ask-fun-d10f0.firebaseapp.com/api/generate-additional-ems?uid=';
r9 = r9.bind(r8)(r10); // r10 = Firebase auth UID of the logged-in user
r7 = _closure1_slot3;
r8 = r7.default;
r7 = r8.get;
r7 = r7.bind(r8)(r9); // HTTP GET, triggered from the client
The app takes your own Firebase uid, concatenates it onto this endpoint, and issues a GET. The backend then writes engagement messages into your account. This is not a message from another human; it is a request to NGL's servers to produce one on your behalf.
The client-side generator that shapes these messages is named, without any euphemism, generateFakeQuestion. The object it builds is the decisive artefact — read the flags closely:
r7 = {
'uid': null, // no sender — nobody sent this
'text': null, // empty body
'game': 'default',
'prompt': 'send me anonymous messages…',
'is_reported': false,
'is_open': false, // lands UNREAD
'is_replied': false, // lands UNANSWERED
'timestamp': null,
'isUserGenerated': true, // ← tagged as if a person made it
'textTranslationKey': null
};
isUserGenerated: true. A message with a null uid, a null text and a null timestamp cannot have come from a person. Yet it is stamped as user-generated at the moment of creation — it enters your inbox wearing the flag that asserts a human made it, indistinguishable from the real thing in the feed. is_open: false and is_replied: false guarantee it arrives as a fresh, unread, unanswered item, maximising the pull to open it.Faking a message is only half the system. The other half decides when to deploy it. The bundle carries a scheduling library keyed by day-of-week and hour, named REENGAGEMENT.*. The keys are literal time slots, and the copy is engineered to imply activity that did not happen.
SATURDAY20_EASTER and VALENTINES.* fire on top of these). Shading = number of distinct notification strings scheduled for that slot. Friday late afternoon through Saturday night is densest; Saturday 20h alone carries four copy variants.| Slot key | Push copy (English) | Implied event |
|---|---|---|
FRIDAY14 | "Someone typed a message…then left 🤫" | Typing that never became a message |
SATURDAY18 | "Typing…" | A person at their keyboard |
TUESDAY20 | "Someone viewed you 👀" | A profile view |
SUNDAY20 | "You have a new message 💌" | An arriving message |
THURSDAY18 | "New messages 💌" | Plural arriving messages |
WEDNESDAY18 | "Someone keeps coming back👀" | Repeat visitor |
FRIDAY21 | "Who sent this? 👀" | Curiosity hook → paywall |
MONDAY17 | "See sender hints 🕵️♂️" | Direct Pro upsell |
MONDAY17 through SUNDAY20, plus seasonal VALENTINES.* and WORLDCUP1/2 variants. Subtexts include "Tap here!", "Tap to reveal!" and "Drop your link and find out".These are not generic reminders. They are manufactured impressions of human attention — "typing", "viewed you", "keeps coming back" — fired at chosen hours to maximise the chance you open the app to an empty or near-empty inbox, then encounter the prompt to post your link or pay to see who's interested.
The engineered messages are not quarantined. They are merged into the same feed as genuine ones. The Redux store holds loveMessages, boostedMessages and reengagementMessages alongside newMessages, and the Firestore document for your account (users/{uid}) carries reengagementMessages next to newMessages as a peer field.
r1 = function(a0) { // Original name: useTopicNotifications
r12 = r2.reengagementMessages; // ← read from user doc, merged into feed
...
r10 = r2.userCountryCode;
r9 = r2.userLanguage;
r8 = r2.userDisablesLoveMessages; // the "Team NGL messages" toggle
...
}
Because the fake and the real share the same collection and the same rendering path, the user has no in-product way to tell them apart. The only visible tell NGL provides is a small "sent with ❤️ from team NGL" label — and even that is what the FTC order took aim at.
Every manufactured message terminates in the same funnel. The user is invited to find out who is interested, then charged weekly for the answer:
This analysis was cross-checked against the public enforcement record, and it is important to state that record precisely rather than repeat loose versions of it.
Verified — FTC and California, July 9 2024. The FTC's case names plaintiffs the Federal Trade Commission and the People of the State of California, and defendants NGL Labs, LLC and officers Raj Vir and Joao Figueiredo. The defendants agreed to $5 million — $4.5M in consumer redress and a $500,000 civil penalty to the Los Angeles District Attorney's Office. The order bars offering the app to under-18s and prohibits misrepresenting the source of messages. The FTC alleged NGL generated fake computer messages that appeared to be from real people and used them to drive Pro subscriptions by suggesting Pro would reveal senders' identities.
Verified — TechCrunch, June 29 and July 11 2022. Sarah Perez reported that NGL sent automated questions even after the shared Instagram Story link was taken down — proving the messages were not from friends — and that NGL charged about $9.99/week for "hints".
Strip the mechanics back and NGL's core loop is this: manufacture the evidence of interest, then sell the resolution. The app's value proposition is that people are curious about you. The fake-message engine supplies that evidence when the real world doesn't provide it, and the reengagement timer ensures it arrives precisely when you're most likely to be alone with an empty inbox. The subscription is the only way out of the anxiety the app itself creates.
Two details elevate this from "dark pattern" to something worse. First, the isUserGenerated: true stamp means a machine-authored message is labelled, at creation, as human — this is not a neutral "sample" message, it is a forgery of a person. Second, the product's audience skews young, and the FTC order had to explicitly bar NGL from offering the app to under-18s — a restriction you do not impose on a company that was acting responsibly toward children.
For the user who started this: messages arriving with no shared link are, on this evidence, almost certainly NGL's own. The "Team NGL messages" setting (backed by the disablesLoveMessages flag) may mute the labelled ones, but it does not disable the reengagement timer or the server-side generate-additional-ems endpoint. The only reliable way to stop the manufactured attention is to stop using the app.
Primary technical evidence is from the analysed artefact ngl.apk (SHA-256 above) and its reconstructed Hermes bundle hbc-out.js. Code blocks are transcribed from that decompilation. Legal references were verified against the linked official sources in October 2026; two widely-repeated but unverifiable claims were removed (see §VII).