Appearance
Client
Client is the public, thin client for the Kairos analytics engine. It opens the upstream session, builds the wire params every analytic needs, calls the prebuilt engine over a stable C ABI, and delivers each emission as a typed row to your callback. Every subscription runs through this single object.
Client::connect blocks until the upstream connect / authentication handshake completes. Once connected, emissions are delivered to .on_event callbacks off a shared worker — no async runtime is required to drive the public surface.
Construction
toml
[dependencies]
kairos = "0.1"rust
use kairos::Client;
let client = Client::connect(("me@example.com", "secret"))?;
# Ok::<(), kairos::LinkError>(())Client::connect is gated on the ffi Cargo feature (default on for the published thin client). The (email, password) tuple is the most ergonomic form; Credentials::new(email, password) and Credentials::from_zeroizing are the explicit constructors when the caller already holds a zeroize::Zeroizing<String> password buffer.
Builder for non-default settings
Client::builder() returns a ClientBuilder for overriding engine settings before connect:
rust
use kairos::{Client, WaitStrategy};
let client = Client::builder()
.wait_strategy(WaitStrategy::Balanced)
.connect(("me@example.com", "secret"))?;
# Ok::<(), kairos::LinkError>(())The wait-strategy choice is a latency-vs-CPU trade-off:
| Strategy | Idle CPU | Wake latency |
|---|---|---|
Adaptive (default) | ≈ 1 core during quiescence | nanoseconds |
Balanced | near zero | tens of microseconds |
Efficient | near zero | up to a millisecond |
Adaptive is the default because it delivers the lowest possible wake latency on the live tick path. Long-lived services, laptops, and shared containers minimising idle CPU should select Balanced or Efficient.
Mode namespaces
| Method | Returns | Purpose |
|---|---|---|
client.live() | Scope<'_> | Live streaming namespace. |
client.historical(start, end) | Scope<'_> | Bounded [start, end] YYYYMMDD historical namespace. Preview (historical-mode feature). |
client.replay() | Scope<'_> | Bounded replay namespace over a dataset staged on the engine link. Preview (replay-mode feature). |
Scope exposes the per-analytic builder accessors. Every accessor returns an AnalyticBuilder<'_, P> pre-seeded with the analytic's default wire params. The fluent chain:
rust
use kairos::{Client, VolatilityRow};
client
.live()
.volatility(["SPX"]) // per-analytic builder; names the universe
.min_emit_interval_ms(5_000) // cadence setter
.on_event(|row: &VolatilityRow| { // terminal verb; fires per emission
println!("{}", row.value);
})?;The accessor argument takes any Into<Targets>: a bare symbol, a list of symbols, an option (root, expiration, right, strike) 4-tuple, or a mixed list. Bare symbols expand to the whole chain on each symbol plus the underlying; contracts are identified by the 4-tuple, never by a wire-internal id. Per-analytic cadence and knobs are set with chainable per-field setters before the terminal .on_event, which registers the typed callback and returns an EventSubscription handle. See the subscription builder for the setters and consuming events for the delivery model.
Cheap clones
Client wraps an Arc over the engine link; client.clone() is a single atomic refcount bump. Pass it across threads and own one per-task subscription if your downstream is multi-threaded.
Shutdown
Call unsubscribe() on each EventSubscription handle to stop delivery and release its subscription, then drop the Client (and the last clone) to release the engine link and tear down the upstream session:
rust
volatility_sub.unsubscribe(); // stops + releases the volatility subscription
greeks_sub.unsubscribe(); // stops + releases the greeks subscription
drop(client); // releases the engine linkDropping the client while subscriptions are still active is supported — the link stays alive through the subscription's shared state — but the explicit unsubscribe() calls make the resource-release point obvious in the code path.
Connection-time errors (LinkError)
Client::connect returns Result<Self, LinkError>. The variants mirror the C ABI's KAIROS_ERR_* status codes plus the wire-codec errors:
| Variant | Meaning |
|---|---|
LinkError::UnknownAnalytic(String) | The requested analytic id is not registered with the engine. |
LinkError::NoLiveEntry(String) | The analytic has no live / streaming entry point. |
LinkError::Decode { analytic, detail } | The engine rejected the encoded params blob (or an enum discriminant). |
LinkError::NullArg { analytic, detail } | The engine reported a NULL pointer / bad-argument on a call. Indicates a client-side bug. |
LinkError::Engine { analytic, detail } | The engine run / subscribe / connect failed with a generic non-OK status. |
LinkError::HistoricalUnwired(String) | The historical-over-C-ABI path is a documented follow-up and is not yet wired. |
LinkError::Encode(CodecError) | A wire-params encode failed before the call reached the engine. |
LinkError::Dropped { dropped_since_last_poll, detail } | The engine's per-subscription outbound buffer overflowed since the last successful poll and one or more Arrow IPC batches were dropped. |
LinkError is #[non_exhaustive]; downstream match arms include a catchall _. New variants land as additive changes.
A connect failure aborts startup. The client ships no lazy / on-demand reference-data path — every engine cache that the analytic surface depends on is either present at connect or the call returns an error.
See also
- Subscription builder — the per-analytic fluent builder shape.
- Errors — the full
LinkErrortaxonomy with codec detail.