Skip to content

Flowfull-Rust

Official Library + Starter Kit

Rust has two official pieces:

Use the flowfull crate from flowfull-rust when you need the Rust client package in your own backend. Use flowfull-rust-starter when you want a complete Axum backend template already wired with Flowfull concepts.

Status

Available - official Rust crate plus production-shaped starter with Axum, SQLx, Bridge validation, role middleware, and PASETO Trust token helpers.

Install the crate:

toml
flowfull = "0.1"

Or clone the starter:

bash
git clone https://github.com/pubflow/flowfull-rust-starter.git my-backend
cd my-backend
cp .env.example .env
cargo run

The Rust starter depends on the official flowfull crate and keeps the same Flowfull model: Bridge Validation, auth middleware, portable database patterns, and custom backend routes.

Included / Expected Features

  • Rust 1.70+ - Modern Rust with async/await
  • Actix-web or Axum - Fast, safe web framework
  • Diesel or SeaORM - Type-safe database abstraction
  • All 7 Core Concepts - Bridge Validation, HybridCache, Trust Tokens, etc.
  • Memory Safety - Leveraging Rust's ownership system
  • Zero-Cost Abstractions - Maximum performance
  • Async/Await - Tokio runtime

Starter Structure

flowfull-rust-starter/
├── src/
│   ├── lib/
│   │   ├── bridge/           # Bridge Validation
│   │   ├── cache/            # HybridCache
│   │   ├── auth/             # Auth Middleware
│   │   ├── database/         # Database
│   │   ├── tokens/           # Trust Tokens (PASETO)
│   │   └── config/           # Configuration
│   ├── routes/               # API Routes
│   │   ├── auth.rs
│   │   └── users.rs
│   └── main.rs               # Entry Point
├── migrations/               # Database Migrations
├── tests/
├── .env.example
├── Cargo.toml
└── README.md

Technology Stack

TechnologyPurposeVersion
RustLanguage1.70+
Actix-web/AxumWeb FrameworkLatest
Diesel/SeaORMORMLatest
tokioAsync RuntimeLatest
redisRedis ClientLatest
rusty-pasetoTrust TokensLatest
serdeSerializationLatest

Core Concepts (Preview)

1. Bridge Validation

rust
use serde::{Deserialize, Serialize};
use reqwest::Client;

#[derive(Debug, Clone)]
pub struct BridgeValidator {
    flowless_url: String,
    bridge_secret: String,
    client: Client,
}

#[derive(Debug, Serialize)]
pub struct ValidationOptions {
    pub ip: Option<String>,
    pub user_agent: Option<String>,
    pub device_id: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct Session {
    pub user_id: String,
    pub email: String,
}

impl BridgeValidator {
    pub fn new(flowless_url: String, bridge_secret: String) -> Self {
        Self {
            flowless_url,
            bridge_secret,
            client: Client::new(),
        }
    }
    
    pub async fn validate_session(
        &self,
        session_id: &str,
        opts: ValidationOptions,
    ) -> Result<Session, Box<dyn std::error::Error>> {
        let response = self.client
            .post(format!("{}/api/bridge/validate", self.flowless_url))
            .json(&serde_json::json!({
                "session_id": session_id,
                "bridge_secret": &self.bridge_secret,
                "ip": opts.ip,
                "user_agent": opts.user_agent,
                "device_id": opts.device_id,
            }))
            .send()
            .await?;
        
        let session: Session = response.json().await?;
        Ok(session)
    }
}

2. HybridCache

rust
use std::sync::Arc;
use tokio::sync::RwLock;
use redis::AsyncCommands;

pub struct HybridCache {
    redis: redis::Client,
    lru: Arc<RwLock<lru::LruCache<String, Vec<u8>>>>,
}

impl HybridCache {
    pub fn new(redis_url: &str, max_size: usize) -> Result<Self, redis::RedisError> {
        Ok(Self {
            redis: redis::Client::open(redis_url)?,
            lru: Arc::new(RwLock::new(lru::LruCache::new(max_size))),
        })
    }
    
    pub async fn get<F, T>(
        &self,
        namespace: &str,
        key: &str,
        fetcher: F,
    ) -> Result<T, Box<dyn std::error::Error>>
    where
        F: FnOnce() -> Result<T, Box<dyn std::error::Error>>,
        T: serde::Serialize + serde::de::DeserializeOwned,
    {
        let cache_key = format!("{}:{}", namespace, key);
        
        // Layer 1: LRU
        {
            let mut lru = self.lru.write().await;
            if let Some(cached) = lru.get(&cache_key) {
                return Ok(serde_json::from_slice(cached)?);
            }
        }
        
        // Layer 2: Redis
        let mut conn = self.redis.get_async_connection().await?;
        if let Ok(cached) = conn.get::<_, Vec<u8>>(&cache_key).await {
            let mut lru = self.lru.write().await;
            lru.put(cache_key.clone(), cached.clone());
            return Ok(serde_json::from_slice(&cached)?);
        }
        
        // Layer 3: Database
        let value = fetcher()?;
        let serialized = serde_json::to_vec(&value)?;
        
        // Store in Redis
        let _: () = conn.set_ex(&cache_key, &serialized, 300).await?;
        
        // Store in LRU
        let mut lru = self.lru.write().await;
        lru.put(cache_key, serialized);
        
        Ok(value)
    }
}

3. Auth Middleware (Actix-web)

rust
use actix_web::{dev::ServiceRequest, Error, HttpMessage};
use actix_web_httpauth::extractors::bearer::BearerAuth;

pub async fn require_auth(
    req: ServiceRequest,
    credentials: BearerAuth,
) -> Result<ServiceRequest, Error> {
    let validator = req.app_data::<BridgeValidator>()
        .ok_or_else(|| actix_web::error::ErrorInternalServerError("Validator not found"))?;
    
    let session = validator
        .validate_session(
            credentials.token(),
            ValidationOptions {
                ip: req.peer_addr().map(|addr| addr.ip().to_string()),
                user_agent: req.headers()
                    .get("user-agent")
                    .and_then(|h| h.to_str().ok())
                    .map(String::from),
                device_id: None,
            },
        )
        .await
        .map_err(|_| actix_web::error::ErrorUnauthorized("Invalid session"))?;
    
    req.extensions_mut().insert(session.user_id);
    Ok(req)
}

Repository

Resources

Support