This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
flutter pub get- Install dependenciesflutter run- Run the applicationdart run build_runner build -d- Generate required files (localization, code generation)flutter test- Run unit testsflutter test integration_test/- Run integration testsflutter analyze- Static code analysis and lintingflutter format .- Format code
- Run
dart run build_runner build -dafter installing dependencies or updating localization files - This generates files needed by
flutter_intland other code generators
flutter analyze- ✅ ALWAYS run after any code change - Mandatory before commitsflutter test- ✅ ALWAYS run after any code change - Mandatory before commitsdart run build_runner build -d- 🟡 Only when code generation needed (models, providers, mocks, localization)flutter test integration_test/- 🟡 Only for significant changes (core services, main flows)
- Uses Riverpod for dependency injection and state management
- Providers are organized by feature in
features/{feature}/providers/ - Notifier pattern for complex state logic (authentication, order management)
- Notifiers encapsulate business logic and expose state via providers
- SubscriptionManager Enhancement: Includes manual initialization (
_initializeExistingSessions()) to prevent orders getting stuck in previous states after app restart - protected by regression test
- Sembast NoSQL database for local persistence
- Database initialization in
shared/providers/mostro_database_provider.dart - Repository pattern: All data access through repository classes in
data/repositories/ - Models exported through
data/models.dart
- NostrService (
services/nostr_service.dart) manages relay connections and messaging - All Nostr protocol interactions go through this service
- MostroFSM (
core/mostro_fsm.dart) manages order state transitions
- GoRouter for navigation (configured in
core/app_routes.dart) - flutter_intl for internationalization (
l10n/directory) - Background services in
background/for notifications and data sync
- Feature-based organization:
features/{feature}/{screens|providers|notifiers|widgets}/ - Shared utilities and widgets in
shared/ - Repository pattern for data access
- Provider pattern for dependency injection
- FSM pattern for order lifecycle management
- Automatic Sync: Real-time synchronization with Mostro instance relay lists via kind 10002 events
- Manual Addition: Users can add custom relays with strict validation (wss://, domains only, connectivity required)
- Instance Validation: Author pubkey checking prevents relay contamination between Mostro instances
- Two-tier Testing: Nostr protocol + WebSocket connectivity validation
- Memory Safety: Isolated test instances protect main app connectivity during validation
- Dual Storage Strategy: Mostro/default relays stored in
settings.relays, user relays stored insettings.userRelayswith full metadata preservation - Source Tracking: Relays tagged by source (user, mostro, default) for appropriate handling and storage strategy
- Smart Re-enablement: Manual relay addition automatically removes from blacklist, Mostro relay re-activation removes from blacklist during sync
- URL Normalization: All relay URLs normalized by removing trailing slashes to ensure consistent matching
- Implementation: Located in
features/relays/with core logic inRelaysNotifier
- Users can manually add relays via
addRelayWithSmartValidation()method - Five sequential validations: URL normalization, duplicate check, domain validation, connectivity testing, blacklist management
- Security requirements: Only wss:// protocol, domain-only (no IP addresses), mandatory connectivity test
- Smart URL handling: Auto-adds "wss://" prefix if missing
- Source tracking: Manual relays marked as
RelaySource.user - Blacklist override: Manual addition automatically removes relay from blacklist
- Active Storage:
settings.relayscontains active relay list used by NostrService - Metadata Storage:
settings.userRelayspreserves complete JSON metadata for user relays - Lifecycle Management:
removeRelayWithBlacklist()adds Mostro/default relays to blacklist,removeRelay()permanently deletes user relays - Storage Synchronization:
_saveRelays()method synchronizes both storage locations
- Author pubkey validation prevents relay contamination between different Mostro instances
- Subscription cleanup on instance switching via
unsubscribeFromMostroRelayList() - State cleanup removes old Mostro relays when switching instances via
_cleanMostroRelaysFromState()
- Two-tier connectivity testing: Primary Nostr protocol test (REQ/EVENT/EOSE), WebSocket fallback
- Domain-only policy: IP addresses completely rejected
- URL normalization: Trailing slash removal prevents duplicate entries
- Instance-isolated testing: Test connections don't affect main app connectivity
The app follows a specific initialization order in appInitializerProvider:
- NostrService Initialization: Establishes WebSocket connections to configured relays
- KeyManager Initialization: Loads cryptographic keys from secure storage
- SessionNotifier Initialization: Loads active trading sessions from Sembast database
- SubscriptionManager Creation: Registers session listeners with
fireImmediately: false - Background Services Setup: Configures notification and sync services
- Order Notifier Initialization: Creates individual order managers for active sessions
- SessionNotifier must complete initialization before SubscriptionManager setup
- SubscriptionManager uses
fireImmediately: falseto prevent premature execution - Proper sequence ensures orders appear consistently in UI across app restarts
Comprehensive system that prevents orphan sessions and detects order timeouts through dual protection mechanisms: 10-second cleanup timers and real-time timeout detection via public events.
Automatic cleanup system that prevents sessions from becoming orphaned when Mostro instances are unresponsive:
Order Taking Protection:
- Activation: Started automatically when users take orders (
takeSellOrder,takeBuyOrder) - Purpose: Prevents orphan sessions when Mostro doesn't respond within 10 seconds
- Cleanup: Deletes session, shows localized notification, navigates to order book
- Cancellation: Timer automatically cancelled when any response received from Mostro
- Implementation:
AbstractMostroNotifier.startSessionTimeoutCleanup()method inabstract_mostro_notifier.dart
Order Creation Protection:
- Activation: Started automatically when users create orders (
AddOrderNotifier.submitOrder) - Purpose: Prevents orphan sessions when Mostro doesn't respond to new order creation within 10 seconds
- Cleanup: Deletes temporary session, shows localized notification, navigates to order book
- Cancellation: Timer automatically cancelled when any response received from Mostro
- Implementation:
AbstractMostroNotifier.startSessionTimeoutCleanupForRequestId()method inabstract_mostro_notifier.dart
English: "No response received, check your connection and try again later"
Spanish: "No hubo respuesta, verifica tu conexión e inténtalo más tarde"
Italian: "Nessuna risposta ricevuta, verifica la tua connessione e riprova più tardi"
- Direct Instructions: Receives explicit timeout/cancellation instructions from Mostro via encrypted gift wrap messages (kind 1059)
- No Monitoring Required: No need to monitor public events or compare timestamps
- Real-time: Gift wrap messages delivered through existing SubscriptionManager system
- Integration: Works alongside 10-second cleanup for comprehensive protection
When taker doesn't respond, Mostro sends Action.newOrder gift wrap:
- Session: Preserved (keeps order in "My Trades")
- State: Updated to
Status.pendingwithAction.newOrder - Persistence: Real gift wrap message automatically persisted by message storage system
- Result: Order remains visible and shows pending status after app restart
- Notification: "Your counterpart didn't respond in time"
When user doesn't respond, Mostro sends Action.canceled gift wrap:
- Session: Deleted completely via
sessionNotifier.deleteSession() - State: Provider invalidated and session removed
- Result: Order disappears from "My Trades" and reappears in order book for retaking
- Notification: "Order was canceled"
When orders are canceled, Mostro sends Action.canceled gift wrap:
- Detection: Direct gift wrap instruction processing in
AbstractMostroNotifier.handleEvent() - Session Cleanup: Universal cancellation handling for both timeout and manual cancellation scenarios
- Active Orders: Canceled active orders keep session but update to canceled status
- UI Behavior: Pending/waiting orders disappear, active orders show canceled status
- User Feedback: Shows cancellation notification
- Implementation: Gift wrap handling in
abstract_mostro_notifier.dart
- Dual Protection: 10-second cleanup + gift wrap instructions provide comprehensive coverage
- Simplified Logic: Direct instruction processing eliminates complex event monitoring
- Timer management: Static timer storage with proper cleanup on disposal, differentiated keys (
orderIdvsrequest:requestId) - Error resilience: Timeouts and try-catch blocks prevent app hangs
- Notifications: Differentiated messages for maker vs taker scenarios
- Direct Communication: Mostro decides and instructs directly via encrypted messages
- No Synthetic Messages: Real gift wrap messages contain all needed information
- Session Differentiation: Permanent sessions (orderId) vs temporary sessions (requestId) with appropriate cleanup methods
- Unit tests in
test/directory - Integration tests in
integration_test/ - Mocks generated using Mockito in
test/mocks.dart
- Generated file:
test/mocks.mocks.dartis auto-generated by Mockito - File-level ignores: Contains comprehensive ignore directives at file level
- Regeneration: Use
dart run build_runner build -dto update mocks after changes - No manual editing: Never manually modify generated mock files
- Use Riverpod for all state management
- Encapsulate business logic in Notifiers
- Access data only through repository classes
- Use post-frame callbacks for side effects like SnackBars/dialogs
- Follow existing feature-based folder structure
- Keep UI code declarative and side-effect free
- Use
S.of(context).yourKeyfor all user-facing strings - Refer to existing features (order, chat, auth) for implementation patterns
- All code comments must be in English - No Spanish, Italian, or other languages
- Use clear, concise English for variable names, function names, and comments
- Documentation and technical explanations should be in English
- User-facing strings use localization system (
S.of(context).keyName)
- MostroService - Core business logic and Mostro protocol handling
- NostrService - Nostr protocol connectivity
- Background services - Handle notifications and background tasks
- Key management - Cryptographic key handling and storage
- Exchange service - Fiat/Bitcoin exchange rate handling
- Primary languages: English (en), Spanish (es), Italian (it)
- ARB files location:
lib/l10n/intl_en.arb- English (base language)intl_es.arb- Spanish translationsintl_it.arb- Italian translations
- Generated files:
lib/generated/l10n.dartand language-specific files - Usage: Import
import 'package:mostro_mobile/generated/l10n.dart';and useS.of(context)!.keyName
- Always use localized strings: Replace hardcoded text with
S.of(context)!.keyName - ARB file structure: Add new keys to all three ARB files (en, es, it)
- Parameterized strings: Use proper ARB metadata for strings with parameters
- Regenerate after changes: Run
dart run build_runner build -dafter ARB modifications - Context usage: Pass BuildContext to methods that need localization
- Package: Uses
timeagopackage for relative time formatting - Setup: Locales configured in
main.dartwithtimeago.setLocaleMessages() - Implementation: Custom
timeAgoWithLocale()method in NostrEvent extension - Usage: Automatically uses app's current locale for "hace X horas" vs "hours ago"
- DynamicCountdownWidget: Intelligent countdown widget for pending orders with automatic day/hour scaling
- Implementation: Located in
lib/shared/widgets/dynamic_countdown_widget.dart - Data Source: Uses exact
order_expires_attimestamps from Mostro protocol for precision - Dual Display Modes:
- Day Scale (>24h remaining): Shows "14d 20h 06m" format with day-based circular progress
- Hour Scale (≤24h remaining): Shows "HH:MM:SS" format with hour-based circular progress
- Automatic Transition: Switches at exactly 24:00:00 remaining time
- Localization: Uses
S.of(context)!.timeLeftLabel()for internationalized display - Scope: Only for pending status orders; waiting orders use separate countdown system
- Integration: Shared across TakeOrderScreen and TradeDetailScreen for consistency
Comprehensive system that automatically synchronizes the app's relay list with the configured Mostro instance while providing users full control through an intelligent blacklist mechanism.
- Parses NIP-65 (kind 10002) events from Mostro instances
- Validates relay URLs (WebSocket only)
- Robust handling of different timestamp formats
- Null-safe parsing for malformed events
enum RelaySource {
user, // Manually added by user
mostro, // Auto-discovered from Mostro
defaultConfig, // App default relay
}
class Relay {
final RelaySource source;
bool get canDelete; // User relays only
bool get canBlacklist; // Mostro/default relays
}- New
blacklistedRelays: List<String>field - Backward-compatible serialization
- Automatic migration for existing users
syncWithMostroInstance(): Manual sync triggerremoveRelayWithBlacklist(String url): Smart removal with blacklistingaddRelayWithSmartValidation(...): Auto-removes from blacklist when user manually adds_handleMostroRelayListUpdate(): Filters blacklisted relays during sync
- App Launch: Automatic subscription to kind 10002 events from configured Mostro
- Event Reception: Parse relay list and filter against blacklist
- State Update: Merge new relays while preserving user relays
- NostrService: Automatic reconnection to updated relay list
User removes Mostro relay → Added to blacklist → Never re-added during sync
User manually adds relay → Removed from blacklist → Works as user relay
- Transparent Operation: Sync happens automatically in background
- Full User Control: Can permanently block problematic Mostro relays
- Reversible Decisions: Manual addition re-enables previously blocked relays
- Preserved Preferences: User relays always maintained across syncs
- Real-time Updates: WebSocket subscriptions for instant sync
- Error Resilience: Graceful fallbacks and comprehensive error handling
- Race Protection: Prevents concurrent sync operations
- Logging: Detailed logging for debugging and monitoring
// SettingsNotifier blacklist management
Future<void> addToBlacklist(String relayUrl);
Future<void> removeFromBlacklist(String relayUrl);
bool isRelayBlacklisted(String relayUrl);
// RelaysNotifier smart operations
Future<void> removeRelayWithBlacklist(String url);
Future<void> clearBlacklistAndResync();- Subscription Management: Uses
SubscriptionManagerwith dedicated relay list stream - State Persistence: Blacklist automatically saved to SharedPreferences
- Backward Compatibility: Existing relay configurations preserved and migrated
- Testing: Comprehensive unit tests in
test/features/relays/(currently disabled due to complex mocking requirements)
For complete technical documentation, see RELAY_SYNC_IMPLEMENTATION.md.
- Target: Zero
flutter analyzeissues - Deprecation handling: Always use latest APIs (e.g.,
withValues()instead ofwithOpacity()) - BuildContext async gaps: Always check
mountedbefore using context after async operations - Imports: Remove unused imports and dependencies
- Immutability: Use
constconstructors where possible
- DO NOT add ignore comments to generated files (
*.g.dart,*.mocks.dart) - MockSharedPreferencesAsync warning: File already has
// ignore_for_file: must_be_immutable - duplicate_ignore warnings: Usually caused by adding individual ignores to files with file-level ignores
- Solution: Regenerate files with
dart run build_runner build -dinstead of adding ignores - Mock files: Never manually edit
test/mocks.mocks.dart- it's auto-generated by Mockito
- Branch naming: Feature branches like
feat/feature-name - Commit messages: Descriptive messages following conventional commits
- No Claude references: Don't include Claude/AI references in commit messages
- Code review: All changes should pass
flutter analyzebefore commit
- Complete Localization: Added 73+ new localization keys across all screens
- Three Languages: Full support for English (en), Spanish (es), and Italian (it)
- Advanced Time Formatting: Fixed timeago package to show "hace X horas" instead of "hours ago"
- Localized UI Components: All user-facing strings properly internationalized
- ARB File Management: Organized translation files with proper metadata
- App Icon Improvements: Enhanced app launcher icon with proper adaptive icon support
- Notification Icons: Improved notification icons for better visibility
- Card-Based Settings: Clean, organized settings interface with visual hierarchy
- Enhanced Account Screen: Streamlined user profile and preferences
- Currency Integration: Visual currency flags for international trading
- Relay Management: Enhanced relay synchronization with URL normalization and settings persistence mechanisms
- Zero Analyzer Issues: Resolved 54+ Flutter analyze issues, maintaining clean codebase
- Modern APIs: Updated all deprecated APIs to latest Flutter standards
- BuildContext Safety: Fixed async gaps with proper mounted checks
- Code Cleanup: Removed unused imports and dependencies
- Const Optimization: Added const constructors where possible
- Proper Timeago Localization: Implemented locale-aware time formatting system
- Enhanced NostrEvent Extension: Added locale-aware methods for better UX
- Improved Error Handling: Better user feedback and error recovery
- Background Services: Reliable notification processing
- Mock File Management: Comprehensive documentation to prevent generated file issues
- Dual Storage Implementation: Mostro/default relays persist in
settings.relaysand use blacklist for deactivation, user relays persist insettings.userRelayswith complete JSON metadata viatoJson()/fromJson() - Differentiated Lifecycle Management:
removeRelayWithBlacklist()adds Mostro/default relays to blacklist for potential restoration,removeRelay()permanently deletes user relays from both state and storage - Storage Synchronization:
_saveRelays()method saves all active relays tosettings.relayswhile separately preserving user relay metadata insettings.userRelays - URL Normalization Process: Relay URLs undergo normalization by trimming whitespace and removing trailing slashes using
_normalizeRelayUrl()method throughout blacklist operations in_handleMostroRelayListUpdate() - Settings Persistence Mechanism: The Settings
copyWith()method uses null-aware operators (??) to preserve existing values for selectedLanguage and defaultLightningAddress when not explicitly overridden - Relay Validation Protocol: Connectivity testing follows a two-tier approach: primary Nostr protocol test (sends REQ, waits for EVENT/EOSE) via
_testNostrProtocol(), fallback WebSocket test via_testBasicWebSocketConnectivity() - Blacklist Matching Logic: All blacklist operations normalize both stored blacklist URLs and incoming relay URLs to ensure consistent string matching regardless of format variations
lib/main.dart: Timeago locale setup and app initializationlib/core/app_routes.dart: Navigation configuration and routinglib/core/app_theme.dart: UI theme and styling consistency
lib/l10n/*.arb: Complete translation files for en/es/itlib/generated/l10n*.dart: Generated localization classes- Multiple widget files: Converted hardcoded strings to localized versions
lib/shared/widgets/bottom_nav_bar.dart: Enhanced navigation with notification badgeslib/features/home/screens/home_screen.dart: Modern order book interfacelib/features/relays/widgets/relay_selector.dart: Relay management interface with comprehensive validation protocol and localization support- Settings screens: Card-based layout with improved accessibility
lib/notifications/notification_service.dart: Custom notification icons and improved delivery- Android resources: Added notification icon assets in all density folders
- iOS configuration: Enhanced notification handling for iOS platform
android/app/src/main/res/: Adaptive icon configuration and assetspubspec.yaml: Updated flutter_launcher_icons configuration- Platform-specific assets: Proper density support for all screen sizes
- Zero Flutter Analyze Issues: Maintained throughout development
- Comprehensive Testing: All tests passing with proper mock implementations
- Multi-Language Testing: Verified localization across all supported languages
- Platform Testing: Validated on both Android and iOS devices
- Performance Testing: Ensured smooth animations and responsive UI
- Enhanced CLAUDE.md: Updated development guidelines and project context
- New NOSTR.md: Comprehensive technical documentation for Nostr integration
- Updated README.md: Modern feature overview and development guide
- Code Documentation: Improved inline documentation and comments
- Systematic implementation: Break complex tasks into manageable steps
- Quality focus: Always run
flutter analyzeand fix issues - Documentation: Update this file when making architectural changes
- Testing: Verify changes don't break existing functionality
- Concise responses: Keep explanations brief and to the point
- Code-first: Show implementation rather than lengthy explanations
- Problem-solving: Focus on root cause analysis and systematic fixes
- Best practices: Always follow Flutter and Dart conventions
- Clean commits: Focused, single-purpose commits
- Descriptive messages: Clear commit messages without AI references
- Branch management: Use feature branches for development
- Push workflow: Always test before pushing to remote
pubspec.yaml- Dependencies and Flutter configurationanalysis_options.yaml- Linting rules and code analysislib/core/app_routes.dart- Navigation configurationlib/core/app_theme.dart- UI theme and styling
android/local.properties- Flutter/Android build configuration (git-ignored; generated by CI or locally; includesflutter.minSdkVersion=23to prevent build.gradle auto-modifications; never commit this file or any secrets it may contain)android/app/build.gradle- Android app-specific build configurationandroid/gradle.properties- Gradle build properties and JVM settingsandroid/key.properties- Keystore configuration for APK signing (generated during CI/CD)
lib/features/- Feature-based organizationlib/shared/- Shared utilities and componentslib/data/- Models, repositories, and data managementlib/services/- Core services (Nostr, Mostro, etc.)lib/l10n/- Internationalization filestest/- Unit and integration tests
lib/core/models/relay_list_event.dart- NIP-65 event parser for kind 10002lib/features/relays/relay.dart- Enhanced relay model with source trackinglib/features/relays/relays_notifier.dart- Core relay management and sync logiclib/features/relays/relays_provider.dart- Riverpod provider configurationlib/features/settings/settings.dart- Settings model with blacklist supportlib/features/subscriptions/subscription_manager.dart- Extended with relay list subscriptionsRELAY_SYNC_IMPLEMENTATION.md- Complete technical documentation
lib/generated/- Generated localization files*.g.dart- Generated Riverpod and other code*.mocks.dart- Generated Mockito mock files (especiallytest/mocks.mocks.dart)- Platform-specific generated files
- NEVER manually edit generated files (
.g.dart,.mocks.dart,lib/generated/) - NEVER add individual ignore comments to generated files (e.g.,
// ignore: must_be_immutable) - DO NOT modify
test/mocks.mocks.dart- it already has file-level ignores - If analyzer issues exist: Regenerate files instead of adding ignores
- Mock files specifically: Have file-level
// ignore_for_file: must_be_immutable- don't add more - Regeneration commands:
- Riverpod:
dart run build_runner build -d - Mocks:
dart run build_runner build -d - Localization:
flutter gen-l10n
- Riverpod:
- Always maintain zero Flutter analyze issues
- Test localization changes in all supported languages
- Update this documentation when making architectural changes
- Follow existing patterns when adding new features
- Prioritize user experience and code maintainability
- NEVER manually edit
test/mocks.mocks.dartor any*.g.dartfiles - NEVER add
// ignore: must_be_immutableto individual classes in generated files - MockSharedPreferencesAsync already has file-level ignore - don't add more
- duplicate_ignore warnings are caused by adding individual ignores to files with file-level ignores
- Solution: Always regenerate files instead of adding ignore comments
Last Updated: October 8, 2025
Flutter Version: Latest stable
Dart Version: Latest stable
Key Dependencies: Riverpod, GoRouter, flutter_intl, timeago, dart_nostr, logger, shared_preferences
- ✅ Zero Flutter Analyze Issues: Maintained clean codebase
- ✅ Modern APIs: All deprecated warnings resolved
- ✅ Comprehensive Localization: English, Spanish, Italian support
- ✅ Enhanced UI/UX: Modern card-based interfaces
- ✅ Improved Icons: App launcher and notification icons
- ✅ Documentation: Complete technical and user guides
- 🎯 Core Trading: Full buy/sell order management
- 💬 Secure Chat: NIP-59 encrypted peer-to-peer messaging
- 🔐 Privacy: Hierarchical key management with trade-specific keys
- ⚡ Lightning: Seamless Lightning Network integration
- 🌐 Multi-Platform: Android and iOS native performance
- 📱 Real-Time: Live updates via Nostr protocol
- 🔗 Smart Relay Management: Automatic sync with blacklist control
- UI Modernization: Complete settings and account screen redesign
- Icon Enhancement: Improved app launcher and notification visibility
- Localization Excellence: 73+ new translation keys across 3 languages
- Code Quality: Zero analyzer issues with modern Flutter standards
- Documentation: Comprehensive NOSTR.md and updated README.md
- Relay System Architecture: URL normalization using trailing slash removal, Settings persistence with null-aware operators, two-tier validation protocol (Nostr + WebSocket), and comprehensive multilingual support