Skip to main content
The SDK keeps every config, secret, and flag in an in-memory array that is replaced atomically on each sync. Read methods never block the network after the first sync.

When does the SDK hit the network

PHP-FPM per-request model

In PHP-FPM, each HTTP request boots a fresh PHP process. The in-memory cache is empty at the start of every request and NexusClient::create makes a blocking network call on every request. The TTL has no effect across requests because there is no shared memory.Recommendation: register the client in a DI container with request scope (the default in Symfony/Laravel) so the cache is built once per request and all service classes share it within that request.
For long-running processes (CLI daemons, Swoole workers, ReactPHP servers), the cache persists across multiple reads as expected and the TTL controls background refresh frequency.

ETag / 304 protocol

Every successful sync stores the server’s ETag header. The next sync sends If-None-Match: <etag>. If nothing has changed:
The SDK restarts its TTL window and keeps serving the snapshot it already has. No payload is transferred, and no JSON is parsed. When the ETag changes, the new payload replaces the snapshot in full. Present and fresh are two different questions. The snapshot the server last sent is kept for as long as the client lives; the TTL decides whether it is still considered fresh, not whether it exists. That is what lets a 304 - the expected answer whenever nothing has changed - keep serving real values.

Background sync errors

A TTL-triggered sync that fails for a transient reason - a network failure, a timeout, an unexpected status - does not surface:
  1. The failure is logged as nexus: refresh failed, serving the cached snapshot at error level.
  2. The last known snapshot continues to be served.
  3. The next read retries automatically.
A transient backend blip therefore does not become a customer-facing error, and the application keeps running on the last thing the server said rather than falling back to its compiled-in defaults. Four conditions do surface, because an application must not keep running on data it is no longer entitled to: 401 rejected credentials, 402 billing suspended, 404 service not found, and a WIF session that expired and could not be renewed (NexusSessionExpiredException).

The snapshot is never cleared

No sync failure empties the snapshot - not a rate limit, not a billing error, not a rejected key. What differs is whether the failure is served from the snapshot or raised: a transient one is served, and the four conditions listed above are raised. Once the condition clears, the next sync succeeds normally; nothing latches.

Cache lifetime

The cache lives for the lifetime of the NexusClient object.
  • FPM/HTTP workers - cache lives for one request (PHP process is recycled after the response).
  • CLI daemons / queue workers - cache lives for the lifetime of the process.
Share the client instance rather than constructing one per call:

File-type secrets

Secrets with type = 'file' are materialised on every sync into a directory the client owns:
The directory is created with mode 0700 by mkdir itself, and the files inside are created 0600 with O_EXCL - the mode is applied by the creating syscall, not by a chmod afterwards, which would leave a window where the secret sits on disk at the process umask. O_EXCL also means nothing already at the target path, a symlink included, can redirect the write. Nothing in the path is derived from the key name or the value, so a path that ends up in a log line, an environment variable or a process listing reveals neither. Each client gets its own directory, so two clients holding the same secret - two PHP-FPM workers, for instance - never share a file. Files are removed when the NexusClient is destroyed (__destruct); in FPM that is the end of each request. A process-exit hook clears anything left behind.

Diagnostics

Enable debug-level logging in your PSR-3 logger to see one line per sync:
See Custom logging for how to wire up a logger.