# Cache Behavior

NDK's cache persists event data, delivery state, and decrypted payload sidecars used by app-facing reads and writes.

This guide focuses on the behavior you can rely on when building an app with NDK. It does **not** cover backend implementation details.

## What gets cached

At a high level, NDK may persist:

- Nostr events
- event source relays
- pending delivery state for locally created events
- decrypted plaintext sidecars for encrypted events
- convenience projections like metadata, contact lists, and user relay lists

## What `load`-style reads mean

When NDK reads events from cache, it applies these visibility rules:

- expired events are hidden
- author-deleted events are hidden
- for replaceable and addressable events, only the latest visible winner is returned

App-facing reads return the current logical state, not raw historical storage.

## Event sources vs delivery targets

These are different concepts:

- **Event sources** answer: "where did this event come from?"
- **Delivery targets** answer: "where does this locally created event still need to be sent?"

As an app developer:

- use normal usecases and requests for reading and publishing
- only inspect low-level source relay APIs if your app explicitly needs provenance
- do not assume a source relay is automatically a pending broadcast target

## Local-first publishing

When your app broadcasts an event through normal NDK APIs:

- the event can become visible locally before every relay acknowledges it
- relay delivery progress can survive app restarts when the cache backend is persistent
- retries can continue in the background while the app is running
- replaceable events retry only the newest visible version

This lets the UI update from locally persisted state without waiting for every relay to accept the event.

## Encrypted payload cache

For encrypted content, NDK can cache decrypted plaintext separately from the original event.

This is useful when:

- the app needs to render the same DM or private list repeatedly
- decryption depends on a slow or remote signer

Important behavior:

- the original event stays encrypted in the canonical event store
- plaintext is cached in a separate sidecar keyed by event and viewer
- different viewers may have different cached plaintext results

## Convenience accessors

`metadata`, `follows`, and `userRelayLists` are high-level app APIs backed by the generic event cache.

In practice this means:

- you should keep using the high-level usecases
- you should not maintain a separate storage assumption for metadata/contact lists
- replaceable and deletion semantics are applied consistently across generic event reads and convenience accessors

## Cache eviction

Eviction behavior is:

- removing expired events
- removing author-deleted events
- removing superseded replaceable/addressable events
- optionally applying caps to visible events per kind

If background eviction scheduling is enabled, NDK uses the configured values from `NdkConfig`:

- `cacheEvictionEnabled`
- `cacheEvictionPolicy`
- `cacheEvictionStartupDelay`
- `cacheEvictionInterval`
- `runCacheEvictionOnStartup`

If you enable background eviction scheduling, NDK will periodically run the configured eviction policy while the app is alive.

## Recommended app usage

- Use a persistent `CacheManager` in real apps.
- Read through high-level usecases unless you specifically need low-level control.
- Treat local-first publish as locally durable and eventually relay-delivered.
- Configure eviction for long-lived apps with large caches.
- Let NDK manage decrypted payload caching instead of rewriting encrypted event content yourself.
