Hi everyone! I’d like to share PsstPsst, a messaging app I’ve been working on over the past three months through vibe coding. It’s intended for staying in touch with friends and family, and supports iOS, Android, macOS, Linux, and Windows. Releases are currently available for all platforms except iOS. I haven’t had a chance to test the Linux and Windows builds myself yet. If you try either and run into any problems, I’d really appreciate hearing about them. PsstPsst is built on NIP-17 with NIP-4e, which separates encryption keys from identity keys. This means it isn’t compatible with most existing Nostr clients. You don’t need to think of it as a Nostr DM app, though. I’ve tried to keep Nostr terminology out of the app and make it approachable on its own. My hope is that people can generate a fresh key and use it with their friends and family. Why NIP-4e? With a separate encryption key, the app doesn’t need to repeatedly request encryption and decryption operations from your identity key. This makes it possible to use the app with a bunker remote signer. Encryption keys can be rotated periodically, so a leaked key won’t expose your entire message history. A few things I’d like to add over time: - Small group chats, likely limited to 10 people or fewer. NIP-17 isn’t well suited to large groups, and small circles feel like a good fit for the app’s focus. - Voice and video calls. - A built-in browser for napps. - Communication without an internet connection, using FIPS. There’s still plenty to improve, but I hope some of you might find it useful. If you feel like giving it a try, I’d be grateful for any feedback or bug reports. Thanks for taking a look!

Replies (29)

Aedifico's avatar
Aedifico 3 weeks ago
Exactly my impression as well. Like asking from someone to jump off a cliff.
So now you've joined in gaslighting people about end-to-end encryption If you want to show you're not a complete glowie, try reposting my video about retro-crypto, the app I designed for actual end-to-end encryption If you want to stop gaslighting people or get me to try your app, remove the misleading "end to end" part from the shit
Hi Cody Tested on Android I logged in with a new nsec. Start a new chat Entered your npub or nip05 The app crash immediately without any error code
MissingNo's avatar
MissingNo 3 weeks ago
Yet another Messaging App, we don't have enough already 😅
``` type: crash osVersion: google/shiba/shiba:17/CP2A.260805.005/2026091001:user/release-keys flags: dev options enabled package: chat.psstpsst.app:2, targetSdk 36 process: chat.psstpsst.app processUptime: 8157 + 1043 ms installer: dev.zapstore.app java.lang.IllegalStateException: addViewAt: failed to insert view [436] into parent [438] at index 0 at com.facebook.react.fabric.mounting.SurfaceMountingManager.addViewAt(SurfaceMountingManager.kt:336) at com.facebook.react.fabric.mounting.mountitems.IntBufferBatchMountItem.execute(IntBufferBatchMountItem.kt:122) at com.facebook.react.fabric.mounting.MountItemDispatcher.executeOrEnqueue(MountItemDispatcher.kt:340) at com.facebook.react.fabric.mounting.MountItemDispatcher.dispatchMountItems(MountItemDispatcher.kt:246) at com.facebook.react.fabric.mounting.MountItemDispatcher.tryDispatchMountItems(MountItemDispatcher.kt:93) at com.facebook.react.fabric.FabricUIManager$DispatchUIFrameCallback.doFrameGuarded(FabricUIManager.java:1528) at com.facebook.react.uimanager.GuardedFrameCallback.doFrame(GuardedFrameCallback.kt:42) at com.facebook.react.modules.core.ReactChoreographer.frameCallback$lambda$1(ReactChoreographer.kt:59) at com.facebook.react.modules.core.ReactChoreographer.$r8$lambda$nSkFhrr5T7rop_XKwzlLov4NLLw(ReactChoreographer.kt:0) at com.facebook.react.modules.core.ReactChoreographer$$ExternalSyntheticLambda0.doFrame(D8$$SyntheticClass:0) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1655) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1666) at android.view.Choreographer.doCallbacks(Choreographer.java:1252) at android.view.Choreographer.doFrame(Choreographer.java:1205) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1640) at android.os.Handler.handleCallback(Handler.java:1095) at android.os.Handler.dispatchMessageImpl(Handler.java:135) at android.os.Handler.dispatchMessage(Handler.java:125) at android.os.Looper.loopOnce(Looper.java:296) at android.os.Looper.loop(Looper.java:397) at android.app.ActivityThread.main(ActivityThread.java:9569) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:575) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975) Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. at android.view.ViewGroup.addViewInner(ViewGroup.java:5424) at android.view.ViewGroup.addView(ViewGroup.java:5253) at android.view.ViewGroup.addView(ViewGroup.java:5193) at com.facebook.react.views.view.ReactClippingViewManager.addView(ReactClippingViewManager.kt:36) at com.facebook.react.views.view.ReactClippingViewManager.addView(ReactClippingViewManager.kt:20) at com.facebook.react.fabric.mounting.SurfaceMountingManager.addViewAt(SurfaceMountingManager.kt:333) ... 23 more ```
Nabil's avatar
Nabil 3 weeks ago
Hello, Seeing creators and supporters like you uplift the Nostr community gives us tremendous strength during Nabil's medical recovery. 🌱 We share Nabil's urgent medical journey & proof on our pinned note if you'd like to read: https://chuffed.org/project/urgent-nabil-medical image
Great today there were messaging apps shared on Nostr than people i messaged today.
Congrats on shipping this. One thought on "NIP-4e, which separates encryption keys from identity keys" The hard part is how senders know which encryption key is currently yours, especially how they can know when an old encryption key stopped being yours. Just announcing this through a 10044 doesn't do the trick, since senders can never know if they've received the latest announcement and thus can't be sure that they aren't sending their messages to a compromised encryption key. Inkan solves this by recording key delegation and revocation declarations on-chain. The mechanism doesn't care what the key is used for, so it could be used under a 4E announcement. I actually started experimenting with double ratchet, because Inkan makes it easy for an identity to assign (and revoke / rotate) device-bound keys. View article →
Nova's avatar
Nova 3 weeks ago
Dude! Good idea. Can you include something like WebRTC for video chat?
You’re right, this is indeed a problem. My current solution is relatively simple: when a user rotates their encryption key, they don’t immediately discard the old key. They keep it around for a period of time so they can still decrypt messages that were encrypted with the old key by senders who haven’t received the latest key announcement yet and are still encrypting messages with the old key.
Yes, but an attacker will also be able to decrypt messages encrypted to the old key. Also, the old 10044 is validly signed forever, so an attacker can actively seed these announcements in any place that hasn't received the update. Inkan fixes that as a by-product of its key rotation system, which records revocations on-chain. I may try to add DMs, right now I'm exploring if a double ratchet is feasible.
I’m not sure what an attacker gains by spreading an old 10044 event elsewhere. Clients should be fetching 10044 events from the user’s write relays / DM relays anyway. I don’t think putting it on-chain fundamentally solves the issue of senders not getting the latest update in time. It feels more like a different source of truth, querying relays versus querying the chain.
I think it's different with a chain, you can verify that nothing was omitted. For example the note below has a screenshot of a table showing a complete key delegation history that a client has read off a blockchain:
↑