Roadmap — the full-stack pillars
Rask's north star is a full-stack .NET web framework for a team of one or fifty: UI, data, auth, background work, realtime and deploy from a single C# codebase, and one developer can still ship it alone on one server (the philosophy). This page tracks the pillars that serve that goal — what's shipped and what's next. The through-line for everything stateful is DB-backed by default: it rides the app's own SQLite database, so adding a capability is a package reference, not a new service to operate.
Shipped
✅ shipped · ◐ partly, with documented limits · ❌ not shipped
| Pillar | Status | Where |
|---|---|---|
| UI across two hosts | ✅ | Server (WebSocket live diff) and WASM (client-side + PWA) — one component. |
The rask CLI |
✅ | cli.md — new / dev / db / deploy. |
| CRUD pattern | ✅ | A CQRS + EF Core vertical slice — encapsulated entity, validation, pages — documented as code in tutorial chapter 2, with jobs, email and cache following the same shape. |
| CQRS / mediator | ✅ | Rask.Cqrs — source-generated, reflection-free. |
| Data layer | ✅ | Rask.Data — Aggregate<TId> and Entity<TId> + interceptors (audit, opt-in soft delete, concurrency, domain events), generated read faces and form models, and Current for the signed-in user with nothing injected. |
| Query cache | ✅ | Rask.Query — TanStack Query's model over the CQRS dispatcher: dedup, staleness, background refetch, commands through Command<T>, and a write through Rask.Data refreshing the queries about what it wrote. |
| Realtime subscriptions | ◐ | subscriptions.md — a published CQRS event re-renders every subscribed page, narrowed by a record and admitted by a watch policy, from WebAssembly over server-sent events. One process: events do not yet cross between processes or hosts (below). |
| Multi-tenancy | ✅ | multi-tenancy.md — one trait, PerTenant, partitions a table by tenant: a TenantId on the root, a query filter nothing composes away, children read through their root, the tenant from the signed-in user; jobs, mail, outbox, cache, storage and accounts honour it. |
| Full-text search | ✅ | full-text-search.md — HasFullTextSearch + Search(text), ranked and diacritic-insensitive, on SQLite (FTS5), in the browser and on PostgreSQL (tsvector + GIN). |
| Transactional outbox | ✅ | Rask.Outbox — durable, crash-safe domain-event delivery on the app's own database. |
| Background jobs | ✅ | Rask.Jobs — durable enqueued/delayed/recurring work on the app's own database, at-least-once with backoff. |
| Transactional email | ✅ | Rask.Mail — durable email queued on the app's own database, delivered off the request thread over SMTP; bodies are Rask components. |
| Cache | ✅ | Rask.Cache — a developer-facing cache on the app's own database; standard IDistributedCache plus a typed ICache with Cache.Remember(…).For(…), absolute/sliding expiry. |
| File storage | ✅ | Rask.Storage — uploads kept on disk, in an S3-compatible bucket or in Azure Blob, with a StoredFile row per file on the app's own database; content types sniffed from the bytes, public and temporary URLs, downloads behind your own check. Files on the disk provider are not yet backed up. |
| Production SQLite | ✅ | sqlite.md — WAL/busy-timeout pragmas, continuous backup (Litestream), snapshots. |
| The door out of one box | ❌ | Not shipped — the PostgreSQL and SQL Server providers exist, but rask new starts on SQLite and rask deploy provisions SQLite only (below). Jobs, mail and the outbox do lease the work they claim (scaling.md), so the claim is safe when several processors race and a lease bounds, but does not eliminate, a duplicate side effect. See below. |
| Auth — sign-in | ✅ | authentication.md — the cookie session, claims, authorization, and hardening guidance. |
| Auth — user store | ✅ | Accounts on the app's own User aggregate, with a revocable session row per device, on by default. Register, sign in and sign out work in a fresh app with no auth code; the first account to register is the administrator. Email confirmation, password reset, passkeys and sign-in throttling ship on; MFA beyond passkeys and external sign-in providers are not shipped yet. |
| Web Push (server send) | ✅ | webpush.md — Rask.WebPush: VAPID (RFC 8292) + aes128gcm (RFC 8291), zero deps. |
| Deploy to one box | ✅ | rask deploy — bare-VPS setup (Docker, deploy login, firewall, SSH hardening), build over SSH, zero-downtime, auto-HTTPS (Caddy), multi-app on one host, GitHub Actions. |
| Dead letters & queue health | ✅ | dashboard.md — Rask.Dashboard mounts /_rask over the outbox, jobs, mail and cache: queue depth, what has given up, the error behind it, and one click to retry. Plus the log (a live tail, and searchable history with Rask.Logging) and the live SQLite pragmas. Fail-closed behind an authorization policy. |
| Logs that survive a restart | ✅ | logging.md — Rask.Logging keeps the ILogger pipeline in a database of its own: buffered off the request thread, batched to disk, retention by age and row count, searchable from /_rask. |
| Operate what you shipped | ✅ | rask deploy status / logs / rollback — what's running, its logs, and putting the previous image back. |
| Continuous backup | ✅ | sqlite.md — rask new wires Litestream; one variable at deploy time turns it on. |
| Secrets | ◐ | secrets.md — environment variables, remembered by name so a redeploy can't drop one. No vault, rotation, or encryption at rest. |
Planned — DB-backed pillars
Each of these is designed to persist to the same SQLite database the app already has — no external broker, no Redis, no separate infrastructure for a hello-world. Ordered by leverage for shipping a product.
Cache — render/fragment cache
The developer-facing cache has shipped. Still planned: a render/fragment cache reusing the framework's existing subtree-cache machinery, to memoize a component subtree across sessions by an explicit key.
Subscriptions — beyond one process
Subscriptions have shipped: a published event reaches every subscribed component, scoped by key and admitted by a watch policy, and from WebAssembly over server-sent events. They reach the process that published them. Still planned: carrying events between processes and hosts — the blue and green containers of a deploy, and the instances behind a load balancer — and resuming a dropped stream from the last event it saw rather than from the latest value.
Not shipped
Listed because a roadmap that only says what exists isn't much use when you're deciding whether Rask fits. None of these has an implementation today — if your product needs one, you'll be writing or renting it.
The rest of the account lifecycle
Accounts themselves shipped: registration, sign-in, sign-out, password hashing (PBKDF2 or
bcrypt), throttling, revocable sessions, email confirmation, password reset, and passkeys — Rask stores the
credential and verifies WebAuthn itself, on the base class library, with no FIDO library. All on by default on the
app's own User.
What is not here yet is MFA beyond what a passkey already is (TOTP codes, recovery codes) and external providers (Google, GitHub) as a battery — an OpenID Connect provider composes by hand today (providers). Each is an addition rather than a redesign — the sealed-token and session machinery those flows need is already here — but none of them exists today.
Another database
The provider packages have shipped: Rask.Postgres and
Rask.SqlServer, and Rask:Database:Provider (sqlite, postgres or sqlserver)
picks between them for an app that opens its context with UseRaskDatabase(sp) — see
Choosing the database. What is not here yet is the tooling around them:
rask new has no database choice — a scaffold starts on SQLite, and moving it is Rask:Database:Provider
and a connection string in appsettings.json, plus migrations generated against the new provider; rask deploy points
Rask:ConnectionStrings:App at a SQLite file on its volume, so a server-database app is deployed another
way for now. On PostgreSQL or SQL Server you also give up everything that treats the database as a file —
Litestream, snapshots, rask db backup. See Scaling for where the single-writer wall
actually is.
Offline-first sync
No CRDT replication, no op log. Several devices sharing one database with no server between them is not something Rask does. (Files uploaded to the server are a different matter and have shipped: kept on disk, in an S3-compatible bucket or in Azure Blob — though files on the disk provider are not yet covered by any backup.)
Rate limiting
No general request rate limiting in the framework. The accounts battery does throttle what it owns — failed
sign-ins per address and client (SignInAttemptsPerMinute), registrations and reset requests — without ever
locking an account (authentication.md). Everything else is
yours: the docs point you at a reverse-proxy rate limit in several places, and rask deploy provisions a stock
Caddy, which can't do it without a plugin — so today that means adding one yourself, or a WAF in front.
Secrets beyond environment variables
See secrets.md for what does exist and, at the bottom, a blunt list of what doesn't.
Horizontal scale
No rask deploy --replicas. Not an oversight, and not a "later" — it would be a flag that is wrong for
the shape of app this framework tells you to build. Every DB-backed pillar writes to the app's own SQLite
database, and SQLite runs one writer, so a second replica of a normal
Rask app is a corruption risk rather than a scaling step.
What one box actually does is measured, not asserted — sessions held and events served,
both reproducible from reports in this repo. Read that before assuming you need a second box; the usual
answer is a smaller page. If you genuinely do outgrow it, the door is a client-server database, and
scaling.md covers what else would have to change (uploads, downloads and sign-in redeem still require
sticky routing; sessions no longer do).
Every pillar above is built up one chapter at a time in the tutorial, into a
single app you scaffold with rask new.
Want to shape the direction? The framework is developed in the open — see the development workflow.