Architecture
RustBaas is split into one workspace with eight Rust crates plus a SvelteKit dashboard. The crate boundaries are real boundaries — each one has a single responsibility and a one-direction dependency graph.
Crate map
| Crate | Purpose |
|---|---|
rustbase-core | IO-free domain types: RealmId, AppId, Record, Schema, FilterNode, ConfigPolicy, error enum, filter parser |
rustbase-db | SQLite layer: system / realm / app pools, migrations, CRUD, filter → SQL, cascade-delete, auto-clamp engine |
rustbase-auth | JWT, argon2, OAuth2, OTP, master / realm / app admin model |
rustbase-realtime | In-process pub/sub broker |
rustbase-storage | Local + S3 file storage via object_store |
rustbase-runtime | Embedded JS/TS runtime, hook dispatch, sandboxing |
rustbase-api | axum handlers (REST, SSE, WebSocket), ApiError → IntoResponse |
rustbase-server | Binary: config, bootstrap, setup wizard, embedded dashboard |
Dependency rules
rustbase-coredepends on no IO crate — notokio, nosqlx, noaxum.- Every other crate depends on
rustbase-core. rustbase-apidepends onrustbase-db,rustbase-auth,rustbase-realtime,rustbase-storage,rustbase-runtime.rustbase-serverdepends onrustbase-api.
That keeps the domain types testable with cargo test -p rustbase-core --no-default-features and prevents accidental coupling.
Request flow
┌─────────────┐ HTTP ┌──────────────┐
│ client │──────────│ rustbase-api │
└─────────────┘ │ (axum) │
└──────┬───────┘
│
┌───────────────────┼──────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌────────────────┐ ┌──────────────────┐
│ rustbase-db │ │ rustbase-auth │ │ rustbase-runtime │
│ (sqlx + SQLite) │ │ (JWT, hash) │ │ (QuickJS) │
└────────┬────────┘ └────────────────┘ └──────────┬───────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ data/*.db │ │ data/hooks/... │
└─────────────────┘ └──────────────────┘Lifecycle hooks publish to the realtime broker after a successful DB write; SSE/WebSocket subscribers consume from the same broker.
Bootstrap sequence
On server start, rustbase-server does the following in order:
- Load config (file + env vars).
- Verify
data/exists (create if missing). - Open the system pool (
data/system.db), run system migrations. - If no master realm exists, create it. If no master admin exists, mark the server as uninitialized and serve only the setup wizard at
/_/setup. - Discover existing realms under
data/realms/and run pending realm migrations for each. - For each realm, discover existing apps and run pending app migrations.
- Initialize the realm and app pool managers (LRU caps).
- Initialize the realtime broker.
- Initialize the storage backend.
- Initialize the JS/TS runtime; load hooks for each
(realm, app). - Optionally start Litestream sidecars.
- Start the axum HTTP server, layered with the setup gate and the trace middleware.
Filter parser
rustbase-core contains a nom-based parser that produces a FilterNode AST:
pub enum FilterNode {
And(Box<FilterNode>, Box<FilterNode>),
Or(Box<FilterNode>, Box<FilterNode>),
Not(Box<FilterNode>),
Eq(String, Value),
Gt(String, Value),
// ...
}The parser is IO-free and dialect-agnostic. rustbase-db translates a FilterNode into a parameterized SQL WHERE clause via filter_to_sql — no string interpolation of user input, every literal becomes a bound parameter.
The same AST is reused by:
- The dashboard for client-side validation.
- The per-collection access-rules engine.
- The JS/TS hooks API for
$app.records.findRecordsByFilter(...).
Error handling
One error enum per crate, thiserror-derived. Boundaries map foreign errors into the local enum:
rustbase-dbmapssqlx::Error→CoreError.rustbase-apimapsCoreError→ApiErrorwhich implementsIntoResponse.
The HTTP layer returns a uniform {code, message} JSON shape for every error, mapped to a sensible status code (404 for not-found, 403 for forbidden, etc.).
Testing
- Unit tests live in-file under
#[cfg(test)] mod tests. - Integration tests live in
tests/per crate. - All DB tests use
sqlite::memory:— fresh DB per test, no Docker, sub-second suite. - A shared test suite in
rustbase-db/src/testing.rsexercises every public DB operation; CI runs it against both:memory:and a temp file to catch durability/WAL issues. - Auto-clamp behavior has a dedicated property test suite.
CI runs cargo fmt --check, cargo clippy -D warnings, the full test suite, and an architecture grep (no unwrap/expect outside tests; rustbase-core stays IO-free) on every PR.