Inspiration 小糠雨, konuka-ame, is the Japanese word for the lightest drizzle, the rain so fine it hangs in the air instead of falling. It never looks like weather, and everything is wet afterwards anyway. That is what a day of thinking feels like to me, and it is where the name comes from.
Thoughts do not arrive finished. Mine show up as fragments, a line, a detail worth keeping, half a list. Every note app I tried wanted me to decide where a fragment belonged before it would let me write it down. Which notebook, which folder, which project. That question is tiny, and it always arrived while the thought was still forming, so often enough I did not write the thing down at all.
A second problem showed up later. The notes I did write were dying anyway, because capturing is easy and returning is work. And a third one: I wanted a page I could fix from my phone without a CMS and without a deploy.
Konuq is the answer to those three.
What it does Konuq is a notes app with no folders. A tag typed inside a note files it. Collections are live queries over tags that fill themselves, and a hub arranges the collections into one space. Any hub publishes as a real website at its own address in one tap. konuq.app is exactly that, a hub written in the app on a phone and published as it is.
Cure is the tab for the months after a note is written. Held in Silence gathers what nobody opened in two months. Left Behind deals out the oldest notes one card at a time, to keep or let go. A quiet push can bring one forgotten note back a week, and a daily nudge can ask for one thought a day into a diary collection. Both are opt-in.
Free covers the whole editor, tags, collections, dictation, the share sheet, Cure and the notifications. Pro adds end-to-end encrypted sync, up to three hubs, and publishing.
Konuq runs on iPhone, iPad, and on Apple silicon Macs through Designed for iPad.
How we built it I built it alone, starting in April 2026, in a little over 300 commits.
The core is Kotlin Multiplatform. The shared module owns the domain models, the feature state, the use cases, the SQLDelight persistence, the snapshot serialization that turns a hub into a publish payload, and the tests. A native SwiftUI shell owns the interface, navigation, purchases, notifications, and for now the cloud networking and the cryptography. Every feature has the same shape from the view down to storage, and every one starts from a failing acceptance test at the feature boundary.
Tags are the clearest example of why the rules live in the core. There are four ways a tag can reach a note: typing it, choosing it in the share sheet, receiving it from another app, or importing it. All four end up as #tag text inside a block, and one shared function reads them. One definition of what a tag is.
The cloud is zero-knowledge. A master key is generated on the device and never leaves it in the clear. A passphrase-derived key wraps it, only the wrapped copy is uploaded, and every record is encrypted with a key derived from the master key. The server holds ciphertext it cannot read. Publishing is the one deliberate exception, since a published hub is public by definition.
Monetization runs on RevenueCat. I migrated from a StoreKit 2 implementation to RevenueCat during Shipaton and rebuilt the lapse policy on top of it. Customer attributes carry a coarse lapse state and the paywall variant, so a Targeting rule can serve a win-back offering to lapsed customers and an Experiment can change the paywall copy without a release.
Notifications run on OneSignal, in a design where note content never reaches OneSignal. The app decides on device which forgotten note to resurface and keeps a small queue in the app group. OneSignal only sees anonymous facts, a bucketed count and a couple of states, and runs the Journeys. The push it sends is content-free. A Notification Service Extension fills in the real note title on the phone before the notification shows, and a tap opens that note.
Challenges we ran into Non-destructive lapse. I had built the subscription lapse to delete extra hubs after a grace period. Watching that happen in a test account felt wrong, so I rewrote it. Hubs are locked now and never deleted, published sites pause, and resubscribing brings everything back untouched. Nobody loses notes over a missed payment.
Notifications for an encrypted app. The obvious integration sends the note title to the push service, and the whole privacy story dies there. The queue file plus the service extension took a few iterations to get right, especially making the extension and the app agree on one file format and one app group.
Tags living in three places. A tag exists as a row on the note, as a row on the collection, and as the #tag token inside the note text. Rename, merge and delete had to touch all three, and leave #tag inside code blocks alone. That work shipped just before the release because it was too fundamental to leave for later.
Using the app to ship the app. I wrote konuq.app inside Konuq, as an ordinary hub, and publishing that hub is how the site exists. Writing seven chapters in my own editor surfaced friction I had stopped noticing, and a number of things got fixed that week purely because they were annoying me.
Accomplishments that we're proud of A hub becomes a website in one tap, and every old address stays alive as a redirect, so links already sent to people never break. End-to-end encryption with a frozen, documented key spec that any future platform can reproduce byte for byte. A push notification that shows a real note title while the push service never learned it. A lapse policy I would be comfortable being on the wrong side of. Packd, my travel app, hands its trip journals to Konuq through a shared container and a deep link, and a journal written there becomes a real Konuq note that can be published with the rest of its hub. What we learned The layer that changes per platform should be the thinnest one. Keeping the rules in Kotlin and the interface in SwiftUI meant a foldable iPhone changed nothing underneath.
Monetization decisions are product decisions. The lapse policy shaped the paywall, the win-back offering and the notification Journeys more than any pricing table did.
Privacy has a cost that has to be paid somewhere. Zero-knowledge sync means a lost passphrase cannot be reset. Content-free pushes mean more code on the device. Both were worth it, and both had to be said out loud in the app rather than hidden.
Building in public works as a forcing function. Every post I promised became a deadline I kept.
What's next for Konuq Cura, a similar-notes detector in the Cure tab, running on Apple Intelligence on device, with merge and link actions. A sticker widget that pins one note to the home screen and toggles its checkboxes in place. Shared notes, with a per-note content key wrapped for each collaborator, so sharing stays end to end encrypted. The cloud and crypto layer moved into the shared Kotlin module, and then Android. Places from Packd, tables from Raviolo, and more block types as the kon-verse grows.
Built With
- kmp
- supabase
- swift
- vercel
Log in or sign up for Devpost to join the conversation.