Skip to content

feat: local first - #679

Merged
frnandu merged 71 commits into
masterfrom
feat/local-first
Jul 24, 2026
Merged

feat: local first#679
frnandu merged 71 commits into
masterfrom
feat/local-first

Conversation

@frnandu

@frnandu frnandu commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

This work established the core local-first delivery and cache model for NDK, focusing first on correctness and high-level behavior before broader sync work.

Key changes

  • Added persistent per-event delivery state in cache, so unpublished events and relay delivery progress survive restarts.
  • Split event provenance from Nip01Event.sources; source relays are now loaded on demand through CacheManager APIs instead of always living on the event object.
  • Deprecated Nip01Event.sources.
  • Added separate storage for relay delivery targets to avoid unsafe concurrent mutation of shared target lists.
  • Added persisted interactive-signing state in EventDeliveryRecord, so local-first work can survive while an event is still unsigned and waiting on an external signer flow.
  • Moved Metadata and ContactList fully onto the generic authoritative event store, keeping them as convenience accessors/views instead of separate primary cache silos.
  • Deprecated dedicated CacheManager write/remove APIs for Metadata and ContactList in favor of generic event persistence.
  • Introduced delivery policy classification for retries:
    • replaceable events use latest-state-only behavior
    • ephemeral events do not retry
    • deletion/control events use faster retry backoff
    • permanent vs transient relay failures are classified from NIP-style prefixes and common relay messages
  • Added periodic due-retry handling for still-connected relays, so transient OK false failures such as rate limits are retried even when no relay connectivity change happens.
  • Extended EventSigner with local-first scheduling hints for delayed signing:
    • requiresInteractiveSigning
    • requiresSignerNetwork
    • signerTransportRelayUrls
  • Extended PendingBroadcastDelivery so it can retry signing as well as relay delivery:
    • unsigned interactive-signer events stay queued locally
    • network-backed signers are retried when their transport relays reconnect
    • successful delayed signing immediately triggers publish flush to connected target relays
    • network-backed signers use slower retry backoff than local interactive signers
  • Added event kind classification helpers in EventKindClassification for replaceable, addressable, regular replaceable, parameterized replaceable, and ephemeral kinds.
  • Replaced blunt numeric event-kind checks with existing constants plus shared helpers, including Metadata.kKind, ContactList.kKind, 10002, and ChannelMetadata.kKind for kind 41.
  • Improved cache visibility rules so reads consistently enforce:
    • replaceable winner selection
    • expiration filtering
    • NIP-09 deletion/tombstone suppression
  • Fixed a critical cache-read bug where ID-based queries bypassed visibility rules by using raw loadEvent(id); they now use visibility-aware loadEvents(ids: ...).
  • Fixed the request pipeline so network-fetched events are only emitted to app-level queries if they remain visible after cache rules, preventing deleted foreign events from resurfacing.
  • Improved MockRelay behavior so deletion events are persisted and can participate in realistic sync/tombstone tests.
  • Added sidecar decrypted payload caching via DecryptedEventPayloadRecord, so encrypted event payloads can be reused from cache instead of re-decrypting on every read.
  • Wired that decrypted payload cache into GiftWrap / NIP-17 unwrapping, including cache-only unwrap paths for instant local reads when decrypted payloads are already available.
  • Added cache-first DM conversation snapshot loading plus concurrent wrapped-message parsing, so DM UIs can render immediately from cache and then refresh in the background.
  • Added cache eviction policy support and background scheduling so bounded caches can trim non-protected event data without breaking local-first correctness for important state.
  • Added a sample-app DM flow and follows integration to exercise cached conversation loading, decrypted payload reuse, and live incoming-message behavior end to end.
  • Added app-facing documentation for the new cache/local-first behavior, persistence tradeoffs, decrypted payload caching, and updated usecase contracts.

Testing added

  • delivery_policy unit tests for retry classification, replaceable policy, auth-required, transient/permanent failures, and raw relay message handling.
  • EventKindClassification tests.
  • cache/entity tests around replaceable/addressable semantics.
  • high-level local-first scenarios covering:
    • offline publish visible locally and delivered later
    • partial multi-relay success with later flush to missing relays
    • offline reactions delivered later
    • offline replaceable supersession sends only newest version
    • queued publish survives full Ndk restart
    • incoming deletion hides cached foreign events
    • deletion-before-target prevents resurrection
    • relay-driven replaceable metadata convergence
    • transient connected rejection retried later
    • permanent rejection not retried forever
  • added high-level local-first coverage for delayed signing flows:
    • network-backed interactive signer stays queued locally while signer transport is offline, then signs and publishes once transport reconnects
    • interactive signer queue survives full Ndk restart and later completes signing plus publish
    • signing can succeed before publish relays are reachable, with delivery flushing later when relay connectivity returns
  • added focused PendingBroadcastDelivery coverage for:
    • signing before broadcast
    • rejection / needs-action classification
    • transport-relay-aware retry triggering for network signers
    • different retry backoff for network vs non-network interactive signers
  • Added implementation coverage around decrypted payload sidecar caching and cache-backed encrypted payload reads through GiftWrap / DM codepaths.

Net result

This moves NDK from cache plus best-effort broadcast toward a real local-first model where app-level reads and writes behave correctly across offline creation, delayed signing, restart recovery, retry, replaceable convergence, foreign-event deletion semantics, bounded-cache operation, and encrypted-content reads that can now become cache-first instead of repeatedly signer-bound.

fixes #230
fixes #470
fixes #684
fixes #685
fixes #687
fixes #184
fixes #591
fixes #563
fixes #547

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 478 files, which is 378 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 714921a4-85e8-4cb4-88c1-78024fc50fda

📥 Commits

Reviewing files that changed from the base of the PR and between 39c9816 and 7fdbebf.

⛔ Files ignored due to path filters (2)
  • packages/sample-app/lib/l10n/generated/sample_app_localizations.dart is excluded by !**/generated/**
  • packages/sample-app/pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (480)
  • .github/workflows/tests.yaml
  • doc/concepts/signers.md
  • doc/guides/cache-behavior.md
  • doc/guides/cli/accounts.md
  • doc/guides/cli/blossom.md
  • doc/guides/cli/broadcast.md
  • doc/guides/cli/files.md
  • doc/guides/cli/index.md
  • doc/guides/cli/req.md
  • doc/guides/cli/wallets.md
  • doc/guides/cli/zaps.md
  • doc/guides/decrypted-payloads.md
  • doc/guides/getting-started.md
  • doc/guides/local-first.md
  • doc/guides/persistence.md
  • doc/index.md
  • doc/library-development/index.md
  • doc/usecases/blossom.md
  • doc/usecases/broadcast.md
  • doc/usecases/dms.md
  • doc/usecases/files.md
  • doc/usecases/follows.md
  • doc/usecases/gift-wrap.md
  • doc/usecases/lists.md
  • doc/usecases/metadata.md
  • doc/usecases/requests.md
  • doc/usecases/user-relay-lists.md
  • doc/usecases/zaps.md
  • packages/bc_ur/lib/bytewords.dart
  • packages/bc_ur/lib/cashu_animated_qr_example.dart
  • packages/bc_ur/lib/cashu_token_ur_encoder.dart
  • packages/bc_ur/lib/cbor_lite.dart
  • packages/bc_ur/lib/constants.dart
  • packages/bc_ur/lib/fountain_decoder.dart
  • packages/bc_ur/lib/fountain_encoder.dart
  • packages/bc_ur/lib/fountain_utils.dart
  • packages/bc_ur/lib/random_sampler.dart
  • packages/bc_ur/lib/ur.dart
  • packages/bc_ur/lib/ur_encoder.dart
  • packages/bc_ur/lib/utils.dart
  • packages/bc_ur/lib/xoshiro256.dart
  • packages/bc_ur/test/cashu_token_ur_encoder_test.dart
  • packages/bc_ur/test/ur_test.dart
  • packages/bip32_keys/lib/src/bip32_ecurve.dart
  • packages/bip32_keys/lib/src/bip32_keys_base.dart
  • packages/bip32_keys/lib/src/bip32_wif.dart
  • packages/drift/example/drift_cache_manager_example.dart
  • packages/drift/lib/src/database/database.dart
  • packages/drift/lib/src/database/database.g.dart
  • packages/drift/lib/src/database/tables.dart
  • packages/drift/lib/src/drift_cache_manager.dart
  • packages/drift/test/database_migration_v6_test.dart
  • packages/drift/test/drift_cache_manager_test.dart
  • packages/ndk/README.md
  • packages/ndk/bin/ndk.dart
  • packages/ndk/example/account_test.dart
  • packages/ndk/example/accounts_example.dart
  • packages/ndk/example/basic_test.dart
  • packages/ndk/example/bunkers_example.dart
  • packages/ndk/example/contact_list_test.dart
  • packages/ndk/example/files/files_example_test.dart
  • packages/ndk/example/metadata_test.dart
  • packages/ndk/example/nip13_pow_example.dart
  • packages/ndk/example/nostrconnect_example.dart
  • packages/ndk/example/nwc/connect_get_info.dart
  • packages/ndk/example/nwc/get_budget.dart
  • packages/ndk/example/nwc/list_transactions.dart
  • packages/ndk/example/nwc/lookup_invoice.dart
  • packages/ndk/example/nwc/make_hold_invoice.dart
  • packages/ndk/example/nwc/make_invoice.dart
  • packages/ndk/example/nwc/notifications.dart
  • packages/ndk/example/sembast/sembast_cache_manager_example.dart
  • packages/ndk/example/user_relay_list_test.dart
  • packages/ndk/example/wallets/add_cashu_wallet.dart
  • packages/ndk/example/wallets/send.dart
  • packages/ndk/example/zaps/receipts_for_event.dart
  • packages/ndk/example/zaps/receipts_for_profile.dart
  • packages/ndk/example/zaps/zap.dart
  • packages/ndk/lib/data_layer/data_sources/http_request.dart
  • packages/ndk/lib/data_layer/data_sources/websocket.dart
  • packages/ndk/lib/data_layer/data_sources/websocket_client.dart
  • packages/ndk/lib/data_layer/io/file_io_native.dart
  • packages/ndk/lib/data_layer/io/file_io_web.dart
  • packages/ndk/lib/data_layer/models/cashu/cashu_event_model.dart
  • packages/ndk/lib/data_layer/models/cashu/cashu_spending_history_event_model.dart
  • packages/ndk/lib/data_layer/models/cashu/cashu_token_event_model.dart
  • packages/ndk/lib/data_layer/models/nip_01_event_model.dart
  • packages/ndk/lib/data_layer/models/wallet_transaction_model.dart
  • packages/ndk/lib/data_layer/repositories/blossom/blossom_impl.dart
  • packages/ndk/lib/data_layer/repositories/cache_manager/mem_cache_manager.dart
  • packages/ndk/lib/data_layer/repositories/cache_manager/ndk_extensions.dart
  • packages/ndk/lib/data_layer/repositories/cache_manager/sembast_cache_manager.dart
  • packages/ndk/lib/data_layer/repositories/cashu/cashu_repo_impl.dart
  • packages/ndk/lib/data_layer/repositories/cashu_seed_secret_generator/dart_cashu_key_derivation.dart
  • packages/ndk/lib/data_layer/repositories/cashu_seed_secret_generator/fake_cashu_seed_generator.dart
  • packages/ndk/lib/data_layer/repositories/nostr_transport/websocket_client_nostr_transport.dart
  • packages/ndk/lib/data_layer/repositories/nostr_transport/websocket_client_nostr_transport_factory.dart
  • packages/ndk/lib/data_layer/repositories/nostr_transport/websocket_nostr_transport.dart
  • packages/ndk/lib/data_layer/repositories/nostr_transport/websocket_nostr_transport_factory.dart
  • packages/ndk/lib/data_layer/repositories/signers/bip340_event_signer.dart
  • packages/ndk/lib/data_layer/repositories/signers/nip46_event_signer.dart
  • packages/ndk/lib/data_layer/repositories/signers/qs_rust_event_signer_native.dart
  • packages/ndk/lib/data_layer/repositories/signers/qs_rust_event_signer_stub.dart
  • packages/ndk/lib/data_layer/repositories/verifiers/bip340_event_verifier.dart
  • packages/ndk/lib/data_layer/repositories/verifiers/rust_event_verifier_native.dart
  • packages/ndk/lib/data_layer/repositories/wallets/mem_wallets_repo.dart
  • packages/ndk/lib/data_layer/repositories/wallets/sembast_wallets_repo.dart
  • packages/ndk/lib/data_layer/repositories/wallets/wallet_extensions.dart
  • packages/ndk/lib/data_layer/repositories/wallets/wallets_repo_stub.dart
  • packages/ndk/lib/domain_layer/entities/blob_upload_progress.dart
  • packages/ndk/lib/domain_layer/entities/blossom_strategies.dart
  • packages/ndk/lib/domain_layer/entities/broadcast_response.dart
  • packages/ndk/lib/domain_layer/entities/broadcast_state.dart
  • packages/ndk/lib/domain_layer/entities/cache_eviction.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_blinded_message.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_event_content.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_keyset.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_melt_response.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_mint_balance.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_mint_info.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_proof.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_quote_melt.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_spending_result.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_token.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_token_event_content.dart
  • packages/ndk/lib/domain_layer/entities/cashu/cashu_token_state_response.dart
  • packages/ndk/lib/domain_layer/entities/connection_source.dart
  • packages/ndk/lib/domain_layer/entities/contact_list.dart
  • packages/ndk/lib/domain_layer/entities/event_cache_records.dart
  • packages/ndk/lib/domain_layer/entities/event_delivery_inspection.dart
  • packages/ndk/lib/domain_layer/entities/filter_fetched_ranges.dart
  • packages/ndk/lib/domain_layer/entities/jit_engine_relay_connectivity_data.dart
  • packages/ndk/lib/domain_layer/entities/metadata.dart
  • packages/ndk/lib/domain_layer/entities/ndk_file.dart
  • packages/ndk/lib/domain_layer/entities/ndk_request.dart
  • packages/ndk/lib/domain_layer/entities/nevent.dart
  • packages/ndk/lib/domain_layer/entities/nip77_state.dart
  • packages/ndk/lib/domain_layer/entities/nip_01_event.dart
  • packages/ndk/lib/domain_layer/entities/nip_01_utils.dart
  • packages/ndk/lib/domain_layer/entities/nip_17_conversation.dart
  • packages/ndk/lib/domain_layer/entities/nip_17_message.dart
  • packages/ndk/lib/domain_layer/entities/nip_51_list.dart
  • packages/ndk/lib/domain_layer/entities/nip_65.dart
  • packages/ndk/lib/domain_layer/entities/nprofile.dart
  • packages/ndk/lib/domain_layer/entities/pending_signer_request.dart
  • packages/ndk/lib/domain_layer/entities/pubkey_mapping.dart
  • packages/ndk/lib/domain_layer/entities/relay.dart
  • packages/ndk/lib/domain_layer/entities/relay_connectivity.dart
  • packages/ndk/lib/domain_layer/entities/relay_set.dart
  • packages/ndk/lib/domain_layer/entities/request_response.dart
  • packages/ndk/lib/domain_layer/entities/request_state.dart
  • packages/ndk/lib/domain_layer/entities/signer_request_rejected_exception.dart
  • packages/ndk/lib/domain_layer/entities/user_relay_list.dart
  • packages/ndk/lib/domain_layer/entities/wallet/providers/cashu/cashu_wallet.dart
  • packages/ndk/lib/domain_layer/entities/wallet/providers/cashu/cashu_wallet_provider.dart
  • packages/ndk/lib/domain_layer/entities/wallet/providers/lnurl/lnurl_wallet.dart
  • packages/ndk/lib/domain_layer/entities/wallet/providers/lnurl/lnurl_wallet_provider.dart
  • packages/ndk/lib/domain_layer/entities/wallet/providers/nwc/nwc_wallet.dart
  • packages/ndk/lib/domain_layer/entities/wallet/providers/nwc/nwc_wallet_provider.dart
  • packages/ndk/lib/domain_layer/entities/wallet/wallet_provider.dart
  • packages/ndk/lib/domain_layer/entities/wallet/wallet_transaction.dart
  • packages/ndk/lib/domain_layer/repositories/blossom.dart
  • packages/ndk/lib/domain_layer/repositories/cache_manager.dart
  • packages/ndk/lib/domain_layer/repositories/cashu_repo.dart
  • packages/ndk/lib/domain_layer/repositories/event_signer.dart
  • packages/ndk/lib/domain_layer/repositories/nostr_transport.dart
  • packages/ndk/lib/domain_layer/usecases/accounts/accounts.dart
  • packages/ndk/lib/domain_layer/usecases/broadcast/broadcast.dart
  • packages/ndk/lib/domain_layer/usecases/broadcast/broadcast_sender.dart
  • packages/ndk/lib/domain_layer/usecases/broadcast/delivery_policy.dart
  • packages/ndk/lib/domain_layer/usecases/broadcast/pending_broadcast_delivery.dart
  • packages/ndk/lib/domain_layer/usecases/bunkers/bunkers.dart
  • packages/ndk/lib/domain_layer/usecases/bunkers/models/bunker_connection.dart
  • packages/ndk/lib/domain_layer/usecases/bunkers/models/bunker_request.dart
  • packages/ndk/lib/domain_layer/usecases/cache_eviction/cache_eviction_scheduler.dart
  • packages/ndk/lib/domain_layer/usecases/cache_read/cache_read.dart
  • packages/ndk/lib/domain_layer/usecases/cache_write/cache_write.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_bdhke.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_cache_decorator.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_export_import.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_keypair.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_keysets.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_proof_select.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_restore.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_seed.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_token_encoder.dart
  • packages/ndk/lib/domain_layer/usecases/cashu/cashu_tools.dart
  • packages/ndk/lib/domain_layer/usecases/connectivity/connectivity.dart
  • packages/ndk/lib/domain_layer/usecases/decrypted_event_payloads/decrypted_event_payloads.dart
  • packages/ndk/lib/domain_layer/usecases/dms/dms.dart
  • packages/ndk/lib/domain_layer/usecases/fetched_ranges/fetched_ranges.dart
  • packages/ndk/lib/domain_layer/usecases/files/blossom.dart
  • packages/ndk/lib/domain_layer/usecases/files/blossom_user_server_list.dart
  • packages/ndk/lib/domain_layer/usecases/files/files.dart
  • packages/ndk/lib/domain_layer/usecases/follows/follows.dart
  • packages/ndk/lib/domain_layer/usecases/gift_wrap/gift_wrap.dart
  • packages/ndk/lib/domain_layer/usecases/jit_engine/jit_engine.dart
  • packages/ndk/lib/domain_layer/usecases/jit_engine/relay_jit_broadcast_strategies/relay_jit_broadcast_other_read.dart
  • packages/ndk/lib/domain_layer/usecases/jit_engine/relay_jit_broadcast_strategies/relay_jit_broadcast_own.dart
  • packages/ndk/lib/domain_layer/usecases/jit_engine/relay_jit_broadcast_strategies/relay_jit_broadcast_specific.dart
  • packages/ndk/lib/domain_layer/usecases/jit_engine/relay_jit_request_strategies/relay_jit_blast_all_strategy.dart
  • packages/ndk/lib/domain_layer/usecases/jit_engine/relay_jit_request_strategies/relay_jit_pubkey_strategy.dart
  • packages/ndk/lib/domain_layer/usecases/jit_engine/relay_jit_request_strategies/relay_jit_specific_strategy.dart
  • packages/ndk/lib/domain_layer/usecases/lists/lists.dart
  • packages/ndk/lib/domain_layer/usecases/lnurl/lnurl.dart
  • packages/ndk/lib/domain_layer/usecases/metadatas/metadatas.dart
  • packages/ndk/lib/domain_layer/usecases/nip05/nip_05.dart
  • packages/ndk/lib/domain_layer/usecases/nip42/auth_event.dart
  • packages/ndk/lib/domain_layer/usecases/nip77/nip77.dart
  • packages/ndk/lib/domain_layer/usecases/nip77/nip77_internal.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/consts/error_code.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/nostr_wallet_connect_uri.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/nwc.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/nwc_connection.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/nwc_notification.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/cancel_hold_invoice.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/list_transactions.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/lookup_invoice.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/make_invoice.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/multi_pay_invoice.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/multi_pay_keysend.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/nwc_request.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/pay_invoice.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/pay_keysend.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/requests/settle_hold_invoice.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/responses/get_budget_response.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/responses/get_info_response.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/responses/list_transactions_response.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/responses/make_invoice_response.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/responses/nwc_response.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/responses/pay_invoice_response.dart
  • packages/ndk/lib/domain_layer/usecases/nwc/tlv_record.dart
  • packages/ndk/lib/domain_layer/usecases/proof_of_work/proof_of_work.dart
  • packages/ndk/lib/domain_layer/usecases/relay_manager.dart
  • packages/ndk/lib/domain_layer/usecases/relay_sets/relay_sets.dart
  • packages/ndk/lib/domain_layer/usecases/relay_sets_engine.dart
  • packages/ndk/lib/domain_layer/usecases/requests/requests.dart
  • packages/ndk/lib/domain_layer/usecases/requests/verify_event_stream.dart
  • packages/ndk/lib/domain_layer/usecases/search/search.dart
  • packages/ndk/lib/domain_layer/usecases/stream_response_cleaner/stream_response_cleaner.dart
  • packages/ndk/lib/domain_layer/usecases/ta/trusted_assertions.dart
  • packages/ndk/lib/domain_layer/usecases/user_relay_lists/user_relay_lists.dart
  • packages/ndk/lib/domain_layer/usecases/wallets/wallets.dart
  • packages/ndk/lib/domain_layer/usecases/zaps/invoice_response.dart
  • packages/ndk/lib/domain_layer/usecases/zaps/zap_receipt.dart
  • packages/ndk/lib/domain_layer/usecases/zaps/zap_request.dart
  • packages/ndk/lib/domain_layer/usecases/zaps/zaps.dart
  • packages/ndk/lib/entities.dart
  • packages/ndk/lib/ndk.dart
  • packages/ndk/lib/presentation_layer/init.dart
  • packages/ndk/lib/presentation_layer/ndk.dart
  • packages/ndk/lib/presentation_layer/ndk_config.dart
  • packages/ndk/lib/shared/bloom_filter/bloom_filter.dart
  • packages/ndk/lib/shared/bloom_filter/bloom_filter_prehash.dart
  • packages/ndk/lib/shared/decode_nostr_msg/decode_nostr_msg.dart
  • packages/ndk/lib/shared/event_filters/nip51_mute_event_filter.dart
  • packages/ndk/lib/shared/helpers/relay_helper.dart
  • packages/ndk/lib/shared/isolates/isolate_manager_io.dart
  • packages/ndk/lib/shared/isolates/isolate_manager_stub.dart
  • packages/ndk/lib/shared/logger/logger.dart
  • packages/ndk/lib/shared/logger/ndk_logger.dart
  • packages/ndk/lib/shared/nips/nip01/client_msg.dart
  • packages/ndk/lib/shared/nips/nip01/event_eviction_planner.dart
  • packages/ndk/lib/shared/nips/nip01/event_kind_classification.dart
  • packages/ndk/lib/shared/nips/nip01/helpers.dart
  • packages/ndk/lib/shared/nips/nip04/nip04.dart
  • packages/ndk/lib/shared/nips/nip13/nip13.dart
  • packages/ndk/lib/shared/nips/nip19/nip19.dart
  • packages/ndk/lib/shared/nips/nip19/nip19_decoder.dart
  • packages/ndk/lib/shared/nips/nip19/nip19_encoder.dart
  • packages/ndk/lib/shared/nips/nip28/channel_metadata.dart
  • packages/ndk/lib/shared/nips/nip44/nip44.dart
  • packages/ndk/lib/shared/nips/nip44/utils.dart
  • packages/ndk/lib/shared/nips/nip65/relay_ranking.dart
  • packages/ndk/lib/shared/nips/nip77/negentropy.dart
  • packages/ndk/lib/src/cli/accounts/accounts_cli_command.dart
  • packages/ndk/lib/src/cli/blossom/blossom_cli_command.dart
  • packages/ndk/lib/src/cli/broadcast/broadcast_cli_command.dart
  • packages/ndk/lib/src/cli/cli_accounts_store.dart
  • packages/ndk/lib/src/cli/cli_command.dart
  • packages/ndk/lib/src/cli/files/files_cli_command.dart
  • packages/ndk/lib/src/cli/ndk_cli_app.dart
  • packages/ndk/lib/src/cli/req_cli_command.dart
  • packages/ndk/lib/src/cli/wallets/wallets_cli_command.dart
  • packages/ndk/lib/src/cli/zaps/zaps_cli_command.dart
  • packages/ndk/lib/src/rust_lib.dart
  • packages/ndk/test/broadcast/broadcast_test.dart
  • packages/ndk/test/cashu/cashu_bdhke_test.dart
  • packages/ndk/test/cashu/cashu_dart_key_derivation_test.dart
  • packages/ndk/test/cashu/cashu_dev_test.dart
  • packages/ndk/test/cashu/cashu_fund_test.dart
  • packages/ndk/test/cashu/cashu_import_export_test.dart
  • packages/ndk/test/cashu/cashu_proof_select_test.dart
  • packages/ndk/test/cashu/cashu_receive_test.dart
  • packages/ndk/test/cashu/cashu_redeem_test.dart
  • packages/ndk/test/cashu/cashu_restore_test.dart
  • packages/ndk/test/cashu/cashu_spend_test.dart
  • packages/ndk/test/cashu/cashu_storage_test.dart
  • packages/ndk/test/cashu/cashu_test_tools.dart
  • packages/ndk/test/cashu/mocks/cashu_http_client_mock.dart
  • packages/ndk/test/cashu/mocks/cashu_repo_mock.dart
  • packages/ndk/test/data_layer/cache_manager/mem_cache_manager_test.dart
  • packages/ndk/test/data_layer/cache_manager/mem_cache_manager_test.mocks.dart
  • packages/ndk/test/data_layer/cache_manager/sembast_cache_manager_test.dart
  • packages/ndk/test/data_layer/io/file_io_test.dart
  • packages/ndk/test/data_layer/nostr_transport/websocket_nostr_transport_test.dart
  • packages/ndk/test/data_layer/nostr_transport/websocket_nostr_transport_test.mocks.dart
  • packages/ndk/test/entities/blob_nip94_test.dart
  • packages/ndk/test/entities/event_cache_records_test.dart
  • packages/ndk/test/entities/filter_fetched_ranges_test.dart
  • packages/ndk/test/entities/filter_test.dart
  • packages/ndk/test/entities/nwc_wallet_test.dart
  • packages/ndk/test/mocks/mock_blossom_server.dart
  • packages/ndk/test/mocks/mock_failing_signer.dart
  • packages/ndk/test/mocks/mock_relay.dart
  • packages/ndk/test/mocks/mock_relay_ephemeral_test.dart
  • packages/ndk/test/mocks/mock_relay_live_subscription_test.dart
  • packages/ndk/test/mocks/mock_relay_replaceable_test.dart
  • packages/ndk/test/mocks/mock_relay_tag_filter_test.dart
  • packages/ndk/test/mocks/mock_slow_signer.dart
  • packages/ndk/test/ndk_test.dart
  • packages/ndk/test/nips/nip01_test.dart
  • packages/ndk/test/nips/nip02_test.dart
  • packages/ndk/test/nips/nip04_test.dart
  • packages/ndk/test/nips/nip05_test.mocks.mocks.dart
  • packages/ndk/test/nips/nip11_relay_info.dart
  • packages/ndk/test/nips/nip13_test.dart
  • packages/ndk/test/nips/nip19_event_getters_test.dart
  • packages/ndk/test/nips/nip19_test.dart
  • packages/ndk/test/nips/nip44_test.dart
  • packages/ndk/test/nips/nip51_test.dart
  • packages/ndk/test/nips/nip65/nip65_test.dart
  • packages/ndk/test/nips/nip65/relay_ranking_test.dart
  • packages/ndk/test/nips/nip77_test.dart
  • packages/ndk/test/relays/nip42_test.dart
  • packages/ndk/test/relays/relay_info_test.dart
  • packages/ndk/test/relays/relay_manager_test.dart
  • packages/ndk/test/relays/relay_sets_test.dart
  • packages/ndk/test/relays/requests_test.dart
  • packages/ndk/test/relays/trailing_slash_test.dart
  • packages/ndk/test/scenarios/list_metadata_test.dart
  • packages/ndk/test/scenarios/multiple_filters_test.dart
  • packages/ndk/test/scenarios/qs_sign_verify_test.dart
  • packages/ndk/test/shared/bloom_filter/bloom_filter_prehash_test.dart
  • packages/ndk/test/shared/bloom_filter/bloom_filter_test.dart
  • packages/ndk/test/shared/event_filters/nip51_mute_event_filter_test.dart
  • packages/ndk/test/shared/event_kind_classification_test.dart
  • packages/ndk/test/shared/helpers/concurrency_limiter_mixin_test.dart
  • packages/ndk/test/shared/helpers/relay_helper_test.dart
  • packages/ndk/test/shared/helpers/url_normalizer_test.dart
  • packages/ndk/test/shared/logger_test.dart
  • packages/ndk/test/signers/bip340_event_signer_test.dart
  • packages/ndk/test/signers/nip46_event_signer_test.dart
  • packages/ndk/test/timeouts/network_speed_test.dart
  • packages/ndk/test/timeouts/query_timeout_test.dart
  • packages/ndk/test/tools/simple_profiler.dart
  • packages/ndk/test/usecases/accounts_test.dart
  • packages/ndk/test/usecases/broadcast_delivery_policy_test.dart
  • packages/ndk/test/usecases/broadcast_jit_test.dart
  • packages/ndk/test/usecases/broadcast_test.dart
  • packages/ndk/test/usecases/cache_eviction_scheduler_test.dart
  • packages/ndk/test/usecases/cache_read_test.dart
  • packages/ndk/test/usecases/cache_write_test.dart
  • packages/ndk/test/usecases/concurrency_check/concurrency_check_test.dart
  • packages/ndk/test/usecases/connectivity/connectivity_test.dart
  • packages/ndk/test/usecases/decrypted_event_payloads/decrypted_event_payloads_test.dart
  • packages/ndk/test/usecases/delivery_inspection_test.dart
  • packages/ndk/test/usecases/dms/dms_test.dart
  • packages/ndk/test/usecases/duplicate_request_test.dart
  • packages/ndk/test/usecases/ephemeral_cache_test.dart
  • packages/ndk/test/usecases/fetched_ranges/fetched_ranges_integration_test.dart
  • packages/ndk/test/usecases/fetched_ranges/fetched_ranges_test.dart
  • packages/ndk/test/usecases/files/blossom_test.dart
  • packages/ndk/test/usecases/files/files_test.dart
  • packages/ndk/test/usecases/follows/follows_test.dart
  • packages/ndk/test/usecases/gift_wrap/gift_wrap_cache_test.dart
  • packages/ndk/test/usecases/gift_wrap/gift_wrap_external_test.dart
  • packages/ndk/test/usecases/gift_wrap/gift_wrap_test.dart
  • packages/ndk/test/usecases/jit_engine/feed_test.dart
  • packages/ndk/test/usecases/jit_engine/relay_jit_connection_test.dart
  • packages/ndk/test/usecases/lists/lists_test.dart
  • packages/ndk/test/usecases/lnurl/lnurl_test.dart
  • packages/ndk/test/usecases/lnurl/lnurl_test.mocks.dart
  • packages/ndk/test/usecases/local_first/local_first_subscription_test.dart
  • packages/ndk/test/usecases/local_first/local_first_test.dart
  • packages/ndk/test/usecases/metadatas/metadata_entity_test.dart
  • packages/ndk/test/usecases/metadatas/metadatas_test.dart
  • packages/ndk/test/usecases/ndk_repeated/ndk_repeated_jit_test.dart
  • packages/ndk/test/usecases/ndk_repeated/ndk_repeated_lists_test.dart
  • packages/ndk/test/usecases/ndk_repeated/test_repeated.dart
  • packages/ndk/test/usecases/nip05/nip05_network.test.mocks.dart
  • packages/ndk/test/usecases/nip05/nip05_network_test.dart
  • packages/ndk/test/usecases/nip05/nip05_network_test.mocks.dart
  • packages/ndk/test/usecases/nip09_addressable_deletion_test.dart
  • packages/ndk/test/usecases/nip42_auth_test.dart
  • packages/ndk/test/usecases/nip77/nip77_integration_test.dart
  • packages/ndk/test/usecases/nwc/bitcoin_network_test.dart
  • packages/ndk/test/usecases/nwc/list_transactions_response_test.dart
  • packages/ndk/test/usecases/nwc/nostr_wallet_connect_uri_test.dart
  • packages/ndk/test/usecases/nwc/nwc_method_test.dart
  • packages/ndk/test/usecases/nwc/nwc_notification_test.dart
  • packages/ndk/test/usecases/paginated_query_test.dart
  • packages/ndk/test/usecases/pending_broadcast_delivery_test.dart
  • packages/ndk/test/usecases/query_cache_by_id_test.dart
  • packages/ndk/test/usecases/signer_error_handling_test.dart
  • packages/ndk/test/usecases/slow_signer_timeout_test.dart
  • packages/ndk/test/usecases/stream_response_cleaner/stream_response_cleaner_test.dart
  • packages/ndk/test/usecases/ta/trusted_assertions_test.dart
  • packages/ndk/test/usecases/ta/trusted_assertions_test_2.dart
  • packages/ndk/test/usecases/user_relay_lists/user_relay_lists_test.dart
  • packages/ndk/test/usecases/zaps/zap_receipt_test.dart
  • packages/ndk/test/usecases/zaps/zap_receipt_test.mocks.dart
  • packages/ndk/test/usecases/zaps/zaps_test.dart
  • packages/ndk/test/usecases/zaps/zaps_test.mocks.dart
  • packages/ndk/test/verifiers/event_id_verification_test.dart
  • packages/ndk/test/verifiers/rust_event_verifier_test.dart
  • packages/ndk/test/verifiers/verify_event_stream_test.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_cashu.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_clear_all.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_contact_list.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_event.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_eviction.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_metadata.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_nip05.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_relay_set.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_search.dart
  • packages/ndk_cache_manager_test_suite/lib/src/cache_manager_test_suite_user_relay_list.dart
  • packages/ndk_flutter/README.md
  • packages/ndk_flutter/android/build.gradle
  • packages/ndk_flutter/android/src/main/AndroidManifest.xml
  • packages/ndk_flutter/android/src/main/kotlin/relaystr/ndk/DartNdkPlugin.kt
  • packages/ndk_flutter/android/src/main/kotlin/relaystr/ndk/Nip55Constants.kt
  • packages/ndk_flutter/lib/data_layer/data_sources/nip55_signer.dart
  • packages/ndk_flutter/lib/data_layer/repositories/signers/nip55_event_signer.dart
  • packages/ndk_flutter/lib/l10n/app_localizations.dart
  • packages/ndk_flutter/lib/l10n/app_localizations_sk.dart
  • packages/ndk_flutter/lib/l10n/app_sk.arb
  • packages/ndk_flutter/lib/main/ndk_flutter.dart
  • packages/ndk_flutter/lib/signers/src/event_signer_web.dart
  • packages/ndk_flutter/lib/signers/src/nip07_event_signer_stub.dart
  • packages/ndk_flutter/lib/signers/src/nip07_event_signer_web.dart
  • packages/ndk_flutter/lib/signers/src/web_event_signer_stub.dart
  • packages/ndk_flutter/lib/widgets/wallets/n_wallet_card.dart
  • packages/ndk_flutter/lib/widgets/wallets/wallet_action_dialogs.dart
  • packages/objectbox/lib/data_layer/db/object_box/db_object_box.dart
  • packages/objectbox/lib/data_layer/db/object_box/schema/db_cashu_mint_info.dart
  • packages/objectbox/lib/data_layer/db/object_box/schema/db_contact_list.dart
  • packages/objectbox/lib/data_layer/db/object_box/schema/db_metadata.dart
  • packages/objectbox/lib/data_layer/db/object_box/schema/db_nip_01_event.dart
  • packages/objectbox/lib/data_layer/db/object_box/schema/db_relay_set.dart
  • packages/objectbox/lib/data_layer/db/object_box/schema/db_user_relay_list.dart
  • packages/objectbox/lib/data_layer/db/object_box/schema/db_wallet_transaction.dart
  • packages/objectbox/lib/objectbox-model.json
  • packages/objectbox/lib/objectbox.g.dart
  • packages/objectbox/test/data_layer/cache_manager/objectbox_cache_manager_test.dart
  • packages/sample-app/README.md
  • packages/sample-app/android/app/build.gradle
  • packages/sample-app/lib/accounts_page.dart
  • packages/sample-app/lib/blossom_page.dart
  • packages/sample-app/lib/dm_live_state.dart
  • packages/sample-app/lib/dm_page.dart
  • packages/sample-app/lib/follows_page.dart
  • packages/sample-app/lib/home_page.dart
  • packages/sample-app/lib/main.dart
  • packages/sample-app/lib/nip55_signer_page.dart
  • packages/sample-app/lib/nwc_page.dart
  • packages/sample-app/lib/nwc_qr_scanner.dart
  • packages/sample-app/lib/pending_requests_page.dart
  • packages/sample-app/lib/profile_page.dart
  • packages/sample-app/lib/quantum_secure_page.dart
  • packages/sample-app/lib/query_performance.dart
  • packages/sample-app/lib/relays_page.dart
  • packages/sample-app/lib/router.dart
  • packages/sample-app/lib/verifiers_performance.dart
  • packages/sample-app/lib/widgets_demo_page.dart
  • packages/sample-app/lib/zaps_page.dart
  • packages/sample-app/pubspec.yaml
  • packages/sample-app/web/index.html

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/local-first

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@frnandu frnandu changed the title feat/local-first feat: local-first Jun 23, 2026
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/local-first

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jun 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.04708% with 692 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.32%. Comparing base (39c9816) to head (f6cbad9).

Files with missing lines Patch % Lines
...ackages/ndk/lib/domain_layer/usecases/nwc/nwc.dart 0.00% 84 Missing ⚠️
...ositories/cache_manager/sembast_cache_manager.dart 87.77% 45 Missing ⚠️
...usecases/broadcast/pending_broadcast_delivery.dart 89.67% 45 Missing ⚠️
...ges/ndk/lib/domain_layer/usecases/cashu/cashu.dart 61.40% 44 Missing ⚠️
...ges/ndk/lib/domain_layer/usecases/lists/lists.dart 68.25% 40 Missing ⚠️
...lib/domain_layer/usecases/cashu/cashu_restore.dart 0.00% 37 Missing ⚠️
.../repositories/cache_manager/mem_cache_manager.dart 89.45% 29 Missing ⚠️
...in_layer/usecases/cashu/cashu_cache_decorator.dart 7.40% 25 Missing ⚠️
...ib/domain_layer/usecases/nip77/nip77_internal.dart 0.00% 24 Missing ⚠️
...lib/domain_layer/entities/event_cache_records.dart 90.04% 21 Missing ⚠️
... and 57 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #679      +/-   ##
==========================================
- Coverage   71.96%   71.32%   -0.65%     
==========================================
  Files         212      225      +13     
  Lines       11019    13201    +2182     
==========================================
+ Hits         7930     9415    +1485     
- Misses       3089     3786     +697     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@frnandu frnandu changed the title feat: local-first feat: local first Jun 23, 2026
@frnandu frnandu added this to the 0.9.0 milestone Jun 25, 2026
@nogringo

nogringo commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

After a restart, I want a way get the list of unbroadcasted events.

@frnandu

frnandu commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator Author

After a restart, I want a way get the list of unbroadcasted events.

ndk.broadcast.loadPendingDeliveries

frnandu and others added 17 commits July 6, 2026 13:21
Reformatted files that only appeared correctly formatted locally
because a stale .dart_tool pub cache let flutter_lints resolve;
CI's format.yml has no `dart pub get` step, so package:flutter_lints
never resolves and the formatter falls back to different defaults.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…idth)

CI's format check runs before pub get, so flutter_lints never resolves
and the formatter falls back to its default page width, which disagrees
with a locally-resolved run on these two statements.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjVUC8pP1TLVRYGL1DuZ8q
Previous format ran via flutter-bundled dart which produced different
output; reformatted all files with standalone Dart SDK matching CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjVUC8pP1TLVRYGL1DuZ8q
Missed by prior glob; formatted with standalone Dart SDK 3.12.2 to match CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjVUC8pP1TLVRYGL1DuZ8q
@frnandu
frnandu merged commit 665d6a6 into master Jul 24, 2026
22 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants