# waitstate
Rust SDK for [WaitState](https://waitstate.io) - adaptive gating that adds fleet-wide traffic intelligence on top of your existing infrastructure.
## Install
```toml
[dependencies]
waitstate = "0.1"
```
## Quick start
```rust
use waitstate_rs::{WaitState, WaitStateConfig};
#[tokio::main]
async fn main() {
let ws = WaitState::start(WaitStateConfig {
publish_key: std::env::var("WAITSTATE_PUBLISH_KEY").unwrap(),
secret_key: std::env::var("WAITSTATE_SECRET_KEY").unwrap(),
..Default::default()
});
// Synchronous, lock-free, zero network calls on the hot path
let decision = ws.gate(Some("free"), Some(1.0));
if !decision.allowed {
eprintln!("rate limited: {}", decision.reason);
return;
}
// ... handle request ...
}
```
## Architecture
```mermaid
graph LR
App["Your App"] -->|"gate(tag, weight)"| SDK["WaitState"]
SDK -->|atomic load| AS["ArcSwap<Policy>"]
AS -->|"GateResult"| SDK
SDK -->|"GateResult"| App
subgraph "Background Tasks (tokio)"
Pulse["Pulse task<br/>(every 20s)"] -->|"POST /v1/pulse<br/>HMAC-signed"| CP["Control Plane"]
CP -->|"updated policy"| Pulse
Pulse -->|swap| AS
Sync["Sync task<br/>(every 30s)"] -->|"refresh JWT +<br/>GET /v1/policy"| CP
Sync -->|swap| AS
end
SDK -->|"AtomicU64 +<br/>DashMap"| Counters["Lock-free<br/>Counters"]
Pulse -->|read & reset| Counters
```
## Features
The crate has two modes controlled by the `runtime` feature (enabled by default):
| `default` (runtime) | Full client: `WaitState::start()`, background tasks, telemetry, auth |
| No features | Pure `gate()` function only - no I/O, no allocations on happy path |
```toml
# Pure gate function only (no tokio, no network)
waitstate = { version = "0.1", default-features = false }
```
## Options
| `publish_key` | `String` | - | Required. From the dashboard. |
| `secret_key` | `String` | - | Required. Store in env vars. |
| `base_url` | `String` | `https://api.waitstate.io` | Control plane URL. |
| `site_id` | `Option<String>` | `None` | Shard key for DO routing. Isolates sites within one org. |
| `pulse_interval_secs` | `u64` | `20` | How often to send telemetry pulses (seconds). |
| `sync_interval_secs` | `u64` | `30` | How often to refresh JWT and fetch policy (seconds). |
| `safe_mode_max_rps` | `u64` | `50` | Max requests/sec when control plane lease expires. |
## How it works
- `gate()` loads the current policy from an `ArcSwap<Policy>` - one atomic pointer load, single-digit microseconds.
- Background tasks send health telemetry (pulses) and sync the policy from the control plane.
- Counters (`usage_delta`, `bounced_delta`, per-tag counts) use lock-free atomics and `DashMap`.
- If the control plane is unreachable, the SDK **fails open** (default policy: `global_max_weight = Infinity`).
- If the lease expires, the SDK enters **safe mode** - a fixed RPS cap (default 50) to prevent runaway traffic.
- Two-key auth: `publish_key` identifies the SDK instance, `secret_key` signs pulses with HMAC-SHA256.
## Documentation
Full docs, API reference, and reflex rule guides: [waitstate.io/docs](https://waitstate.io/docs)
## License
[Apache 2.0](./LICENSE)