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
NewClientis cancelled client.Close()is called
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
- The SDK opens an HTTP streaming response to
<BaseURL>/v1/streamwithAccept: text/event-streamand eitherX-Nexus-API-Key: <key>orAuthorization: Bearer <session_jwt>(when WIF is enabled). - The server immediately sends a
resyncevent, which causes the SDK to callSync(ctx)once to align with the current state. - The connection stays open. Every change event triggers another full sync.
- While the stream is up, the effective polling TTL is bumped to 60 s as a safety net.
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 aStreamObserver 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). TheObserver.OnFallback hook fires when the stream enters this state.
Configure a different schedule with SSEReconnectCooldown in Config:
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 onRetry-After:
- With a parseable
Retry-Afterheader (delta-seconds): the SDK parses the value, clamps it to[5 s, 5 min], firesOnFallback(FallbackReasonRateLimited)once for observability, then sleeps the indicated delay and reconnects automatically insideRunStream. The reconnect attempt does not increment the transport-failure counter -Retry-Afteris a cooperative throttle from the server, not a transport error, so it does not consume one of the three reconnect strikes that lead toMAX_FAILURESfallback. - Without a parseable
Retry-Afterheader: the SDK firesOnFallback(FallbackReasonRateLimited)then schedules an SSE reconnect using theSSEReconnectCooldownschedule (default 5 → 10 → 20 → 40 → 60 minutes). No manual recovery is needed.
Retry-After: 1. The ceiling of 5 min prevents a malicious or accidental large value from stalling the live-update channel indefinitely.
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 toNewClient. 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.
