LaunchDarkly
Load a LaunchDarkly feature flag’s value as config, with native watch via the LaunchDarkly streaming SDK.
| Scheme | launchdarkly:// |
| Module | github.com/xavidop/mamori/providers/launchdarkly |
| Sensitive | no |
| Watch | native (streaming) |
| Auth | LAUNCHDARKLY_SDK_KEY |
Install
go get github.com/xavidop/mamori/providers/launchdarkly
import _ "github.com/xavidop/mamori/providers/launchdarkly"
Using the ref
A launchdarkly:// ref points at one flag key. The flag is evaluated for a fixed evaluation context (your service), and its variation becomes the value.
launchdarkly://<flag-key>
| Part | Required | What it means |
|---|---|---|
<flag-key> | yes | The LaunchDarkly flag key. |
The variation maps to bytes by type: a boolean flag resolves to true / false, a string flag to its string, a number to its decimal form, and a JSON flag to its JSON encoding. A flag key that does not exist resolves to not-found, so default: / optional:"true" applies.
Examples
launchdarkly://enable-new-checkouton a boolean flag resolves totrueorfalse- pair it with aboolfield.launchdarkly://max-batch-sizeon a number flag resolves to e.g.250- pair it with anintfield and avalidaterule.launchdarkly://pricing-configon a JSON flag resolves to the JSON, which you can decode withflatten:"json".
type Config struct {
NewCheckout bool `source:"launchdarkly://enable-new-checkout"`
Pricing PricingConfig `source:"launchdarkly://pricing-config" flatten:"json"`
}
The evaluation context defaults to a stable key (mamori); override it with WithContextKey.
Error classification
Beyond the not-found case, other evaluation failures are classified by the SDK’s per-evaluation error reason:
| Evaluation error kind | mamori kind |
|---|---|
CLIENT_NOT_READY | unavailable |
FLAG_NOT_FOUND | not classified here - already drives not-found |
anything else (MALFORMED_FLAG, USER_NOT_SPECIFIED, WRONG_TYPE, EXCEPTION, …) | unknown |
LaunchDarkly’s per-evaluation error vocabulary is thin: CLIENT_NOT_READY (evaluating before the client finished connecting) is the only kind with a confirmed, unambiguous meaning, so it is the only one mapped. The rest describe internal or data conditions with no reliable single real-world cause and are deliberately left unknown rather than guessed at.
Watch
Watch uses the SDK flag tracker: LaunchDarkly streams flag changes, and mamori emits an update the instant the flag’s value for your context changes. This is a genuine push, not polling.
Configuration
import ldprov "github.com/xavidop/mamori/providers/launchdarkly"
mamori.WithProvider(ldprov.New(ldprov.WithSDKKey(os.Getenv("LAUNCHDARKLY_SDK_KEY"))))
Verified with an injected fake evaluator (including value-change subscriptions, not-found, and injected per-flag failures for error classification), so the watch and providertest ErrorClassification conformance checks run without a live LaunchDarkly. A real-SDK test is provided behind //go:build integration.
Close() is idempotent and terminal: after it returns, every Resolve, and any Watch started after Close, report errors.Is(err, mamori.ErrUnavailable) locally, without contacting LaunchDarkly. It releases the LaunchDarkly client this provider built lazily: its streaming connection and the goroutines that maintain it, including the flag tracker’s event listeners. A client injected with WithClient belongs to the caller and is left connected; New followed by Close with no prior Resolve never connects, so there is nothing to release.
Close does not stop a Watch that is already running. What it does next is decided by the closed SDK client rather than by this provider, so it is not guaranteed to report mamori.ErrUnavailable. A watch running on a client injected with WithClient is unaffected, since Close never touches that client. Cancel the watch’s own context to stop it. Close does not stop a Watch compares every provider.