rifts 0.3.10

Rift Realtime Protocol / 1.0 — server + client implementation
Documentation
= Getting Started
:sectanchors:
:toc: left
:toclevels: 3

== Prerequisites

- Rust {rust-version} edition toolchain (1.85+)
- A running Tokio runtime (multi‑thread recommended)

== Add `rifts` to your project

[source,sh]
----
cargo add rifts
# With persistent storage:
cargo add rifts --features sled
----

== Hello World: a minimal server

Step 1 — write `src/main.rs`:

[source,rust]
----
use rifts::RiftServer;
use std::sync::Arc;
use tokio::sync::Notify;

#[tokio::main]
async fn main() -> rifts::Result<()> {
    let shutdown = Arc::new(Notify::new());
    let shutdown_sig = shutdown.clone();

    tokio::spawn(async move {
        tokio::signal::ctrl_c().await.ok();
        shutdown_sig.notify_one();
    });

    RiftServer::builder()
        .websocket_transport()
        .build()?
        .run("127.0.0.1:9000".parse().unwrap(), shutdown)
        .await?;

    Ok(())
}
----

Step 2 — run it:

[source,sh]
----
cargo run
----

The server is now listening on `ws://127.0.0.1:9000`.

== Choosing a Broker Backend

`RiftServer::builder()` creates a default `InMemoryBroker` — single‑process, no persistence.

[source,rust]
----
use rifts::broker::InMemoryBroker;

// Default (in‑memory, no persistence)
let server = RiftServer::builder()
    .websocket_transport()
    .build()?;
----


== Sled Persistence (feature `sled`)

Enable durable storage that survives process restarts:

[source,rust]
----
use std::sync::Arc;
use rifts::storage::{
    SledEngine, SledOffsetStore, SledLogStore,
    SledDedupeStore, SledSnapshotStore,
};

// Open sled database.
let db = sled::Config::new()
    .path("/var/lib/rifts/broker")
    .open()?;

let offsets = SledOffsetStore::new(SledEngine::new(db.open_tree(b"offsets")?));
let log     = SledLogStore::new(SledEngine::new(db.open_tree(b"log")?));
let dedupe  = SledDedupeStore::new(SledEngine::new(db.open_tree(b"dedupe")?));
let snaps   = SledSnapshotStore::new(SledEngine::new(db.open_tree(b"snapshots")?));

// Create a persistent broker.
let broker = InMemoryBroker::with_stores(
    rifts::TopicProfile::default(),
    std::time::Duration::from_secs(60),
    65_536,
    offsets, log, dedupe, snaps,
);
let server = RiftServer::builder()
    .websocket_transport()
    .broker(Arc::new(broker))
    .build()?;
----

== Authenticated Server

[source,rust]
----
use rifts::session::{TokenAuth, AuthContext, ClientId};

let auth = Arc::new(TokenAuth::new());
auth.register(
    "my-secret-token",
    AuthContext {
        client_id: ClientId::new("user-1"),
        claims:       serde_json::json!({"role": "admin"}),
        mode:         rifts::AuthMode::Bearer,
        hints:        Default::default(),
    },
);

let server = RiftServer::builder()
    .websocket_transport()
    .auth(auth)
    .build()?;
----

== Customising Server Parameters

[source,rust]
----
use std::time::Duration;
use rifts::ServerConfig;

let config = ServerConfig {
    max_payload_bytes:        256 * 1024,
    max_topics_per_connection: 64,
    max_send_queue_bytes:      2 * 1024 * 1024,
    idle_timeout:              Duration::from_secs(120),
    ..ServerConfig::default()
};

let server = RiftServer::builder()
    .websocket_transport()
    .config(config)
    .build()?;
----

See `ServerConfig` for the full set of tunables — all fields carry defaults from the Rift/1 spec §27.1.

== Framework Integration

=== Axum

[source,rust]
----
// Cargo.toml
// rifts = { version = "0.1", default-features = false, features = ["axum"] }

use axum::{Router, routing::get, extract::ws::WebSocketUpgrade};
use rifts::transport::axum::axum_ws_handler;

async fn ws_handler(ws: WebSocketUpgrade) -> impl axum::response::IntoResponse {
    ws.on_upgrade(move |socket| axum_ws_handler(socket, |conn| async move {
        // conn is a Box<dyn TransportConnection>
    }))
}
----

=== Actix‑web

[source,rust]
----
// rifts = { version = "0.1", default-features = false, features = ["actix-web"] }

use actix_web::{web, HttpRequest, HttpResponse};
use rifts::transport::actix::actix_ws_handler;

async fn ws_route(req: HttpRequest, stream: web::Payload) -> HttpResponse {
    actix_ws_handler(req, stream, |conn| async move {
        // conn is a Box<dyn TransportConnection>
    })
}
----

== Next Steps

- xref:architecture.adoc[Architecture] — full crate map, persistence layer.
- xref:examples.adoc[Examples] — Sled, actors, custom AuthProvider.
- xref:protocol/index.adoc[Protocol Reference] — wire format and semantics.