Embedding & Extension (SPI)
Rift is not only a standalone binary — it is a library you can embed in a Rust process, drive over a stable C-ABI from any language, and extend through service-provider-interface (SPI) traits. This section documents that surface: how to run the server in-process, how to bind its admin and metrics planes to your own addresses, how to plug in custom storage backends, and how to call it through the FFI.
Everything here reflects the code that ships today. If a signature in these pages ever disagrees with the source, the source wins — please file an issue.
When to use which entry point
| You want to… | Use | Page |
|---|---|---|
| Run the full Rift server (imposters + admin API + metrics) inside a Rust host | ServerBuilder | Embeddable Server |
Bind the admin or metrics plane to a chosen (or ephemeral :0) address and learn the bound port | AdminApiServer::bind / bind_metrics_server | Embeddable Server |
| Replace a storage backend (flow-state, request journal, proxy recording, response sequencing) | SPI traits on ImposterManager | Extension Points (SPI) |
| Observe reconciliation events or decorate responses | ImposterEventListener / ResponseDecorator | Extension Points (SPI) |
| Apply policy to live exchanges, rescue a no-match, or authorize admin requests | ExchangeInspector / NoMatchInterceptor / AdminAuthorizer | Extension Points (SPI) |
| Drive Rift from a non-Rust host (JVM, Node, Go, …) | The C-ABI (rift-ffi) | FFI (C-ABI) |
| Drive Rift from Java, Scala, Node/TypeScript or Go without writing an FFI bridge yourself | An official SDK | Language SDKs |
Runtime topology is yours to choose when embedding. The
--runtime per-coreflag (Performance → Runtime Topology) is a property of theriftbinary’s bootstrap — an embedded host runsServerBuilderon whatever Tokio runtime it already owns. To opt into the same per-core listener fan-out from a host, pass your worker runtime handles toServerBuilder::accept_runtimes(...)(orImposterManager::with_accept_runtimes(...)); omit them for the default single-listener topology.
Crate map
| Crate | Role | Exposes |
|---|---|---|
rift-mock-core | The engine library — no CLI, no HTTP server wiring. | ImposterManager and the SPI traits (FlowStore, FlowStoreProvider, FlowStoreBackendFactory, ResponseSequencer, RequestJournal, ProxyRecordingStore, ImposterEventListener, ResponseDecorator, NoMatchInterceptor, ExchangeInspector, AdminAuthorizer), tcp_fault_carrier, behaviors, predicates, scripting. |
rift-http-proxy | The server crate — builds the rift binary and hosts the admin/metrics HTTP layer. server::{ServerBuilder, RunningServer, bind_metrics_server, admin_bind_addr}, admin_api::AdminApiServer, bootstrap helpers, the single-port gateway, the front door (RouteObserver), the intercept proxy, install_default_crypto_provider(). | |
rift-ffi | The C-ABI shared library (cdylib) plus an rlib for in-crate tests. | The extern "C" functions (rift_start, rift_serve_admin, …) and the cbindgen header. |
The Node.js package used to live here as
packages/rift-node. It now has its own repository —achird-labs/rift-node— which owns and publishes the npm package; this repository no longer builds or publishes it.
Cargo features
The engines and allocator are feature-gated. Relevant features:
| Feature | Default (rift-http-proxy) | Default (rift-ffi) | Effect |
|---|---|---|---|
redis-backend | on | on | Redis flow-store backend. The store itself lives in the separate rift-store-redis crate; this feature pulls it in and registers it under _rift.flowState.backend: "redis". rift-mock-core has no redis feature and never depends on redis/r2d2, so embedders taking the engine alone do not carry them. |
javascript | on | on | JavaScript scripting engine (Boa) |
mimalloc | on | never forwarded | mimalloc global allocator (a cdylib must not impose an allocator on its host, so rift-ffi deliberately never enables it) |
jemalloc | off | n/a | Opt-in alternative allocator, kept for the #717 bake-off. If both mimalloc and jemalloc are enabled (as in CI’s --all-features lanes), mimalloc wins — this is resolved by cfg precedence, not an error. mimalloc remains the shipped default. |
quamina-matching | on | on | Quamina-backed body-field candidate dimension. Off ⇒ the dimension compiles to a no-op that never prunes and every body predicate is decided by the full Stage-2 evaluation — matching results are identical either way, only the prefilter speed differs. |
Because
rift-http-proxyandrift-ffitakerift-mock-corewithdefault-features = false, each engine feature above must be explicitly forwarded to reach what actually ships.scripts/verify-feature-propagation.shenforces that in CI, so a feature cannot be default-on for the library and silently absent from the binary — which is what #777 fixed.redis-backendis checked by the same script along a different chain, since it is no longer arift-mock-corefeature: it must enabledep:rift-store-redisinrift-http-proxy, andrift-ffimust forward it from there.
A minimal embedding
use clap::Parser;
use rift_http_proxy::install_default_crypto_provider;
use rift_http_proxy::server::{Cli, ServerBuilder};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Required before serving any HTTPS imposter — installs the rustls (ring) provider once.
install_default_crypto_provider();
// Run the standard server exactly as the `rift` binary would, from parsed CLI options.
ServerBuilder::from_cli(Cli::parse()).run().await
}
See Embeddable Server for injecting a custom ImposterManager, binding to ephemeral ports, and graceful shutdown.