Competitive IntelligenceLearnQuran · Product & Engineering Internal · Confidential
APK Reverse-Engineering Teardown

Tarteel — cloud AI that grades your words, not your tajweed

A polished, commercially-mature freemium Quran app built on React Native + Hermes. Its headline "AI" — real-time recitation mistake detection — is server-side cloud speech-to-text: the phone only captures and streams audio. Crucially it grades which words you recite, not how you pronounce them — Tarteel's own UI says it "does not detect Tajweed mistakes … in development." No on-device model, no genuine LLM. It ships no anti-tamper and a client-trusted premium flag, but the flagship AI and synced data stay server-enforced.

App
Tarteel: AI Quran تَرْتِيل
Package
com.mmmoussa.iqra
Version
5.78.0 · build 394
What it is
Cloud-AI recitation & hifz app
Framework / Runtime
React Native New Arch · Hermes v96
Monetization
RevenueCat freemium · Play + Amazon
Prepared
9 June 2026
For
Director & Product Manager
EASY
Tamperability (no anti-tamper)
MOD.
Premium bypass (UI flips; AI server-gated)
B
Security grade
Cloud
AI mistake-detection (server-side)
41
Native .so libraries
184 MB
APK size (base) · ~275 MB installed
conf: high evidence-backed fact conf: med strong inference conf: low weak / uncertain verified re-checked, overrides analysis refuted wrong guess, corrected
Executive summary

The AI is real but lives on Tarteel's servers — and it grades transcription, not tajweed.

Tarteel (com.mmmoussa.iqra, v5.78.0/394) is an AI-powered Quran recitation, reading and memorization (hifz) app built entirely on React Native with the New Architecture (Fabric + TurboModules + Bridgeless) running Hermes bytecode v96 — not Flutter, not native Kotlin, and not Expo.

Its defining feature is real-time recitation mistake detection, and — contrary to a common assumption — this is server-side cloud ASR: the proprietary libNitroTarteel.so is a microphone capture-and-stream client that pushes raw audio over a WebSocket to wss://voice-v2.tarteel.io and receives transcripts/mistakes back. No ML model ships on-device, and the apparent "OpenAI" usage is Sentry SDK auto-instrumentation, not a real LLM call.

The decisive competitive fact is what that AI evaluates: it is a Quran-specialized speech-to-text recognizer that judges which words were recited (missed / extra / wrong word, and in Beta wrong tashkeel) — it does not acoustically grade how the letters were pronounced. On-screen "tajweed" colouring is rule visualization, not a score.

Commercially it is a mature RevenueCat freemium across Google Play and Amazon, with the AI feature paywalled. The app ships no anti-tamper (repackaging is Easy) and a client-trusted premium flag (Moderate to flip), but the flagship AI and all synced data are server-enforced, so a local crack yields an unlocked shell, not a free recognizer.

Bottom line

Tarteel's moat is its server-side Quran ASR plus a deep FSRS hifz engine — not the app. The open gap the category leader has not shipped is genuine acoustic tajweed grading; that, and an on-device recognizer for offline use, is where a competitor can attack.

React Native · New Arch Hermes v96 Cloud ASR (server-side) Tamper: Easy Premium bypass: Moderate
What it is

Identity, category & how it's built

At a glance
Category
Education — Qur'an recitation, reading & memorization (hifz) with cloud-AI mistake detection
Audience
Muslims memorizing & perfecting recitation; multi-language UI (EN/ID/MS/AR/UR/FR)
Framework
Bare React Native, New Architecture · Hermes bytecode v96 (HermesVM 0.14.0) conf: high
Distribution
Google Play & Amazon Appstore (split APK / App Bundle)
Min / Target SDK
24 (Android 7) → 36; compiled against API 36
Accounts
Social sign-in (Apple/Google/Facebook/email) → server-issued JWT (stored in MMKV)
Show framework-detection evidence (bare RN, not Expo)
  • index.android.bundle (17.5 MB Hermes) + libhermesvm.so (reports 0.14.0, bytecode v96) + libreactnative.so → React Native on the New Architecture (Fabric + TurboModules + Bridgeless).
  • Authoritative autolinked PackageList.java registers 50 native packages, none Expo; no expo-modules-core and 0 Expo native libs → bare RN (Community CLI), not Expo.
  • Heavy Margelo Nitro adoption replaces Expo equivalents: NitroMmkv/SQLite, Notifee+FCM, react-native-bootsplash/localize/audio-api.
  • Not Flutter (no libapp.so/libflutter.so), not native Kotlin (logic is Hermes bytecode).
Core features & content shipped
Cloud-AI mistake detection 604-page King Fahd Mushaf FSRS spaced repetition Wird daily tasks Spot Testing (active recall) Tajweed colour-coding + guide Multi-reciter audio Translations & tafsir Full-text search Goals · streaks · challenges Leaderboards & groups Home-screen widgets Alim Program

Core loop: read a full 604-page King Fahd (QCF) Mushaf → recite aloud with real-time cloud-AI mistake detection → memorize via FSRS spaced repetition ("Wird" daily tasks + "Spot Testing" active recall, incl. multiplayer). Layered with tajweed, multi-reciter audio, translations/tafsir, search, gamification, social and widgets.

AI & evaluation

What the "AI" is, what it evaluates & how good it can be

Four questions, in order: is the AI real and where does it run; what does it actually evaluate (transcription or genuine tajweed); how good can it be; and how would you measure that. Load-bearing claims were re-checked by adversarial agents.

Ground truth · where it runs

The recitation AI is real and runs on Tarteel's servers. The decompiled Kotlin client (ai.tarteel.shared.mic.SharedMicStream) is a thin audio-up / message-down pipe: it opens a WebSocket to wss://voice-v2.tarteel.io ("Connecting to VS" = Voice Server), sends a JSON START_STREAM handshake, streams raw PCM as binary WS frames (sendBinaryMessage), and forwards inbound text results to a single onMessageListener. There is no on-device recognizer or model; libNitroTarteel.so is a capture/stream client. verified · high

What it evaluates · the decisive fact

The AI is a Quran-specialized speech-to-text engine: it recognizes the word sequence you recite and matches it to the expected ayah, flagging which words are wrong / missed / extra (and, in Beta, wrong tashkeel). It does not acoustically grade how you pronounce — makharij, sifaat, madd duration, ghunnah or qalqalah are not scored. This is stated in Tarteel's own UI:

"Tarteel does not detect Tajweed mistakes but this feature is in development inshaAllah."
"Unannounced Pronunciation (tajweed) mistakes not yet supported."

On-screen tajweed colouring is rule visualization, and even "Tajweed Guided Review" is driven by mistake history (the word/tashkeel log), not an acoustic grade. verified · high

AI / algorithmTypeProvider / mechanismWhere it runsConf.
Recitation mistake detection Streaming speech-to-text (word recognition) Tarteel's own voice backend cloud ASR Server · wss://voice-v2 high
Tashkeel (diacritic) mistakes Same transcript pipeline, text-tier Server-produced, gated by isDiacritized — self-labeled Beta Server high
FSRS spaced repetition Scheduling algorithm (not ML) FSRSAlgorithm/FsrsScheduler — hifz review timing On device high
Tajweed colour-coding Rule visualization (not acoustic grading) colorTajweedMadd/Ghunnah/Qalqalah over mushaf text On device high
"OpenAI / LLM" Not present — false positive Sentry SDK auto-instrumentation strings, no key / endpoint / model refuted

The recitation pipeline — capture on device, recognition on the server

1

Mic capture On device

Oboe + react-native-audio-api capture; Recorder emits raw PCM. A local RMS volume meter (calculateVolume) drives the UI only — no on-device VAD or recognition.

2

START_STREAM handshake (JSON) On device → WS

{"event":"START_STREAM","data":<ClientConfig>} — target ayah range, modelName, recitationMode, the routing flags isDiacritized / isDualModel / isNewSttServer, and the user's JWT.

3

Audio streamed up (binary) On device → WS

Raw PCM chunks sent as binary WebSocket frames (sendBinaryMessage); muting simply drops frames client-side.

4

Server STT recognition Server · opaque

voice-v2.tarteel.io runs the Quran-specialized recognizer (dual fast+accurate and/or preview model per flags), aligns to the expected text, and emits PARTIAL_TRANSCRIPT / FINAL_RESULT_DETAILS / RESULT messages. Internals are not statically inspectable.

5

Messages down → classify Server + client

JS maps results to 5 mistake types + per-word scores. The recognized-word → expected-text alignment locus (client vs server) is not statically determinable — the server is given the expected range, and the client also ships per-word logic.

6

Persist & sync On device

Each mistake row (expectedTranscript, receivedTranscript, positions, time offsets) → local SQLite → synced to /v1/profile/mistakes.

What it evaluates — transcription vs genuine tajweed verified

Recitation dimensionGraded by the AI?Mechanism / evidence
Word substitution / omission / insertionYes — coreASR word-sequence match vs expected ayah → mistakeTypeIncorrectWord / MissedWord / ExtraWord. What the engine is built & tuned for.
Tashkeel / harakat (diacritics)Yes — BetaServer-produced, text/transcription-tier (mistakeTypeIncorrectTashkeel); requested via isDiacritized. UI: "a new feature and may miss some of your tashkeel mistakes."
Memorization peekYes — clientmistakeTypePeekedWord — a UI reveal the server can't observe, yet shares the mistake schema ⇒ taxonomy finalized client-side.
Tajweed quality (makharij, madd timing, ghunnah, qalqalah)NoExplicitly absent: "does not detect Tajweed mistakes … in development"; colorTajweed* = visualization only.
Voice / accent / maqam qualityNoOut of model scope — a correct word sequence in any accent passes; no acoustic-quality score field in the persisted schema.

Net: a reciter who says the right words with poor tajweed generally passes. The product is a word-verifier, not a pronunciation coach.

How good is it? — accuracy evaluation mostly inference

Honest answer

The recognizer runs entirely on voice-v2.tarteel.io; its confidence thresholds, N-best collapse and true error rates are server-internal and not observable statically. Below is an evidence-bounded ceiling & failure-mode assessment, plus the controlled test we would run to actually measure it (and why we did not).

An actively-tuned recognizer (good sign)
  • Dual-model ensemble. The isDualModel flag's own description: "use 2 models simultaneously (fast + accurate) to improve speed without compromising accuracy."
  • Preview/beta models with an in-app "Open Beta Model Feedback Form" and beta-model; copy: "Old preview model unavailable. Switched to a new preview model.", "The Preview Model has no guaranteed uptime."
  • Server A/B routing via isNewSttServer / GrowthBook key new-stt-server — accuracy iterated server-side, flag-gated.
  • Training-data flywheel. shouldCollectAudio / shouldLabelAudio ⇒ user recitations can be harvested & labelled to improve the model.
Inferred ceiling & failure modes

Should be strong: a closed Quranic corpus with the expected ayah range supplied a priori (startPosition/endPosition) makes word substitution/omission/insertion the easy case — plausibly high-90s% word accuracy on clear murattal in good audio (inferred).

Must degrade on: tashkeel minimal pairs (Beta), near-homophones, accents / melodic maqam, fast ḥadr tempo, and noisy or packet-dropped audio. Tajweed quality is a hard blind spot.

Tarteel itself measures FAR/FRR
  • "did the Preview Model detect mistakes incorrectly?"False-Reject
  • "did the Preview Model miss intentional mistakes?"False-Accept

The user-facing "Adaptive Mode" mistake threshold is exactly the FAR↔FRR dial.

Show how we would measure it definitively — and why we did not

A controlled minimal-pair test set would isolate each failure mode:

  • Substitution / omission / insertion pairs — correct ayah vs the same ayah with exactly one word swapped / dropped / inserted ⇒ expect exactly one Incorrect/Missed/ExtraWord at that position.
  • Tashkeel-only pair — identical consonantal skeleton, one deliberately wrong harakat (isDiacritized=true) ⇒ probes the Beta diacritic path.
  • True-tajweed control (the key one) — the same correct words recited once with correct madd/ghunnah/qalqalah and once deliberately violating them ⇒ hypothesis predicts no mistake either way, confirming transcription-not-tajweed.
  • Repeat each under: clean vs noisy vs packet-loss audio · slow murattal vs fast ḥadr · native vs non-Arab accent · min vs max threshold.

Metrics: WER/CER from the engine's own receivedTranscript vs expectedTranscript; per-mistake-type False-Accept and False-Reject rates; the FA/FR tradeoff curve across the threshold.

Not executed — out of scope. The recognizer is behind wss://voice-v2.tarteel.io, a competitor's paid endpoint, and START_STREAM requires a valid Tarteel JWT tied to the paid Mistake-Detection entitlement (we hold no account). Per the workflow's Authorization & Scope, accuracy/bypass feasibility is assessed from the code, never by hitting their live paid service. The ceiling above therefore stays explicitly inferred.

Show "on-device vs server" + OpenAI=Sentry evidence
  • Streaming client, no recognizer. SharedMicStreamKt.startStreamJson() serializes the config into {"event":"START_STREAM","data":…}; onAudioDatawebsocket.sendBinaryMessage; onMessageReceived forwards inbound text to the JS listener. No transcribe()/recognize()/loadModel()/infer().
  • Zero ML models on device. No .onnx/.tflite/.pt/.gguf/.mlmodel anywhere; none of the 41 native libs is an inference runtime (no ONNX/TFLite/Whisper/Vosk/Kaldi).
  • Config negotiated with the server. SharedSTTConfig carries Google-Cloud-Speech-style fields (sampleRate, channels, fileFormat, modelName, languageCode, enableInterimResults, enableWordTimeOffsets, maxAlternatives); SharedClientConfig adds authToken, userId, deviceId, recitationMode, startPosition/endPosition, isDiacritized, isDualModel, isNewSttServer, shouldCollectAudio, shouldLabelAudio.
  • "OpenAI / LLM" is a false positive. The instrumentOpenAiClient strings are vendored Sentry JS SDK auto-instrumentation (shipped alongside identical instrumentAnthropicAiClient / GoogleGenAI / LangChain hooks). No api.openai.com, no /v1/chat/completions, no gpt-* model id, no sk- key.
  • Caveat: client-only conclusions. What Tarteel's servers run internally (any LLM, the exact recognizer) is not statically observable.
Show mistake taxonomy & local schema
  • 5 mistake types: mistakeTypeMissedWord, ExtraWord, IncorrectWord, IncorrectTashkeel (Beta), PeekedWord (memorization). Modes: recitationMode = DETECTION (flag as you go) or CORRECTION (track correctedUpToWordNumber).
  • Local SQLite (Nitro SQLite) mistake row: mistakeType, expectedTranscript, receivedTranscript, positions, ayah, correctedUpToWordNumber, startTimeMs, endTimeMs, syncedAt — stores both expected and received transcript (a word-sequence diff) + per-word time offsets that power Mistake-Playback.
  • Sensitivity = the user-tunable mistakeThreshold ("Adaptive Mode"); isStartPositionStrict / isInitialTargetRangeStrict + MUSHAF_MARGIN_OF_ERROR_SECONDS tune start/timing strictness.
  • Open questions: server-side confidence/N-best collapse and numeric thresholds are opaque; the alignment locus (client vs server) is undetermined; whether tashkeel uses a diacritic-aware model vs post-hoc diacritization is opaque.
Tamper & mod resistance

How hard is this app to repackage or modify?

EASYTamperability

A bare RN/Hermes app with zero app-authored anti-tamper: no signature self-check, no Play Integrity/SafetyNet, no certificate pinning, low obfuscation. Repackage + re-sign is trivial. The only thing stopping a free "paid-AI" mod is that the cloud ASR and premium entitlements are gated server-side by a Tarteel JWT + RevenueCat receipt — not by the APK. A mod yields an unlocked shell, not working paid AI.

Defensive controlStatusWhat we foundConf.
Signing scheme v2 + v3 APK Signing Block present (IDs 0x7109871a v2, 0xf05368c0 v3, + Play source-stamp); zero .RSA/.SF v1 files. Any tamper invalidates the signature → attacker must fully re-sign with their own key. high
Signature / integrity self-check Absent App never reads its own signatures[0] to compare a known hash. Only GET_SIGNATURES/getSigningInfo hits are GMS (GoogleSignatureVerifier) and Adjust (fingerprinting Facebook's cert) — neither gates Tarteel. A re-signed mod is not detected. high
Play Integrity / attestation Absent No play.core.integrity, no IntegrityManager, no SafetyNet/DeviceCheck. The only "attestation" hits are GMS FIDO2/WebAuthn (passkeys), unrelated. high
Certificate pinning Absent No networkSecurityConfig/<pin-set>; OkHttp CertificatePinner ships but is never instantiated with pins; no TrustKit. Traffic to api/voice/RevenueCat is MITM-inspectable with a user CA on a rooted device. high
Root / debug detection Dormant A full native root toolkit ships inside react-native-nitro-device-info (isDeviceCompromised(): Magisk/KernelSU/APatch/su/busybox/test-keys), but the JS layer never calls it → capability present, not in force. App not declared debuggable. high
Code obfuscation Low App Kotlin/Java keep descriptive names (ai.tarteel.shared.mic.*); ~2 single-letter classes total → R8 mangling effectively off. No string encryption. Hermes bytecode is the only mild speed-bump; JS identifiers (isPremium, mistakeDetection) are intact and patchable. med

Post-mod functionality — after a generic repackage + re-sign with an attacker key

CapabilityOutcomeWhy
Cloud AI mistake-detectionNot unlockedApp still runs (no integrity check), but the STT session needs a valid JWT + paid entitlement at voice-v2 — a free mod has neither → 401/403. Modding alone does not grant paid AI.
Premium UI gatesCosmetic onlyLocal isPremium flag can be flipped to show premium UI, but any server-validated feature re-checks and fails (see Paywall).
Reciter audio playbackSurvivesStreamed from unauthenticated CDN (audio-cdn.tarteel.ai/quran/…mp3) — no token/pinning → plays unchanged (free content anyway).
Offline Mushaf readingSurvivesFully local (bundled fonts + SQLite via DBManager); no auth/signature dependency.
FCM push / Google Sign-InBreaksBound to package + signing-cert SHA registered with the Firebase/Google project; re-signing with an attacker key fails closed.
Analytics / attributionSurvivesSentry/Mixpanel/Adjust/GrowthBook use client-embedded DSNs/keys that don't verify the APK signature → keep sending (Adjust may mis-attribute).
Show tamper-detection commands & output
tamper-checks.sh
# signing scheme — v2/v3 block, no v1 JAR sig
strings -a com.mmmoussa.iqra-394.apk | grep -q "APK Sig Block 42" # => present
unzip -l com.mmmoussa.iqra-394.apk | grep -ciE '^META-INF/[^/]+\.(RSA|SF)$' # => 0

# pinning / attestation / root-detection / integrity signals (app code)
grep -raEi "certificatePinner|network_security_config|play.core.integrity|SafetyNet|IntegrityManager" \
  decompiled/sources # => only GMS/OkHttp boilerplate, ZERO app-authored

# root toolkit exists but is never invoked from JS
grep -ac "isDeviceCompromised\|verifyDeviceIntegrity\|checkMagisk" analysis/bundle_strings.txt
# => 0 invocations => dormant capability

# obfuscation: app symbols retained => unobfuscated
grep -ac "ai/tarteel/shared/mic/SharedMicStream\|isPremium\|mistakeDetection" analysis/bundle_strings.txt
# => many hits => R8 mangling effectively off for app code
Premium / paywall bypass

How easily could someone unlock the paid tier for free?

MODERATEBypass risk

The paywall UI is client-trusted: a single Redux boolean (selectIsPremiumUser) derived from an unverified, cached RevenueCat CustomerInfo. Flipping it unlocks locally-bundled / UI-only perks (no-ads, on-device translations/tafsir/word-by-word, tajweed colours). But the flagship cloud AI and all synced data authenticate against Tarteel's servers (which even expose /v1/account/verify/sub/) — so a local crack yields an unlocked shell, not a free working recognizer.

Bypass factorStatusDetailConf.
Entitlement location Mixed UI gate = client-trusted boolean from cached CustomerInfo (held entirely in JS/Redux; libNitroTarteel.so has zero purchase logic). Feature value = server-enforced (JWT-authed API + voice WS). high
Local-flag tamper Easy One well-named selector selectIsPremiumUser survives in the Hermes string table → quick to locate and force true (or seed MMKV with a premium-looking CustomerInfo). med
Entitlement verification mode Disabled RevenueCat Trusted Entitlements SDK ships (entitlementVerificationMode token present) but the app never opts in → cached CustomerInfo consumed without signature verification. med
Offline unlock Partial UI/local-only perks unlock offline; anything server-validated or server-stored cannot be conjured by a flag flip. med
Server-side resistance Holds Cloud AI session + profile/mistakes/wirds endpoints re-derive trust from the account JWT, not the client flag. Single unknown: whether voice-v2 enforces entitlement per session is not statically verifiable. med

What actually unlocks after a local isPremium flip

Premium featureResultWhy / gatekeeper
No-adsUnlocksPure client decision off the premium boolean; no ad-network gate → unlocks fully offline.
Word-by-word / tafsir / translations / tajweed coloursPartialAlready-bundled content renders once the flag flips (UI gate only); additional packs fetched from download/pl-cms.tarteel.ai may be entitlement-gated at download.
Cloud AI mistake-detection (+ tajweed/tashkeel)Server-gatedUI/entry unlocks, but the session needs voice-v2 to accept a START_STREAM carrying the JWT/userId; tashkeel is just the isDiacritized param of that session.
Memorization planning · advanced spot-testing · challengesPartialLocal toggles flip; but synced plans (/v1/profile/manual_memorization_entries), recitation-scored spot-tests (reuse the cloud ASR) and multiplayer need the server.
Mistake history / frequencyStays lockedServer-stored, fetched from /v1/profile/mistakes; a local flag can't fabricate server-side history.
Cross-device syncStays lockedInherently server-authoritative (JWT-authed profile endpoints); the backend decides what to sync per entitlement.
Show paywall / entitlement evidence
  • Billing SDK: RevenueCat (react-native-purchases) over Google Play Billing 7.1.1 and Amazon Appstore IAP (ProxyAmazonBillingActivity) — dual-store. Bridge events RNPurchases-CustomerInfoUpdated; full CustomerInfo surface (getCustomerInfo, addCustomerInfoUpdateListener, invalidateCustomerInfoCache, restorePurchases, getOfferings).
  • The premium gate funnels through one Redux selector selectIsPremiumUser derived from the cached entitlements map; gating components subscribe to it. No per-action server re-check found on the client side.
  • Upsell surfaces: MistakeDetectionUpsellModal, MemorizationModeUpsellModal, compareFeatures* — UI gated on the boolean.
  • Free-trial-led funnel with server-side /v1/account/activate_trial_offer (a Cloudflare Worker) and win-back / referral loops. Exact SKUs/prices are runtime-fetched from RevenueCat Offerings (not in the bundle).
Security audit

Network, permissions & secrets

BOverall grade

A hardened manifest baseline and correct secrets hygiene (cleartext off, backup off, not debuggable, only publishable client keys), undermined chiefly by missing certificate pinning, missing device attestation, and a leaked dev voice endpoint. No genuinely sensitive server secret was found in the bundle or native libraries.

Permissions

PermissionJustificationAssessment
RECORD_AUDIO / FOREGROUND_SERVICE_MICROPHONERecitation listening / STT capture (MicRecordingService)justified
FOREGROUND_SERVICE_MEDIA_PLAYBACKReciter audio player (TrackPlayer)justified
INTERNET / ACCESS_NETWORK_STATEAPI + voice WS streamingjustified
SCHEDULE_EXACT_ALARM / RECEIVE_BOOT_COMPLETED / POST_NOTIFICATIONSPrayer/streak reminder notifications (Notifee)justified
com.android.vending.BILLING / gms…AD_IDRevenueCat purchases / Adjust + Mixpanel attributionjustified
READ/WRITE_EXTERNAL_STORAGELegacy storage — no maxSdkVersion capminor hygiene

No SMS / CONTACTS / LOCATION / CAMERA / READ_PHONE_STATE — proportionate, no over-reach. Exported surface minimal (MainActivity deep-link handler, widget providers, BootReceiver, guarded SDK receivers); the app's own services & FileProviders are exported=false.

Network, data & secrets

Hardening baseline
  • good Cleartext disabled (usesCleartextTraffic="false")
  • good Backup disabled (allowBackup="false")
  • good Not debuggable (release default)
  • gap No explicit network-security-config
Network security
  • medium No certificate pinning → MITM-able
  • medium No Play Integrity / attestation
  • low Dev voice endpoint ships in prod bundle
  • low Hermes + readable native symbols → reversible
Secrets & data
  • good Only publishable client keys present
  • good Zero sk_live / AKIA / PEM / client_secret
  • good No hardcoded Bearer/Basic creds
  • note libsigner.so = Adjust signer (not Tarteel)
Show embedded keys (all publishable) & the libsigner correction
KeyTypeRisk
goog_… / appl_…RevenueCat public SDK keyspublishable · low
Sentry DSN (o298228.ingest.sentry.io/5216068)Ingest-only, write-onlypublishable · low
prod_EIZy…GrowthBook client key (read-only)publishable · low
AIzaSy…Firebase/google-services client API keyrestrict to package+SHA-1
Google OAuth client idPublic identifierpublishable · low

libsigner.so is NOT a Tarteel signer claim refuted — it is the third-party Adjust SDK signature library (com.adjust.sdk.sig), building an authorization: Signature … header for Adjust attribution anti-fraud. Zero references to "tarteel", "voice", "hmac" or "bearer". Tarteel API auth is JWT-based in the JS layer.

Backend & API

Endpoints, hosts & the auth model

A first-party REST API on api.tarteel.io (versioned /v1/), a dedicated real-time WebSocket voice cluster, a Payload CMS, and a multi-host CDN tier. Auth is social sign-in exchanged for a server-issued JWT.

HostRoleNotable paths / notesConf.
api.tarteel.ioPrimary product REST API (/v1/)account/auth, profile, mistakes, srs, leaderboards, groups, alim, aadhigh
wss://voice-v2.tarteel.ioReal-time STT / mistake detection (prod)Streaming recitation recognition; dev variant voice-v2-dev leaked into prodhigh
api.tarteel.aiInfra / version gate/v1/mobile/minimum-supported-version/med
audio-cdn / static-cdn / download.tarteel.aiReciter audio & asset CDN/quran/<reciter>/<sssaaa>.mp3; asset-manifestshigh
pl-cms.tarteel.aiPayload CMS — app-driven content"pl" = Payloadmed
srs-onboarding.worker.tarteel.aiCloudflare Worker — trial activation/v1/account/activate_trial_offer/high
Account / Auth
  • /v1/account/authenticate
  • /authenticate_with_token
  • /social_auth_create_account
  • /validate_social_auth_sign_in
  • /activate_trial_offer
Profile (main user data)
  • /v1/profile/mistakes
  • /srs_settings, /wirds
  • /manual_memorization_entries
  • /recordings, /bookmarks
  • /canny_sso, /hydrate
Social / Program
  • /v1/leaderboards/…
  • /profile/community_group/join
  • /profile/family_group/invite
  • /v1/alim/apply/status
  • /v1/aad/schedule (Ayah-a-Day)
Auth model high

Social sign-in (Apple/Google/Facebook/email) is exchanged server-side for a JWT bearer token stored client-side via MMKV. Open question: whether the JWT is short-lived with refresh, and whether it sits in MMKV vs Android Keystore. Third-party backends are cleanly separable: api.mixpanel.com, Sentry ingest, cdn.growthbook.io, errors.rev.cat, tarteel-app.firebaseio.com, tanzil.network (canonical Quran text).

Tech stack

What powers the app

LayerTechnologyEvidence
Runtime / frameworkReact Native New Arch · Hermes bytecode v96libhermesvm.so (0.14.0) + libreactnative.so
Proprietary coreMargelo Nitro module — 14 manager specslibNitroTarteel.so (MicStream, Session, SRS, Wird, Widget…)
Margelo Nitro stackNitroModules, NitroMmkv, NitroHaptics, RNNitroSQLite, NitroDeviceInfoReplaces Expo equivalents
UI / graphicsSkia (~11 MB), Reanimated 4 + Worklets, Moti, Gesture Handler, SVGlibrnskia.so, libreanimated.so
Audioreact-native-audio-api (Oboe + bundled FFmpeg) capture; track-player (Media3/ExoPlayer) playbackliboboe.so, libav*.so
State / storageRedux Toolkit + redux-persist (MMKV) + TanStack Query; MMKV + Nitro SQLite + AsyncStorage + DataStoreReduxMMKV; libRNNitroSQLite.so
PaymentsRevenueCat — dual-store (Play + Amazon)react-native-purchases; ProxyAmazonBillingActivity
Analytics / growthMixpanel · Adjust (+SKAdNetwork) · GrowthBook · Sentry (JS+NDK) · Firebase (FCM) · Canny · Shake12+ third-party SDKs
BackendFirst-party REST + real-time WS voice cluster + Payload CMS + CDN tierapi.tarteel.io, wss://voice-v2
Show full native-library inventory (41 in arm64-v8a)
  • Proprietary: libNitroTarteel.so (2.1 MB, 14 Nitro managers; no ML/crypto/TLS libs).
  • RN core: libhermesvm / libreactnative / libjsi / libfbjni / libhermestooling (hosts the voice WS transport).
  • Graphics/audio: librnskia (~11 MB), libreanimated / libworklets, libreact-native-audio-api, liboboe, libavcodec/avformat/avutil/swresample (FFmpeg, playback decode — no Opus → not mic encoding).
  • Nitro/data: libNitroModules / libNitroMmkv / libmmkv / libRNNitroSQLite / libNitroHaptics / libnitrodeviceinfo.
  • SDKs: libsentry(+android), libsigner (= Adjust signer), 8× libreact_codegen_*, Fresco image pipeline, DataStore, libc++_shared.
Monetization & growth

A RevenueCat freemium, dual-store, AI-anchored funnel

Subscription backbone
  • RevenueCat "Tarteel Premium" — Offerings, Entitlements, promotional/win-back offers, CustomerCenter.
  • Dual store — Google Play Billing 7.1.1 and Amazon Appstore IAP (AmazonPurchaseScreen) — unusual reach.
  • Free-trial-led funnelstartNDayFreeTrial, intro pricing, server-side activate_trial_offer.
  • Win-back & churnpurchasePackageWithWinBackOffer, exit-survey deep link.
  • Seasonal — Sha'ban/Ramadan-timed upsells (shabanMistakeDetectionUpsell).
Growth, experimentation & attribution
  • GrowthBook A/B + gradual rollout (useIsDualModelEnabled, useIsNewSttServerEnabled, useIsSpotTestingMultiplayerEnabled).
  • Mixpanel — monetization/engagement events (recordUpsellTriggered, purchaseSuccess).
  • Adjust — install-referrer + SKAdNetwork conversion values.
  • Referral loop — DiscoverAndEarn / VirtualCurrency, family/group invites, Discord gating.
  • Multi-provider auth reduces signup friction → conversion.

Paywalled feature matrix (Free vs Premium)

CapabilityPremium-gated signal
AI Mistake Detection (flagship)MistakeDetectionUpsellModal, compareFeaturesMistakeDetectionExplanation
Mistake History / Frequency / PlaybackcompareFeaturesMistakeHistory / MistakeFrequency
Tajweed & Tashkeel mistakescompareFeaturesTajweedMistakes / TashkeelMistakes
Memorization planning · advanced spot testingmemorizationModeUpsell, spotTestingUpsellSelfAssessment
Ads removal · cross-device synccompareFeaturesNoAds, compareFeaturesCrossDeviceSyncing
Word-by-word · tafsir · translationscompareFeaturesWordByWord / Tafsir / VariousRecitations

Exact SKUs, durations and price points are not in the bundle (runtime-fetched from RevenueCat Offerings). The flagship AI mistake-detection is the correct paywall anchor.

Product features

The deep feature set behind the core loop

Mushaf reader
  • 604-page King Fahd (QCF) Mushaf — 604 per-page glyph fonts, page-accurate typography
  • Multiple scripts: QPC Hafs v1/v2/v4, IndoPak 15-line, DigitalKhatt, Uthmani, Nastaliq
  • Word-by-word data; gesture-driven reader; sepia/dark, zoom, SRS-state page colours
Recitation & mistake detection
  • Real-time cloud STT sessions (DETECTION / CORRECTION)
  • 5 mistake types incl. tashkeel/tajweed; threshold & strictness controls
  • Verse peeking; review your own recordings; results sync to /v1/profile/mistakes
Memorization (Hifz) + SRS
  • FSRS spaced-repetition scheduling (tunable retention, weights, intervals)
  • Wird daily review tasks; Spot Testing active recall (incl. multiplayer)
  • Manual memorization entries; memorization chunks; progress tracking
Tajweed & audio
  • Tajweed colour-coding + interactive Guide (Madd, Qalqalah, Tafkhim, Silent, Ghunnah)
  • Many reciters streamed from audio-cdn.tarteel.ai (Alafasy, Husary, Abdul Basit, Sudais…)
  • Word-level highlight sync; gapless audio; background playback; per-reciter download
Content
  • 8+ translations (Khattab, Sahih Intl, Pickthall, Yusuf Ali…)
  • Tafsir (partial) med
  • Full-text search (FTS; Arabic + transliteration)
  • Bookmarks with sorting
Engagement
  • Goals, streaks (saver), habits, challenges (multiplayer)
  • "Ayah a Day" daily-verse habit + widget
  • Home-screen widgets: Recite, Search, Streak, Ayah-a-Day
  • Notifications & reminders (Notifee + FCM)
Social / account
  • Leaderboards + community/family groups (hifz network)
  • Social sharing (verses, streaks, recitation images)
  • Multi-provider auth (Google/Apple/Facebook/email/OTP)
  • Alim Program — scholarship/referral application
Strategic notes · analyst assessment

What this means for us

The following are interpretation and recommendation (not reverse-engineered facts) — framed for product and engineering decisions.

Opportunity The tajweed-grading gap is the opening.

Tarteel's AI grades word transcription, not pronunciation/tajweed quality — it says so itself ("in development"), and tashkeel is Beta. For a tajweed-first builder (e.g. LearnQuran's own com.bi.learnquran), genuine acoustic tajweed feedback — scoring madd duration, ghunnah, qalqalah and makharij, ideally on-device for offline + privacy — is the differentiator the category leader has not shipped.

Positioning The moat is the backend ASR, not the app.

Tarteel's defensible asset is its server-side Quran ASR (the voice-v2 cluster + recognition models), not client code. Cloning the RN app gains nothing without the recognizer. Conversely the cloud dependency means recitation only works online — a real UX gap a competitor could attack with a (harder-to-build) on-device recognizer for offline practice.

Reliability Memorization is the retention engine — and the bar.

The hifz system — FSRS spaced repetition, Wird daily tasks, Spot Testing active recall, multiple Mushaf scripts — is well beyond a typical "read + play audio" app. This is the structural reason a user stays for months; a new entrant competing on reading alone will struggle. The bar is now "structured, scheduled memorization with AI feedback."

Exposure Security is "good enough," with two clear gaps.

Correct secrets hygiene and a hardened manifest, but no pinning and no attestation means the server must bear all abuse/entitlement enforcement. For a paid AI API, requests can be replayed/MITM'd from a rooted device. Adding certificate pinning + Play Integrity would be low-effort, high-signal hardening — a lesson for our own app.

Them vs LearnQuran
Tarteel — observed

Cloud-AI word recognition + deep hifz

  • Yes — Strong server-side Quran ASR; mature FSRS hifz engine + gamification
  • Yes — Sophisticated, calendar-aware monetization (RevenueCat, dual-store, GrowthBook)
  • No — Grades transcription only — no acoustic tajweed quality (their words: "in development")
  • No — Online-only recitation; no anti-tamper / pinning / attestation
LearnQuran — recommended posture

Own the gap: acoustic tajweed, offline-capable

  • Yes — Ship genuine pronunciation/tajweed grading (madd, ghunnah, qalqalah, makharij)
  • Yes — On-device recognizer for offline practice + privacy where feasible
  • Yes — Match the hifz bar (structured SRS) and harden (pinning + Play Integrity)

Net read: Tarteel sets the bar on cloud word-recognition and structured memorization, but leaves genuine tajweed grading and offline recitation wide open. The takeaway is a niche to own (pronunciation quality) and a discipline to keep (server-side enforcement + client hardening).

Appendix

Raw evidence & provenance

A · Methodology, provenance & caveats
Source artifact
com.mmmoussa.iqra-394.apk (base 184 MB; ~275 MB installed) + config splits
App identity
Tarteel: AI Quran · com.mmmoussa.iqra · 5.78.0 (build 394)
Targets
Min SDK 24 → Target/compile SDK 36
Framework
Bare React Native, New Arch · Hermes bytecode v96 (HermesVM 0.14.0)
Method
Static decompilation only — jadx, unzip, strings, grep, plus a multi-agent analysis with adversarial verification of high-stakes claims. No dynamic / runtime analysis, no traffic capture.
Output dir
by-claude-tarteel-ai/decompiled/, decompiled-with-res/, assets/, native/, analysis/
Analysis date
9 June 2026

Hermes string run-on caveat: the Hermes string table packs literals back-to-back, so strings output frequently concatenates unrelated literals (e.g. download.tarteel.ainstrumentOpenAiClient); substring matches were treated as fuzzy and corroborated. Adversarial verification re-checked the load-bearing claims, and the corrected statements override the source analyses: (1) recitation ASR is cloud, not on-device; (2) "OpenAI" = Sentry instrumentation; (3) only publishable keys; (4) libsigner.so = Adjust signer. AI-evaluation re-verification: "evaluates transcription not tajweed" CONFIRMED; recognizePrefixSuffix is a Reanimated false friend (dropped); tashkeel is server-produced (not client-derived) and Beta; the alignment locus (client vs server) is not statically determinable; the AI accuracy ceiling is inferred, not measured (the recognizer is a paid, out-of-scope endpoint).

Provenance & ethics. All findings derive from static analysis of a publicly distributed application binary for authorized competitive-research and defensive-security purposes. No active testing, traffic capture, or disruption of any live service was performed. "Strategic notes" reflect analyst interpretation, not statements of fact about the subject app.

B · Full endpoint inventory
First-party — API & voice
  • https://api.tarteel.io/v1/…
  • https://api.tarteel.ai/v1/mobile/minimum-supported-version/
  • wss://voice-v2.tarteel.io
  • wss://voice-v2-dev.tarteel.io
  • https://srs-onboarding.worker.tarteel.ai/v1/account/activate_trial_offer/
First-party — CDN & content
  • https://audio-cdn.tarteel.ai/quran/<reciter>/<sssaaa>.mp3
  • https://static-cdn.tarteel.ai/asset-manifests/…
  • https://download.tarteel.ai · https://pl-cms.tarteel.ai
  • http://tanzil.network
Third-party
  • https://api.mixpanel.com
  • …@o298228.ingest.sentry.io/5216068 (+2nd DSN)
  • https://cdn.growthbook.io · https://rt.growthbook.io
  • https://errors.rev.cat
  • https://tarteel-app.firebaseio.com

Note: the raw endpoints.txt is dominated by Hermes string-table run-on artifacts (e.g. https://download.tarteel.ainstrumentOpenAiClient); only cleanly de-concatenated hosts are treated as real.

C · Decompiled snippets & key strings
ai/tarteel/shared/mic/SharedMicStream(Kt).java — the WS client
# control messages are JSON; audio is binary frames
startStreamJson(config) => {"event":"START_STREAM","data":<ClientConfig>}
endStreamJson()         => {"event":"END_STREAM"}
onAudioData(byte[] data) => websocket.sendBinaryMessage(data)   # raw PCM
onMessageReceived(String message) => onMessageListener.invoke(message)
calculateVolume(data) = 20*log10(RMS)   # local meter only, no VAD
LogKt.d: "Connecting to VS..." / "Connected to VS" / "Started voice session"

# SharedClientConfig — the START_STREAM handshake fields
appVersion, audioConfig(=SharedSTTConfig), authToken, deviceId, devicePlatform,
isMemorization, isDiacritized, isDualModel, isInitialTargetRangeStrict,
isNewSttServer, isStartPositionStrict, startPosition, endPosition,
recitationMode, sessionId, userId, shouldCollectAudio, shouldLabelAudio

# SharedSTTConfig — Google-Cloud-Speech-style streaming ASR request
fileFormat, channels, sampleRate, modelName, languageCode,
enableInterimResults, enableWordTimeOffsets, maxAlternatives
AI evaluation — strings in index.android.bundle
# what it evaluates — Tarteel's own disclaimers
"Tarteel does not detect Tajweed mistakes but this feature is in development inshaAllah."
"Unannounced Pronunciation (tajweed) mistakes not yet supported."
"Tashkeel mistake detection is a new feature and may miss some of your tashkeel mistakes."

# mistake taxonomy (word/diacritic-level, not acoustic)
mistakeTypeMissedWord · ExtraWord · IncorrectWord · IncorrectTashkeel · PeekedWord
expectedTranscript · receivedTranscript · recitationMode = DETECTION|CORRECTION

# model iteration — Tarteel measures FAR/FRR itself
"use 2 models simultaneously (fast + accurate)..."   # isDualModel
"did the Preview Model detect mistakes incorrectly?"  # False-Reject
"did the Preview Model miss intentional mistakes?"    # False-Accept
"The Preview Model has no guaranteed uptime."   new-stt-server (GrowthBook)
D · Detection commands & raw output
framework + ai + model detection
# framework fingerprint — React Native + Hermes
ls native/lib/arm64-v8a/   # => libhermesvm.so + libreactnative.so => RN/Hermes
ls assets/flutter_assets 2>/dev/null   # => absent => not Flutter

# on-device ML model check (ABSENCE is the finding)
find assets native \( -name "*.tflite" -o -name "*.onnx" -o -name "*.gguf" \)
# => none => no on-device model
ls native/lib/arm64-v8a/ | grep -iE "tflite|tensorflow|onnx|whisper|vosk"
# => none => no inference runtime linked

# cloud ASR endpoint + OpenAI=Sentry check
grep -aoE "wss://[a-z0-9.-]+" analysis/bundle_strings.txt | sort -u
# => wss://voice-v2.tarteel.io (+ -dev)
grep -ac "api.openai.com\|/v1/chat/completions\|sk-[A-Za-z0-9]" analysis/bundle_strings.txt
# => 0  => "OpenAI" strings are Sentry instrumentation, not an LLM call
E · Full permission & manifest dump
AndroidManifest.xml
# uses-permission
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.android.vending.BILLING"/>
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>   # legacy, no maxSdkVersion

# application flags
android:debuggable          => ABSENT (false)
android:usesCleartextTraffic => "false"
android:allowBackup         => "false"
# signing: v2 + v3 APK Signing Block; no v1 .RSA/.SF