Skip to main content
The SDK can hold a persistent GET /v1/stream connection open in a goroutine. Whenever the Nexus backend emits a change event (config.updated, flag.toggled, secret.created, resync, …), the SDK triggers an immediate full sync - so the cache is updated within milliseconds of the change, instead of waiting for the next TTL tick.

Auto-start

NewClient starts RunStream automatically in a background goroutine. No manual call is needed. The goroutine blocks (reading the SSE stream and triggering syncs) until either:
  • The context passed to NewClient is cancelled
  • client.Close() is called
In a typical service, defer client.Close() is the only shutdown call needed - it cancels the internal stream goroutine and cleans up file-type secret temp files.

What happens under the hood

  1. The SDK opens an HTTP streaming response to <BaseURL>/v1/stream with Accept: text/event-stream and either X-Nexus-API-Key: <key> or Authorization: Bearer <session_jwt> (when WIF is enabled).
  2. The server immediately sends a resync event, which causes the SDK to call Sync(ctx) once to align with the current state.
  3. The connection stays open. Every change event triggers another full sync.
  4. While the stream is up, the effective polling TTL is bumped to 60 s as a safety net.
The streaming HTTP client is a separate instance from the one you pass via Config.HTTPClient - the SDK clones your client at construction time and zeros the Timeout. This is why you can use a familiar http.Client{Timeout: 10*time.Second} for /sync without that timeout killing the long-lived SSE connection. See Configuration.

Observer hooks

Pass a StreamObserver via Config.Observer to receive structured callbacks for every SSE lifecycle transition (OnConnected / OnDisconnected / OnEvent(name) / OnReconnectAttempt(n) / OnFallback(reason) / OnQuarantined(reason, expiresAt)). See Stream observer for the full contract.

auth_expiring control event (WIF)

When WIF is enabled, the backend emits an auth_expiring SSE control event ~5 minutes before the session JWT expires. The SDK reacts by triggering a non-blocking session refresh - it does NOT call /sync, and the event is NOT surfaced through OnEvent. When the server force-closes the stream at expiry, the SDK reconnects with the freshly-issued bearer.

Reconnection

Any transport error (server restart, TCP drop, network blip) triggers a reconnect with exponential backoff: 1s - 2s - 4s - 8s - 16s - 30s (capped at 30s). After three consecutive transport errors the SDK sleeps the first reconnect-schedule interval then reconnects SSE automatically. By default the schedule progresses 5 → 10 → 20 → 40 → 60 minutes (capped at 60 min on each subsequent fallback). The Observer.OnFallback hook fires when the stream enters this state. Configure a different schedule with SSEReconnectCooldown in Config:
The reconnect counter resets to 0 after every successful SSE connection. The manual re-launch pattern from earlier SDK versions is no longer needed.

429 Too Many Requests

Every project tier has a per-service stream connection limit: Exceeding the limit returns 429 Too Many Requests, typically with a Retry-After header indicating when the client should try again.

Behaviour (v0.6.0+)

The SDK applies a presence-conditional refinement on Retry-After:
  • With a parseable Retry-After header (delta-seconds): the SDK parses the value, clamps it to [5 s, 5 min], fires OnFallback(FallbackReasonRateLimited) once for observability, then sleeps the indicated delay and reconnects automatically inside RunStream. The reconnect attempt does not increment the transport-failure counter - Retry-After is a cooperative throttle from the server, not a transport error, so it does not consume one of the three reconnect strikes that lead to MAX_FAILURES fallback.
  • Without a parseable Retry-After header: the SDK fires OnFallback(FallbackReasonRateLimited) then schedules an SSE reconnect using the SSEReconnectCooldown schedule (default 5 → 10 → 20 → 40 → 60 minutes). No manual recovery is needed.
The clamp floor of 5 s prevents a hostile or buggy server from inducing reconnect spam via a Retry-After: 1. The ceiling of 5 min prevents a malicious or accidental large value from stalling the live-update channel indefinitely.
The sleep respects context cancellation - if ctx is cancelled during the wait, RunStream returns promptly instead of waiting for the full Retry-After window.

SSE is always on

NewClient always starts the stream goroutine. Pure TTL-polling mode is not supported - the goroutine runs until the context is cancelled or Close() is called.

Lifecycle

The stream goroutine is bound to the context passed to NewClient. Cancelling that context or calling client.Close() stops it. Close() is the preferred shutdown signal - it cancels the stream and cleans up temp files.

What events trigger a sync

The SDK does not apply event payloads directly to the cache - every event triggers a full re-sync that returns the current authoritative state. This avoids any drift between the SDK’s view and the backend.