Reference
@lucerna-dev/gates-openfeature is a server OpenFeature provider over @lucerna-dev/gates-node in remote mode. The provider is stateless: it defines no initialize and no onClose, and every resolution is a server-side evaluation.
Options
new LucernaProvider(options);| Option | Default | What it does |
|---|---|---|
serverKey | — | required; a secret key with the gates:evaluate grant (ck_srv_…). Client keys are refused at construction. |
baseUrl | https://api.uselucerna.app | override for self-hosted or local development |
remoteCacheTtlMs | 0 | memoize answers in-memory per flag + identity; kill-switch answers stay capped at 10s (the kill-propagation budget) |
requestTimeoutMs | 5000 | per-request timeout |
onError | — | tap for evaluation failures — resolvers themselves never throw |
fetch | platform fetch | override the transport (tests, custom dispatchers) |
Evaluation context → Gates identity
| OpenFeature | Gates |
|---|---|
targetingKey | userId — the sticky-bucketing unit for rollouts and experiments |
| string / number / boolean attribute | a stringified trait, what targeting rules match against |
Date attribute | an ISO-8601 trait |
null / undefined / nested structures and arrays | dropped — Gates traits are a flat string map |
No targetingKey means an anonymous identity: kill switches and fully-on flags still answer correctly; percentage rollouts and experiments answer safely off.
Type mapping
| OpenFeature read | Gates concept | Resolution details |
|---|---|---|
getBooleanValue | feature flag (kill switches folded in) | reason TARGETING_MATCH |
getStringValue | experiment variant | reason SPLIT with variant set; your default with reason DEFAULT when not assigned |
getNumberValue / getObjectValue | — | your default with error code TYPE_MISMATCH, answered locally without a round trip |
String reads record the experiment exposure server-side on the same request — the OpenFeature read is the exposure moment.
Failure semantics
Resolvers never throw. A failed evaluation resolves the safe Gates default — false for flags, your default for variants — and surfaces the underlying error through onError. Two consequences worth knowing:
- A misconfigured key is quiet: everything reads as off. Wire
onErrorso it isn't. - An unknown flag key reads as
TARGETING_MATCHwithfalserather thanFLAG_NOT_FOUND— the eval endpoint answers safe defaults for unknown keys and failures alike.
Requests, coalescing and caching
Each resolution is one round trip. Two mechanisms reduce that:
- In-flight coalescing (always on): concurrent identical resolutions — same flag, same identity — share a single request. Render fan-outs and the Flags SDK's per-flag evaluation both benefit.
remoteCacheTtlMs(opt-in): memoize answers for repeat reads in a warm process. Raising it trades flag-propagation latency for fewer requests; kill-switch answers are always capped at 10s regardless, because kills promise ≤10s propagation.