Concepts · Evaluation
Hybrid evaluation
Server code evaluates flags locally from an in-memory ruleset. Browsers and phones ask us to evaluate and get answers, never rules. Same logic, different trust boundaries.
Two paths, one evaluator#
A flag evaluation answers one question: for this flag, in this environment, and this context (a user key plus attributes), which variation do we serve? Where that question is answered matters for latency, availability and what data is exposed.
Darkship answers it in two places. Pick a path below to see which parts of the system it touches.
Evaluation path explorer#
ETag; every evaluation after that is a function call in your process.Server vs client at a glance#
| Property | Server SDK | Client SDK |
|---|---|---|
| Key | ds_srv_ (secret) | ds_cli_ (public by design) |
| Where it evaluates | In your process, from a local ruleset | On the eval service, via POST /v1/sdk/evaluate |
| Network on each call | None | None after the first fetch; results cached per context for the session |
| Receives | Rules, segments, salts, schemas | Resolved values for one context |
| Context leaves the process | Never | Sent for evaluation, used in memory, discarded |
| Counts as a billable request | Each ruleset fetch, including 304 revalidations | Each remote evaluation |
Survives an eval outage | Yes, keeps the last ruleset | Yes, from the SDK's session cache; defaults for new contexts |
Choosing a key#
- Anything you run: API servers, workers, cron jobs, serverless functions. Use a server key and the Node or Python SDK.
- Anything you ship: web pages, mobile apps, desktop apps. Use a client key. Treat what it reveals (the values of your flags for a context you supply) as public.
- Other languages: call the REST API; there is no local evaluation without an SDK. See the data plane endpoints.
A client key can only evaluate; it cannot read rules or change anything. You can restrict it to an allow-list of origins, but that is defence in depth, not authentication. Never put anything in a flag value that must stay confidential from your own users.
One implementation, the same answer#
Rule matching, percentage bucketing and type handling are specified once and verified in every SDK and in the eval service against a shared set of language-neutral conformance fixtures (ruleset + context → expected variation). If the Python SDK, the JavaScript SDK and the remote endpoint disagree for the same ruleset and context, that is a bug in us, not a feature.
The fixed algorithm is described in Targeting and rollouts.
What we promise#
| Objective | Target |
|---|---|
eval availability | 99.9% |
eval latency, server-side, warm cache | p95 < 50 ms |
| Console click to SDK seeing the change | p95 < 5 s |
These apply to the service. A server SDK evaluating locally has no network round trip to measure.
Before the first ruleset arrives#
Evaluation never blocks and never throws. If you call it before the ruleset has loaded, you receive the default you passed, with reason NOT_READY. To avoid serving defaults on a cold start, either await waitForReady() at boot, or pass a bootstrap snapshot (see init options).
Thanks. This helps us prioritise the docs.