Skip to main content
CoreSDK
Getting Started

Rust Quickstart

Embed coresdk-engine directly in your Rust service — no sidecar required.

Rust Quickstart

coresdk-engine embeds the full engine in-process. Auth, Rego policy evaluation, PII masking, and OTel tracing all run inside your Rust service — no sidecar, no network hop.

Install

cargo add coresdk-engine tokio --features tokio/full
cargo add axum serde serde_json tracing tracing-subscriber

Cargo.toml:

[dependencies]
coresdk-engine = "0.1"
tokio  = { version = "1", features = ["full"] }
axum   = "0.7"
serde  = { version = "1", features = ["derive"] }
serde_json = "1"
tracing = "0.1"

Initialize the engine

use std::sync::Arc;
use coresdk_engine::Engine;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt::init();

    // Initialize from environment variables
    let engine = Arc::new(Engine::from_env()?);

    // ...
    Ok(())
}
Env varDefaultDescription
CORESDK_JWKS_URLRemote JWKS URL for real JWT verification
CORESDK_TENANT_IDdefaultDefault tenant if not in JWT
CORESDK_SERVICE_NAMEaxum-appOTel service name
CORESDK_FAIL_MODEopenopen = allow on error · closed = deny
CORESDK_POLICY_POOL_SIZEmin(cpus, 8)Rego engine pool size

Auth middleware (Tower / Axum)

use axum::{
    extract::{Request, State},
    http::{HeaderMap, StatusCode},
    middleware::{self, Next},
    response::{IntoResponse, Response},
};
use coresdk_engine::{Engine, auth::decision::AuthRequest, error::ProblemDetail};
use std::sync::Arc;

#[derive(Clone)]
struct AppState {
    engine: Arc<Engine>,
}

/// Tower middleware: validates JWT, injects user claims as a request extension.
async fn auth_middleware(
    State(state): State<AppState>,
    headers: HeaderMap,
    mut request: Request,
    next: Next,
) -> Response {
    let token = headers
        .get("authorization")
        .and_then(|v| v.to_str().ok())
        .and_then(|v| v.strip_prefix("Bearer "))
        .unwrap_or("");

    if token.is_empty() {
        return ProblemDetail::unauthorized("Missing Bearer token")
            .into_response();
    }

    match state.engine.auth().authorize(AuthRequest {
        token: token.to_string(),
        ..Default::default()
    }) {
        Ok(decision) if decision.allowed => {
            request.extensions_mut().insert(decision.claims);
            next.run(request).await
        }
        Ok(_) => ProblemDetail::unauthorized("Token rejected").into_response(),
        Err(e) => ProblemDetail::unauthorized(e.to_string()).into_response(),
    }
}

Full Axum app

use axum::{Router, routing::get, extract::State, Json};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt::init();

    let state = AppState {
        engine: Arc::new(Engine::from_env()?),
    };

    let app = Router::new()
        .route("/healthz", get(healthz))
        .route("/me",      get(me))
        .route("/products", get(list_products))
        .layer(middleware::from_fn_with_state(state.clone(), auth_middleware))
        .with_state(state);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
    tracing::info!("listening on :3000");
    axum::serve(listener, app).await?;
    Ok(())
}

async fn healthz() -> Json<serde_json::Value> {
    Json(serde_json::json!({"status": "ok"}))
}

async fn me(
    axum::extract::Extension(claims): axum::extract::Extension<serde_json::Value>,
) -> Json<serde_json::Value> {
    Json(claims)
}

async fn list_products(
    axum::extract::Extension(claims): axum::extract::Extension<serde_json::Value>,
    State(state): State<AppState>,
) -> Json<serde_json::Value> {
    let tenant = claims["tenant_id"].as_str().unwrap_or("default");
    Json(serde_json::json!({
        "tenant": tenant,
        "products": [],
    }))
}

Role guard

fn check_role(claims: &serde_json::Value, role: &str) -> Result<(), Response> {
    let roles = claims["roles"].as_array().map(|a| {
        a.iter().filter_map(|r| r.as_str()).collect::<Vec<_>>()
    }).unwrap_or_default();

    if !roles.contains(&role) {
        return Err(ProblemDetail::forbidden(
            format!("Role '{}' required", role)
        ).into_response());
    }
    Ok(())
}

async fn create_product(
    axum::extract::Extension(claims): axum::extract::Extension<serde_json::Value>,
    Json(body): Json<serde_json::Value>,
) -> Response {
    if let Err(r) = check_role(&claims, "editor") { return r; }
    Json(serde_json::json!({"created": body})).into_response()
}

Rego policy evaluation (ABAC)

The engine pool (N regorus::Engine instances, one per blocking thread) evaluates policies without mutex contention:

use coresdk_engine::policy::decision::PolicyInput;

async fn get_document(
    axum::extract::Extension(claims): axum::extract::Extension<serde_json::Value>,
    Path(doc_id): Path<String>,
    State(state): State<AppState>,
) -> Response {
    let tenant = claims["tenant_id"].as_str().unwrap_or("default").to_string();
    let subject = claims["sub"].as_str().unwrap_or("").to_string();
    let roles: Vec<String> = claims["roles"]
        .as_array().unwrap_or(&vec![])
        .iter().filter_map(|r| r.as_str().map(String::from)).collect();

    let engine = state.engine.clone();
    let doc_id_clone = doc_id.clone();

    // spawn_blocking: regorus eval is sync + CPU-bound
    let allowed = tokio::task::spawn_blocking(move || {
        engine.policy().evaluate("data.authz.allow", PolicyInput {
            tenant_id: tenant,
            subject,
            action: "read".into(),
            resource: format!("documents/{}", doc_id_clone),
            context: serde_json::json!({"roles": roles}),
        })
    }).await.unwrap_or(Ok(false)).unwrap_or(false);

    if !allowed {
        return ProblemDetail::forbidden("Access denied").into_response();
    }

    Json(serde_json::json!({"id": doc_id, "content": "..."})).into_response()
}

RFC 9457 errors

ProblemDetail serializes to application/problem+json:

use coresdk_engine::error::ProblemDetail;
use axum::response::IntoResponse;

// Helpers
ProblemDetail::unauthorized("Missing Bearer token").into_response()
ProblemDetail::forbidden("Role 'admin' required").into_response()
ProblemDetail::not_found("Product 42 not found").into_response()

Response shape:

{
  "type":   "https://coresdk.io/errors/unauthorized",
  "title":  "Unauthorized",
  "status": 401,
  "detail": "Missing Bearer token"
}

Run

RUST_LOG=info cargo run

# Health check
curl http://localhost:3000/healthz

# With JWT (fail-open without JWKS configured)
curl -H "Authorization: Bearer my-token" http://localhost:3000/me

Full working example

rust/axum-app/ — complete project with auth middleware, policy pool, ABAC route, RFC 9457 errors, OTel tracing, Docker.

On this page