# 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](https://rask.sh/docs/guides/one-person-framework/)). 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](#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`](https://rask.sh/docs/guides/cli/) — `new` / `dev` / `db` / `deploy`. |
| **CRUD pattern** | ✅ | A CQRS + EF Core vertical slice — encapsulated entity, validation, pages — documented as code in [tutorial chapter 2](https://rask.sh/docs/guides/02-first-feature/), with jobs, email and cache following the same shape. |
| **CQRS / mediator** | ✅ | [`Rask.Cqrs`](https://rask.sh/docs/guides/cqrs/) — source-generated, reflection-free. |
| **Data layer** | ✅ | [`Rask.Data`](https://rask.sh/docs/guides/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`](https://rask.sh/docs/guides/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`](https://rask.sh/docs/guides/subscriptions/) — 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](#subscriptions--beyond-one-process)). |
| **Multi-tenancy** | ✅ | [`multi-tenancy.md`](https://rask.sh/docs/guides/multi-tenancy/) — 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`](https://rask.sh/docs/guides/full-text-search/) — `HasFullTextSearch` + `Search(text)`, ranked and diacritic-insensitive, on SQLite (FTS5), in the browser and on PostgreSQL (`tsvector` + GIN). |
| **Transactional outbox** | ✅ | [`Rask.Outbox`](https://rask.sh/docs/guides/outbox/) — durable, crash-safe domain-event delivery on the app's own database. |
| **Background jobs** | ✅ | [`Rask.Jobs`](https://rask.sh/docs/guides/jobs/) — durable enqueued/delayed/recurring work on the app's own database, at-least-once with backoff. |
| **Transactional email** | ✅ | [`Rask.Mail`](https://rask.sh/docs/guides/mail/) — durable email queued on the app's own database, delivered off the request thread over SMTP; bodies are Rask components. |
| **Cache** | ✅ | [`Rask.Cache`](https://rask.sh/docs/guides/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`](https://rask.sh/docs/guides/file-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`](https://rask.sh/docs/guides/sqlite/) — 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](#another-database)). Jobs, mail and the outbox do **lease** the work they claim ([`scaling.md`](https://rask.sh/docs/guides/scaling/#running-more-than-one-instance)), so the claim is safe when several processors race and a lease bounds, but does not eliminate, a duplicate side effect. See [below](#not-shipped). |
| **Auth — sign-in** | ✅ | [`authentication.md`](https://rask.sh/docs/guides/authentication/) — 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](#not-shipped) yet. |
| **Web Push (server send)** | ✅ | [`webpush.md`](https://rask.sh/docs/guides/webpush/) — `Rask.WebPush`: VAPID (RFC 8292) + aes128gcm (RFC 8291), zero deps. |
| **Deploy to one box** | ✅ | [`rask deploy`](https://rask.sh/docs/guides/cli/) — 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`](https://rask.sh/docs/guides/dashboard/) — `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`](https://rask.sh/docs/guides/logging/)) and the live SQLite pragmas. Fail-closed behind an authorization policy. |
| **Logs that survive a restart** | ✅ | [`logging.md`](https://rask.sh/docs/guides/logging/) — `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`](https://rask.sh/docs/guides/cli/) — what's running, its logs, and putting the previous image back. |
| **Continuous backup** | ✅ | [`sqlite.md`](https://rask.sh/docs/guides/sqlite/) — `rask new` wires [Litestream](https://rask.sh/docs/guides/sqlite/#continuous-backup-with-litestream); one variable at deploy time turns it on. |
| **Secrets** | ◐ | [`secrets.md`](https://rask.sh/docs/guides/secrets/) — 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](https://rask.sh/docs/guides/cache/). 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](https://rask.sh/docs/guides/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](https://rask.sh/docs/guides/authentication/): 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](https://rask.sh/docs/guides/authentication-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`](https://rask.sh/docs/guides/data/#postgresql) and
[`Rask.SqlServer`](https://rask.sh/docs/guides/data/#sql-server), 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](https://rask.sh/docs/guides/data/#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](https://rask.sh/docs/guides/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](https://rask.sh/docs/guides/file-storage/): 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](https://rask.sh/docs/guides/authentication/#passwords-and-throttling)). 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`](https://rask.sh/docs/guides/secrets/) 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](https://rask.sh/docs/guides/sqlite/#in-a-docker-container), 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](https://rask.sh/docs/guides/scaling/) — 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](https://rask.sh/docs/guides/00-overview/), 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](https://rask.sh/docs/guides/development-workflow/).
