Skip to content

Repository files navigation

FeatBit Server-Side SDK for Rust

Introduction

This is the Rust Server-Side SDK for the 100% open-source feature flag management platform FeatBit.

The SDK is designed for long-lived, multi-user systems such as web servers and backend applications. Create one FbClient per FeatBit environment and reuse it for the lifetime of the process. It is not intended for untrusted client-side, desktop, mobile, or embedded applications, where an environment secret could be exposed.

OpenFeature support lives in the separate FeatBit OpenFeature Provider for Rust repository.

Data Synchronization

The SDK keeps feature flags and segments synchronized over WebSocket and evaluates them from an immutable in-memory snapshot. Flag changes are pushed to the client; interrupted connections reconnect automatically and request updates from the last known data version.

Evaluation is synchronous, local, and performs no network I/O. A previously initialized client continues to evaluate its last snapshot with ClientStatus::Stale while the connection recovers. See Offline Mode when the application must use a static bootstrap without network access.

Get Started

Installation

The latest stable SDK is available on crates.io. Add it and a logger implementation with Cargo:

cargo add featbit-server-sdk
cargo add env_logger

These commands update Cargo.toml. To pin this release exactly, use cargo add featbit-server-sdk@=0.1.1. The equivalent manual configuration is:

[dependencies]
featbit-server-sdk = "0.1.1"
env_logger = "0.11"

The Cargo package uses hyphens and is imported as featbit_server_sdk in Rust code.

Prerequisite

Before using the SDK, obtain the environment secret and SDK URLs:

Use wss:// and https:// endpoints in production. The default ws://localhost:5100 and http://localhost:5100 endpoints are intended for local development.

Quick Start

use featbit_server_sdk::{FbClient, FbOptions, FbUser};

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    env_logger::init();

    let options = FbOptions::builder(std::env::var("FEATBIT_ENV_SECRET")?)
        .streaming_url(std::env::var("FEATBIT_STREAMING_URL")?)
        .event_url(std::env::var("FEATBIT_EVENT_URL")?)
        .build()?;
    let client = FbClient::with_options(options);

    if !client.initialized() {
        eprintln!("FbClient is not initialized; variation calls use fallbacks");
    }

    let user = FbUser::builder("user-123")
        .name("Ada")
        .custom("country", "CN")
        .build();
    let enabled = client.bool_variation("new-checkout", &user, false);

    if enabled {
        // Run the new code path.
    }

    client.close();
    Ok(())
}

Examples

The Axum example creates one client during startup, shares cloneable handles through application state, drains in-flight requests, and then closes the SDK. It exposes:

  • GET /health/ready for readiness checks;
  • GET /api/flags/{flag_key}/evaluate for browser-friendly evaluation with query parameters;
  • POST /api/flags/{flag_key}/evaluate for JSON request bodies.

For GET evaluation, targetingKey, name, and defaultValue are reserved parameters. Every other query parameter becomes a custom user attribute:

http://127.0.0.1:3000/api/flags/example-flag/evaluate?targetingKey=user-123&country=CN&plan=pro&defaultValue=false

SDK

FbClient

FbClient is the heart of the SDK. It owns data synchronization, local evaluation, analytics delivery, and lifecycle state. Applications should instantiate a single client for each FeatBit environment.

Cloning an FbClient is cheap: every clone shares the same snapshot and background workers.

Using Default Options

Defaults connect to a local FeatBit evaluation server:

use featbit_server_sdk::FbClient;

# fn example() -> Result<(), featbit_server_sdk::ConfigError> {
let client = FbClient::new("environment-secret")?;
# client.close();
# Ok(())
# }

Using Custom Options

use std::time::Duration;

use featbit_server_sdk::{FbClient, FbOptions};

# fn example() -> Result<(), featbit_server_sdk::ConfigError> {
let options = FbOptions::builder("environment-secret")
    .streaming_url("wss://app-eval.featbit.co")
    .event_url("https://app-eval.featbit.co")
    .start_wait(Duration::from_secs(3))
    .build()?;
let client = FbClient::with_options(options);
# client.close();
# Ok(())
# }

Construction can wait up to start_wait for initial data. In an async application, construct the client with tokio::task::spawn_blocking so the bounded startup wait does not block an async worker.

Logging

The SDK uses Rust's standard log facade and never installs or replaces a global logger. The application chooses the implementation:

fn main() {
    env_logger::init();
    // Construct FbClient after the logger is installed.
}

Set RUST_LOG=featbit_server_sdk=debug for lifecycle and delivery diagnostics. Environment secrets, authorization headers, and complete user/event bodies are not logged.

FbUser

FbUser describes the subject of an evaluation. key is mandatory and must uniquely identify the user. name and custom string attributes can be referenced by targeting rules and included in FeatBit analytics:

use featbit_server_sdk::FbUser;

let bob = FbUser::builder("a-unique-user-key")
    .name("Bob")
    .custom("age", "15")
    .custom("country", "FR")
    .build();

assert_eq!(bob.key(), "a-unique-user-key");

Evaluating Flags

The SDK calculates flag values locally from the latest consistent snapshot. Each type has a value-only method and a detail method:

Flag type Value-only method Method with diagnostics
Boolean bool_variation bool_variation_detail
Integer int_variation int_variation_detail
Float float_variation float_variation_detail
String string_variation string_variation_detail
JSON json_variation json_variation_detail

Value-only methods always return the supplied fallback when the client is not ready, the flag does not exist, remote data is malformed, the context is invalid, or the variation has the wrong type. Application-facing evaluation methods do not panic.

use featbit_server_sdk::{FbClient, FbUser};

# fn example(client: &FbClient, user: &FbUser) {
let enabled = client.bool_variation("new-checkout", user, false);
let detail = client.bool_variation_detail("new-checkout", user, false);

println!(
    "value={}, variation={}, reason={}",
    detail.value, detail.variation_id, detail.reason
);
# let _ = enabled;
# }

all_variations returns a sorted, inspection-only snapshot of every known flag and does not emit evaluation analytics.

Status and Lifecycle

FbClient::status returns:

Status Meaning
NotReady No valid data set has been received yet
Ready The local snapshot is synchronized
Stale The last snapshot is usable while synchronization recovers
Closed The client was closed or synchronization stopped unrecoverably

flush requests a non-blocking analytics flush. flush_and_wait waits up to a caller-provided timeout and reports whether covered events were delivered. close is bounded and idempotent; call it after the application has drained in-flight work.

Offline Mode

Offline mode performs no network I/O and sends no analytics. Without bootstrap data, no feature flags or segments are available and evaluations return their caller-provided fallbacks.

Do not construct a production bootstrap file by hand because the FeatBit data-sync format is protocol-owned and may evolve. Download the current flags and segments for an environment directly from its Evaluation Server or FeatBit Agent:

curl --fail --show-error \
  --header "Authorization: <your-environment-secret>" \
  "<evaluation-server-or-agent-url>/api/public/sdk/server/latest-all" \
  --output featbit-bootstrap.json

For a direct Evaluation Server connection, use the HTTP(S) base URL also supplied to event_url, such as http://localhost:5100 for local development. The Authorization value is the FeatBit environment secret, not a REST API access token. See Retrieve feature flags with API for the server-side endpoint contract.

The response is a full data-sync envelope with messageType = "data-sync" and data.eventType = "full". Make the initial request without a timestamp query parameter because incremental responses are not valid bootstrap documents. See examples/featbit-bootstrap.json for a synthetic, non-production example. A downloaded snapshot contains flag configuration, targeting rules, and segments: treat it as sensitive configuration, do not commit a production snapshot, and refresh it whenever the environment changes.

Use the downloaded file to initialize the local snapshot:

use featbit_server_sdk::{FbClient, FbOptions};

# fn example() -> Result<(), Box<dyn std::error::Error>> {
let bootstrap = std::fs::read_to_string("featbit-bootstrap.json")?;
let options = FbOptions::builder("offline-placeholder")
    .offline(true)
    .disable_events(false, false)
    .bootstrap_json(bootstrap)
    .build()?;
let client = FbClient::with_options(options);
# client.close();
# Ok(())
# }

Bootstrap JSON is accepted only in offline mode so static data cannot compete with the live synchronizer. The SDK validates that the document is a full data-sync envelope. Offline mode does not poll for updates; unknown flags continue to resolve to fallbacks.

Disable Events Collection

disable_events(disable, allow_track) controls automatic evaluation analytics and explicit tracking independently:

Configuration Automatic evaluation events Explicit evaluation/metric tracking
disable_events(false, true) (default) enabled allowed
disable_events(false, false) enabled rejected
disable_events(true, true) disabled allowed
disable_events(true, false) disabled rejected; no event worker

For application-controlled exposure tracking, disable_events(true, true) is recommended.

Experiments (A/B/n Testing)

Successful detail evaluations retain an immutable event snapshot. This lets the application record the exact variation only after a real exposure, even if the flag changes in the meantime:

use featbit_server_sdk::{FbClient, FbUser};

# fn user_was_exposed() -> bool { true }
# fn example(client: &FbClient, user: &FbUser) {
let detail = client.bool_variation_detail("new-checkout", user, false);
if user_was_exposed() {
    if let Some(event) = detail.evaluation_event.as_ref() {
        let _accepted = client.track_eval_event(user, event);
    }
}

let _accepted = client.track_metric_event(user, "checkout-completed", 1.0);
# }

Evaluation and tracking calls enqueue without waiting for network I/O. Pending analytics are bounded by both event count (10,000 by default) and approximate retained payload memory (64 MiB by default), including queued, buffered, and in-flight events. Large user attributes and JSON variation values are delivered intact when admitted; the SDK never truncates them. Applications that intentionally send larger payloads can raise FbOptionsBuilder::max_event_queue_size_bytes. Analytics may be dropped under sustained overload rather than delaying application requests.

Integration Adapters

evaluate_raw, complete_raw_evaluation, and observe_evaluation_error form the transport-neutral adapter boundary. They expose FeatBit reason, flag metadata, and the immutable event snapshot without depending on an external standard. Most applications should use the typed variation methods.

The separate OpenFeature provider uses this boundary and does not maintain a second evaluation engine.

OpenTelemetry Evaluation Events

The optional featbit-server-sdk-opentelemetry crate emits feature_flag.evaluation through an application-owned OpenTelemetry logger. It excludes context identifiers and raw variation values by default and remains independent of FeatBit analytics delivery.

Supported Rust Versions

The crate uses Rust edition 2021 and has a minimum supported Rust version (MSRV) of 1.95.0. CI tests Rust 1.95, 1.96, and 1.97. Stable Rust only is supported.

Getting Support

See Also

Development

The architecture and compatibility requirements are documented in AGENTS.md. Run the full quality gate before submitting a change:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo test --workspace --doc

The crates.io approval, versioning, prerelease, and recovery workflow is documented in RELEASING.md.

License

Licensed under the Apache License, Version 2.0. See LICENSE.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages