> ## Documentation Index
> Fetch the complete documentation index at: https://docs.westyx.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust SDK - API Reference

> Every public method and type exported by the Westyx Nexus Rust SDK.

Every public symbol exported by `westyx-nexus`. All read methods are non-blocking - they hit the in-memory cache. Only `NexusClient::create` and `sync` make network calls.

## Construction

### `NexusClient::create(config: NexusConfig) -> Result<NexusClient, NexusError>`

Creates a client and runs a **blocking** initial sync. Returns the populated client, or a `NexusError`.

```rust theme={null}
use westyx_nexus::{NexusClient, NexusConfig, NexusError};
use std::time::Duration;

let client = NexusClient::create(NexusConfig {
    api_key: std::env::var("NEXUS_API_KEY").unwrap_or_default(),
    endpoint: std::env::var("NEXUS_URL").expect("NEXUS_URL not set"),
    ttl: Some(Duration::from_secs(60)),
    wif: None,
})?;
```

| Result                             | When                                               |
| ---------------------------------- | -------------------------------------------------- |
| `Ok(NexusClient)`                  | Success - cache populated                          |
| `Err(NexusError::Unauthorized)`    | Invalid API key, slug/key mismatch, or revoked key |
| `Err(NexusError::SyncFailed(msg))` | Network error or other initial sync failure        |

## Configs

### `fn get_config(&self, key: &str) -> Option<serde_json::Value>`

Returns the resolved config value, or `None` when the key is not in the cache.

```rust theme={null}
if let Some(val) = client.get_config("database.url") {
    let url = val.as_str().unwrap();
}
```

The value is a `serde_json::Value` - use `.as_str()`, `.as_f64()`, `.as_bool()`, or deserialise into a typed struct with `serde_json::from_value`.

### `fn get_all_configs(&self) -> HashMap<String, serde_json::Value>`

Snapshot copy of every config key-value pair currently in the cache. The returned map is independent of the SDK's internal storage; mutating it has no effect.

## Feature flags

### `fn get_flag(&self, key: &str, default_value: bool) -> bool`

Returns the `is_active` state of a flag, or `default_value` when the key is absent.

```rust theme={null}
let is_on = client.get_flag("checkout.v2", false);
```

The backend computes `is_active` from `is_enabled`, `starts_at`, and `ends_at`. The SDK never recomputes this.

### `fn get_all_flags(&self) -> HashMap<String, bool>`

Snapshot copy of every flag key-`is_active` pair in the cache.

## Secrets (secret key only)

### `fn get_secret(&self, key: &str) -> Result<String, NexusError>`

Returns the plaintext value of a secret. Works for both text-type and file-type secrets - for file-type secrets it returns the raw file contents as a `String`.

```rust theme={null}
match client.get_secret("DB_PASSWORD") {
    Ok(val) => println!("secret: {} bytes", val.len()),
    Err(NexusError::PublicKeyRestricted) => eprintln!("public key cannot read secrets"),
    Err(NexusError::ServiceKindMismatch) => eprintln!("frontend service has no secrets"),
    Err(NexusError::SecretNotFound(key)) => eprintln!("secret '{}' not in cache", key),
    Err(e) => return Err(e),
}
```

| Error                 | When                                                            |
| --------------------- | --------------------------------------------------------------- |
| `PublicKeyRestricted` | The client uses a public key - returned before any network call |
| `ServiceKindMismatch` | The service `kind` is `"frontend"`                              |
| `SecretNotFound(key)` | The key is not in the cache                                     |

### `fn get_secret_file_path(&self, key: &str) -> Result<PathBuf, NexusError>`

Returns the `PathBuf` of the temp file that holds the value of a **file-type** secret. The SDK materialises every file-type secret to disk on every sync at:

```
<std::env::temp_dir()>/nexus-<sanitised-key>-<sha256-prefix>
```

The hash prefix is derived from the secret value, so the path changes when the value changes. Files are written with mode `0o600`.

```rust theme={null}
let cert_path = client.get_secret_file_path("TLS_CERT")?;
// pass cert_path to rustls, openssl, or any file-based API
```

Returns the same `NexusError` variants as `get_secret` when the key is absent, the client uses a public key, the kind is `"frontend"`, or the secret is not of file type.

## Write methods (secret key only)

Write methods issue network calls and return errors if the client uses a public key.

### `fn set_secret(&self, key: &str, value: &str) -> Result<(), NexusError>`

Creates or updates a secret value.

```rust theme={null}
client.set_secret("STRIPE_KEY", "wxs_abc...")?;
```

### `fn delete_secret(&self, key: &str) -> Result<(), NexusError>`

Deletes a secret and all its versions.

```rust theme={null}
client.delete_secret("OLD_KEY")?;
```

### `fn delete_secret_version(&self, key: &str, version: u32) -> Result<(), NexusError>`

Deletes a specific version of a secret, leaving other versions intact.

```rust theme={null}
client.delete_secret_version("STRIPE_KEY", 3)?;
```

## A/B testing

### `fn evaluate_ab(&self, keys: &[&str], user_id: &str, attributes: &HashMap<String, Value>) -> Result<HashMap<String, bool>, NexusError>`

Batch-evaluates feature flags through the **AB Testing** add-on. Issues a single `POST /v1/flags/evaluate-ab` and returns a map of flag key to evaluated boolean. Unlike `get_flag`, this method **always** performs a network call and does **not** consult the local cache.

```rust theme={null}
use std::collections::HashMap;
use serde_json::json;

let mut attrs = HashMap::new();
attrs.insert("country".to_string(), json!("HU"));
attrs.insert("plan".to_string(), json!("pro"));

let results = client.evaluate_ab(
    &["checkout.v2", "homepage.banner"],
    "user-42",
    &attrs,
)?;

if results.get("checkout.v2").copied().unwrap_or(false) {
    // user is bucketed into the new checkout
}
```

| Error                 | When                                                                        |
| --------------------- | --------------------------------------------------------------------------- |
| `AbAddonNotAvailable` | Backend returned HTTP 403 - the project does not have the AB Testing add-on |
| `Unauthorized`        | Backend returned HTTP 401                                                   |
| `Billing`             | Backend returned HTTP 402                                                   |

## SSE

### `fn run_stream(&self)`

Spawns a background `std::thread` that holds a persistent `GET /v1/stream` SSE connection. **Non-blocking** - returns immediately after spawning the thread.

```rust theme={null}
client.run_stream(); // returns immediately; background thread handles SSE
```

The background thread triggers a full sync on every change event. After 3 consecutive transport errors, the thread exits and the SDK falls back to TTL polling. See [SSE live updates](sse-live-updates) for full semantics.

## Manual sync

### `fn sync(&self) -> Result<(), NexusError>`

Forces a sync now, bypassing the TTL. Atomically replaces the cache. ETag / `If-None-Match` is used automatically; a `304 Not Modified` resets the TTL without downloading data. Rarely needed - the SDK syncs automatically.

```rust theme={null}
client.sync()?;
```

## Metadata

### `fn key_type(&self) -> &str`

`"sk"`, `"pk"`, or `""` (when authentication is exclusively via WIF with no static key set).

### `fn kind(&self) -> String`

The service kind reported by the backend: `"backend"`, `"frontend"`, or `""` if not yet determined.

### `fn synced_at(&self) -> Option<Instant>`

`Instant` of the last successful sync, or `None` before the first sync completes.

### `fn billing_overdue(&self) -> bool`

`true` when the most recent sync returned HTTP 402. While set, background refresh is suspended and the last cached snapshot is served.

## Structs

### `NexusConfig`

```rust theme={null}
pub struct NexusConfig {
    pub api_key: String,
    pub endpoint: String,
    pub ttl: Option<Duration>,
    pub wif: Option<WifConfig>,
}
```

### `WifConfig`

```rust theme={null}
pub struct WifConfig {
    pub enabled: bool,
    pub provider: String,   // "kubernetes" | "aws" | "aws_iam" | "gcp" | "azure" | "auto"
    pub token_source: Option<Box<dyn Fn() -> Result<String, NexusError> + Send + Sync>>,
    pub audience: Option<String>,    // GCP/Azure-IMDS audience (api://<client-id> on Azure IMDS)
    pub aws_region: Option<String>,  // aws_iam STS region (defaults to the AWS chain + EC2 IMDS)
}
```

## `NexusError` enum

```rust theme={null}
pub enum NexusError {
    Unauthorized,
    NotFound,
    PublicKeyRestricted,
    ServiceKindMismatch,
    SecretNotFound(String),
    SyncFailed(String),
    Billing,
    RateLimited,
    Quarantined { reason: String, expires_at: String },
    WifNotConfigured,
    WifTokenExchangeFailed(String),
    SessionExpired,
    AbAddonNotAvailable,
    Http(Box<ureq::Error>),
    Json(serde_json::Error),
    Io(std::io::Error),
}
```

Use `match` on variants. `NexusError` implements `std::error::Error` and `Display`. See [Error handling](error-handling) for when each variant is returned.
