<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Hyo on Medium]]></title>
        <description><![CDATA[Stories by Hyo on Medium]]></description>
        <link>https://medium.com/@hyodotdev?source=rss-6de083ee3256------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*YcQFlr4MqLn1AUP9uO0KNg.jpeg</url>
            <title>Stories by Hyo on Medium</title>
            <link>https://medium.com/@hyodotdev?source=rss-6de083ee3256------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 23 Jun 2026 18:31:25 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@hyodotdev/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Detecting Failed Subscription Payments on the Client — No Backend Required]]></title>
            <link>https://medium.com/dooboolab/detecting-failed-subscription-payments-on-the-client-no-backend-required-c11aaba08c30?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/c11aaba08c30</guid>
            <category><![CDATA[openiap]]></category>
            <category><![CDATA[mobile-development]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[in-app-purchase]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Thu, 16 Apr 2026 13:17:03 GMT</pubDate>
            <atom:updated>2026-04-16T13:24:16.005Z</atom:updated>
            <content:encoded><![CDATA[<p>How one cross-platform event turns involuntary churn into a recoverable moment, across iOS, Android, React Native, Flutter, Godot, and Kotlin Multiplatform.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r-J0mT49YNdOF0m_gE2CiA.png" /></figure><p>Your user’s credit card expires.</p><p>Their subscription renewal fails.</p><p>Your app does not know.</p><p>A few days later, the platform cancels the subscription. The user opens your app, finds that they have lost access, and leaves.</p><p>Not because they wanted to cancel.</p><p>Not because your product failed.</p><p>But because nobody told them to update their payment method.</p><p>This is <strong>involuntary churn</strong> — and for subscription-based apps, it is one of the most frustrating forms of churn because it is often recoverable.</p><p>The traditional solution is server-to-server notifications.</p><p>Apple sends your backend a webhook. Google sends your backend a real-time developer notification. Your backend updates the subscription state and, if you have the infrastructure in place, pushes a message back to the user.</p><p>That works.</p><p>But it also assumes you have backend infrastructure, webhook handling, notification delivery, retry logic, monitoring, and entitlement synchronization already set up.</p><p>Many indie developers and small teams do not.</p><p>So we shipped a different approach.</p><h3>Introducing subscriptionBillingIssue</h3><p>subscriptionBillingIssue is a client-side event that fires when an active subscription enters a billing-problem state.</p><p>No backend.</p><p>No webhook endpoint.</p><p>No server polling.</p><p>The app detects the billing issue directly from the platform billing SDK and can react immediately while the user is still inside the app.</p><p>The goal is simple:</p><blockquote><em>Detect a failed subscription payment early enough that the user can fix it before they lose access.</em></blockquote><h3>How It Works</h3><p>The implementation differs by platform, but the developer experience stays consistent across the OpenIAP ecosystem.</p><h3>iOS 18+</h3><p>On iOS 18 and later, Apple introduced the StoreKit 2 Message API.</p><p>This allows the system to deliver messages to the app while it is running. One of those messages is .billingIssue, which is triggered when the user has an auto-renewable subscription whose renewal failed.</p><p>OpenIAP listens for this message and checks the current subscription entitlements.</p><p>If a subscription is in either:</p><ul><li>inBillingRetryPeriod</li><li>inGracePeriod</li></ul><p>OpenIAP emits one subscriptionBillingIssue event for each affected subscription.</p><p>This gives the app a chance to show a user-facing prompt before the subscription fully expires.</p><h3>Android — Play Billing 8.1+</h3><p>On Android, Google added Purchase.isSuspended in Play Billing 8.1.</p><p>This flag indicates that a subscription has been suspended because of a payment failure.</p><p>OpenIAP checks this flag during:</p><ul><li>getAvailablePurchases</li><li>onPurchasesUpdated</li></ul><p>To avoid repeatedly showing the same warning, events are deduplicated by purchase token per app session.</p><p>So if the same suspended subscription appears multiple times during one session, the app only receives one billing issue event.</p><h3>Meta Horizon / Quest</h3><p>Meta Horizon currently uses the Horizon Billing Compatibility SDK, which targets Play Billing 7.0.</p><p>Because Play Billing 7.0 does not expose Purchase.isSuspended, billing issue detection is not available on Horizon yet.</p><p>The listener still exists for API parity, but calling it logs a warning and does nothing.</p><p>This is intentional and documented behavior.</p><h3>What the Code Looks Like</h3><h3>React Native / Expo</h3><pre>import {<br>  subscriptionBillingIssueListener,<br>  deepLinkToSubscriptions,<br>} from &#39;react-native-iap&#39;; // or &#39;expo-iap&#39;<br><br>const subscription = subscriptionBillingIssueListener((purchase) =&gt; {<br>  Alert.alert(<br>    &#39;Payment Issue&#39;,<br>    &#39;Your subscription needs attention. Update your payment method to keep access.&#39;,<br>    [<br>      {<br>        text: &#39;Fix Now&#39;,<br>        onPress: () =&gt;<br>          deepLinkToSubscriptions({<br>            skuAndroid: purchase.productId,<br>            packageNameAndroid: &#39;com.example.app&#39;,<br>          }),<br>      },<br>    ],<br>  );<br>});<br>// Cleanup<br>subscription.remove();</pre><p>You can also handle the same event through the useIAP hook:</p><pre>const { requestPurchase } = useIAP({<br>  onSubscriptionBillingIssue: (purchase) =&gt; {<br>    // Show your &quot;fix payment&quot; UI<br>  },<br>});</pre><h3>Flutter</h3><pre>final iap = FlutterInappPurchase.instance;<br><br>iap.subscriptionBillingIssueListener.listen((purchase) {<br>  showDialog(<br>    context: context,<br>    builder: (_) =&gt; AlertDialog(<br>      title: Text(&#39;Payment Issue&#39;),<br>      content: Text(&#39;Update your payment method for ${purchase.productId}&#39;),<br>      actions: [<br>        TextButton(<br>          onPressed: () =&gt; iap.deepLinkToSubscriptions(<br>            options: DeepLinkOptions(<br>              skuAndroid: purchase.productId,<br>              packageNameAndroid: &#39;com.example.app&#39;,<br>            ),<br>          ),<br>          child: Text(&#39;Fix Now&#39;),<br>        ),<br>      ],<br>    ),<br>  );<br>});</pre><h3>Kotlin Multiplatform</h3><pre>kmpIapInstance.subscriptionBillingIssueListener<br>    .onEach { purchase -&gt;<br>        kmpIapInstance.deepLinkToSubscriptions(<br>            DeepLinkOptions(<br>                skuAndroid = purchase.productId,<br>                packageNameAndroid = &quot;com.example.app&quot;,<br>            ),<br>        )<br>    }<br>    .launchIn(scope)</pre><h3>Godot</h3><pre>godot_iap.subscription_billing_issue.connect(<br>    func(purchase: Dictionary) -&gt; void:<br>        show_payment_issue_dialog(purchase[&quot;productId&quot;])<br>)</pre><h3>The Recommended UX Pattern</h3><p>The recommended user experience is intentionally simple.</p><p>When the event fires, do not immediately block the user. Instead, show a clear, non-intrusive message that explains the situation and gives them a direct way to fix it.</p><p>A good flow looks like this:</p><ol><li>The billing issue event fires.</li><li>The app shows a non-blocking banner.</li><li>The banner says something like:<br> <strong>“Your subscription needs attention.”</strong></li><li>The user taps <strong>“Fix payment method.”</strong></li><li>The app calls deepLinkToSubscriptions().</li><li>The platform opens the correct subscription management screen.</li><li>The user updates their payment method.</li><li>The subscription can recover on the next billing attempt.</li></ol><p>Both OpenIAP example apps implement this exact pattern.</p><p>The subscription screen shows an orange warning banner with a dismiss button and a <strong>Fix payment method</strong> action.</p><p>Simple.</p><p>Visible.</p><p>Recoverable.</p><h3>Why Client-Side Detection?</h3><p>Server-to-server notifications are still the best solution for critical backend business logic.</p><p>You should still use server-side verification for entitlement enforcement, auditability, fraud protection, and subscription state reconciliation.</p><p>But for the user-facing moment — the moment where you simply want to say:</p><blockquote><em>“Your payment method failed. Please update it before you lose access.”</em></blockquote><p>Client-side detection has real advantages.</p><h3>1. Zero Infrastructure</h3><p>There is no webhook endpoint to host.</p><p>No backend route to secure.</p><p>No notification delivery system to maintain.</p><p>No server polling.</p><p>No queue.</p><p>No retry system.</p><p>For indie developers and small teams, this matters.</p><p>You can add a billing recovery flow directly inside the app without building an entire subscription backend first.</p><h3>2. The User Sees It While They Are Active</h3><p>Push notifications are easy to ignore.</p><p>Emails are easy to miss.</p><p>But if the user opens your app while the subscription is in a billing retry or suspended state, the app can show the message immediately.</p><p>That creates a much better recovery moment.</p><p>The user is already engaged.</p><p>They are already using the product.</p><p>They are much more likely to fix the issue right there.</p><h3>3. It Works Without Your Server</h3><p>The detection comes from the platform billing SDK.</p><p>That means the app can surface the warning without first making a network request to your backend.</p><p>This is especially useful for apps that are offline-first, lightweight, or still early in their infrastructure journey.</p><h3>4. It Complements Server-Side Verification</h3><p>This feature does not replace backend validation.</p><p>It is not intended to be the only source of truth for subscription access.</p><p>Instead, think of subscriptionBillingIssue as an early warning system.</p><p>Your backend may still be responsible for final entitlement decisions.</p><p>But the client can now help the user fix a payment problem before it turns into a cancellation.</p><h3>Platform Support</h3><p>PlatformSignalDeliveryDeduplicationiOS 18+ / Mac Catalyst 18+Message.Reason.billingIssuePush while app is activePer message, scans current entitlementsAndroid / Play Billing 8.1+Purchase.isSuspendedgetAvailablePurchases and onPurchasesUpdatedBy purchaseToken, once per sessionAndroid / Meta HorizonNot availableNever firesN/AiOS 17 and earlierNot availableSilent no-opN/AmacOS / tvOS / watchOS / visionOSMessage not availableSilent no-opN/A</p><p>On Android, listener collections use CopyOnWriteArraySet, and token deduplication uses ConcurrentHashMap.newKeySet().</p><p>This makes listener add/remove operations safe against iteration from background dispatchers.</p><h3>Package Versions</h3><p>This feature ships across the OpenIAP ecosystem:</p><p>PackageVersionopeniap-apple2.1.0openiap-google2.1.0react-native-iap15.2.0expo-iap4.1.0flutter_inapp_purchase9.2.0kmp-iap2.2.0godot-iap2.2.0</p><h3>The Monorepo Advantage</h3><p>This feature landed across the entire OpenIAP ecosystem in a single coordinated update:</p><ul><li>Schema update</li><li>iOS implementation</li><li>Android implementation</li><li>Horizon no-op support</li><li>React Native bridge</li><li>Expo support</li><li>Flutter bridge</li><li>Kotlin Multiplatform bridge</li><li>Godot bridge</li><li>useIAP hook support</li><li>Example app UI</li><li>Documentation</li><li>Robolectric test</li><li>Sandbox E2E guide</li></ul><p>That is exactly why we moved the OpenIAP framework libraries into one monorepo.</p><p>One spec.</p><p>One source of truth.</p><p>One feature, shipped across every supported framework.</p><h3>Final Thoughts</h3><p>Failed subscription payments are not always lost customers.</p><p>Sometimes they are just users who need a timely reminder.</p><p>With subscriptionBillingIssue, apps can now detect billing problems directly on the client and guide users to fix their payment method before access is lost.</p><p>It is not a replacement for server-side subscription infrastructure.</p><p>But it gives every developer — including indie developers and small teams — a practical recovery path that works directly inside the app.</p><p>And for subscription products, that small moment can make a big difference.</p><p>OpenIAP is the standardized protocol for in-app purchases.</p><p>Website: <a href="https://openiap.dev">https://openiap.dev</a><br> Release notes: <a href="https://www.openiap.dev/docs/updates/releases#releases-2026-04-16">https://www.openiap.dev/docs/updates/releases#releases-2026-04-16</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c11aaba08c30" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dooboolab/detecting-failed-subscription-payments-on-the-client-no-backend-required-c11aaba08c30">Detecting Failed Subscription Payments on the Client — No Backend Required</a> was originally published in <a href="https://medium.com/dooboolab">Hyo Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[We Merged 5 IAP Libraries Into One Monorepo — Here’s Why and How]]></title>
            <link>https://medium.com/dooboolab/we-merged-5-iap-libraries-into-one-monorepo-heres-why-and-how-a8fef8c700c7?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/a8fef8c700c7</guid>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[openiap]]></category>
            <category><![CDATA[monorepo]]></category>
            <category><![CDATA[in-app-purchase]]></category>
            <category><![CDATA[mobile-development]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Thu, 16 Apr 2026 12:50:02 GMT</pubDate>
            <atom:updated>2026-04-16T12:55:54.686Z</atom:updated>
            <content:encoded><![CDATA[<h4>How OpenIAP consolidated react-native-iap, expo-iap, flutter_inapp_purchase, kmp-iap, and godot-iap into a single repository without breaking a single downstream user.</h4><p>In-app purchase libraries are uniquely painful to maintain across multiple repositories. A single type change in the core schema ripples into Swift, Kotlin, TypeScript, Dart, and GDScript — five PRs, five CI pipelines, five version bumps, and five chances for something to drift.</p><p>We maintained exactly this setup for years. Five separate repos, each with its own release cycle, its own copy of generated types, and its own set of contributors who had no visibility into what the other platforms were doing.</p><p>In April 2026, we merged everything into one monorepo. This is what we learned.</p><h3>The Problem</h3><p>OpenIAP defines a cross-platform specification for in-app purchases. The core packages — a GraphQL schema, an iOS library (StoreKit 2), and an Android library (Google Play Billing + Meta Horizon) — live in the `openiap` repo. Five framework SDKs consume them:</p><p>- <a href="https://github.com/hyodotdev/openiap/tree/main/libraries/react-native-iap"><strong>react-native-iap</strong></a> — React Native via Nitro Modules<br>- <a href="https://github.com/hyodotdev/openiap/tree/main/libraries/expo-iap"><strong>expo-iap</strong></a> — Expo Modules<br>- <a href="https://github.com/hyodotdev/openiap/tree/main/libraries/flutter_inapp_purchase"><strong>flutter_inapp_purchase</strong></a>— Flutter method channels<br>- <a href="https://github.com/hyodotdev/openiap/tree/main/libraries/kmp-iap"><strong>kmp-iap</strong></a> — Kotlin Multiplatform via cinterop<br>- <a href="https://github.com/hyodotdev/openiap/tree/main/libraries/godot-iap"><strong>godot-iap</strong> </a>— Godot 4.x GDExtension + AAR plugin</p><p>Each SDK had its own GitHub repo, its own `openiap-versions.json` pinning core dependency versions, and its own CI that downloaded pre-built artifacts from GitHub Releases.</p><p>A typical schema change looked like this:</p><p>1. PR to <a href="https://github.com/hyodotdev/openiap">openiap</a> — update GraphQL, regenerate types, update Swift + Kotlin<br>2. Tag + release `openiap`<br>3. Open 5 PRs across 5 repos, each updating `openiap-versions.json` and syncing generated types<br>4. Wait for 5 CIs to pass<br>5. Merge and publish 5 packages (npm x 2, pub.dev, Maven Central, GitHub Release)</p><p>Steps 3–5 were where things broke. A field name would get corrected in one PR but not another. A Kotlin type would drift from the Dart equivalent. A release would ship with a stale dependency version because someone forgot to bump `openiap-versions.json`.</p><h3>The Decision</h3><p>We considered three options:</p><p><strong>Option A: Git submodules.</strong> Each SDK repo adds `openiap` as a submodule. Types are always at the pinned commit. But submodules are hostile to contributors — `git clone — recurse-submodules` is a speed bump that filters out casual contributions, and CI caching with submodules is fragile.</p><p><strong>Option B: Published package dependencies.</strong> Each SDK depends on published <a href="https://central.sonatype.com/artifact/io.github.hyochan.openiap/openiap-google">openiap-google</a> / <a href="https://cocoapods.org/pods/openiap">openiap-apple</a> packages from Maven Central, CocoaPods, etc. This is what we already had, and it was the source of the drift problem. You can’t atomically ship a core change + all SDK updates.</p><p><strong>Option C: Monorepo.</strong> Move all five SDK directories into `openiap/libraries/`. One PR touches core + all SDKs. One CI runs all checks. Version drift becomes a compile error, not a production bug.</p><p>We chose <strong>C</strong>.</p><h3>The Migration</h3><p>The actual move was mechanical:</p><pre><br>openiap/<br>├── packages/ # Core (unchanged)<br>│ ├── apple/<br>│ ├── google/<br>│ ├── gql/<br>│ └── docs/<br>├── libraries/ # NEW — moved from separate repos<br>│ ├── react-native-iap/<br>│ ├── expo-iap/<br>│ ├── flutter_inapp_purchase/<br>│ ├── godot-iap/<br>│ └── kmp-iap/<br>└── libraries-versions.jsonc # “local” vs published toggle</pre><p>The key design decision was `libraries-versions.jsonc`:</p><pre>{<br> “expo-iap”: “local”,<br> “react-native-iap”: “local”,<br> “flutter_inapp_purchase”: “local”,<br> “kmp-iap”: “local”<br>}</pre><p>When set to local, each library resolves its openiap dependency from the monorepo source. When set to a version string like 4.0.0-rc.1, it pulls the published package. This lets contributors develop against HEAD and release managers validate against published artifacts — same repo, same CI.</p><h3>What Changed for Contributors</h3><p><strong>Before:</strong> Clone the SDK repo, clone openiap separately, set $IAP_REPOS_HOME and $OPENIAP_HOME environment variables, run a sync script.</p><p><strong>After:</strong> Clone one repo. Everything compiles.</p><p><strong>Before:</strong> A type change requires coordinating across repos with different maintainers and review cadences.</p><p><strong>After:</strong> One PR. The type regeneration script (`bun run generate`) updates Types.swift, Types.kt, types.ts, types.dart, and types.gd in a single pass, and the sync script copies them to all five libraries. If any library doesn’t compile, the PR doesn’t merge.</p><h3>What Changed for CI</h3><p>Each library kept its own CI workflow (separate YAML files), but they all trigger on the same PR. A schema change in `packages/gql/` triggers:</p><p>- ci.yml — Apple `swift build &amp;&amp; swift test`, Google `compilePlayDebugKotlin &amp;&amp; compileHorizonDebugKotlin`, Docs typecheck<br>- ci-react-native-iap.yml — `yarn typecheck &amp;&amp; yarn test`<br>- ci-expo-iap.yml — `bun run tsc — noEmit &amp;&amp; bun run test`<br>- ci-flutter_inapp_purchase.yml — `flutter analyze &amp;&amp; flutter test`<br>- ci-kmp-iap.yml — `gradlew :library:compileDebugKotlinAndroid`<br>- ci-godot-iap.yml — GDScript syntax check</p><p>If any fails, the PR is blocked. Version drift is now a CI failure, not a bug report.</p><h3>What We’d Do Differently</h3><p><strong>Git history.</strong> We moved directories rather than using `git filter-repo` to transplant history. This means `git log libraries/react-native-iap/src/index.ts` starts at the consolidation commit, not at the file’s original creation. For a library with 10+ years of history like react-native-iap, that’s a real loss. If we did it again, we’d invest the time in proper history grafting.</p><p><strong>Gradual migration.</strong> We moved all five at once. In retrospect, moving one library first (expo-iap, the smallest) as a proof-of-concept would have let us iron out CI and dependency resolution issues before committing to the full migration.</p><h3>Results</h3><p>In the weeks since consolidation:</p><p>- Zero type drift incidents (previously ~1 per release cycle)<br>- PR-to-all-platforms merge time dropped from 2–3 days to same-day<br>- New feature PRs (like `subscriptionBillingIssue`) ship core + all 5 SDK bridges + docs + example apps in a single PR<br>- Contributor onboarding simplified to one clone + one build command per platform</p><p>The monorepo isn’t magic. It’s a trade-off: you accept a larger repo, more complex CI triggers, and occasionally longer CI times in exchange for atomic cross-platform changes and zero coordination overhead. For a project whose entire value proposition is cross-platform consistency, that trade-off is clear.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AMnvXgzxGt8euihJ7-WY0g.png" /><figcaption><a href="https://openiap.dev">openiap.dev</a></figcaption></figure><ul><li>OpenIAP is the standardized protocol for in-app purchases across platforms. Learn more at <a href="https://openiap.dev"><strong>openiap.dev</strong></a>.</li><li>The consolidation discussion: <a href="https://github.com/hyodotdev/openiap/discussions/86)*">https://github.com/hyodotdev/openiap/discussions/86</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a8fef8c700c7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/dooboolab/we-merged-5-iap-libraries-into-one-monorepo-heres-why-and-how-a8fef8c700c7">We Merged 5 IAP Libraries Into One Monorepo — Here’s Why and How</a> was originally published in <a href="https://medium.com/dooboolab">Hyo Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LangChain for Mobile, Entirely On-Device — Meet Locanara]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/dooboolab/langchain-for-mobile-entirely-on-device-meet-locanara-33112ade3b0e?source=rss-6de083ee3256------2"><img src="https://cdn-images-1.medium.com/max/1648/1*AFz_w6O6n954uIb4qoPXeg.png" width="1648"></a></p><p class="medium-feed-snippet">Building LangChain for Mobile: How We Designed an On-Device AI Framework for iOS and Android</p><p class="medium-feed-link"><a href="https://medium.com/dooboolab/langchain-for-mobile-entirely-on-device-meet-locanara-33112ade3b0e?source=rss-6de083ee3256------2">Continue reading on Hyo Dev »</a></p></div>]]></description>
            <link>https://medium.com/dooboolab/langchain-for-mobile-entirely-on-device-meet-locanara-33112ade3b0e?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/33112ade3b0e</guid>
            <category><![CDATA[apple-intelligence]]></category>
            <category><![CDATA[ai-on-device]]></category>
            <category><![CDATA[on-device-llm]]></category>
            <category><![CDATA[gemini-nano]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Thu, 19 Feb 2026 23:08:27 GMT</pubDate>
            <atom:updated>2026-02-19T23:39:40.691Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Moltbot: A Bold Proposal for Where AI Should Live — and Why It’s Not Ready Yet]]></title>
            <link>https://hyodotdev.medium.com/moltbot-a-bold-proposal-for-where-ai-should-live-and-why-its-not-ready-yet-9e9d60a86e6c?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/9e9d60a86e6c</guid>
            <category><![CDATA[moltbot]]></category>
            <category><![CDATA[clawdbot]]></category>
            <category><![CDATA[claude]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[agents]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Fri, 30 Jan 2026 14:27:38 GMT</pubDate>
            <atom:updated>2026-01-30T14:27:38.848Z</atom:updated>
            <content:encoded><![CDATA[<p>Less than a week has passed, and once again the tech world is buzzing with news of a new AI agent.</p><p>In this article, I want to take a closer look at one of the most talked-about projects recently: <strong>Clawdbot</strong> — now reborn as <strong>Moltbot</strong> after a trademark complaint from Anthropic forced a name change.<br>As the name suggests, it has shed its old skin. But has it truly become stronger?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*azqBr_EULo9kd_CDUZaE9w.png" /></figure><p>Right now, communities like X and Reddit are on fire over Moltbot. This AI runs <strong>24/7</strong>, all year round. One viral post claimed that a user entrusted the bot with <strong>$1 million</strong> in capital to manage. The bot reportedly analyzed over <strong>3,000 reports in real time</strong>, charted every technical indicator, and executed stock trades nonstop.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FBdPhbo8H_Yn0fTkjKO4_w.png" /></figure><p>The result?<br> All the money was lost — but, as the post jokingly put it, <em>“the process was beautiful.”</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CBgifUqKaUKe2K7h2GIvYw.png" /></figure><p>At this very moment, people are posting screenshots of Moltbot automatically sending affectionate messages to their girlfriends, writing complex code, or trading stocks on their behalf. Yet the developer has been clear: this started as a <strong>personal hobby project</strong>, meant to inspire people — not a magic tool to make them rich.</p><p>Despite that, many have misunderstood it as a shortcut to becoming a millionaire. Some even claim there’s a <strong>Mac mini shortage</strong> because people are buying them in bulk just to run Moltbot 24/7.</p><p>So what exactly is this thing? And why are people reacting this way?</p><p>Let’s break it down — and then look at it honestly from a developer’s perspective.</p><h3>Installation &amp; Setup (Quick Notes)</h3><p>Since many readers have already heard about Moltbot, I’ll keep this short.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1UsoG182gnbyMWfeL1V4Cg.png" /></figure><p>One small tip: I initially tried installing it via <strong>npm</strong>, but ran into a missing CLI error. Reinstalling via <strong>curl</strong> worked without issues.</p><p>During setup, you’re greeted with a rather alarming warning: the bot can control your file system. This is not something to click through lightly. Read it carefully before proceeding.</p><p>You then enter onboarding, where you choose a model. Instead of entering an API key (and risking surprise billing later), I opted for <strong>Google OAuth</strong>, since I already subscribe to Gemini. I selected <strong>Gemini 2.5 Pro</strong> for better performance.</p><p>You’re asked about channels, skills, hooks, gateways, and execution environments. Many of these involve fairly powerful permissions. I skipped most of them for now and chose to run the bot in the <strong>terminal UI</strong>, which the project itself recommends.</p><p>Once launched, a browser window opens alongside the terminal. The bot asks for your name — and from there, you can literally watch it <strong>store and remember information about you in real time</strong>.</p><p>That’s enough for the walkthrough. Now let’s talk about what actually matters.</p><h3>1. The Real Innovation: Not Technology, but Positioning</h3><p>Moltbot’s GitHub stars are skyrocketing, and there’s a reason.<br> But if we strip things down to their technical essence, Moltbot is <strong>not fundamentally new technology</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NcqF9uWuNvk_aU7yDeG9xA.png" /></figure><p>Calling LLMs, storing state, connecting external tools — all of this can already be done with Claude Skills, ChatGPT Actions, LangChain, or Zapier. Even the so-called “gateway” is, in practice, just a long-running bot with webhooks and a scheduler.</p><p>So why are people so impressed?</p><p>Because the innovation isn’t <em>how</em> it works — it’s <strong>where it lives</strong>.</p><p>Traditional AI tools like ChatGPT require <em>you</em> to open an app and initiate work. You are always in control. Moltbot, on the other hand, <strong>lives inside your messenger</strong> — WhatsApp, Telegram, the same place you already spend your day. It’s always on. Always present.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*k9hXm4Sv_wD5D78Yjf1TrQ.png" /></figure><p>If ChatGPT suddenly started messaging you first, it would feel like spam.<br> But Moltbot feels like a personal assistant.</p><p>The technology is similar. The <strong>UX and power dynamics are not</strong>.</p><p>Who controls the conversation?<br> Who is always running?</p><p>This shift — the idea of an AI agent that <em>lives where you live</em> — is the first reason people are excited.</p><h3>The One Genuine Technical Differentiator: Disk-Based Memory</h3><p>There <em>is</em>, however, one truly meaningful technical distinction.</p><p>Moltbot uses the <strong>local hard disk as long-term memory</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TFXPMIbtcodKXHQaiLWD6g.png" /></figure><p>Most cloud AIs eventually forget earlier context as conversations grow longer. Moltbot stores conversations and state as files and reloads them later. As long as your disk has space, memory is effectively unlimited.</p><p>This disk-backed, persistent memory model is genuinely hard for traditional cloud services to replicate — and it is Moltbot’s strongest technical advantage.</p><h3>2. Model Limitations and the Refactoring Hell Problem</h3><p>Despite its innovative positioning, there are serious concerns from a professional developer’s perspective.</p><h3>Memory Is Not Truth</h3><p>Many people assume that because the bot “remembers,” it will automatically improve over time. That’s a dangerous misunderstanding.</p><p>Without a <strong>Single Source of Truth</strong>, persistent memory simply preserves <em>mistakes</em> along with correct information.<br> If your system doesn’t have a top-level constitution — immutable rules that override everything else — the AI will faithfully repeat bad assumptions forever.</p><p>Right now, Moltbot lacks this.</p><h3>The Inability to Stop</h3><p>This is the most critical issue.</p><p>Agents like Moltbot suffer from the same fundamental problem that plagued <strong>Auto-GPT</strong>: once they start down a wrong path, they <strong>don’t know when to stop</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RhSdgZ9ZDlDAb8-SgbMwWA.png" /></figure><p>Humans feel uncertainty. When something feels off, we pause, reconsider, or ask for help. Current LLM-based agents don’t. If the initial assumption is wrong, they keep building on it, refactoring again and again — until the entire project collapses into a mess.</p><p>This isn’t a Moltbot-specific design flaw. It’s a limitation of today’s models.<br> They lack robust uncertainty detection and the ability to say, <em>“I’m not sure — I need human input.”</em></p><p>The result is what many developers have experienced firsthand: <strong>refactoring hell</strong>.</p><p>In short, Moltbot is an excellent proposal for <em>where</em> AI should live — but the underlying model maturity isn’t there yet for it to be a primary development tool. For now, it should be treated as a powerful <strong>experimental platform</strong>, not production infrastructure.</p><h3>3. About the Mac Mini Frenzy</h3><p>Finally, let’s talk hardware.</p><p>The model limitations I mentioned will improve over time. And when they do, we’ll move away from cloud APIs toward <strong>local, always-on intelligence</strong>. That’s the future Moltbot hints at.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7qr9WbuoZPacMmSgMJla5g.png" /></figure><p>For that reason, I don’t recommend buying a base-model Mac mini <em>just</em> to run Moltbot today.</p><p>But if your goal is to someday run <strong>Gemini-2.5-Pro-level intelligence locally</strong>, without worrying about usage fees, that’s a different story.</p><p>In that future, the most important spec won’t be CPU speed.</p><p>It will be <strong>memory</strong>.</p><p>I strongly believe that machines with <strong>at least 64GB of RAM</strong> will become the foundation of personal AI infrastructure. Prepare for that future, and you’ll be ready when the models finally catch up.</p><p>Thanks for reading.<br> If you have thoughts or questions, I’d love to continue the discussion in the comments.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9e9d60a86e6c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why did Anthropic allow “other people’s models” in Claude Code?]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/dooboolab/why-did-anthropic-allow-other-peoples-models-in-claude-code-b0a050b15fec?source=rss-6de083ee3256------2"><img src="https://cdn-images-1.medium.com/max/1692/1*3iin8bATR2sRjOAEpQNbbA.png" width="1692"></a></p><p class="medium-feed-snippet">How &#x201C;technical permission&#x201D; became a strategy to own the interface, not just the model.</p><p class="medium-feed-link"><a href="https://medium.com/dooboolab/why-did-anthropic-allow-other-peoples-models-in-claude-code-b0a050b15fec?source=rss-6de083ee3256------2">Continue reading on Hyo Dev »</a></p></div>]]></description>
            <link>https://medium.com/dooboolab/why-did-anthropic-allow-other-peoples-models-in-claude-code-b0a050b15fec?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/b0a050b15fec</guid>
            <category><![CDATA[anthropic-claude]]></category>
            <category><![CDATA[open-code]]></category>
            <category><![CDATA[claude]]></category>
            <category><![CDATA[ollama]]></category>
            <category><![CDATA[claude-code]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Wed, 21 Jan 2026 16:40:51 GMT</pubDate>
            <atom:updated>2026-01-23T12:51:31.599Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[AI Is Dumb, So I Code Everything Myself]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/dooboolab/ai-is-dumb-so-i-code-everything-myself-6e6e3004351e?source=rss-6de083ee3256------2"><img src="https://cdn-images-1.medium.com/max/1146/1*5sIAkQ8UrG0oIRDOrU6gjw.png" width="1146"></a></p><p class="medium-feed-snippet">&#x201C;AI Is Dumb, So I Code Everything Myself.&#x201D;</p><p class="medium-feed-link"><a href="https://medium.com/dooboolab/ai-is-dumb-so-i-code-everything-myself-6e6e3004351e?source=rss-6de083ee3256------2">Continue reading on Hyo Dev »</a></p></div>]]></description>
            <link>https://medium.com/dooboolab/ai-is-dumb-so-i-code-everything-myself-6e6e3004351e?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/6e6e3004351e</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[opensource-ai]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Mon, 19 Jan 2026 01:23:57 GMT</pubDate>
            <atom:updated>2026-01-21T10:11:23.554Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Running AI Locally on Apple Silicon with MLX]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/dooboolab/running-ai-locally-on-apple-silicon-with-mlx-6e6b29ee10cf?source=rss-6de083ee3256------2"><img src="https://cdn-images-1.medium.com/max/1920/1*OJAqA4lcozw0v_8mW2w2Uw.jpeg" width="1920"></a></p><p class="medium-feed-snippet">When people talk about AI today, the first things that usually come to mind are cloud APIs and massive GPU servers. Models live somewhere&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/dooboolab/running-ai-locally-on-apple-silicon-with-mlx-6e6b29ee10cf?source=rss-6de083ee3256------2">Continue reading on Hyo Dev »</a></p></div>]]></description>
            <link>https://medium.com/dooboolab/running-ai-locally-on-apple-silicon-with-mlx-6e6b29ee10cf?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/6e6b29ee10cf</guid>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[on-device-llm]]></category>
            <category><![CDATA[ai-on-device]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[mlx]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Wed, 07 Jan 2026 22:00:00 GMT</pubDate>
            <atom:updated>2026-01-07T22:00:12.910Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Building In-App Purchases for Godot Engine: OpenIAP Unlocked]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/dooboolab/building-in-app-purchases-for-godot-engine-the-openiap-journey-112c98c765fd?source=rss-6de083ee3256------2"><img src="https://cdn-images-1.medium.com/max/1200/1*oV0gwf1BIe58Kuxf0tTA9A.jpeg" width="1200"></a></p><p class="medium-feed-snippet">How we brought a unified IAP experience to Godot, inspired by years of cross-platform development</p><p class="medium-feed-link"><a href="https://medium.com/dooboolab/building-in-app-purchases-for-godot-engine-the-openiap-journey-112c98c765fd?source=rss-6de083ee3256------2">Continue reading on Hyo Dev »</a></p></div>]]></description>
            <link>https://medium.com/dooboolab/building-in-app-purchases-for-godot-engine-the-openiap-journey-112c98c765fd?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/112c98c765fd</guid>
            <category><![CDATA[godot-engine]]></category>
            <category><![CDATA[in-app-purchase]]></category>
            <category><![CDATA[openiap]]></category>
            <category><![CDATA[godot]]></category>
            <category><![CDATA[mobile-development]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Thu, 01 Jan 2026 01:07:58 GMT</pubDate>
            <atom:updated>2026-01-01T14:38:17.279Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[kstyled: Compile-Time styled-components for React Native (with Zero Runtime Cost)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/dooboolab/kstyled-compile-time-styled-components-for-react-native-with-zero-runtime-cost-e1015a2f2440?source=rss-6de083ee3256------2"><img src="https://cdn-images-1.medium.com/max/1536/0*4ybgc7o1ZcyyS6Yh.png" width="1536"></a></p><p class="medium-feed-snippet">When you build a React Native app that actually ships to users, styling becomes more than &#x201C;just CSS&#x201D;.</p><p class="medium-feed-link"><a href="https://medium.com/dooboolab/kstyled-compile-time-styled-components-for-react-native-with-zero-runtime-cost-e1015a2f2440?source=rss-6de083ee3256------2">Continue reading on Hyo Dev »</a></p></div>]]></description>
            <link>https://medium.com/dooboolab/kstyled-compile-time-styled-components-for-react-native-with-zero-runtime-cost-e1015a2f2440?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/e1015a2f2440</guid>
            <category><![CDATA[styled]]></category>
            <category><![CDATA[stylesheets]]></category>
            <category><![CDATA[react-native]]></category>
            <category><![CDATA[css]]></category>
            <category><![CDATA[styled-components]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Thu, 04 Dec 2025 15:50:50 GMT</pubDate>
            <atom:updated>2025-12-04T16:28:01.339Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Ending the In-App Purchases Hell: From Native SDKs to Cross-Platform Libraries for React Native…]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/dooboolab/ending-the-in-app-purchases-hell-from-native-sdks-to-cross-platform-libraries-for-react-native-14c18fa3436c?source=rss-6de083ee3256------2"><img src="https://cdn-images-1.medium.com/max/896/1*JoCArEUj2WDoccY5EN92Ew.png" width="896"></a></p><p class="medium-feed-snippet">TL;DR
I spent 2025 rebuilding every IAP library I&#x2019;ve maintained for years&#x200A;&#x2014;&#x200A;React Native, Expo, Flutter&#x200A;&#x2014;&#x200A;and realized the ecosystem was&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/dooboolab/ending-the-in-app-purchases-hell-from-native-sdks-to-cross-platform-libraries-for-react-native-14c18fa3436c?source=rss-6de083ee3256------2">Continue reading on Hyo Dev »</a></p></div>]]></description>
            <link>https://medium.com/dooboolab/ending-the-in-app-purchases-hell-from-native-sdks-to-cross-platform-libraries-for-react-native-14c18fa3436c?source=rss-6de083ee3256------2</link>
            <guid isPermaLink="false">https://medium.com/p/14c18fa3436c</guid>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[in-app-purchase]]></category>
            <category><![CDATA[cross-platform]]></category>
            <category><![CDATA[mobile-development]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Hyo]]></dc:creator>
            <pubDate>Sun, 30 Nov 2025 12:52:55 GMT</pubDate>
            <atom:updated>2025-11-30T14:46:25.782Z</atom:updated>
        </item>
    </channel>
</rss>