The problem
Recovery apps mostly sell measurement, where the counter is the product and a relapse means a reset and a dose of shame — or they sell paywalled education. None of them sell surviving the next fifteen minutes, which is the moment that actually decides the outcome.
What it does
One tap opens Craving Mode: a full-screen breathing orb whose haptic cadence lets you shut your eyes, an in-flow check-in on whether the wave is passing, then a drawer offering a delay timer, a grounding exercise, or a one-tap message to a support contact wired up during onboarding. A slip routes into a debrief rather than resetting a streak.
How it's built
No login wall, but real billing and cross-device sync
Anonymous users are keyed by device, which forces the merge into a real account to have a defined answer for every unique constraint in the schema rather than a generic reparent. It runs as one transaction, blind-reparenting foreign-key-only models and applying an explicit account-wins policy per constraint.
Identity headers stripped on every inbound request
The edge proxy deletes the identity and device headers from every request — including server actions, which arrive on page routes rather than API routes — before re-injecting an identity decoded from a signed token. Tokens are device-bound with a long expiry, so the session is also re-read and revoked tokens rejected.
A breath engine that is the same on both platforms, and testable
One inhale-hold-exhale cycle drives web and mobile. On mobile the animation sequence is re-armed each cycle rather than looped infinitely, so phase callbacks and escalating haptics stay in lockstep; Reduce Motion degrades to opacity with the haptics still carrying the cadence. The timings are hoisted into a shared module so CI can assert them.
Duty of care expressed as fail-closed defaults
The research step for any health content throws rather than letting a model fall back on memory, and refuses to proceed below two usable primary sources. The image relevance judge returns false when the evaluation call itself fails, on the reasoning that a missing image is always the safe outcome on a recovery post.
The hard part
Identity, mostly: supporting real payments and cross-device sync while never forcing a login means the anonymous-to-account merge has to be exhaustive rather than best-effort, with a stated policy for every uniqueness rule in the schema. The rest is deciding, repeatedly, which direction each failure should fall in — and for this product almost every one falls closed, because on a recovery app the cost of showing something wrong is not symmetrical with the cost of showing nothing.
Stack
- Next.js
- React Native
- Expo
- Anthropic
- OpenAI
- Skia
- RevenueCat
Built, shipped and maintained by me. Try Salt Mammal, or see the rest of the work.
