> ## Documentation Index
> Fetch the complete documentation index at: https://docs.westyx.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust SDK - Caching Behaviour

> When does the Rust SDK hit the network, thread-safe snapshot, ETag/304 support, and file-type secret lifecycle.

The SDK keeps every config / secret / flag in an **in-memory snapshot** protected by an `Arc<Mutex<CacheState>>`. The snapshot is replaced atomically on each sync. Read methods never block on the network; only the background refresh thread does.

## When does the SDK hit the network

| Event                             | Result                                                                                                |
| --------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `NexusClient::create(config)`     | **Blocking** full sync on the calling thread. Returns once the cache is populated (or with an error). |
| Read within the TTL window        | **No network** - served from the in-memory cache                                                      |
| Read after TTL has elapsed        | Stale cache returned **immediately**; background thread scheduled to refresh                          |
| Server returns `304 Not Modified` | TTL reset, no payload downloaded                                                                      |
| SSE event received                | Immediate full sync, regardless of TTL                                                                |
| SSE stream connected              | Effective TTL bumped to **at least 60 s** as a safety net                                             |

## Thread-safe snapshot guarantee

The snapshot is an `Arc<Mutex<CacheState>>` shared by all clones. The mutex is held only for the duration of the replacement write - read methods lock briefly, copy the snapshot, then release immediately.

```rust theme={null}
let val_a = client.get_config("a"); // sees snapshot N
// ... background thread syncs ...
let val_b = client.get_config("b"); // sees snapshot N+1 (or still N - always consistent)
```

Two goroutines reading concurrently see either the same old snapshot or the same new one - never a mix. The maps returned by `get_all_configs()` and `get_all_flags()` are defensive copies; mutating them has no effect on the cache.

## All clones share one cache

Because all clones hold an `Arc` to the same inner state, a sync performed by any clone - whether triggered by TTL expiry, an SSE event, or a manual `sync()` call - is immediately visible to all other clones.

```rust theme={null}
let c1 = client.clone();
let c2 = client.clone();

// A sync triggered through c1 updates the cache seen by c2 instantly.
c1.sync()?;
assert_eq!(c1.synced_at(), c2.synced_at());
```

## ETag / 304

Every successful sync stores the server's `ETag`. The next sync sends `If-None-Match: <etag>`. If nothing has changed:

```
HTTP/1.1 304 Not Modified
```

...the SDK resets the TTL and serves the existing snapshot. **No payload transferred.**

When the ETag changes, the new payload replaces the cache atomically.

## Background sync errors

Background syncs **never return errors** to the caller. If the network call fails:

1. The existing cache continues to be served.
2. The next read after the TTL elapses retries automatically.

The application is never notified - a transient backend hiccup does not surface as a caller-visible error.

## File-type secrets

File-type secrets are written to disk on every successful sync when their value changes:

```
<std::env::temp_dir()>/nexus-<sanitised-key>-<sha256-prefix>
```

The hash prefix ensures the file path changes whenever the value changes; the stale file is removed automatically when the new file is written. Files are created with mode `0o600`.

`get_secret_file_path(key)` returns the current `PathBuf`. Use this to pass a cert or key file to `rustls`, `openssl`, or any other library that expects a file path rather than an in-memory value.

## Drop and cleanup

`NexusClient` implements `Drop`. When the **last** clone is dropped:

1. The background SSE thread (if running) is signalled and joined.
2. All temp files written for file-type secrets are deleted.

This ensures the process does not leave credentials behind in `$TMPDIR` after the client goes out of scope.

```rust theme={null}
{
    let client = NexusClient::create(config)?;
    client.run_stream();
    // ... use client ...
} // Drop runs here: SSE thread joined, temp files removed
```

## Diagnostics

| Method                     | What it tells you                                            |
| -------------------------- | ------------------------------------------------------------ |
| `client.synced_at()`       | `Option<Instant>` of the last successful sync                |
| `client.key_type()`        | `"sk"` or `"pk"` - confirms the SDK saw the right key prefix |
| `client.billing_overdue()` | `true` when the last sync returned HTTP 402                  |

## Concurrency model

* **All read methods** (`get_config`, `get_secret`, `get_flag`, ...) are safe to call from any thread concurrently.
* **Background sync thread** - at most one at a time, scheduled on TTL expiry.
* **SSE thread** - at most one, started when `run_stream()` is called.
* **`Arc<Mutex<CacheState>>`** - the mutex is never held across a network call; it is only locked to read or replace the snapshot in memory.
