Local-First Behavior

NDK treats app writes and reads as local-first when the relevant data is stored in the configured cache backend.

Publish behavior

When an event is created through NDK broadcast flows:

  • the event can be written to the cache before every relay acknowledges it
  • the event can become visible to app reads from that cached state
  • relay delivery can continue after the initial publish call finishes

This applies to normal events as well as targeted relay delivery used for replies, reactions, and other gossip-aware publish paths.

Delivery state

NDK keeps delivery state in the configured cache backend.

That state includes:

  • aggregate event delivery status
  • per-relay delivery targets
  • retry timing and retry outcome

Behavior depends on the backend:

  • with MemCacheManager, delivery state exists while the process is alive
  • with a persistent backend, delivery state also survives restart

Retry behavior

NDK retries pending delivery in the background while the app is running.

Retry behavior includes:

  • retry on reconnect when a relay becomes reachable
  • periodic retry for targets that are due even if connectivity state does not change
  • per-event-kind retry policy differences
  • permanent failure detection for relay responses that should not be retried forever

Replaceable events

For replaceable and addressable events:

  • app reads return the latest visible winner
  • background delivery follows that same visible winner
  • older superseded offline versions are not kept in active retry once a newer visible version exists

Reads after write

After a local publish, app-facing reads may show the event before every relay confirms it.

This means:

  • your UI can update from NDK state immediately
  • relay acknowledgement is a separate concern from local visibility
  • delivery progress should be treated as asynchronous

What this means for apps

  • use a persistent cache backend if you want local-first behavior to survive restart
  • treat locally visible publish as durable in the cache, not necessarily fully delivered to every relay yet
  • do not assume one failed relay means the publish is globally failed
  • for replaceable data, assume the newest visible version is the authoritative one