The full-stack .NET web framework
Data, queries, auth, background jobs, email, realtime and deploy — one framework and one C# codebase, for a team of one or fifty.
Declare an aggregate, query it from a page, ship it with rask deploy. The UI is C# components, live over a WebSocket or running in WebAssembly — with React, Vue or Angular components inside them as islands.
// A table, its columns and a generated read face.
// No DbSet, no configuration class, no registration.
public sealed class Product
: Aggregate<Guid, Timestamps, Versioned, Model, Deletable>
{
[MaxLength(200)]
public string Name { get; private set; } = "";
[Range(0, 1_000_000)]
public decimal Price { get; private set; }
public bool InStock { get; private set; } = true;
}The whole stack · one codebase
Data, queries, auth, jobs, email, cache, files, realtime, tenants and search ship with the framework and ride your app's own database — no broker to stand up, no Redis to run, and a Redis you already have plugs in as the cache. They are built on the standard .NET pieces — EF Core, hosted services, ILogger, IDistributedCache — so each is a package reference, not a new box to operate.
Derive from Aggregate<Guid, Timestamps, Versioned> and the class is the table — its base names what it carries (timestamps, a version, soft delete, a tenant), plus a generated read face to query. EF Core on SQLite, PostgreSQL or SQL Server.
QueryClient.Query(…) in Render — cached, deduplicated, refetched after a write. Source-generated CQRS underneath, and the same call from the browser or the server.
Register, sign in, passkeys and a session row per device with no auth code — sign-in pages scaffolded into your app, the first account made admin. OIDC providers when you need them.
Enqueued, delayed and recurring work stored in your database and run by a hosted worker — at-least-once, with exponential backoff.
Mail queued on the same database and delivered over SMTP off the request thread; the bodies are components.
Each handler chooses: in memory at once, or durable — its outbox row committed in the same transaction as the data, then run at-least-once. Crash-safe, no message broker.
A database-backed IDistributedCache plus Cache.Remember(key, load).For(10.Minutes) — or point it at the Redis you already run.
Rask.Storage keeps uploads on disk, in S3-compatible storage or in Azure Blob, with a row per file — public or expiring links, and the content type sniffed from the bytes.
QueryClient.Subscribe<OrderPlaced>() in a page, and every OrderPlaced published afterwards re-renders it — narrowed by a record, opened only through a policy.
Send Web Push from your backend on your own VAPID keys (RFC 8292/8291) — zero external dependencies.
PerTenant on a table adds the tenant column, the query filter and tenant-prefixed indexes; Tenant.Across() is the one greppable way around them.
Product.Search("red anvil") — ranked, best match first, with highlights and snippets, on SQLite FTS5 or PostgreSQL.
A dashboard at /_rask over every battery's own table — queue depth, dead letters and the errors behind them, cache, a log tail, SQLite status. Fail-closed behind an authorization policy.
rask new scaffolds a working app with data, auth and the batteries wired; rask dev runs it with hot reload; rask db migrates and backs up.
rask deploy takes a bare VPS to a live HTTPS site — Docker, a non-root deploy user, firewall + SSH hardening, and zero-downtime swaps.
Frontend · C# components
Components are plain C# classes — no .razor, no JavaScript, no second language. Roslyn source generators build each one's chain and typed route URLs, trim-safe and reflection-free.
[Route("/counter")]
public sealed partial class Counter : Component
{
private int _count;
protected override Component? Render() =>
Button.OnClick(() => _count++)[$"Current count: {_count}"];
}Current count
A chain surface per component — Card.Title(…) — that demands what the component can't do without, plus type-safe Routes.* URL builders. Rename a route, break the build — never a dead link.
Form.Model(m) with two-way binding and inline .Validate(…) rules, sync or async. DataAnnotations and FluentValidation work too, checked again on the server.
Drop a sibling {Component}.css/.ts. Auto-scoped, no leaks — a mismatch is a build error. Tailwind v4 compiles from dotnet build, with no npm and no config file.
Every daisyUI component as a C# component — Ui.Button, Ui.DataGrid, Ui.Tree — accessible and themed, with no npm and no Tailwind config.
IndexedDB, passkeys, WebRTC with its signaling, view transitions and background sync as awaitable C# — and every other web API straight from MDN through Rask.Web. Serial, USB, HID and Bluetooth pickers are WebAssembly's.
A typed manifest, a default service worker, offline and background sync — the wasm template is installable out of the box.
Edit Render() or scoped css/js under rask dev and it re-renders live — the closest a compiled framework gets to a no-build loop.
A WASM app renders every route to real HTML at publish, so a crawler is served the page rather than a spinner. On the server, every page is live, and its first response waits for its data.
A missing required step, a public setter on an aggregate, an image with no alt text — each is a RASK error at build time that names the fix, several with an IDE quick-fix.
One component model · every host
The identical C# component runs unchanged across every host — you choose the runtime per project, not per component.
ASP.NET host. State lives on the server; a live diff streams to the browser over a WebSocket. Nothing to compile client-side.
AddRask() · MapRask<TApp>()The same component runs fully client-side on the browser's Mono/WASM runtime via JSImport/JSExport. Ships as an installable, offline PWA.
WasmHostBuilder.CreateDefault()Serves a Rask WebAssembly app from the ASP.NET host that answers its API, cached by what its publish guarantees, with pre-compressed variants.
AddRaskSpaHost() · MapRaskSpa()Three front ends · one back end
Rask is a superset, not a rival: React, Preact, Solid, Vue, Svelte, Angular and Lit components, and real Blazor components, run inside C# pages as islands, against the same C# back end — mixed freely in one component tree.
C# components server-rendered over a WebSocket, every state change streaming as a minimal diff. Pick -t wasm-hosted and the same components publish as a WebAssembly bundle the host serves, out of the same project.
A .tsx, .vue, .svelte or Lit file as an ordinary Rask component — props declared in C#, callbacks re-entering C#, and the live diff leaving the subtree to its own renderer.
A real Blazor component — MudBlazor, Radzen, your own Razor Class Library — as an ordinary Rask component, rendered into the first response with no Blazor circuit.
class Chart : BlazorComponent<MudChart>Prerequisite · .NET 10 SDK
ASP.NET live-server app, batteries includedcurl -sSL https://rask.sh/rask.sh | shrask new MyAppcd MyApp && rask devNothing preinstalled — it adds the .NET SDK too, under $HOME, no sudo. Windows: irm https://rask.sh/rask.ps1 | iex.
Accounts and cookie sign-in are already on · full path in the getting-started guide.