Skip to content
DG

Shipping beats perfection.

Kathmandu, Nepal — Remote worldwide

Back to writing
Product & Mobile

Shipping AuraLock with React Native & Kotlin

Building AuraLock — choosing what matters vs blocking, React Native + native Kotlin modules, Play Store policy. Founder notes from zero to launch.

React Native Kotlin Android Product Led Play Store
Durga Gairhe
Durga Gairhe
Software Engineer & Product Builder · Kathmandu, Nepal
AuraLock — Android focus app cover 1376×768

AuraLock started with a single inversion: instead of choosing what to block, choose what matters. That one philosophy — start with what matters, not what to avoid — shaped every decision that followed: UX copy, architecture, store policy, and what I deliberately refused to build.

This is the full build log, told through the PEM framework I use for products and writing: Point → Evidence → Meaning. For each major choice, I state the point, show the evidence that shaped it, and explain what it means for the product and for you if you’re building solo.

Why the inversion matters

Point: Most focus apps are punitive.

They open with a negative frame: “Select apps to block.” The user’s first action is to declare what they cannot do. The system then locks, scolds, or shames. It feels like being parented by your phone. AuraLock flips it: you create an Aura — a curated set of apps and sites that stay available for a session. Everything else gently becomes unavailable. The feeling is curation, not control.

Evidence: In early interviews and store review mining, 70–80% of users described blockers as “annoying” or “easy to disable.” One user said, “I just turned it off when I needed TikTok, then never turned it back on.” Play Store reviews for top blockers repeat “too easy to bypass” far more than “helpful.” When I tested two empty states — A: “Choose what to block” vs B: “Choose what stays” — B increased Aura completion by ~38% and first-7-day return by 22%. The wording was the feature.

Meaning: Language is the interface. “Choose what stays” aligns with identity: I am protecting my focus versus I am being locked out. That subtle ownership changes retention. It also simplifies the product: an allow-list is clearer than a block-list with exceptions (you need Slack for Work but not for Family dinner — a block-list can’t model that cleanly, an Aura can).

Start with what matters, not what to avoid.

If you’re building: find the inversion that turns a negative loop into a positive one, then let it veto features.

React Native + Kotlin, not either/or

Point: Split the stack so UI ships weekly while enforcement stays bulletproof.

AuraLock is a hybrid app. The shell is React Native + TypeScript for UI, navigation, and Supabase-backed state (sessions, Focus Score, XP, goals, journal). The spine is Kotlin native modules for device-level enforcement.

Evidence: This split was earned. Early prototypes that tried to enforce purely in JS missed OEM edge cases — battery savers killing background work, UsageStats delays, and overlay inconsistencies. Moving enforcement to Kotlin — UsageStatsManager for detection, AccessibilityService as a fallback for reliable overlay/enforcement where OEMs restrict, and a correctly declared ForegroundService — closed those gaps. UI iteration stayed at React speed: I could ship a new journal prompt or quote rotation without touching native.

  • UI layer: React Native, React Navigation, Supabase (Auth, Postgres, Edge Functions for session lifecycle, gamification, scheduled Auras), Focus Score pipeline, XP/levels/achievements, Goals, Daily Journal, quotes.
  • Native layer: Kotlin modules — AuraEnforcementModule, UsageStatsModule, ScheduleModule — handling foreground app detection, enforcement overlay, and scheduled triggers.
  • Web layer: Next.js + Supabase backend plus Chrome & Firefox extensions (Manifest V3 declarativeNetRequest) mirroring the Aura for domains.

The bridge is intentionally minimal: React Native tells Kotlin to start an Aura allow-list for a set duration with specific packages and domains, Kotlin enforces at the OS layer, and JavaScript listens for breach and completion events. No content is read — only package-level signals for privacy.

Performance notes from production: cold start Aura creation under 900ms median, enforcement check under 80ms median.

Meaning: The boring decision — keep enforcement native — made the interesting decision — a calm, minimal UI — possible. When you own both sides, you make better tradeoffs: “if we can’t enforce it reliably, we shouldn’t promise it in the UI.”

Play Store is a product discipline

Point: Shipping to Play Store as a solo founder is a full-time product job.

The code was ~40% of the work. The rest was policy, plumbing, and pixels.

Evidence: The path was: target SDK 34 → foreground service type specialUse with disclosure video → AccessibilityService justification (why overlay is needed for focus, not for data) → Data Safety form (what is collected: package names, session timestamps; what is not: app content) → privacy policy specificity (exact retention window) → store listing at 6 density buckets + feature graphic + 8 screenshots per locale. Two rejections taught the spec:

  1. Foreground service disclosure too vague — added explicit “AuraLock keeps your Aura active when you switch apps” plus a 30s capture.
  2. Accessibility wording implied data collection — rewrote to “detects foreground app package to enforce your chosen allow-list; no content read.”

Each rejection became a checklist item in docs/play-store.md so the next submit was deterministic.

Meaning: If you treat Store as QA, you ship once. If you treat it as an adversarial reviewer, you ship forever. I now version store assets like code: store/screenshots/{density}/, store/privacy-policy.md, store/disclosure.md, and run a pre-submit lane: npm run store:check that lints copy against the policy regex.

What I deliberately cut

Point: Knowing what not to build is the craft.

Gamification was rebuilt three times.

  • V1 rewarded time spent in Aura → incentivized leaving the phone idle.
  • V2 added leaderboards → felt competitive, not calm, and noisy for solo users.
  • V3 rewards Aura completion, streaks of chosen focus, and gentle recovery — miss a day, you don’t lose everything; complete 3 Auras, you level. This lifted 7-day return 18% without increasing session churn.

Evidence: Other cuts with rationale logged in ADR:

  • Auto-block lists → too parental, violated philosophy.
  • Complex rule builder (IF time AND location AND app → block) → power user trap, hurt onboarding completion (-31% in test).
  • Social sharing of Focus Score → premature distribution; core loop not yet retained.
  • Always-on enforcement → conflicted with intentionality; Aura should be chosen, not ambient.

Meaning: Every feature must pass: “Does this serve intentional focus?” If the answer was “maybe” or “later,” it was cut. That veto is the product. It keeps the app understandable in 5 seconds and maintainable by one person.

The solo loop that shipped

Point: Own the full lifecycle, iterate weekly, let philosophy decide scope.

My weekly cadence: Monday scope veto (one Aura flow tweak), Tue–Thu build, Fri test on 3 devices (Pixel, Samsung, Xiaomi — OEM variance is real), Sat store/screenshots/docs, Sun journal + next veto. The loop is calm on purpose; rush creates clever code and vague copy, both of which users feel.

Evidence: AuraLock is live on Play Store, Chrome Web Store, and Firefox Add-ons with the same Aura definition synced across phone and browser. The backend (Next.js + Supabase) manages Aura definitions, schedules, and sync; native handles enforcement; extensions handle domain blocking. One repo, three surfaces, one philosophy.

Meaning: When you own frontend, native, backend, and store surfaces, tradeoffs get honest. You can’t hide a weak enforcement story behind nice UI, and you can’t hide a confusing UX behind strong enforcement. Coherence becomes your moat.

Takeaway — your PEM checklist:

  • Point: “Choose what stays” beats “choose what to block.”
  • Evidence: 38% more completions, 22% better early retention, 18% lift after V3 gamification, 2 policy rejections turned into checklist.
  • Meaning: Find the positive inversion, split UI from enforcement, treat Store as architecture, and cut until the product is obvious. Then ship every week. If you want to focus, protect the focus — don’t punish the distraction.

Next: I go deep on zero-knowledge sharing with Web Crypto in CipherOnce — why the key lives in the fragment and the server never sees your secret.

Key takeaway

Building AuraLock — choosing what matters vs blocking, React Native + native Kotlin modules, Play Store policy. Founder notes from zero to launch. — built with intent, shipped end-to-end.

Durga Gairhe
Durga Gairhe
Software Engineer & Product Builder · Kathmandu, Nepal

I work across software engineering, product development, design, mobile, testing, and strategy — from first sketch to release.

Enjoyed this?

Get the next build log — no spam, one email per post.

Subscribe →
← More writing By Durga Gairhe