This version is in beta. Some features may change before release.

What is Umbral?

A batteries-included web framework for Rust.

Umbral is a batteries-included web framework for Rust. You declare your data and you get migrations, CRUD, an admin, and an optional REST API almost for free, with Rust's compile-time guarantees instead of runtime hope.

The name is the adjective 'of the shadow' (from Latin umbra, shadow).

Info

Umbral is early/alpha and published on crates.io - start from the umbral facade. It's usable today: declare a model, run managed migrations, port an existing database via inspectdb, and wire the built-in plugins - auth, sessions, permissions, the auto admin (with dashboard widgets + custom views), REST + OpenAPI, background tasks, realtime (SSE + WebSockets), OAuth, caching, email, storage/media, multitenancy, health checks, request logs, analytics, and more. Render HTML through the templates substrate and drive it all through the project's cargo run -- CLI (plus the global umbral scaffolding binary from cargo install umbral-cli). It's 0.0.x, so APIs still move before 1.0. Start at Your first app.

Why Umbral exists

Web development in Rust is unassembled. Unlike Rails, there's no mainstream "just works" path. Every team picks and wires a web framework, a database layer, a migration tool, serialization, auth, and background jobs.

Two things suffer:

  • Greenfield productivity. A basic CRUD/REST API takes days of plumbing before you write any real code.
  • Porting. Moving an existing API to Rust means re-deriving the schema, the models, and the conventions by hand. There's no inspectdb, no managed migrations, no obvious on-ramp.

Umbral targets that on-ramp.

The shape

Thin core, plugin-heavy

Auth, sessions, admin, tasks, and REST are plugins. Structurally they're identical to third-party ones. A REST-free app compiles with zero serializer code.

Managed migrations from day one

Declare or change a model; an autodetected migration is generated; migrate applies it. The declare → migrate → change → migrate cycle is the product, not a later feature.

The easy path is the safe path

Nullable columns are Option<T>. Errors are Result. Backend mismatches fail at boot. SQL is always parameterized.

Stand on shoulders

axum, sqlx, sea-query, and tower do the heavy lifting. Umbral reimplements conventions and integration, not HTTP, async, SQL, or JSON.

What you write

A model is a struct. A migration is generated. A handler reads ambient data. This is the target shape:

Code
rust
use umbral::prelude::*;
 
#[derive(Model)]
pub struct Post {
pub id: i64,
pub title: String,
pub body: String,
pub published: Option<DateTime<Utc>>,
}
 
async fn list_published() -> Result<Json<Vec<Post>>> {
let posts = Post::objects()
.filter(post::PUBLISHED.is_not_null())
.order_by(post::PUBLISHED.desc())
.limit(20)
.fetch()
.await?;
Ok(Json(posts))
}

No State<DbPool> in the handler signature. No axum:: types. The ORM is ambient; the request body, params, and session are explicit arguments.

Status

Umbral is early/alpha and published on crates.io - every crate ships under the umbral-* namespace; start with the umbral facade. It's at 0.0.x: usable, but pre-1.0, so APIs will still move before things stabilize. Development happens in the open, and each feature gets a doc page here as it lands.

For the long version, read the design specs in the repository: arch.md (architecture), umbral-PRD.md (product requirements), and docs/specs/ (per-subsystem deep specs).

Roadmap

Much of the original backlog has already shipped - request logs (umbral-logs), realtime SSE and WebSockets (umbral-realtime), RTE/Markdown admin field widgets, the admin dashboard widget system + custom views, and GraphQL (umbral-graphql) all landed. A few notable items are still ahead:

WebSocket playground

An opt-in playground tab that connects to declared realtime endpoints with the same UI shape as the REST playground.

Push notifications

Web-push / device notifications layered on the realtime + tasks plugins.

Auto-SEO helpers

Fill in missing titles and image alt text automatically at render time.

For everything available today, browse the sidebar; the full punch-list lives in planning/features.md in the repository.