What it does
Michizure splits trip costs for the trip you are actually on. There are no accounts: a room is a URL plus a passphrase that you send separately. Once a room is open on your phone it keeps working with no signal — every change is saved to the device first and sent to the server afterwards — and it syncs when signal returns. The server stores only ciphertext and thin metadata.
The demo video (75 seconds, on an Android phone) shows the offline path: airplane mode on, an expense added, airplane mode off, and the room returning to "saved". Then the one paid feature — exporting the room's expenses and settlement as CSV, gated behind a one-time purchase powered by RevenueCat. The app's interface is in Japanese.
Choices we made on purpose
- No accounts. Nothing to sign up for and nothing to lose but the passphrase. The price is that we cannot recover a lost one.
- Encryption happens in the browser. The key is derived from the passphrase (PBKDF2, 600,000 iterations) and never sent. The passphrase is generated by default: five words from a 1,024-word Japanese list.
- Conflicts are surfaced, not guessed at. Expenses added on each side merge automatically. When two devices change the same expense, the app shows both versions and asks — and until you choose, that device sends nothing to the server.
- Money is divided in whole yen. The remainder is assigned, not rounded away: ¥100 between three people is 34/33/33, and every device agrees on who carries the extra yen.
How it is built
TypeScript with no UI framework (an esbuild bundle), a service worker that caches the app shell so it reloads with no network, and Cloudflare Workers with one Durable Object per room on the server. The native shell is Capacitor; we built and launched both the iOS and Android shells to confirm they run — iOS in the Simulator, Android on real hardware, which is also where the demo video was recorded. Apache-2.0.
How RevenueCat is used
The hard part of monetization here is not checkout — it is that the app has no accounts, so there is no user to attach an entitlement to. Attaching it to the room would make the server store "this room is paid"; introducing accounts would undo the premise of the product. We attach it to the device, with a Restore button, and keep paid features to things the device can decide by itself. The RevenueCat SDK runs in the native shell only and is lazy-loaded; the web build never touches it.
In this submission the purchase is configured against RevenueCat's Test Store, as the video shows, so no real-money purchase is possible. The app is not released to any store yet, and the RevenueCat keys are not in the repository.
What we are honest about
The README lists these in full; the ones that matter most:
- A lost passphrase cannot be recovered, and a device that has opened a room keeps its contents and key in plaintext in local storage.
- You cannot verify the encryption yourself: we serve the JavaScript that does it.
- Metadata is visible to the server, and Cloudflare keeps request data including IP addresses.
- Room creation is unauthenticated and its rate limit is weak: in a production test on 2026-09-18, about 195 consecutive requests never received a 429. We plan to replace it with a Durable Object counter after submission.
- The connection indicator does not probe reachability.
What we learned
We audited every factual claim in the README against the code, with a second reader trying to refute each finding. It found things we had written wrongly — including a limitation we had already fixed, and a rate limit we had described as working when it did not.
Built by waiteu. Source code: https://github.com/waiteu-git/michizure
Built With
- aes-gcm
- android
- capacitor
- cloudflare-workers
- durable-objects
- esbuild
- ios
- pbkdf2
- revenuecat
- service-worker
- typescript
- vitest
- web-crypto-api
Log in or sign up for Devpost to join the conversation.