Main development is on decentralized git:
htree://npub1xdhnr9mrv47kkrn95k6cwecearydeh8e895990n3acntwvmgk2dsdeeycm/iris-chat-flutter
End-to-end encrypted mobile chat app using the Nostr Double Ratchet protocol.
- End-to-end encryption - Messages encrypted using Double Ratchet (Signal protocol)
- Decentralized - Uses Nostr relays, no central server
- Offline support - Messages queued when offline, sent when connected
- QR code invites - Easy contact sharing via QR codes or links
- Cross-platform - Android and iOS
- Flutter with Riverpod for state management
- Rust native library (ndr-ffi) for cryptography via FFI
- SQLite for local message storage
- Secure storage for private keys
- Flutter 3.24+
- For Android: Android SDK
- For iOS: macOS with Xcode
flutter build apk --releaseTo build a signed Android APK locally and publish it to Zapstore:
cp .env.zapstore.example .env.zapstore.local
$EDITOR .env.zapstore.local
./tool/publish_zapstore_android.shNotes:
zapstore.yamlincludes the committed Nostrpubkeyrequired for first publish verification- the publish script reads signing config from the shell environment or
.env.zapstore.local - it stops with a clear error if no signing env is present
- set either
SIGN_WITHdirectly orNOSTR_KEY_PATHfor the Nostr signer - set
ANDROID_KEYSTORE_PATH,ANDROID_KEYSTORE_PASSWORD, andANDROID_KEY_PASSWORD, and optionallyANDROID_KEY_ALIAS - it links the APK signing certificate to
wss://relay.zapstore.devnoninteractively usingzsp --offlineplusnak - it publishes via
zsp --offline, direct Blossom uploads, andnak, which avoids thezspupload timeout path I hit locally - set
SKIP_PUBLISH=1to stop after the signed local APK build and validation steps - set
INSTALL_ON_DEVICE=1andCAPTURE_SCREENSHOT=1to verify the installed Android UI locally
- Build the native library on macOS (see
ios/Frameworks/README.md) - Run:
flutter build ios --releaseiris-chat-flutter checks in a host macOS static library at
libndr_ffi.a for native macOS test/build use. Do not copy the debug Rust
artifact into this repo.
Use the release archive from nostr-double-ratchet:
cd ~/src/nostr-double-ratchet/rust
cargo build -p ndr-ffi --release
cp target/release/libndr_ffi.a ~/src/iris-chat-flutter/libndr_ffi.aThe debug archive in target/debug/libndr_ffi.a is much larger because it
includes debug symbols and should not be committed.
The release workflow auto-detects signing configuration from GitHub Actions secrets. If all required secrets for a platform are present, signing is enabled. If none are present, unsigned artifacts are built. Partial secret setup fails the workflow.
Required macOS signing secrets:
MACOS_SIGNING_IDENTITY(for example:Developer ID Application: Example Corp (TEAMID))MACOS_CERTIFICATE_P12(base64-encoded.p12that includes private key)MACOS_CERTIFICATE_PASSWORD
Optional macOS notarization secrets (enable notarization when all are set):
MACOS_NOTARIZE_APPLE_IDMACOS_NOTARIZE_APP_PASSWORD(app-specific password)MACOS_NOTARIZE_TEAM_ID
Required Android signing secrets:
ANDROID_KEYSTORE_B64(base64-encoded.jks/.keystore)ANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORD
# Install dependencies
flutter pub get
# Run code generation (freezed, riverpod)
dart run build_runner build
# Run tests
flutter test
# Check the committed macOS FFI artifact guard
flutter test test/unit/macos/ndr_ffi_artifact_test.dart
# Run the Android mobile push device e2e with automatic notification permission grant
./tool/run_android_mobile_push_e2e.sh <device-id>
# Run analyzer
flutter analyzelib/
├── config/ # Providers, router, theme
├── core/ # FFI bindings, services
├── features/ # Feature modules
│ ├── auth/ # Identity management
│ ├── chat/ # Messaging
│ ├── invite/ # QR invites
│ └── settings/ # App settings
└── shared/ # Common utilities
MIT