{- | The refresh / cache / expiry / concurrency policy behind a 'Ecluse.Core.Credential.CredentialProvider'. The interesting part of outbound auth is not the cloud call but the /policy/ around it: serve a cached token, refresh it proactively before it expires, never stampede the token API, and stay up across a transient mint outage. That policy is identical for every cloud, so it lives here once, parameterised over a tiny per-cloud 'rcMint' leaf (CodeArtifact's @GetAuthorizationToken@, an ADC OAuth2 token, …) and an injected 'rcClock'. Only 'rcMint' touches a network; everything else is deterministic, so the whole policy is unit-tested with a fake clock and a fake mint (see @docs\/architecture\/cloud-backends.md@ → "Credential Provider"). == The policy * __Proactive, background refresh.__ A token is refreshed when the clock passes a fraction ('rcRefreshAt', ~80%) of its lifetime, with 'rcJitter' to desynchronise a cohort of instances, plus a hard floor near expiry. Because the current token stays valid during the refresh, the request hot path __never blocks on a mint__ in the common case -- the refresh runs in the background and swaps the token in when it lands. * __Single-flight.__ At most one mint is ever in flight per provider (an STM flag), so a cohort of callers crossing the threshold together never stampedes the cloud token API; the rest serve the still-valid cached token. * __Serve-stale on failure, behind a circuit breaker.__ A failing mint does not fail the caller while the cached token is still valid -- the wrapper keeps serving it and retries later. Repeated failures __trip a circuit breaker__ that fast-fails further mints for a cooldown ('rcBreakerCooldown') before a single half-open probe tests recovery, so a sustained outage neither hammers the token API nor adds latency. Only an __expired__ token together with a still-failing mint surfaces as an exception to the caller (the breaker shares its shape with the effectful-rule tier -- see @docs\/architecture\/rules-engine.md@ → "Effectful-rule failure"). A 'CredentialProvider' always backs the mirror-target __write__; under the default @passthrough@ access strategy that is its only use, so even a fully failed refresh touches only the mirror publish and never the client serve path. Where a mount instead puts a provider on the private-upstream __read__ (the @service@ and service-populated @delegated-cache@ strategies), that dependent operation /is/ a client read, so an exhausted read credential degrades serving. The refresh policy here is identical either way (see @docs\/architecture\/access-model.md@ → "Credential supply"). The implementation lives in "Ecluse.Core.Credential.Refresh.Internal"; this module re-exports only the stable surface a caller needs. -} module Ecluse.Core.Credential.Refresh ( -- * Configuration RefreshConfig (..), defaultRefreshConfig, -- * The refreshing provider refreshingProvider, -- * Telemetry reporters RefreshReporter (..), noRefreshReporter, CredentialReporters (..), noCredentialReporters, -- * Failure CredentialError (..), ) where import Ecluse.Core.Credential.Refresh.Internal ( CredentialError (..), CredentialReporters (..), RefreshConfig (..), RefreshReporter (..), defaultRefreshConfig, noCredentialReporters, noRefreshReporter, refreshingProvider, )