Skip to content

barrydeen/wisp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

240 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Wisp

A minimal, performant Android client for the Nostr protocol. Built with Kotlin and Jetpack Compose, Wisp prioritizes decentralization, intelligent relay routing, and a clean native experience.

Status: Early alpha (v0.1.0) β€” actively developed, expect breaking changes.


Table of Contents


Why Wisp

Most Nostr clients treat relays as interchangeable dumb pipes. Wisp takes a different approach β€” it implements the outbox/inbox relay model from day one, routing messages intelligently based on where users actually publish and read. The result is faster event delivery, less wasted bandwidth, and a client that actively promotes the decentralized architecture Nostr was designed for.

Wisp is built to be fast, lightweight, and respectful of both your device and the relay network.


Key Features

Intelligent Outbox/Inbox Relay Routing

Wisp implements a full outbox/inbox model with relay scoring:

  • Outbox reads: Fetches a user's posts from their write relays (where they actually publish), not from a hardcoded list
  • Inbox writes: Delivers replies and reactions to a user's read relays (where they actually look), ensuring they see your interactions
  • Relay scoring: Tracks relay reliability and author coverage to optimize which relays to query, minimizing redundant connections
  • Smart relay hints: When tagging events, selects relay hints that overlap between your outbox and the target's inbox for optimal discoverability
  • Ephemeral connections: Dynamically opens short-lived relay connections as needed (up to 30) with automatic cleanup after 5 minutes of inactivity
  • Fallback strategies: Gracefully degrades to broadcast mode for users without published relay lists

Performance First

  • LRU caching across events (5,000), profiles, reactions, reposts, and zaps β€” data is fetched once and reused
  • Off-main-thread processing: All event parsing and relay communication runs on background dispatchers
  • Debounced UI updates: Feed emissions are coalesced to one per 16ms frame, preventing excessive recompositions from rapid relay events
  • Deduplication: Atomic check-then-put prevents the same event from being processed twice across multiple relays
  • Lazy profile loading: Metadata is fetched asynchronously in batches with periodic sweep cycles
  • Relay cooldowns: Failed relays get a 5-minute cooldown before retry, preventing connection storms

Promotes Decentralization from the Start

  • No hardcoded "mega-relay" dependency β€” default relays are starting points, not requirements
  • First-class NIP-65 relay list support encourages users to publish their own relay preferences
  • Outbox model means the client respects where users choose to publish, not where the client developer decided to look
  • Blocked relay support (NIP-51 kind 10006) lets users opt out of specific relays entirely
  • DM relay sets (NIP-51 kind 10050) allow separate relay infrastructure for private messaging
  • Blossom media uploads distribute content across decentralized media servers instead of centralized CDNs

NWC Wallet Integration

  • Full NIP-47 Nostr Wallet Connect support
  • Connect any NWC-compatible Lightning wallet via nostr+walletconnect:// URI
  • Check balance, pay invoices, and create invoices directly from the app
  • Send Lightning zaps to posts with optional messages
  • Zap receipt tracking and display on the feed

Blossom Media Support

  • Upload images and media to decentralized Blossom servers
  • Manage your server list (kind 10063) with per-account isolation
  • Nostr event-based authentication for uploads (kind 24242)
  • Multi-server fallback β€” tries each configured server until one succeeds
  • Automatic EXIF stripping where supported by the server

Private Messaging

  • NIP-17 gift wrap encryption: Three-layer privacy model (rumor β†’ seal β†’ gift wrap) with timestamp randomization
  • NIP-44 modern encryption: ECDH + HKDF + XChaCha20 + HMAC-SHA256, replacing legacy NIP-04
  • Conversation key caching: Expensive ECDH computations are cached per peer
  • Separate DM relays: Publish DMs to dedicated relay sets for better privacy

Safety Controls

  • Mute lists (NIP-51 kind 10000) for blocking pubkeys
  • Keyword muting for content filtering
  • Relay blocking to opt out of specific relays
  • All safety lists sync across clients via published Nostr events

Additional Features

  • Thread view with NIP-10 reply threading and root resolution
  • Notifications aggregating mentions, reactions, and zaps
  • Search for profiles and content
  • Bookmarks and pins (NIP-51)
  • Custom follow sets (NIP-51 kind 30000) as alternative feed sources
  • Reposts with tracking and display
  • Emoji reactions (NIP-25) with custom emoji picker
  • NIP-05 DNS verification with caching
  • NIP-19 bech32 encoding β€” npub, nsec, note, nevent, nprofile support
  • Nostr URI rendering in post content (nostr:npub..., nostr:note...)
  • QR code display for sharing keys and profiles
  • Multiple account support with per-account encrypted storage
  • Biometric authentication for key access
  • Relay console for debugging relay communication
  • Profile editing with metadata publishing
  • Onboarding flow with follow suggestions for new users

Screenshots

Coming soon


Architecture

Wisp follows an MVVM architecture with clear layer separation:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   UI Layer                   β”‚
β”‚         Jetpack Compose Screens              β”‚
β”‚    (FeedScreen, ThreadScreen, DmScreen...)   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚               ViewModel Layer                β”‚
β”‚   FeedViewModel, ThreadViewModel,            β”‚
β”‚   DmConversationViewModel, WalletViewModel   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚              Repository Layer                β”‚
β”‚  EventRepo, ContactRepo, DmRepo, NwcRepo,   β”‚
β”‚  RelayListRepo, BlossomRepo, MuteRepo...     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚              Protocol Layer                  β”‚
β”‚   Nip01, Nip02, Nip10, Nip17, Nip19,        β”‚
β”‚   Nip25, Nip44, Nip47, Nip51, Nip57, Nip65  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚               Relay Layer                    β”‚
β”‚   RelayPool, OutboxRouter, RelayScoreBoard,  β”‚
β”‚   SubscriptionManager, Relay (WebSocket)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Design Decisions

  • No database: All state is held in-memory (LRU caches) or in SharedPreferences/EncryptedSharedPreferences. This keeps the app simple and fast β€” events are fetched from relays on each session, as Nostr intended.
  • NIP objects: Each NIP is implemented as a Kotlin object with static helper functions (e.g., Nip17.createGiftWrap()), making the protocol layer modular and testable.
  • Flow-based reactivity: SharedFlow for relay events, StateFlow for UI state. No RxJava, no LiveData β€” pure coroutines.
  • Encrypted key storage: Private keys never touch plain SharedPreferences. AES256-GCM via Android's EncryptedSharedPreferences.

Project Structure

app/src/main/kotlin/com/wisp/app/
β”œβ”€β”€ nostr/          # Protocol implementations (NipXX.kt objects)
β”‚   β”œβ”€β”€ Event.kt        # Core event structure, signing, serialization
β”‚   β”œβ”€β”€ Filter.kt       # Subscription filters
β”‚   β”œβ”€β”€ Keys.kt         # Key generation and conversion
β”‚   β”œβ”€β”€ Nip02.kt        # Follow list management
β”‚   β”œβ”€β”€ Nip10.kt        # Reply threading
β”‚   β”œβ”€β”€ Nip17.kt        # Gift wrap DMs
β”‚   β”œβ”€β”€ Nip19.kt        # Bech32 encoding
β”‚   β”œβ”€β”€ Nip25.kt        # Reactions
β”‚   β”œβ”€β”€ Nip44.kt        # Modern encryption
β”‚   β”œβ”€β”€ Nip47.kt        # Wallet Connect
β”‚   β”œβ”€β”€ Nip51.kt        # Lists (mute, bookmark, pin, follow sets)
β”‚   β”œβ”€β”€ Nip57.kt        # Zaps
β”‚   └── Nip65.kt        # Relay list metadata
β”œβ”€β”€ relay/          # Relay connection and routing
β”‚   β”œβ”€β”€ Relay.kt            # WebSocket connection per relay
β”‚   β”œβ”€β”€ RelayPool.kt        # Connection pool (persistent + ephemeral)
β”‚   β”œβ”€β”€ OutboxRouter.kt     # Outbox/inbox routing logic
β”‚   β”œβ”€β”€ RelayScoreBoard.kt  # Relay quality scoring
β”‚   └── SubscriptionManager.kt  # Subscription lifecycle
β”œβ”€β”€ repo/           # Data repositories and persistence
β”œβ”€β”€ viewmodel/      # Screen ViewModels
β”œβ”€β”€ ui/             # Jetpack Compose screens and components
β”‚   β”œβ”€β”€ screen/         # Full screens (Feed, Thread, DM, etc.)
β”‚   └── component/      # Reusable UI components
└── db/             # Database layer

Supported NIPs

NIP Description Status
01 Basic protocol flow Implemented
02 Follow lists Implemented
04 Encrypted DMs (legacy) Implemented (fallback)
05 DNS-based verification Implemented
09 Event deletion Implemented
10 Reply threading Implemented
11 Relay information Implemented
17 Private DMs (gift wrap) Implemented
18 Reposts Implemented
19 Bech32 encoding Implemented
25 Reactions Implemented
44 Versioned encryption Implemented
47 Wallet Connect (NWC) Implemented
51 Lists Implemented
57 Lightning zaps Implemented
65 Relay list metadata Implemented

Getting Started

Requirements

  • Android 8.0 (API 26) or higher
  • A Nostr keypair (you can generate one in-app or import an existing nsec)

Installation

APK downloads will be available on the Releases page once published.

First Launch

  1. Create or import a key β€” Generate a fresh keypair or paste your existing nsec
  2. Set up your profile β€” The onboarding flow walks you through name, picture, and bio
  3. Follow some people β€” Wisp suggests popular accounts to get your feed started
  4. Configure relays β€” Your relay list is published as a NIP-65 event so other outbox-aware clients can find you

Building from Source

Prerequisites

Build

# Clone the repository
git clone https://github.com/barrydeen/wisp.git
cd wisp

# Build debug APK
./gradlew assembleDebug

# Install on connected device
./gradlew installDebug

Run Tests

./gradlew test

Contributing

Contributions are welcome! Wisp is an open-source project and we appreciate help from the community.

How to Contribute

  1. Fork the repository
  2. Create a branch for your feature or fix:
    git checkout -b feature/your-feature-name
  3. Make your changes β€” follow the existing code patterns and conventions
  4. Test your changes on a real device or emulator
  5. Commit with a clear, descriptive message
  6. Open a pull request against main

Code Conventions

  • Kotlin with Jetpack Compose β€” no XML layouts
  • NIP implementations go in NipXX.kt as Kotlin object with static helper functions
  • Events are created via NostrEvent.create(privkey, pubkey, kind, content, tags)
  • Hex encoding uses ByteArray.toHex() / String.hexToByteArray() extensions
  • Coroutines for all async work β€” Dispatchers.Default for CPU-bound, Dispatchers.IO for network
  • StateFlow for UI state, SharedFlow for relay events
  • Keep functions small and focused. Prefer clarity over cleverness.

Areas Where Help is Needed

  • UI/UX polish and accessibility improvements
  • Additional NIP implementations
  • Testing β€” unit tests and integration tests
  • Performance profiling and optimization
  • Translations and localization
  • Documentation improvements

Reporting Issues

Found a bug or have a feature request? Open an issue with:

  • Steps to reproduce (for bugs)
  • Expected vs actual behavior
  • Device and Android version
  • Relevant logs from the in-app relay console (if applicable)

Roadmap

Near Term

  • Local database for offline access and faster startup (Room or SQLDelight)
  • Image and video previews in the feed
  • Hashtag following and trending topics
  • Push notifications via UnifiedPush
  • Profile banner images
  • Event deletion (NIP-09) UI
  • Improved thread view with collapsible replies

Medium Term

  • NIP-42 relay authentication
  • NIP-96 file storage integration
  • Long-form content (NIP-23) reading and publishing
  • Community/group support (NIP-72)
  • Relay discovery and recommendations
  • Advanced search with relay-side filtering
  • Custom emoji packs (NIP-30)
  • Media gallery per profile

Long Term

  • Marketplace integration (NIP-15)
  • Tor/proxy support for enhanced privacy
  • Offline-first architecture with background sync
  • Widgets for Android home screen
  • Wear OS companion app
  • Full accessibility audit and WCAG compliance

Ongoing

  • Performance optimization and memory profiling
  • Expanded NIP coverage as the protocol evolves
  • UI refinements based on community feedback
  • Security audits and hardening

Tech Stack

Component Technology
Language Kotlin 2.0
UI Framework Jetpack Compose (Material 3)
Networking OkHttp 4 (WebSocket)
Image Loading Coil 3
Serialization kotlinx.serialization
Cryptography secp256k1-kmp (Schnorr), Bouncy Castle (XChaCha20), Android Security Crypto (AES-GCM)
Navigation Jetpack Navigation Compose
Media Media3 / ExoPlayer
QR Codes ZXing
Build Gradle 8.7 / AGP 8.7
Min SDK Android 8.0 (API 26)
Target SDK Android 15 (API 35)

License

Wisp is released under the MIT License.

MIT License

Copyright (c) 2025 Barry Deen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Built with care for the Nostr ecosystem.