💡 Rullst CLI - Full Command Reference
The Command Line Interface (cargo-rullst) scaffolds projects, invokes build
tools, and provides bounded static-analysis and deployment helpers.
The CLI’s --help output is authoritative for the installed version. This page
documents the principal commands of the version 13 prerelease CLI and their
security boundaries. Cargo never installs a prerelease unless you name it, so
cargo install cargo-rullst installs the end-of-life 12.x CLI; install this one
with an exact version:
cargo install cargo-rullst --version =13.0.0-alpha.4 --locked
cargo-rullst is a Core package. Two commands carry their own Maturity
label below the command name, Experimental: make:iot and green. The
maturity tiers page explains what the label
promises.
Machine-readable output
A command whose result is data accepts --json. The contract is the same for
all of them:
- Standard output carries exactly one JSON document and nothing else: no progress lines, banners, tables or ANSI escape codes, whether or not the terminal supports colour.
- The document has a top-level
schemafield naming its shape and version,rullst.<name>.v1. A new field is added without a new version; a removed or renamed field, or a changed meaning, getsv2. Documents that predate this contract also keep theirschema_versionfield, with the same value. - Human messages, progress and failures go to standard error. Exit statuses are
the same with and without
--json: a failed check or a refused request still exits non-zero, and the document is still printed when the command could compute one (doctor,deploy:doctorandacademy:doctorprint their report and then exit with status 1 when a check fails). - A request that has no JSON form (for example
pkg add --json) is refused on standard error with a non-zero exit status and prints nothing on standard output.
| Command | schema |
|---|---|
cargo rullst info --json | rullst.cli-info.v1 |
cargo rullst doctor --json | rullst.cli-doctor.v1 |
cargo rullst audit --json | rullst.cli-audit.v1 |
cargo rullst inspect routes --json | rullst.cli-routes.v1 |
cargo rullst inspect models --json | rullst.cli-models.v1 |
cargo rullst inspect schema --json | rullst.cli-schema.v1 |
cargo rullst pkg list --json | rullst.cli-pkg.v1 |
cargo rullst upgrade --dry-run --json | rullst.upgrade-plan.v1 |
cargo rullst deploy:doctor --json | rullst.deployment-diagnostic.v1 |
cargo rullst academy:doctor --json | rullst.academy-diagnostic.v1 |
cargo rullst update project prepare --json | rullst.project-preparation-result.v1 |
cargo rullst update project verify --dry-run or --json | rullst.project-verification-plan.v1 (dry run), rullst.project-verification.v1 |
cargo rullst update project review --json | rullst.project-review-report.v1 |
cargo rullst update project apply --json and recover --json | rullst.project-application.v1 |
cargo rullst green --json (experimental) | rullst.green.v1 |
pkg list --json prints {"schema": "rullst.cli-pkg.v1", "table": "dependencies", "packages": ["rullst-auth"]}; table is
workspace.dependencies for a virtual workspace manifest. audit --report json
is a different interface: it writes SECURITY_REPORT.json
(rullst.cli-audit-report.v1) and cannot be combined with --json. The
files cargo rullst generate:ai-context writes (rullst.project-context.v1)
are described in project context. db:status is
printed by your application, not by the CLI, and has no --json form yet.
🏗️ 1. Project Initialization & Maintenance
cargo rullst (no arguments): home screen
Running the CLI without a subcommand opens the v13 home: the RULLST wordmark in a blue → green → orange gradient, the slogan line with the installed version, and a summary of where you are.
- Inside a Rullst project (the current directory or a parent has a
Cargo.tomlwhose package depends onrullst): the package name, the enabledrullstfeatures (defaultwhen default features are on), the database family and where it is configured (the processDATABASE_URL, then.env, then[database].urlinRullst.toml;TURSO_DATABASE_URLfor a Turso-primary project), the number ofsrc/migrationsfiles and the latest one, and the Git branch. Connection URLs are never displayed. The menu leads with dev, dash, scaffolding, database,doctorand deploy, keeps every project operation of earlier releases, and offers Green Report (cargo rullst green, Experimental). Started from a subdirectory, menu commands run at the project root (exceptnew). The migration count is the files on disk; runcargo rullst db:statusfor the applied state. - Outside a project: the summary links the docs (start here and this reference) and the menu leads with project creation, followed by the operations for an existing project.
Both menus include Search All Commands, a fuzzy command palette over every
command, including the ones attached at runtime (update ..., make:privacy,
deploy:doctor, completions, …). Type to filter by name, alias or
description; every word must match, names rank above descriptions, and ties go
to the shorter name, then alphabetical order. ↑/↓ (or Tab, Ctrl+P/N) move,
Enter runs the selection (asking first for its required arguments, as a menu
when the values are fixed), Esc returns to the home menu and Ctrl+C exits.
cargo rullst --help is the non-interactive equivalent.
The opening animates for about 0.7 s on every run; any key skips the
animation. RULLST_OPENING=daily animates only the first run of each day
(later runs draw the final frame instantly) and RULLST_OPENING=static never
animates. For daily, the day of the last animation is kept in the user cache
directory
($XDG_CACHE_HOME/rullst-ui-v1 or ~/.cache/rullst-ui-v1;
%LOCALAPPDATA%\rullst-ui-v1 on Windows), never in the project. If that file
cannot be read or written, the opening is static.
Colours use 24-bit RGB when COLORTERM is truecolor or 24bit and the
nearest xterm 256-colour entry otherwise. RULLST_REDUCED_MOTION=1 (or
true/yes) keeps the colours without animation. NO_COLOR prints the plain
line RULLST v<version> · SECURE, FAST AND AI-NATIVE RUST FRAMEWORK while the
menu stays interactive. When standard input, output or error is not a
terminal, CI is set or TERM=dumb, the CLI prints the plain line, the
summary and the equivalent commands, then exits successfully without
prompting.
In a terminal, at most once a day, the home (or any command, on standard
error after it finishes) prints A newer cargo-rullst is available: cargo install cargo-rullst --version =X --locked when crates.io has a newer release
of the same major; the catalog is fetched in the background at most once a day
and cached privately, and nothing is installed. RULLST_DISABLE_UPDATE_CHECK=1,
CARGO_NET_OFFLINE=true or CI=true turns it off. See
Update notice and installing a newer CLI.
Errors and exit codes
Every failure is printed on standard error by one shared report:
error: Not inside a Rullst project
What happened `cargo rullst make:model` must run at the root of a Rullst project; ...
How to fix The project root is `../..`: run `cd ../..` and try again.
Docs https://rullst.github.io/Rullst/book/start-here.html
The common failures have their own title and fix: not inside a Rullst project
(pointing to an enclosing project root when there is one), a missing tool
(cargo, rustc, rustup, git, docker, wasm-bindgen, kubectl or
npm), a missing Rust target (rustup target add <target>), an unreachable
database, a port already in
use, permission denied, invalid input, network failures, an interrupted
prompt and a starter that moved to the repository’s examples (new --blueprint lms|blog|portfolio|erp). Anything else gets a generic report naming the failed command.
Connection-string passwords (up to the host, even when they contain an
unencoded @), *_KEY/*_SECRET/*_TOKEN/password values (also under the
quoted keys of JSON or Debug maps), bearer tokens and well-known credential
formats (including Stripe sk_live_/whsec_ secrets) are masked, and
terminal control characters are replaced, in every line of the report. The
whole message is redacted before a long one is cut to 4,096 characters.
A command started from the home menu or the command palette prints its own
report, and cargo rullst then exits with that command’s status (for example
2 for a usage error) without a second report.
-v/--verbose (accepted by every command) adds the underlying causes and
the error’s Debug form, still redacted. A panic prints a short “Internal
error” report instead of the default panic message and backtrace unless -v
or RUST_BACKTRACE is set.
| Exit status | Meaning |
|---|---|
0 | Success (doctor: no failed check; warnings are allowed). |
1 | The command failed, including doctor with a failed (✗) check. |
2 | Usage error: an unknown command, invalid arguments or an argument that is not valid UTF-8. |
An unknown command lists the closest commands at the level where it was typed
(cargo rullst make:modek suggests make:model; cargo rullst update projetc
suggests update project; cargo rullst db lists the db:* group). Ranking
ignores case and separators (make-model), then uses edit distance with
transpositions, an exact group:name segment (migrate) and prefixes.
Colour follows the home screen rules on each stream: none for NO_COLOR, CI,
TERM=dumb or a stream that is not a terminal. When standard output is not
coloured, generator output is plain as well. Long steps (the first build of
new, the Cargo gates of upgrade) animate a spinner only when standard
output is a terminal without NO_COLOR, CI, TERM=dumb or
RULLST_REDUCED_MOTION; otherwise, as in CI logs and pipes, they print one
plain line without escape sequences or carriage returns.
After a generator succeeds, the CLI prints up to three concrete Next steps
(for example cargo rullst db:migrate after make:model -m). Generators that
already print their own instructions (new, auth, deploy,
make:k8s) add none, and --json invocations never print them.
cargo rullst completions <shell>
Prints a completion script for bash, zsh, fish, powershell or elvish
generated from the complete command tree, runtime-only commands included.
Scripts complete the rullst executable that cargo install cargo-rullst
installs beside cargo-rullst (Cargo’s own completion does not delegate the
arguments of external subcommands); --bin cargo-rullst targets the other
executable.
cargo rullst completions bash > ~/.local/share/bash-completion/completions/rullst
cargo rullst completions zsh > ~/.zfunc/_rullst # with fpath+=(~/.zfunc)
cargo rullst completions fish > ~/.config/fish/completions/rullst.fish
cargo rullst completions powershell >> $PROFILE
cargo rullst info [--json] (alias version)
Prints the CLI version, platform, rustc/cargo versions and the detected
project (name, root, rullst requirement, features, database family and
source, migration files, Git branch). It reads local files and runs
rustc --version/cargo --version only; connection URLs are never printed.
--json prints:
{
"schema": "rullst.cli-info.v1",
"schema_version": "rullst.cli-info.v1",
"cli": { "name": "cargo-rullst", "version": "13.0.0" },
"platform": { "os": "linux", "arch": "x86_64" },
"toolchain": { "rustc": "1.98.1", "cargo": "1.98.1" },
"project": {
"name": "shop",
"root": "/work/shop",
"relative_root": null,
"rullst": "13",
"features": ["default", "orm"],
"database": { "status": "configured", "kind": "SQLite", "source": ".env" },
"migrations": { "count": 2, "latest": "m20260101000000_create_users" },
"git_branch": "main"
}
}
project is null outside a project; toolchain values are null when the
tool is missing. database.status is configured, not_configured or
unreadable (with reason); rullst is the version requirement, or path,
git or workspace.
cargo rullst new <name>
Creates a Rullst project from scratch. Since version 12, new intentionally
generates one audited application architecture: Active Record for
database-backed code and server-rendered html! views enhanced with HTMX for
full-stack pages.
In a terminal, cargo rullst new (with or without a name) runs the v13
create wizard; the home screen’s Create New Project entry runs the same
wizard. It asks, one screen at a time:
- the project name, when none was given (letters, digits,
_and-, starting with a letter; Rust keywords and existing paths are refused). A name given on the command line is checked by the same package-name rules before the first question; - the starter — Blank (a server-rendered web page with HTMX), API (the Blank starter as a headless JSON API) or SaaS (authentication, Stripe billing and a pricing page) — each with a one-line description and a compact preview of the file tree it generates. The preview is rendered by the real project writers into a private temporary directory that is removed immediately; nothing is built and no network is used;
- the primary database (Turso and No database only for Blank and API);
- optional features (zero or more): AI, Redis adapters, Dockerfile and the Turso, MongoDB, DuckDB, SurrealDB and Qdrant add-ons;
- a review of the answers, the file tree and the commands
newwill run (the initial migration, unless skipped or database-free), with Create the project, Back and Cancel.
↑/↓ (or j/k, or a digit) moves, Space toggles a feature, Enter confirms
and Esc, Backspace or ← returns to the previous question. On a terminal too
short for every choice, the list scrolls with the highlighted entry and counts
the hidden ones. Cancel and Ctrl+C
create nothing. Colours follow the home screen: NO_COLOR removes them and
24-bit colour needs COLORTERM=truecolor. Every question has a flag; a
question answered by a flag is skipped. When standard input, output or error
is not a terminal, CI is set or TERM=dumb, new never prompts: without
--default it exits with an error that names the flags instead.
After creation, new prints numbered next steps: cd <name>,
cargo rullst dev (which applies pending migrations) and the URL of the
generated welcome page (http://127.0.0.1:<port>, where the port comes from
PORT, the project’s .env, [app].port in Rullst.toml, then 3000; JSON
APIs show a curl command instead).
The choices that materially change the generated application:
- Starter: Blank, API and SaaS (
--blueprintvaluesblank,apiandsaas;apiis the Blank blueprint with--api). The LMS, Blog, Portfolio and ERP blueprints left the CLI in 13.0.0-alpha.4 and are now complete applications in the repository’sexamples/directory; see Starters that moved to the examples. - Persistence: a primary relational backend (SQLite, PostgreSQL, MySQL, MariaDB, or bounded Turso-primary for blank/API) plus optional Turso/libSQL, MongoDB, DuckDB, SurrealDB, and Qdrant capabilities. The optional selector accepts zero or more choices and omits capabilities already selected by the primary profile or flags. Specialized adapters remain separate from SQLx Active Record.
- Application profile: the Blank and SaaS starters use the audited
html!SSR/HTMX path; API (--api) uses the headless JSON path. Repository, LiveView, Wasm Island, Pico.css and Tera foundations remain application-owned APIs and are not presented as equivalent generated profiles. - Arguments:
<name>: The folder and package name (e.g.,my_startup).
- Optional Flags:
--api: Scaffolds a headless JSON API from the Blank starter (no HTML view rendering), the same as--blueprint api;--blueprint saasrejects it instead of ignoring it. Its example write route,POST /api/messages, is a bearer machine endpoint: clients sendAuthorization: Bearer <API_TOKEN>with the randomAPI_TOKENwritten to.env, and the server does not start without it (see which security layer to use). The interactive wizard then skips its starter question instead of letting its Blank default replace the flag.--docker: Adds a multi-stageDockerfileand.dockerignore. The.dockerignoremirrors the generated.gitignore: it excludes.envand.env.*(except.env.example), SQLite and DuckDB files (*.db,*.sqlite,*.sqlite3,*.duckdband their journals, one list shared by both files) and the host-local.cargo/config.tomldescribed below, so the builder’sCOPY . .never sends them to a (possibly remote) builder. An existing.dockerignoreis kept unchanged. Since 13.0.0-alpha.3 the dependencies are built in their own cached layer without an extra tool: aplannerstage copies only the manifests,Cargo.lockandrust-toolchain(.toml), withfn main() {}standing in for every target (src/main.rs,src/lib.rs,src/bin/*.rs, explicitpath = "*.rs"targets) and build script, and the builder runscargo build --release --lockedon that skeleton beforeCOPY . ., touches the real.rsfiles and builds again with--locked. Docker reuses the dependency layer until a manifest, the lockfile or the set of targets changes; workspace members keep their paths. Since v13 the runtime stage isgcr.io/distroless/cc-debian12:nonroot(glibc, libgcc, libstdc++, CA certificates and tzdata; no shell or package manager). It runs as UID/GID 10001 withHOME=/app, sets the production bind address and copies local static/config assets when present. The builder stage prepares the/apptree, so an explicit SQLite selection still uses the writable/app/datadirectory. Without a shell the image has noHEALTHCHECK; let the platform probe/healthand/readyover HTTP. The migration job runs the binary directly (/app/<name> db:migrate). The image size of this template has not been measured yet; measure it withdocker image lsin CI or on a machine with Docker. Secrets are never embedded and no anonymous volume is declared. Run schema migrations as one deployment job before starting or rolling multiple replicas; the generated image deliberately does not race migrations from every application process. Compose services, persistent-volume ownership, backup/restore and platform deployment hardening remain explicit project work. Every starter mountsrullst::health::health_router(), so the/healthand/readyprobes used bymake:k8smanifests and thedeployCaddyfile answer200without authentication.--turso: Adds Turso/libSQL as a side store beside the primary database: the direct Hrana HTTP v3 adapter, checked migrations and its real-SQL offline development fallback. It does not imply transparent replication.--mongodb: Adds MongoDB as a side store: typed document CRUD and its deterministic offline store.--duckdb: Adds DuckDB as a side store for in-process analytics; the optional native dependency increases the first build time.--surrealdb: Adds SurrealDB as a side store: HTTP document CRUD and bounded read-only graph queries.--qdrant: Adds Qdrant as a side store for bounded dense-vector operations and generates empty/mock_*-compatible environment fields; it is not the SQL primary.--nixand--buildahwere removed in 13.0.0-alpha.4; see Advanced deployment.--default: Skips every question and uses deterministic defaults for anything no flag sets (nameapp, Blank, SQLite, no features), intended for CI and reproducible scaffolding.--blueprint <blank|api|saas>: Selects the starter; the wizard skips that question.lms,blog,portfolioanderpstill parse but fail before anything is written, naming the example that replaced them (exit status 1).--database <sqlite|postgres|mysql|mariadb|turso>: Selects the primary database; the wizard skips that question. Network databases must be configured before migration bootstrap. The SQLx backends (sqlite,postgres,mysql,mariadb) work with every starter.tursois accepted only with the Blank starter (web page or--api) and has no Nexus, Studio database pages,Schemamigrations or SQLx ORM; the wizard’s choice,--helpand the review/dry-run screen say so. See Database Support.--no-database: Generates the Blank or API starter without a primary relational database; it conflicts with--databaseand--blueprint saasrejects it.--ai: Enables the umbrella AI facade in the generated manifest.--redis: Enables the umbrella Redis queue and cache capabilities and the Redis-structures side store (the direct ORM Redis feature).--skip-initial-migration: Generates the project without running the best-effort initial database migration.cargo rullst devapplies pending migrations when it starts; runcargo rullst db:migrateexplicitly otherwise.--dry-run(v13): Prints the plan (the answers, a compact file tree with the exact number of files and the commands it would run) and exits without creating anything. With--defaultit never prompts; in a terminal without--defaultthe wizard’s review ends with Finish the dry run.--vcs <git|none>(v13, defaultgit): Likecargo new, initializes a Git repository in the new project whengitis installed and the destination is not already inside a Git work tree;noneskips it. The generated.gitignorekeeps.env,.env.*(except.env.example) and/targetout. Without Git,newprints a warning and keeps the project;cargo rullst auditchecks committed secrets through Git.
Since v13, --blueprint, --database, --no-database, --ai and --redis
no longer require --default: in a terminal they answer their questions and
the wizard asks the rest. --api selects the API starter; --no-database and
--database turso limit the starter question to Blank and API. The retired
--hot-reload still requires --default and is rejected.
When the generating Linux host has mold or lld, the project’s
.cargo/config.toml selects it to speed up local linking. That file describes
the generating machine only: the generated .gitignore and .dockerignore
exclude it, so CI runners, teammates and the container builder (which has
neither linker) build with the toolchain default.
Since v13 the generated Cargo.toml also has a [profile.release] with
lto = "thin", codegen-units = 1 and strip = "symbols"; panic stays
unwind so one panicking handler cannot stop the server. Release builds take
longer and their backtraces lose symbol names; the measured release defaults
state the size and build-time effect on one starter. Since 13.0.0-alpha.4 it
has a [profile.dev] as well: the application keeps line tables (file:line in
panics and backtraces) and dependencies are built without debug information,
which shortens debug builds and rebuilds; see Build times.
Without --skip-initial-migration, project creation performs the first Cargo
build before applying migrations. A clean first build can take several minutes,
especially for the larger SaaS profile; the animated status remains visible
while Cargo is working. Later migration and server runs reuse that project-local
build cache.
Preview a starter without writing anything:
cargo rullst new shop --default --blueprint saas --dry-run
For example, the release gate can generate a SaaS starter without prompts or network-dependent bootstrap work:
cargo rullst new packaged-saas --default --blueprint saas --skip-initial-migration
A deterministic profile can pin the starter, database and optional features:
cargo rullst new operations-portal --default --blueprint saas \
--database mariadb \
--ai --redis --skip-initial-migration
Every generated manifest also declares validator 0.21 with its derive
feature. #[derive(rullst::Validate)] expands to ::validator::… paths that
only a direct dependency resolves, so ValidatedForm/ValidatedJson DTOs
compile without adding it by hand; Rullst Core already builds that version.
Generated SQLx applications disable the default features of both the umbrella
rullst dependency and the direct rullst-orm dependency, and select exactly
one strict primary profile (strict-sqlite, strict-postgres, or
strict-mysql; MariaDB uses the MySQL protocol). This prevents an implicit
SQLite default from masking the chosen backend and keeps the other drivers,
including bundled SQLite, out of the build. Turso-primary projects enable
rullst-orm’s turso feature (which includes SQLx’s SQLite driver), and
database-free projects depend on neither the ORM nor a SQLx driver.
Generated-project verification boundary
The repository does not treat template rendering as sufficient evidence. A
structural contract materializes 10 internal starter/profile shapes (five
public directly linked layouts plus five legacy DLL layouts retained for
regression) and checks paths, Rust syntax and manifests. A slower four-case
set crosses every starter, hot and non-hot layouts, database/API boundaries and
a SaaS release build, runs every generated test target, and constructs the
public router of each hot-reload project using offline-safe defaults. A
separate seven-case test invokes the public cargo rullst new binary and
verifies exact feature selection for SQLite, PostgreSQL, MySQL, MariaDB, AI,
Redis, Turso, MongoDB, DuckDB, SurrealDB and Qdrant across the Blank, API and
SaaS starters plus a polyglot profile. The invocation starts outside the source checkout, proving
that an unpublished pre-release CLI retains its exact matching checkout as a
path source instead of requesting unavailable registry packages.
The public polyglot profile uses cargo check in that CLI-level set because a
second bundled-DuckDB test build adds no adapter behavior and can consume
several GiB on small machines. DuckDB, MongoDB, SurrealDB, Turso and Qdrant
runtime behavior is exercised by their dedicated ORM matrices instead. These
gates prove reproducible local generation and bounded offline construction;
they do not prove provider accounts, production deployment, browser behavior
or application-specific authorization.
Starters that moved to the examples
Since 13.0.0-alpha.4 cargo rullst new offers three starters: Blank, API and
SaaS. The other blueprints left the CLI and live on as complete applications
in the Rullst repository, tested in its workspace:
Former --blueprint | Example | What it shows |
|---|---|---|
lms | examples/lms | Course catalog, modules and lessons, accessible player, enrollment, owner-bound progress, sign-in and a Nexus admin |
portfolio | examples/portfolio | A developer portfolio edited in Nexus |
erp | examples/erp | Products, stock movements and orders behind one administrator policy |
blog | examples/blog | The blog showcase (a larger application than the old starter) |
--blueprint lms, blog, portfolio or erp still parses but fails before
anything is written: the report names the example directory and its GitHub URL
and exits with status 1. The examples are applications to copy from, not
supported starters: there is no generator or upgrade path behind them. Each
README says how to run it and how to switch its workspace dependencies to the
published crates. Applications generated earlier from these blueprints keep
working: their code is theirs.
cargo rullst tour (v13)
A short guided walkthrough of the main commands in seven skippable steps:
new, dev, dash, make:*, db:*, doctor and generate:ai-context.
Each step explains what the command family does, lists commands to try and
offers one read-only example that runs only when you pick it:
cargo rullst new tour-demo --default --dry-run for new and --help for the
others. Next,
Previous (or Esc) and Quit the tour move between steps. The tour needs no
network and never creates or changes a project.
cargo rullst tour # guided, in a terminal
cargo rullst tour --list # every step and its example, without prompting
Outside an interactive terminal (pipes, CI, TERM=dumb) cargo rullst tour
prints the same list as --list and exits successfully.
cargo rullst upgrade
Plans or applies a transactional application upgrade. The target defaults to
the exact installed cargo-rullst version; --to <VERSION> accepts an exact
version in the same major release train as that CLI. The command fails before
writing anything when the target is older than a managed requirement’s lower
bound or than a Rullst package locked in Cargo.lock, so an older CLI never
downgrades the project; install a CLI that is not older than the project.
# Human-readable plan; no writes or dependency resolution
cargo rullst upgrade --dry-run
# Versioned machine-readable plan
cargo rullst upgrade --dry-run --json
# Apply every source rule, not only those introduced after the project's version
cargo rullst upgrade --dry-run --all-rules
# Backed-up apply + cargo fix + cargo check
cargo rullst upgrade
# Deliberately inspect a failed partial migration instead of auto-rollback
cargo rullst upgrade --keep-on-failure
# Recover a persisted snapshot, including after interruption
cargo rullst upgrade --restore target/rullst-upgrades/<run-id>
The CLI uses Cargo metadata to scope workspace manifests, preserves TOML
comments/order, updates normal, inline, workspace, target-specific and renamed
Rullst dependencies, and reports unversioned path/git entries. Before applying,
it snapshots workspace manifests, the root Cargo.lock, and Rust sources under
target/rullst-upgrades/; a failed Cargo gate restores them by default. The
reports use the rullst.upgrade-plan.v1 schema and include version-selected
source findings.
The v13 CLI’s rullst-upgrade-rules-v4 catalog (111 rules) parses Rust sources
(with syn; comments, doc comments and strings never match an API rule), Cargo
manifests and generated project files, and reports each finding as
MUST-CHANGE (a compile-breaking or API-shape change) or REVIEW (changed
behaviour of an API, feature or configuration in use) with a stable V13-*
code, file:line, a one-line message and the title of its row in the
v13 migration guide.
Findings do not stop the transaction; a failed Cargo gate names the
must-change findings and repeats the guidance of V13-CAPITAL-REMOVED and
V13-MAIL-REMOVED (removed APIs reported for review).
Each rule records the release that introduced its change. The plan reads the
project’s Rullst version (the oldest version a Rullst requirement admits or
Cargo.lock holds); for a project already on a 13.0 prerelease it applies only
the rules introduced after that version and prints how many earlier rules it
skipped and how many findings they would have added. --all-rules applies
every rule; a v12 project, or one whose version cannot be read, gets every
rule anyway.
update project verify/review/apply refuse a preparation with must-change
findings; preparations apply every rule. In JSON, each
source_findings element keeps path, line, code, severity (BLOCKER
or REVIEW) and message and adds kind, introduced_in, migration_row
and migration_url; the root adds finding_counts, unscanned_sources,
migration_guide and rule_selection (current_version, all_rules,
skipped_rules, hidden_findings). See the tutorial
and its rule classification.
Since 12.1, managed requirements use exact =VERSION pins. The final
cargo check --workspace --all-targets --locked validates the lockfile produced
by cargo fix without resolving a different version. Broader dependency ranges
remain an application decision after reviewing the update.
Process-level fixtures upgrade v12 origins, reject retired pre-v12 origins with
v12-first guidance, verify restoration across multiple workspace
members, retain a deliberately failed edit only with --keep-on-failure, and
restore that retained snapshot on demand. Symlinked Rust sources are rejected
before a transaction begins. This is recovery evidence for the bounded file and
Cargo operation; it is not an automatic application, database or deployment
migration.
The command does not install the CLI globally, rewrite Axum/SQLx/Tokio imports, run database migrations, modify secrets or authorization, validate live providers, or replace the project’s test suite. Follow the assisted upgrade tutorial and the v13 migration guide.
Update notice and installing a newer CLI
The CLI never downloads, verifies or replaces its own binary. Install a newer one with Cargo, using its exact version:
cargo install cargo-rullst --version =VERSION --locked
At most once a day, in an interactive terminal, cargo rullst tells you when a
newer release of its major is on crates.io: a newer stable release, or for a
prerelease CLI also a newer prerelease. The notice prints that exact command
(A newer cargo-rullst is available: cargo install cargo-rullst --version =X --locked) on the home screen, or on standard error after a command finishes.
It never delays or fails a command:
- the crates.io catalog is fetched in a background thread at most once a day
(one bounded HTTPS request, 4 seconds in total, no redirects) and kept in
the private user cache (
$XDG_CACHE_HOME/rullst-update-v1,$HOME/.cache/rullst-update-v1or%LOCALAPPDATA%\rullst-update-v1; unsafe owners, permissions or links make it unusable); - the notice itself reads only that cache, and the day of the last notice and
of the last request are small stamps in
rullst-ui-v1/in the same cache base; RULLST_DISABLE_UPDATE_CHECK=1,CARGO_NET_OFFLINE=true,CI, and any standard stream that is not a terminal silence it completely: no request, no file and no output.
Removed in 13.0.0-alpha.4: the self-updater (update check,
update verify, update stage and update install review|apply|recover)
and the CLI half of update guided (--scope, --root, --allow-major,
--prerelease). The native CLI executables and their manifests stay
attached to each GitHub release and can be checked by hand as described in
Verifying a release.
cargo rullst update guided (since 12.1.0)
cargo rullst update guided --to VERSION --project ./my-app
cargo rullst update guided --to VERSION --project ./my-app --offline
This interactive entry point composes the isolated project commands described
below: prepare, verify, review and apply. --to must be in this CLI’s major,
because the running CLI carries only its own major’s migration rules; to move
to another major, install that CLI first and run its guided flow. Each complete
review appears before its own default-no confirmation. The flow reuses the
exact version, directories and review digest without shell commands or manual
copying. It reports elapsed milliseconds per executed stage, excluding time
spent answering prompts. Declining stops before the next operation; completed
steps and their recovery records remain available.
Project verification asks whether Cargo may use the network and requires
explicit consent to execute trusted build scripts, macros and tests.
--offline keeps it offline. Feature selection and command deadlines use the
same --all-features, --features, --no-default-features and
--timeout-seconds options as project verification. PATH, databases and
deployments are not changed.
Piped input/output rejects before I/O; use the explicit commands with JSON and review digests for automation. This flow does not bypass ownership or recovery checks.
cargo rullst update project prepare (since 12.1.0)
Prepare dependency edits in a private source copy for review:
cargo rullst update project prepare --project ./my-app --to VERSION --json
The project must be a Git working directory containing Cargo.toml. The default
target is this CLI’s exact version, within its major train. Tracked and
non-ignored untracked files are copied with their current contents, including
uncommitted edits and tracked deletions. The root Cargo.lock is retained even
when ignored by an older generator. Other ignored files are omitted; tracked
secrets are still tracked inputs. The original files and Git index are not
edited. Limits are 100,000 entries, 64 MiB/file and 512 MiB/source snapshot;
the two copies can consume about 1 GiB before any build.
The command rejects linked/special inputs, unsupported paths, unknown migration
origins, ambiguous/unversioned managed dependencies and version/lockfile
downgrades. It accepts source majors 12 and 13 and records the
rullst-upgrade-rules-v4 source findings in the plan; verification, review and
application refuse a preparation with must-change findings (fix them in the
original project and prepare again), while review findings stay in the plan.
Older applications first upgrade to v12 with the v12 CLI. The
exact-version editor preserves TOML
comments. Offline, locked, dependency-free Cargo metadata enumerates workspace
members inside the copy. Rustup auto-installation is disabled using its
documented environment setting;
Git and Cargo must already be installed on absolute trusted PATH entries.
Every Cargo/rustc invocation in a project copy (preparation, verification,
review and apply) runs with RUSTUP_TOOLCHAIN pinned to the caller’s
toolchain: an inherited RUSTUP_TOOLCHAIN (rustup sets it for cargo rullst),
otherwise rustup’s configured default. A rust-toolchain/rust-toolchain.toml
in the project, including a path toolchain, is therefore not honored. Run the
CLI from outside an untrusted project: invoking cargo rullst inside it lets
rustup select that project’s toolchain for the CLI process itself.
The result points to before/, candidate/ and preparation.json inside the
private update cache. JSON uses rullst.project-preparation-result.v1, with a
rullst.project-preparation.v1 record of original file hashes and absences.
Compare the two trees and inspect the plan. Failed preparation removes its own
new staging directory. Successful preparations remain for review and can be
deleted by the caller after use.
Preparation executes no builds, procedural macros or tests, does not resolve a
new candidate lockfile, and authorizes neither execution nor application. The
copy is not a sandbox. Candidate verification, review, application and
recovery are described below. The existing upgrade command remains separate.
cargo rullst update project verify (since 12.1.0)
Review the commands and then explicitly authorize trusted project execution:
cargo rullst update project verify --prepared PATH --dry-run --all-features
cargo rullst update project verify --prepared PATH --allow-project-code --all-features --json
PATH is the private directory returned by preparation. The command validates
its records, before/candidate copies and current original files under an
exclusive operation lock. Unknown/stale inputs and unresolved migration findings
fail before project code runs. Fix findings in the original and prepare again.
Builds and tests use another fresh private copy, preserving the reviewed copy.
The sequence probes rustc --version --verbose and cargo --version, resolves
Cargo.lock with cargo update --workspace, checks all workspace targets, and
runs workspace tests with the resolved lockfile. That resolution keeps every
existing lock entry the edited manifests still accept and resolves only what
the dependency edits require; it does not upgrade unrelated pins (a missing
lockfile is resolved in full). Every managed Rullst package must resolve to the
exact target.
Default features apply unless --all-features, --features names or
--no-default-features selects another policy. This verifies that policy only;
application-specific service/browser/deployment tests remain separate.
Cargo is offline by default. --allow-network permits dependency retrieval but
cannot override CARGO_NET_OFFLINE=true. Rustup does not install missing
toolchains, and the project’s toolchain file is ignored as described above; set
RUSTUP_TOOLCHAIN explicitly to verify with the project’s pinned channel. --timeout-seconds bounds each command (default 900, range 1–3,600);
stdout/stderr are each capped at 8 MiB. Private logs retain failed Cargo output;
failure, cancellation or timeout never records acceptance. Builds/tests inherit
the caller’s environment and can affect external files, databases or services:
this copy and process cleanup are not a sandbox. Use trusted projects only.
Success emits rullst.project-verification.v1 and a private verification.json
with commands, log hashes and final file digests. Its verified candidate has the
resolved lockfile; original files are not edited. Source or baseline changes
during verification and unexpected candidate writes are rejected. Review the
complete diff and logs; compiler overrides/wrappers require separate review.
The report is not a reusable apply token and grants no application/deployment
authority. Final native application/recovery acceptance remains open.
Retained source copies can consume up to about 2 GiB, plus build outputs/logs.
cargo rullst update project review (since 12.1.0)
cargo rullst update project review --verified PATH --json
Use verified_directory from the verification result. Review validates the
stored command policy, successful statuses, bounded logs, original/prepared
files and verified candidate again under operation locks. Changed evidence is
rejected. Git produces the full dependency diff with external helpers, text
conversion and paging disabled; output is bounded to 8 MiB. No build/test runs
and original files remain untouched.
JSON (rullst.project-review-report.v1) contains the review record
(rullst.project-review.v1) with before/after file hashes, the full diff
and review_sha256 binding its evidence and contents. The digest is not
authorization to apply changes; review does not invoke the legacy in-place
upgrade command. The digest also binds the source access policies.
cargo rullst update project apply|recover (since 12.1.0)
cargo rullst update project apply --verified PATH --approved-review SHA256 --json
cargo rullst update project recover --verified PATH --approved-review SHA256 --json
After reviewing the complete diff and logs, supply that exact review_sha256.
Stop editors/builds and other writers first. The CLI revalidates all evidence,
locks the canonical source within the configured private cache, stages every
replacement and persists an intent before changing any original. Only reviewed
workspace manifests and the root lockfile can change. Original hardlink aliases
are not truncated, and a newly created lockfile cannot overwrite a competing file.
Recovery checks every target before restoring any file. A changed file must match
this operation’s before or after state and access policy; later conflicting edits
are refused. Unrelated files remain untouched. A root lockfile originally absent
is removed only if it still matches the recorded created file. Repeating recovery
is supported. Keep both private source copies and application.json until finished.
Replacement is atomic per file, not for the whole workspace. Partial failures
retain recovery evidence and report progress. Unix mode/owner/group and Windows
owner/group/DACL/integrity label are bound to the review (1 MiB total policy budget). Unix extended ACLs/xattrs and special
mode bits (except the SELinux security.selinux label, which the replacement
staged in the same directory receives like any new file there), Windows read-only/special attributes, alternate streams, resource/central-access
policies and policies that cannot be
recreated exactly require manual handling. Other updater cache configurations and
filesystem aliases do not share the lock. Forced termination during staging may
leave sibling .rullst-update-stage-* files. Timestamp and Windows audit-policy preservation are not implemented;
files that require them need manual handling. Native and process-interruption/power-loss acceptance remains pending.
Recovery does not undo application-code effects, databases or deployments.
cargo rullst pkg <action> [name]
Adds or lists rullst-*/rullst_* crate dependencies in the local
Cargo.toml. It executes no package code, queries no registry and changes no
application routing; Cargo resolves the crate on the next build.
- Subcommands:
add <package_name>: Adds the dependency (e.g.,cargo rullst pkg add rullst-auth) toCargo.tomlwith this CLI’s version as its requirement; an existing entry is left unchanged. In a virtual workspace manifest it adds the entry to[workspace.dependencies], for members to use with{ workspace = true }.list: Scans and lists all activerullst-*community extensions installed in your project (the workspace dependencies of a virtual workspace manifest).
An unknown action, or add without a package name, fails with a non-zero exit
status.
cargo rullst add
cargo rullst add <capability> [--dry-run] (v13) enables a facade capability
in an existing project. Capabilities: mail, auth, ai, nexus and
studio.
It runs only at the root of a Rullst project (a Cargo.toml whose
[dependencies] declare rullst), asks nothing and:
- enables the capability’s
rullstfeature inCargo.toml. The edit keeps comments, formatting and the other features; a feature that already implies it (for examplemailerorauth-sqlite) counts as enabled; - appends the capability’s variables to
.env.exampleunder a# ── <Capability> (added by cargo rullst add …) ──heading, creating the file when it is missing. Values are placeholders, empty ormock_*(the offline mocks of AGENTS.md 3.5), never real secrets; a variable that is already assigned or commented out is not repeated..envis changed only when it lacks a variable the application needs to start in development, and then receives the same mock value and is reported; none of the current capabilities needs one, so.envstays as it is; - prints the wiring code to paste and where it goes. Generated projects have
no marked insertion points, so
addnever edits application source; - prints one to three next steps.
| Capability | rullst feature | .env.example | Next steps |
|---|---|---|---|
mail | mail | MAIL_FROM, # MAIL_DRIVER, # RESEND_API_KEY | make:mail Welcome, dev |
auth | auth | APP_KEY placeholder | auth, make:mfa |
ai | ai | OPENAI_API_KEY=mock_openai_key, commented Anthropic, Gemini and Ollama entries | make:chat-session, generate:ai-context |
nexus | nexus | empty NEXUS_ADMIN_USERNAME and NEXUS_ADMIN_PASSWORD (required by release builds unless Nexus uses the application’s sign-in, NexusAuthPolicy::application) | dev, then /nexus |
studio | studio | none | dev, then http://127.0.0.1:5555 |
Running it again changes nothing and reports that the capability is already
enabled. --dry-run prints the planned diff of every file and writes nothing.
Outside a project it fails with the shared error report (exit status 1); an
unknown capability is a usage error (exit status 2).
cargo rullst add mail --dry-run
cargo rullst add mail
🛠️ 2. Architecture Scaffolding (make:*)
Rullst generators write the files described under each command. Some commands
also register modules and refresh .llms.txt; this is command-specific, and a
failed best-effort context refresh does not roll back generated source.
make:controller, make:model, make:middleware, make:worker,
make:island, auth, make:billing, make:cors and make:jwt also refresh
the generate:diagram output diagram.md: a missing file is created, while a
diagram.md the generator did not write is kept and reported instead of
replaced. Review the diff and run cargo check after scaffolding.
cargo rullst make:resource <name>
Scaffolds the bounded starting files for a CRUD resource in one command: a
Model (src/models/<name>.rs), Migration
(src/migrations/m<timestamp>_create_<plural>.rs, for example
create_categories for Category), Controller
(src/controllers/<name>_controller.rs), and HTML view placeholders
(views/<name>/index.html and views/<name>/form.html). It does not infer
application fields, register routes, establish ownership/RBAC, or turn the
placeholder handlers into a complete authorized CRUD implementation. Like
make:model --migration, it keeps an existing model and does not add a second
create-table migration for it. Mount the
routes behind the canonical security baseline, render request-scoped CSRF
tokens in state-changing forms, complete validation/persistence, and run the
application’s authorization-negative tests.
- Arguments:
<name>(e.g.,Productorproduct). - Optional Flags:
--api: Scaffolds a headless JSON API resource controller instead of HTML views.
cargo rullst make:controller <name>
Generates src/controllers/<name>_controller.rs (posts and
PostsController both produce posts_controller.rs) with placeholder CRUD
handlers (index, show, store, update, delete). It registers the
module in src/controllers/mod.rs and adds pub mod controllers; to
src/main.rs when that file exists; it does not add application routes
automatically.
- Arguments:
<name>(e.g.,UsersControllerorusers). A name whose module or type would not be a non-keyword Rust identifier (for exampleBad.Name) is rejected before any file is edited. - Optional Flags:
--api: Instead of returning HTML Views via thehtml!macro, the generated methods will automatically extract/returnJson<T>. The command prints theroutes!lines (/api/<name>and/api/<name>/{id}) and theMachineEndpoint::bearerlines formachine_endpoints()insrc/main.rs: in staging and production JSON clients reach the write routes only as machine endpoints, or the browser CSRF check refuses them with 403 (see which security layer to use).
cargo rullst make:model <name>
Creates a model struct in src/models/ with the ORM annotations. SQLx projects
receive FromRow plus Orm and import only those two derives (use rullst::db::{FromRow, Orm};), so the new file compiles without warnings;
Turso-primary projects receive
#[derive(rullst_orm::Orm)] #[orm(backend = "turso")] and an i64 primary
key. Backend detection reads the generated manifest and does not treat an
additive --turso integration as the primary ORM. Like make:resource, it
rejects a name whose module or type would not be a non-keyword Rust identifier
(for example Match, which would declare pub mod match;) before writing.
- Arguments:
<name>(e.g.,BlogPost). - Optional Flags:
--migrationor-m: Simultaneously generates a reversible migration with the correctly pluralized table name. An existing model file is kept, and the migration is skipped when the model already existed or a*_create_<table>.rs/*_create_<table>_table.rsmigration exists, since a second create migration would drop the live table on rollback.
cargo rullst make:chat-session
Adds application-owned conversational memory for the project’s primary ORM.
It generates and registers ChatSession and ChatMessage, a reversible
migration, and StatefulChat. SQLx and the bounded Turso-primary profile receive
backend-specific code; the command also enables the orm and ai umbrella
features if necessary.
cargo rullst make:chat-session
cargo rullst db:migrate
Save the generated ChatSession before constructing StatefulChat. Each
service instance serializes concurrent sends, restores at most the newest 100
messages in chronological order, persists the user message before provider
dispatch and persists the assistant response only after success. Database and
provider failures are returned as StatefulChatError; they are never silently
discarded. Multi-process ordering, tenant authorization, retention and deletion
remain application responsibilities. The command refuses to overwrite an
existing chat scaffold.
cargo rullst make:middleware <name>
Generates a standard Axum/Rullst Middleware struct in src/middlewares/. Perfect for injecting headers, checking authentication, rate limiting, or logging.
A name whose module and function would not be a non-keyword Rust identifier is
rejected before any file is edited.
cargo rullst make:island <name>
Creates a frontend interactive “Islands Architecture” component (similar to Fresh or Astro) in src/islands/. It generates the Rust infrastructure that, during build, will be transparently compiled to WebAssembly to run in the browser.
cargo rullst make:worker <name>
Creates an asynchronous background worker in src/workers/ against the queue
backends currently implemented in Core (memory, SQLite, and optional Redis).
RabbitMQ is not generated by this command. The generated handler logs only the
job name and its number of payload fields, never the payload itself, because
jobs often carry addresses, tokens or other personal data.
cargo rullst make:migration <name>
Generates a timestamped reversible Rust migration for the project’s primary
backend. SQLx projects use the schema DSL; Turso-primary projects use
TursoMigration and parameterized TursoStatement values, and regenerate a
fallible typed migration registry. The name is lowercased with - mapped to
_ and otherwise kept intact (modify_users_email produces
m<timestamp>_modify_users_email); names with other characters, such as
add_index.v2, are rejected. Regenerating src/migrations/mod.rs fails, naming
the file, when an m*.rs file there is not a valid Rust module name.
cargo rullst make:migration:auto
Takes no arguments and needs a SQLite DATABASE_URL in .env; with another
database, a Turso-primary project or no .env, it prints a notice and writes
nothing. It compares the #[derive(Orm)] models under src/ with the
database. It ignores framework tables (migrations and rullst_*) and writes a migration only for
additive changes (new tables or columns), with drops of model-less tables or
columns included as commented-out code for review. When only such destructive
differences remain, it lists them and writes no migration. Column types follow
the field’s Rust type: i8/i16/i32 use integer, i64 big_integer,
f32/f64 float, bool boolean, and String, SecretString, chrono
date/time types and Json use string. A new table declares non-Option
fields NOT NULL. A required field added to an existing table is declared
NOT NULL DEFAULT 0, 0.0 or '' (a commented backfill to review); a
required date, encrypted or JSON field added to an existing table has no
neutral value and is refused. Any other type (for example Vec<u8>, Uuid or
an application enum) is refused, naming the field, and nothing is written; add
such columns with make:migration.
cargo rullst make:billing
Scaffolds a SaaS billing starting point with subscription models, authenticated billing routes, and signed-webhook integration points. Provider credentials, tenant policy, and deployment behavior still require application configuration.
- Optional Flags:
--model <MODEL>(defaultUser): The billable model; the subscription and customer tables reference it through a<model>_idcolumn (Teamgivesteam_id).
Empty or mock_* credentials select a local development fixture, which is
refused in production. Other credentials select the real Stripe profile
(BILLING_PROVIDER=stripe, the only accepted value), which creates provider
customers, checkout sessions and portal sessions, persists the
owner/customer/attempt bindings and processes signed webhooks atomically. It
requires BILLING_ACCOUNT_ID=acct_..., an sk_test_/rk_test_ or
sk_live_/rk_live_ BILLING_API_KEY, a strong BILLING_WEBHOOK_SECRET and
an HTTPS BILLING_REDIRECT_URL; live keys additionally require
BILLING_LIVE_ACKNOWLEDGEMENT=I_UNDERSTAND_REAL_CHARGES. To bill through
another gateway, implement the Capital provider traits in application code; see
Writing your own payment provider.
Mixed mock/real credentials, incomplete configuration and any other
BILLING_PROVIDER value return HTTP 503. The generated BILLING.md lists the
permissions, webhook events, recovery procedures and remaining limits.
Hosted checkout also requires the submitting page’s CSP to allow its exact
reviewed destination in form-action. The SaaS starter selects Stripe and
generates form-action 'self' https://checkout.stripe.com while retaining the
rest of Core’s strict policy. make:billing prints this requirement and leaves
your existing policy for review. A custom provider requires its exact checkout
origin; do not allow https: or wildcard domains. Keep a single form-action directive in security.csp and
review proxy/CDN policies too: another restrictive CSP still applies.
Independently validate each returned URL (HTTPS, exact host/port, no embedded credentials) and the session’s owner, product and test/live mode before a 303. Test the form submission in a real browser: a successful HTTP redirect alone does not prove that CSP permits navigation. Before enabling live billing, resolve an owner’s persisted open attempt before charging the new-session quota; resume only a retrieved, fully bound open session. Expired, completed and uncertain outcomes require separate handling and reconciliation.
cargo rullst make:mail <Name>
Scaffolds a registered transactional mailable. --welcome, --reset, --otp
and --invoice select the bounded built-in variants (at most one); without a
flag the command generates a custom message type. It enables the umbrella
mailer feature, uses the rullst::mail facade, escapes dynamic HTML and
refuses invalid identifiers, path traversal or an existing target. Generated
mailables set no from, so the facade uses the MAIL_FROM (or [mail] from)
default sender, which new projects list in .env.example next to a
MAIL_DRIVER hint; staging and production must select a driver before
sending. Delivery credentials, URL
semantics, tenant policy and provider operation remain application
responsibilities.
cargo rullst make:mail-invoice [Name]
Generates PaymentReceiptEmail by default and enables mailer. The result is
a bounded commercial payment receipt (international_receipt, optional
with_document_url) that states it is not a tax authorization; it runs the
mail pre-flight before returning the message. A custom valid struct name may be
supplied positionally.
cargo rullst make:mail-dunning [Name]
Generates PaymentDunningEmail by default with explicit gentle D+1,
action-required D+3, and service-status D+7 stages. The application remains
responsible for calculating the due state, scheduling delivery, enforcing its
disclosed billing policy, and reconciling payment. The generated build path
runs the mandatory pre-flight and rejects dangerous links.
cargo rullst make:age-gate (v13 prerelease preview)
Adds an explicit first-party declaration before each visit to the recognized
SaaS starter’s /dashboard. Generation requires a server-owned policy version,
threshold and single-tenant deployment reference; it never chooses a universal
legal minimum age. The existing authentication and CSRF layers protect both
GET and POST. A successful answer remains declared, and authorizes only that
dashboard rendering after durable one-use consumption.
cargo rullst make:age-gate \
--privacy-source /path/to/Rullst/rullst-privacy \
--minimum-age 18 \
--policy-version dashboard-v1 \
--tenant-ref application-tenant-ref \
--replay-store sqlite
The threshold above is an example for an application-assessed low-assurance
policy. Select postgres explicitly for a shared database across hosts.
Configure the required private key and replay database as described by the
generated AGE_GATE.md; missing state or configuration denies access. The
candidate defaults to the registry version matching this CLI. The source
override shown above is required for development before that version is published,
unless an explicit archive-only patch is configured. Generation refuses unknown
privacy dependencies or unrecognized authentication/route shapes before writing.
It composes with make:privacy when both use the same registry/local source;
both consumers’ explicit dependency features are preserved.
Review the generated diff before deployment. It does not install facial models,
verified guardianship, reusable age flags or global compliance.
It targets the recognized SaaS starter (--blueprint saas, the default) and
requires --tenant-ref. v13 removed the LMS target together with the complete
Academy scaffold. The tenant is compiled into the generated configuration; no
query parameter, header or form field is read to select it.
cargo rullst make:privacy (v13 prerelease preview)
Adds authenticated preferences at /privacy, an optional personalized greeting
at /privacy/personalization, and a private own-account JSON download at
/privacy/export. The concrete export projects only the authenticated account’s
ID, name and email; it does not complete broader queued privacy requests or
export subscription, learning, guardian, backup or processor records.
cargo rullst make:privacy \
--privacy-source /path/to/Rullst/rullst-privacy \
--purpose-version greeting-v1 \
--validity-seconds 86400 \
--tenant-ref application-tenant-ref
The version and lifetime are explicit application choices; the engineering cap
of 365 days is not a legal retention rule. It targets the recognized SaaS
starter and requires --tenant-ref; v13 removed the LMS target. The tenant is
compiled into the generated configuration. The privacy routes take no query
string: a request carrying one, or an x-school-id header, is refused rather
than allowed to select another tenant or account.
The generated consumer composes with make:age-gate in either installation order.
Omitting --privacy-source selects this CLI’s matching registry version; before
publication use the explicit local override shown above or a reviewed archive patch.
Unknown authentication, dependencies, routes or existing output files require
manual integration rather than overwriting application code.
Follow generated PRIVACY.md: provision an independent random
RULLST_PRIVACY_FORM_KEY_HEX and initialize a new private local consent file with
cargo run --bin privacy-init, with RULLST_PRIVACY_DATABASE exported into that
process environment. Ordinary opening never creates or repairs missing state.
The generated 10,000-record SQLite store requires one trusted shared local file;
it does not provide multi-host replication. Preferences and personalization deny
unavailable state, while the authenticated export remains independent of that
store and form key.
Choices are initially unselected. Refusal and withdrawal produce a generic greeting; an earlier positive form cannot undo a completed withdrawal. Forms bind the displayed notice, revision, account, tenant and session and expire after five minutes. The server checks current permission before the optional name query. Add visible application navigation and review the documented backup, retention and broader rights obligations before deployment. This bounded consumer does not establish worldwide legal compliance or verify age.
cargo rullst make:jwt
Injects a pre-configured boilerplate Middleware into your project for strict JWT Authentication (verifying Bearer tokens in the Authorization header).
cargo rullst make:cors
Generates and configures full CORS (Cross-Origin Resource Sharing) options in your project with recommended security defaults (blocking unused methods, restricting origins).
Projects generated by older CLI versions retain the middleware that was copied into their source tree and must be reviewed manually. Follow the CORS scaffold security advisory to detect origin reflection/wildcards and migrate to the current fail-closed allowlist.
cargo rullst make:pwa (13.0.0-alpha.3)
Makes an HTML application an installable Progressive Web App and wires it into the starter’s own router and page layouts:
cargo rullst make:pwa
cargo rullst make:pwa --name "Field Notes" --short-name Notes \
--theme-color '#0f172a' --background-color '#020617' --lang pt-BR --start-url /
| Flag | Default | Meaning |
|---|---|---|
--name | [package].name in words (field-notes → Field Notes) | 1-64 letters, digits, spaces, ., -, _ |
--short-name | the name when it has at most 12 characters, else its first word cut to 12 | label under the home-screen icon |
--theme-color, --background-color | #0f172a, #020617 | #RRGGBB |
--lang | en | language tag such as en or pt-BR |
--start-url | / | a path on this site that the installed app opens |
--android | off | also write android/twa-manifest.json (Bubblewrap) and PWA-ANDROID.md; needs --host and --package-id |
--host | none | with --android: the site’s HTTPS host, such as notes.acme.dev |
--package-id | none | with --android: the Android application ID you own (com.example.* is refused) |
It writes static/manifest.webmanifest (id and scope /, display
standalone, icons 192 and 512 plus a maskable 512), four placeholder PNG
icons in static/icons/ (the Rullst logo; replace them), static/pwa.js,
src/pwa.rs, the worker in src/pwa/sw.js and PWA.md. src/pwa.rs serves
/sw.js (text/javascript, Cache-Control: no-cache, scope /) and
/offline (no-cache, so the worker may store it), and its head() returns
the manifest link, theme-color, the apple-touch icon and the registration
script. The existing source is edited in three recognized places:
- the router:
.merge_axum(crate::pwa::router().into_axum())follows the one.merge_axum(rullst::health::health_router())of the crate root (a merge in areturnexpression, such as the fallback router of an application generated from the former ERP starter, does not count); - every page layout in
src/:{crate::pwa::head()}before the</head>of anhtml!document, the same tags before the</head>of a document written in a string, andrender_page_with_lang(...)calls becomerender_page_with_head(..., crate::pwa::head(), content); - the crate root (
src/lib.rs, elsesrc/main.rs) declaresmod pwa.
The registration script is a same-origin file, so the default
Content-Security-Policy (script-src 'self' 'nonce-…', worker-src 'self',
the manifest under default-src 'self') allows it without a nonce. The worker
precaches /offline and the PWA’s static files, goes to the network first for
navigations and unversioned static files, serves content-hashed static files
from the cache first, and never stores a non-GET request, another origin or a
response marked no-store or private; Rullst answers no-store by default,
so authenticated pages stay off the device. A new worker waits until the user
chooses Reload in the page’s update prompt (SKIP_WAITING). The cache is
named after [package].version.
The generated router also serves /.well-known/assetlinks.json (Digital
Asset Links for the Google Play package) once RULLST_ANDROID_ASSET_LINKS or
[[android.asset_links]] in Rullst.toml names the app and its signing
certificate fingerprints; see rullst::pwa::AssetLinks.
The whole plan is checked before any write. Running the command again changes
nothing; a generated file that now differs (edited, or another --name) stops
the run without writing. --android can follow a plain run with the same
flags and adds only the Android files. The JSON API starter, a project without an HTML
layout, and a router without the health merge are refused; wire such a project
by hand as the PWA guide shows.
cargo rullst make:iot <DeviceName>
Maturity: Experimental · tiers
Scaffolds and registers a telemetry-only IoT module in src/iot/ using the
public rullst::iot::SensorTelemetry facade, and enables the iot feature in
the application manifest. Unsafe identifiers/path traversal and existing target
files are rejected. It does not install an MQTT/CoAP transport, HAL, firmware,
or claim broker connectivity.
cargo rullst make:k8s
Scaffolds cloud-native Kubernetes manifest files in the k8s/ directory (deployment.yaml, service.yaml, configmap.yaml, hpa.yaml, ingress.yaml, and all-in-one.yaml) pre-configured with liveness (/health) and readiness (/ready) HTTP probes.
The command fails before writing anything when any of these manifests already
exists, and it does not write through a symlinked k8s/ directory or file; move
customized manifests aside to regenerate the templates.
Object names, the image reference and the ingress host use the [package]
name as a lowercase RFC 1123 label: characters other than letters and digits
become - (my_app becomes my-app), and the label is capped at 55
characters so suffixed names such as <name>-service stay valid.
cargo rullst make:scalar
Scaffolds a Scalar API Documentation controller at
src/controllers/docs_controller.rs. The interactive view loads a pinned CDN
asset; its local fallback is status-only and final CSP/network policy belongs to
the application.
cargo rullst make:live <ComponentName>
Scaffolds a LiveView-style server component at src/live/<name>.rs using a
WebSocket and HTMX out-of-band swaps. Application JavaScript may be unnecessary,
but HTMX remains client-side JavaScript and the generated transport requires
origin, reconnect, and backpressure review. It must run in a Rullst project
root, and a name whose module or type would not be a non-keyword Rust
identifier (for example ../notes, self or Bad.Name) is rejected before
anything is written.
cargo rullst deploy
Writes the files that run the application on any Linux VM with Docker or Podman, each only when it is absent (existing files are kept, and nothing is written through a link):
Dockerfileand.dockerignore: the same multi-stage build asdockerize, with its dependency-cache (planner) stage and a distroless, non-root runtime image. When the[database] urlinRullst.toml(or, without one,DATABASE_URLin.env.example) is SQLite, the image setsDATABASE_URL=sqlite:///app/data/rullst.db?mode=rwc, so the database lives in theapp_datavolume; an existing Dockerfile without it gets a warning;compose.yaml: the app and a Caddy reverse proxy with automatic HTTPS forDOMAIN. Both containers have a read-only root filesystem, every capability dropped (Caddy keeps onlyNET_BIND_SERVICEfor ports 80 and 443) andno-new-privileges; the app reads.env.productionand keeps its data in theapp_datavolume; the app’s/healthendpoint is checked from the Caddy container, because the app image has no shell or HTTP client. It works withdocker composeandpodman compose;Caddyfile: proxies to the app, checks its/healthevery 5 seconds and stops routing to it while that fails, and holds each request for up to 15 seconds while the app is down (a restart or an update) instead of answering503at once.
Caddy is pinned to 172.31.250.10 on a 172.31.250.0/24 compose network,
and the command adds that address to [security] trusted_proxies in
Rullst.toml (before a missing Dockerfile is scaffolded, so the image
copies it), so rate limits see each client instead of Caddy. An existing
trusted_proxies list is left unchanged with a warning. The command prints
the next steps: create .env.production, point DNS at the VM, run
DOMAIN=example.com docker compose up -d --build (or
podman compose up -d --build) and run db:migrate once per release.
Nothing is uploaded, started or deployed. A Cargo.toml package name that is
not a valid Cargo name is refused before anything is written, because the
Dockerfile names the binary after it.
Step by step: Deploy to a Linux VM, and the guides for Hetzner Cloud, DigitalOcean, Oracle Cloud and Google Cloud.
Removed in 13.0.0-alpha.4: the Fly.io, Railway and Render targets
(--platform fly|railway|render now fails before writing anything;
--platform vps, the former name of this path, is still accepted). Provider
files you already have keep working with the provider’s own CLI. Nix,
Buildah, Podman Quadlet and Kubernetes are described in
Advanced deployment. Credentials, migrations,
availability, DNS and rollback remain operator responsibilities.
cargo rullst auth
Creates an authentication starting point in your codebase, including:
- User model and migration with asynchronous Argon2 password hashing and a
session_epochcolumn. - Auth Controllers (Login, Registration, Logout, and
logout_everywhere, which increments the account’s session epoch to end its session on every device). - Encrypted-session middleware that inserts the signed-in user’s id as
Extension<i32>and refuses a cookie whose session epoch is no longer the account’s. - A password controller (
src/controllers/password_controller.rs), a reset token model (src/models/password_reset.rs) and apassword_reset_tokensmigration: change password (signed in; checks the current password, keeps only the asking browser signed in) and reset by email (a 30-minute single-use token stored as a SHA-256 digest, one live token per account, the same answer for unknown emails; a reset signs out every device and redirects to/login?reset=done). Reset links useRULLST_PUBLIC_ORIGIN, never the request’sHostheader. Account emails are written in the request’s language (therullst::i18nlocale, elseAccept-Language) and name the productAPP_NAMEor the editableAPPLICATION_NAMEconstant. - HTML Views for Login, Signup, Forgot password, Reset password and Change password.
It targets the SQLx ORM: Turso-primary projects are rejected. The command
enables the orm, auth and mail umbrella features, registers the generated
controllers, middlewares, models and pages modules in src/lib.rs (or
src/main.rs), and refreshes the migration registry. It fails before writing
anything when src/models/user.rs, src/models/password_reset.rs,
src/controllers/auth_controller.rs, src/controllers/password_controller.rs,
src/middlewares/auth_middleware.rs or src/pages/auth.rs already exists,
when a *_create_password_reset_tokens_table.rs migration exists, or
when a *_create_users.rs/*_create_users_table.rs migration already creates
the users table (the blank database starter and the SaaS starter ship one). It also refuses a project without a SQL migration runner (pub mod migrations; in src/main.rs or src/lib.rs plus a
rullst::artisan!(crate::migrations::get_migrations()) call, which database
starters include), such as a --no-database starter, because nothing would
compile or apply the users migration. Generated code names the ORM through the
rullst::orm facade, so no direct rullst-orm dependency is needed. Mounting
routes (including POST /logout/everywhere and /password/change behind
auth_middleware, and credential_rate_limit on the password submissions),
the security baseline and the mail settings (MAIL_DRIVER, MAIL_FROM,
RULLST_PUBLIC_ORIGIN) remain application work, as does calling
User::revoke_sessions when you deactivate an account. The SaaS starter and
the LMS example (examples/lms) ship the same password flows already mounted.
cargo rullst make:mfa
Scaffolds a server-side RFC 6238 TOTP second factor: src/controllers/mfa.rs
and a reversible user_mfa_factors migration (one factor per account). The
secret is generated and stored on the server and bound to the signed-in
account: mfa_setup, mfa_confirm and mfa_verify take the user id from the
Extension<i32> that the cargo rullst auth middleware inserts, and a client
never submits a secret. Verification uses verify_totp_step_after with an
atomic conditional update of last_accepted_step, so each code is accepted at
most once. Setup returns the secret and otpauth:// URI once with
Cache-Control: no-store; enrollment stays pending until mfa_confirm
accepts a current code.
The command targets the SQLx ORM (Turso-primary projects and projects without
a migration runner are rejected, as for cargo rullst auth), enables
the orm and security umbrella features, registers the module, refreshes the
migration registry and refuses to overwrite an existing src/controllers/mfa.rs
or *_create_user_mfa_factors_table.rs migration. Mount the handlers as POST
routes behind the authentication middleware, CSRF protection and rate limiting,
and require a recent password check before enrollment. To gate login, keep the
session pending until verify_second_factor succeeds; the generated auth
controller issues a full session after the password. Secrets are stored
unencrypted in the database, so protect that table and its backups like
credentials. Recovery codes and factor reset remain application work.
cargo rullst make:oauth <provider> (v13 prerelease preview)
Adds sign-in with Google, GitHub, Microsoft or Apple to the account model of
cargo rullst auth or of the SaaS starter (and of applications generated from
the former LMS starter, such as examples/lms). Run it once per provider;
other providers are refused until they have a generator and tests.
cargo rullst make:oauth google # or github, microsoft, apple
cargo rullst db:migrate # once, after the first provider
The first provider writes src/controllers/oauth/mod.rs (shared flow, account
linking and sign-in), src/models/oauth_identity.rs and an
oauth_identities migration (provider and subject unique, the user id with
ON DELETE CASCADE, created at). Every provider writes
src/controllers/oauth/<provider>.rs (settings and the /auth/<provider>
and /auth/<provider>/callback handlers), registers it in the shared module
and documents its settings in .env.example; a second provider adds no
migration and shares the table, the flow and the linking policy. The command
enables the oauth umbrella feature and the challenge store of the project’s
database (oauth-sqlite, oauth-postgres or oauth-mysql) and adds an
application feature oauth-mock = ["rullst/oauth-mock"]. In the SaaS starter
and LMS-starter applications it also mounts the routes after the password login, with the same
credential_rate_limit, calls controllers::oauth::<provider>::initialize()?
at the start of router() and adds a “Sign in with …” link to the login page;
for a cargo rullst auth project, whose router is the application’s, it
prints the routes to mount.
The flow keeps the OAuth state, PKCE verifier and OIDC nonce in
rullst_connect_login_challenges (created on first use) and only an opaque id
in the rullst_oauth_challenge cookie (ten minutes, HttpOnly,
SameSite=Lax, Path=/auth/<provider>, Secure in staging and production);
see Tutorial 42.
The callback consumes the challenge once, checks the state and the PKCE
verifier and, for OpenID Connect providers, the ID token and its nonce, then:
- an identity already in
oauth_identities(provider and subject) signs in to its account; - otherwise the provider must have verified the email; an unverified email is refused;
- a verified email that belongs to no account creates one without a password, linked to the identity;
- a verified email that already belongs to an account is refused by default
(
EXISTING_ACCOUNT_POLICY = ExistingAccountPolicy::Refuseinsrc/controllers/oauth/mod.rs), because registration does not prove that whoever registered the address owns it.LinkAndResetPasswordlinks it instead, removes the account’s password and signs out every device in the same transaction. The policy applies to every provider, including a second provider of an account that signed in with the first.
What counts as the subject and as a verified email differs per provider:
| Provider | Subject | Verified email |
|---|---|---|
google (OpenID Connect) | ID token sub | email with email_verified: true |
github (OAuth 2.0) | numeric account id of GET /user, never the login name | the primary address of GET /user/emails marked verified; the default user:email scope grants it |
microsoft (OpenID Connect) | <tid>:<oid> (directory and object IDs) | email only with the optional claim xms_edov: true, which the app registration must request with email |
apple (OpenID Connect, form POST) | ID token sub | email with email_verified; possibly a private relay address |
Sign-in issues the password login’s cookie, make_login_cookie_with_epoch with
the account’s current session epoch; an account whose session epoch the
password login refuses is refused the same way. The page to return to comes
from ?next= on /auth/<provider>, and only a same-site absolute path is
kept.
Settings (process environment, then .env); setting none of a provider’s
credentials turns it off and hides its link, a partial set stops startup:
- Google:
GOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRET. - GitHub:
GITHUB_CLIENT_ID,GITHUB_CLIENT_SECRETof an OAuth App. - Microsoft:
MICROSOFT_TENANT(required with the credentials:common,organizations,consumersor a directory ID GUID; there is no default, because it decides who can create accounts),MICROSOFT_CLIENT_ID,MICROSOFT_CLIENT_SECRET. - Apple:
APPLE_CLIENT_ID(the Services ID),APPLE_TEAM_ID,APPLE_KEY_IDandAPPLE_PRIVATE_KEY, the PEM text of the.p8key (newlines may be written as\n). Rullst Connect signs a five-minute ES256 client secret with it for each code exchange; a key that cannot sign stops startup. Keep it in the environment or a secret store: the command never writes it to a file. <PROVIDER>_REDIRECT_URLdefaults toRULLST_PUBLIC_ORIGINfollowed by/auth/<provider>/callback, and tohttp://localhost:3000/...in development; staging and production without either stop at startup.mock_*values select the offline provider, which signs in[email protected]without leaving the application; the command prints the exact settings, such asGITHUB_CLIENT_ID=mock_github GITHUB_CLIENT_SECRET=mock_github cargo run --features oauth-mock. Without that feature the application stops at startup with an error naming it, and staging and production refuse mock credentials.
Apple POSTs its callback from appleid.apple.com, so make:oauth apple
mounts POST /auth/apple/callback (plus a GET that only the offline mock
uses), makes the challenge cookie SameSite=None; Secure, and lists
/auth/apple/callback in [security] csrf_signed_webhook_paths of
Rullst.toml (creating the file when missing): instead of the CSRF token,
the handler requires the state of the login challenge that the browser’s
challenge cookie names. Its name arrives only on the first authorization, in
the unsigned user form field, and only names a new account. Register the
MAIL_FROM domain with Apple’s Private Email Relay service so account emails
reach relay addresses.
The command requires a strict single-database profile (strict-sqlite,
strict-postgres or strict-mysql, as cargo rullst new --database ...
selects), the User model with session_epoch, current_session_epoch and
find_by_email, the generated auth_controller helpers, src/pages/auth.rs
and a migration runner. It refuses Turso-primary projects, and it fails before
writing anything when any output exists or the provider is already installed.
It does not edit auth_controller.rs or user.rs.
🗄️ 3. Database and Migrations (db:*)
Every db:* command (including cargo run -- db:migrate and a deployed
binary’s db:migrate job) and studio select the database exactly like
Server: the process DATABASE_URL, then DATABASE_URL in ./.env (which
never overrides a variable already set in the process), then [database].url
in Rullst.toml, read with a TOML parser. A Server::with_db URL applies when
Server::run intercepts the command. Without a configured database, a db:*
command exits with status 1 instead of creating a SQLite file. Configuration
errors (reported without file content) and database initialization failures
also exit with status 1.
cargo rullst db:migrate
Runs cargo run -- db:migrate, which the application’s
rullst::artisan!(crate::migrations::get_migrations()) call intercepts. It
applies, in one new batch, every migration registered in
src/migrations/mod.rs whose name is not yet recorded in the ORM’s
migrations table, calling each migration’s up.
cargo rullst db:rollback
Reverts the last batch: it calls down for each migration recorded in the
latest batch, newest first, and removes their records.
cargo rullst db:status
Lists every registered migration as Applied or Pending according to the
migrations table.
cargo rullst db:seed
Runs the seeders (rullst::db::Seeder implementations) that main passes as
the second argument of rullst::artisan!(migrations, seeders), for example an
initial administrator or development data.
Turso-primary starters handle db:migrate, db:rollback and db:status in
their generated main.rs through TursoOrm instead of rullst::artisan!.
cargo rullst studio
Runs cargo run -- studio, which Core’s Artisan handler serves as a smaller
local compatibility UI (database, AI, telemetry, security, capital and span
pages) on 127.0.0.1:5555 until it stops; it is not the full rullst-studio
router that debug builds of the generated starters start on the same port
(see Rullst Studio). Treat it as a privileged
development tool; do not expose it publicly without an independently reviewed
authentication, authorization, and TLS boundary.
cargo rullst studio --remote [--port PORT] needs no project: it starts the
installed standalone rullst-studio binary
(cargo install rullst-studio --version =13.0.0-alpha.4 --features remote --locked;
Cargo installs a prerelease only when its version is named),
which serves Studio’s remote-projects mode on 127.0.0.1:5556 (or PORT)
and reads the production monitoring endpoint
of the projects you add. Without the binary it explains how to install it.
🧠 4. Analyzers and Code Generators (generate:*)
cargo rullst generate:openapi
Reads recognizable route and Rustdoc patterns and writes an OpenAPI 3.0 draft to
openapi.json in the project root.
Dynamic routes, custom extractors, and semantic constraints may require manual
edits; validate the result with an OpenAPI validator before publishing it.
cargo rullst generate:ts
Scans recognizable route declarations in src/main.rs and src/lib.rs and
emits rullst-client.ts with unchecked request/response placeholders. Axum
{name} and {*name} captures (and legacy :name segments) become method
arguments interpolated with encodeURIComponent (per segment for a wildcard).
State-changing requests (anything but GET, HEAD, OPTIONS and TRACE)
follow the CSRF middleware’s double-submit contract: the client echoes the
rullst_csrf cookie in the X-CSRF-Token header and sends cookies with
credentials: 'same-origin'. The cookie is set by a GET/HEAD response, so
load a page or issue a GET before the first state-changing call. Outside a
browser, or for a cross-origin baseUrl, pass
new RullstClient(baseUrl, { csrfToken: () => token, credentials: 'include' }).
Review the output before use; route scanning does not establish DTO shapes,
serialization or authorization.
cargo rullst generate:api (v13 candidate)
Consumes one explicit bounded OpenAPI 3.1 profile and generates Rust DTOs/codecs,
a typed TypeScript HTTP client and a canonical schema copy. Requires --schema
and --output; --check verifies freshness without writes. Unsupported shapes
fail before generation. See the profile and executable acceptance.
cargo rullst generate:diagram
Analyzes primary and foreign keys defined in your Models and exports a diagram.md file containing Mermaid.js code, visually generating an Entity-Relationship (ER) diagram.
The file starts with a generator marker comment. An existing diagram.md is
replaced only when it carries that marker (or is the single unmarked Mermaid
block earlier releases wrote); the command refuses a hand-written file or a
symlink, so move it aside to regenerate the diagram.
cargo rullst generate:models / cargo rullst make:models-from-db
Connects to an existing database and generates reviewable starter structs from
the tables and columns visible in SQLite or the current PostgreSQL/MySQL schema.
Table lookups are parameterized and SQL identifiers are allowlisted.
PostgreSQL and MySQL metadata columns are cast to text and aliased, so
PostgreSQL 12+ sql_identifier columns and MySQL 8 upper-case labels are read
portably; a missing or undecodable metadata column fails with an error rather
than a panic. Table module names are normalized, while collisions and database columns that would
require an unsupported ORM field remapping fail before the output directory is
written. Existing model files are never replaced: if any <table>.rs target
already exists, the command fails before writing anything. An existing
mod.rs keeps its content and receives only missing pub mod declarations.
The bounded type mapping falls back to String; review keys, relations, custom
types, schema selection and generated files before compiling them.
- Required Flags:
--driver:postgres,mysql, orsqlite.--url: The complete connection string.
- Optional Flags:
--output: Where to save the generated structs (Default:src/models).
cargo rullst generate:ai-context [--check]
The v13 candidate writes a bounded .llms.txt and .rullst/context-map.json
with dependency metadata, configuration key names and source paths. It creates
AGENTS.md only when absent and preserves existing project instructions.
--check detects missing, altered or stale inventory without writing files.
See project context for limits, exclusions and legacy migration.
cargo rullst ai (removed)
The terminal AI assistant of the earlier 13.0 prereleases (ai, ai connect,
ai status, ai disconnect, ai upgrade, ai fix and ai review) was
removed in 13.0.0-alpha.4. cargo rullst ai …, with any arguments, prints
what replaces it and exits with status 1; the command is hidden from --help,
completions, the command palette and the home screen.
Use your editor’s or terminal’s AI assistant instead, with the project’s
AGENTS.md and .llms.txt
(generate:ai-context writes and
checks them) and the framework’s published
llms.txt. The rullst-ai crate,
which applications use to call models, is unchanged. In place of
ai fix <error-id>, the development error page’s “Ask your AI assistant”
panel gives a prompt to copy into your assistant and the path of the recorded
error context. Credentials that
ai connect saved stay in ~/.config/rullst/credentials.toml
($XDG_CONFIG_HOME/rullst/, or %APPDATA%\rullst\ on Windows), and its git
checkpoints under refs/rullst/ai-checkpoints/, until you delete them. See
the migration guide.
cargo rullst eject [--force] [--output <path>]
Writes a reviewable Axum/Tokio entry-point template (src/ejected_main.rs). It
is not a translation of the project’s main.rs: it serves a placeholder route
from application_routes(), wraps it in the configured Rullst security
baseline (apply_security_baseline with Rullst.toml, as Server applies it)
and reaches Axum through rullst::web::axum, so it compiles without a direct
Axum dependency. Move the application’s routes and module declarations into it
before use. Static files, rate limiting, lifecycle probes, hot reload,
sessions, authentication and authorization are not included.
- Flags:
--force: Replacessrc/main.rswith the template after copying the original tosrc/main.rs.rullst-backup(an existing backup stops the command). Application routes are not carried over.--output <path>: Specifies a custom output path belowsrc/for the ejected file.
cargo rullst deploy:doctor (v13 candidate)
Read-only inspection of a local deployment configuration snapshot:
cargo rullst deploy:doctor --env-file .env.production --json
cargo rullst deploy:doctor --config Rullst.production.toml --process-env
Reuses Core environment/security validation, catches obvious key/configuration
mistakes and identifies application-policy reviews without echoing values.
--target <production|staging> (default production) names the expected
environment; --json prints rullst.deployment-diagnostic.v1.
Explicit environment sources remain separate. Exit zero covers only the inspected
local profile; deployment_verified remains false. See the
input and output contract before using it in CI.
cargo rullst inspect [target]
Scans source files and prints structural summaries in the terminal without starting a server, expanding macros or connecting to a database.
- Arguments:
[target]: The item or file to inspect (defaultroutes):routeorroutes: Listsget/post/put/deletedeclarations written asmethod("path" => handler)on one line in the regular.rsfiles undersrc/. Likeauditandgenerate:diagram, the walk does not follow symlinks and is bounded in depth and entries; it reports when a bound left the table incomplete.modelormodels: Lists the structs, enums andpubfields declared insrc/models.schema: Prints, as JSON, the table, fields, Rust types and optionality of every#[derive(Orm)]struct undersrc/(the extractormake:migration:autouses). It describes the models, not the live database. A project-providedrullst-schema.jsonis printed instead when present; Rullst does not generate that file.<path/to/file.rs>: Displays the first 40 lines of any target Rust file with line numbers.
- Optional Flags:
--json: Printsroutes,modelsorschemaas one versioned document (modelsandschemasince 13.0.0-alpha.4); another target with--jsonis refused.
inspect routes --json prints rullst.cli-routes.v1:
{
"schema": "rullst.cli-routes.v1",
"schema_version": "rullst.cli-routes.v1",
"complete": true,
"incomplete_reason": null,
"routes": [
{ "method": "GET", "path": "/posts/{id}", "handler": "controllers::posts::show" }
]
}
complete is false when the bounded source walk stopped early or src/ is
missing, with the reason in incomplete_reason.
inspect models --jsonprintsrullst.cli-models.v1:complete,incomplete_reasonandfiles, one entry persrc/models/*.rsfile (notmod.rs, no symlinks) with its publicstructandenumitems. A struct hasfields(name,type); an enum hasvariants. A file that is not valid Rust is named inincomplete_reason.inspect schema --jsonprintsrullst.cli-schema.v1: the samesourceandmodelsas the human output (struct,table,fieldswithname,rust_type,optional), or, when the project providesrullst-schema.json,source: "rullst-schema.json"with the parsed file undersnapshot.
🚀 5. Development, Infrastructure, and Build
cargo rullst dash
Opens the Ratatui development control surface in an interactive terminal. The dashboard reports the probed application port, supervised auto-reload state, the child process exit state, and the configured database profile; it does not label a database as connected merely because a URL exists. Logs and input queues are bounded, ANSI control sequences are removed, terminal state is restored on error, and the owned application process is stopped and reaped when the dashboard exits.
Live metrics (v13). Once a second the dashboard reads
GET /_rullst/dev-telemetry from the application it started, on
127.0.0.1:<port>, and shows only what the application reports:
- requests per second over the last 10 s, from the application’s exact request counters, and 4xx/5xx totals since the process started;
- errors: 5xx responses and their share of all requests over the last 60 s;
- p50/p95 latency over the last 60 s and a p95-per-poll sparkline (up to two
minutes), computed from the individual requests the dashboard observed. They
are marked
sampledwhen more requests arrived between two polls, or before the first poll of a process, than the 64 newest the application returns; - the newest requests with status, method, duration and path (never the query string);
- ORM queries since start and slow ORM operations (at least 100 ms);
- possible N+1 queries (see N+1 query warning);
- the pending jobs of a queue.
Counters, latency samples and slow operations restart with every new process
(a rebuild or r); the request list and the sparkline keep earlier entries.
Histories are bounded: 120 sparkline points, 50 requests, 16 slow operations and at most
4,096 latency samples. A response larger than 256 KiB, with another schema or
with inconsistent counters is rejected, and control or bidirectional-formatting
characters are replaced before anything reaches the terminal. A value the
application does not report is shown as not reported, never as zero. Only
the process the dashboard started is measured: a response carrying another
RULLST_DEV_GENERATION, for example from a second dev/dash already
listening on the port, replaces the metrics row with an ANOTHER PROCESS ON
THE PORT panel instead of showing that process’s figures.
The endpoint exists only in a debug build running in Development under
cargo rullst dev/dash; see the
telemetry guide. When the
application does not serve it, the metrics row is replaced by a TELEMETRY NOT
AVAILABLE panel with these steps:
- Serve the application with
rullst::Serverfrom Rullst 13 or newer (the generated starters do). - Run a debug build in Development: leave
RULLST_ENV/APP_ENVunset or setRULLST_ENV=development. - Press
r(or save a file) to restart.
| Value | Reported when | To enable it |
|---|---|---|
| Requests, latency, errors | The endpoint answers. | The steps above. |
| ORM queries and slow operations | Rullst’s default tracing subscriber is installed (Server::run installs it when the application has none) and keeps rullst_orm INFO spans. | Do not install another global subscriber first, and do not filter out rullst_orm=info with RUST_LOG. |
| Queue pending | The application passes its queue to the server. | Server::new(router).with_dev_queue(queue) (accepts a Queue or an Arc<Queue>). |
ORM queries count the outermost rullst.orm.query span of each ORM operation
(model queries, saves, deletes and Orm::raw). A chunk/chunk_by_id
traversal is not counted itself: each page it fetches and each operation its
handler runs counts separately. One operation can run several SQL statements,
and SQL executed directly through SQLx is not counted.
N+1 query warning
When the application reports that one request ran the same ORM operation at
least three times (the threshold Studio’s trace view uses), a POSSIBLE N+1
QUERIES panel appears below the logs. Each line shows the request’s method
and matched route (GET /posts/{id}), the operation fingerprint
(Comment.select_many (comments), built from the ORM span’s model, operation
and table labels; Rullst never records SQL text or bound values) and the
repetition count, followed by a hint to load the related rows eagerly or batch
the lookups into one query. Inspect the handler before changing it: repeating
an operation can be intentional. Raw statements (Orm::raw) and operations
started on another task (tokio::spawn) are not attributed to a request, so
they never produce a finding. The VERIFIED PROJECT STATE panel shows the
detection state in its N+1 check line: the number of findings since the
process started, N+1 detection needs request-correlated ORM telemetry (Rullst Core 13) when the application’s telemetry has no request-correlated ORM data
(an older Rullst Core), or N+1 detection needs ORM query telemetry when ORM
queries themselves are not reported (see the table above).
Performance note: a request that runs one ORM lookup per row (for example
loading each post’s comments inside a loop over posts) makes one database round
trip per row. Eager loading runs one
related-model query for all parents of a batch, and an IN query can fetch
the rows of several keys at once; measure the route before and after the
change.
Terminals at least 26 rows tall show the metrics row and terminals at least 105 columns wide add the recent-requests panel; shorter terminals show a one-line summary in the header. The layout adapts to narrower terminals and provides these keyboard controls:
r: restart the application from the current build (no rebuild). It is refused, with a notice, while the application is still starting, while a migration runs and while a saved change is being rebuilt (a successful rebuild restarts the application itself). The notice clears once the restarted application is ready, or says that its readiness was not confirmed or that it exited.o: open the application.s: probe the loopback Studio endpoint and open it only when reachable.d: open existing Scalar docs. Missing files produce explicitcargo rullst make:scalarguidance rather than silently modifying the project.m: rundb:migrateasynchronously and report its real exit result./: search both log panes;fcycles all/warning+error/error filtering.Tab: switch the focused log pane; arrows and Page Up/Page Down scroll it.c: clear dashboard logs.?: keyboard and metrics help. Any key closes it;qstill quits.qorEsc: exit (Esccloses the help first).
The footer lists d and Tab only on terminals at least 120 columns wide; the
help always lists every key.
The animated neon palette is enabled only for an interactive terminal. Set
RULLST_REDUCED_MOTION=1 (or true/yes) to stop the animation and keep the
colors. NO_COLOR removes the colors only; set both for a color-free, static
interface. Non-interactive automation should use
cargo rullst dev; dash fails clearly when no terminal is attached.
cargo rullst dev
Builds and starts a directly linked application. Saving source, static assets,
templates, Cargo.toml, Cargo.lock, Rullst.toml or .env schedules a
coalesced rebuild. A failed build leaves the current application running. A
successful build creates an owned executable snapshot, stops the previous
process and starts its replacement. The snapshot avoids locking Cargo’s build
output on Windows. Initial migrations run before startup; later migrations
remain an explicit command.
The same-origin browser client polls an opaque process-generation marker and
refreshes only when a different generation serves successfully. This is enabled
only in debug/development. Readiness verifies that marker, not just an open port.
Changing the configured port requires restarting the CLI. In-memory state and
unsaved browser state reset during reload. The process receives a bounded
shutdown interval before forced termination; this is a development facility.
Ctrl+C, SIGTERM (an IDE stop button or kill) and SIGHUP (a closed terminal)
end dev and dash the same way: the application’s process group is stopped
and its executable snapshot removed (on Windows, Ctrl+C and closing the console).
No scaffold question is required: dev and dash enable auto-reload, while
cargo run runs the application normally. The legacy --hot-reload scaffold
flag is rejected (since v12) because DLLs can split ORM/Tokio globals. Existing
legacy scaffolds can use their directly linked router; the supervisor removes
HOT_RELOAD from its child’s environment.
See Supervised Development Auto-Reload for limitations, failure recovery and the v13 architecture decision.
dev keeps its plain output; at startup it prints one line suggesting
cargo rullst dash for live requests, latency, errors and database metrics.
- Optional Flags:
--ts-sync: Regenerates the TypeScript client SDK (rullst-client.ts, asgenerate:tswrites it from the routes insrc/main.rsandsrc/lib.rs) after the initial build and after every successful rebuild. A failed generation is reported and the application keeps running.
cargo rullst build:client
Builds the library for wasm32-unknown-unknown, runs wasm-bindgen, and writes a
separate static/rullst-islands.js hydrator that awaits binding initialization.
It parses Cargo.toml, merges the required cdylib crate type without replacing
existing library crate types, and honors an explicit lib.name. The command
checks/installs the Rust target and wasm-bindgen-cli; any failed tool step
aborts. Bundle size and browser performance depend on the generated application
and must be measured.
- Flags:
--debug(builds the Wasm library with Cargo’s debug profile instead of--release).
cargo rullst build
Creates the monolithic final Production binary of the backend and writes Brotli
(.br) and Zstandard (.zst) siblings next to the html, css, js, json,
svg, wasm, xml and txt files under static/. The server prefers a
sibling over its source, so rerun the command after editing an asset and before
building an image; cargo rullst dev removes siblings that are not newer than
their source at startup and on every change (siblings without a source file are
kept).
- Flags:
--debug(builds with Cargo’s debug profile instead of--release).
cargo rullst green
Maturity: Experimental. It replaces cargo rullst footprint (13.0.0-alpha.1
to alpha.3) and is part of the main CLI, so it needs no separate install.
Reports figures it measured for the app, each with its method: the release
binary size, a local container image’s size (--image, through docker or
podman image inspect), the idle resident memory of the started app (Linux
VmRSS), the third-party dependency count from Cargo.lock and, with
--bench, the app process’s CPU time during a bounded closed-loop loopback
load and the CPU time per request. Anything unavailable is NOT MEASURED with
the reason. It measures no energy: a Software Carbon Intensity (SCI,
ISO/IEC 21031:2024) figure appears only as an estimate labelled estimate,
computed from the measured CPU time, your --cpu-watts and your
--grid-intensity, with every input shown.
Without --url it builds the release binary (reusing an existing build),
starts it with RULLST_ENV=production (or --env staging, which accepts the
starters’ mock billing credentials) on a free 127.0.0.1 port, measures it
and stops it. With --url it measures an app already running on loopback and
refuses any other host; it makes no network calls beyond loopback and the
local container engine, and never fetches grid intensity.
cargo rullst green --image my-app:latest
cargo rullst green --url http://127.0.0.1:3000 --path /health --bench --json
cargo rullst green --bench --cpu-watts 15 --grid-intensity 120
- Flags:
--url <http://127.0.0.1:PORT>,--env <production|staging>(defaultproduction; not with--url),--path <PATH>(default/),--image <NAME>,--bench,--duration <10s|1m|1500ms>(default10s, 1 s to 10 min; needs--bench),--concurrency <N>(default 4, at most 256; needs--bench),--cpu-watts <W>(needs--bench),--grid-intensity <gCO2e/kWh>(needs--cpu-watts),--embodied <gCO2e>(needs--grid-intensity),--json(versionedrullst.green.v1). - Exit status:
0when the measurement ran, even withNOT MEASUREDfields;1when the build failed or the app could not be started or reached;2for invalid arguments.
See the green guide for what each figure means and how to choose a grid intensity.
cargo rullst dockerize
Writes the Dockerfile (plus .dockerignore when absent) into an existing
project, as new --docker does. It refuses to replace an existing
Dockerfile; move a customized file aside to regenerate the template. The
binary is named after [package].name, read with a TOML parser (app when
Cargo.toml has no package name). As in deploy, a project whose
Rullst.toml (or .env.example) names a SQLite database gets
DATABASE_URL=sqlite:///app/data/rullst.db?mode=rwc in the image. cargo rullst deploy writes the same Dockerfile together with compose.yaml and the
Caddyfile.
Removed in 13.0.0-alpha.4: nixify, generate:buildah, foundry:init,
foundry:deploy and make:grpc. Files they generated stay yours; see
Advanced deployment for Nix, Buildah, Podman
Quadlet and Kubernetes.
🛡️ 6. Security, Compliance & System Diagnostics
cargo rullst audit
Executes bounded automated checks across recognized source, configuration, route, dependency, and local network patterns, and can invoke installed dependency scanners. Static findings require human review and are not a penetration test or compliance certification.
- Optional Flags:
--ai: Prints fixed, rule-based remediation suggestions after the checks. It calls no AI model or network service; the flag keeps its legacy name.--compliance: WritesSECURITY_COMPLIANCE.md, an evidence report. Each executed check isNO FINDINGS,NO FINDINGS OUTSIDE EXCEPTIONS,FINDINGS,GENERATED,OBSERVED,NOT CHECKED, orERROR, and control families outside the command’s scope areNOT EVALUATED. It never reportsPASSand is not a SOC 2, ISO 27001, or transport certification.--idor: Fails on parameterized routes without an explicit adjacent// rullst-access: public|owner|role|admin — reasonclassification.ownerrequiresRbacGuard::authorize_owner_or_role;rolerequires a recognized role guard;adminrequiresRequireRoleLayerorNexusAuthPolicy::protect_router;publicis restricted to recognized GET routes. The marker goes on the route’s line or the line above it; in a multi-line.route(call, on the line above the path literal. The guard may appear anywhere in the same crate’ssrctree outside comments and#[cfg(test)]items, so the check does not prove that the route is mounted behind it. Manual review and runtime negative tests remain required.--geiger: Inventoriesunsafein the dependency tree. Unsafe may be justified and requires review; the command does not prove a zero-unsafe invariant.--sbom: Generates a standardized CycloneDX 1.5 JSON Software Bill of Materials (sbom-cyclonedx.json) fromCargo.lock, with the SHA-256 checksums the lockfile records. It contains no license metadata.--audit-ignore RUSTSEC-YYYY-NNNN: Passes one explicit, repeatable advisory exception tocargo audit. A successful run is reported as NO FINDINGS OUTSIDE EXCEPTIONS, not “no findings”; the caller must separately version, own, review, and expire every exception.--network: Checks a bounded list of local ports/bindings for potentially exposed services; it is not a comprehensive network scan. The TCP listener inventory runsss -ltnH(Linux iproute2). Where it cannot run, as on macOS, Windows or a Linux image without iproute2, the check is reported asERRORand the command exits non-zero instead of reporting a clean scan.--report [md|html|json](new in 13.0): Runs the static report checks below (mapped to OWASP ASVS 5.0.0 Level 1, with a personal-data inventory and accessibility heuristics) and writesSECURITY_REPORT.md(the default),SECURITY_REPORT.htmlorSECURITY_REPORT.jsonin the current directory;--output <path>chooses another file (its directory must exist). It reuses thecargo auditstep with its--audit-ignoreexceptions and the--idorscanner, and cannot be combined with--json,--compliance,--sbom,--network,--geigeror--ai. See the security report guide.--json: Moves the progress lines to standard error and prints onerullst.cli-audit.v1summary on standard output; the exit status is unchanged (non-zero for findings or incomplete requested checks):
{
"schema": "rullst.cli-audit.v1",
"schema_version": "rullst.cli-audit.v1",
"issues_found": 0,
"checks": [
{ "id": "secret_scan", "status": "not_checked", "count": null, "exceptions": [], "detail": "no .env file was available to inspect" },
{ "id": "dependency_audit", "status": "no_findings_outside_exceptions", "count": null, "exceptions": ["RUSTSEC-2099-0001"], "detail": "..." }
]
}
Check ids are secret_scan, dependency_audit, unsafe_scan, idor_scan,
sbom and network_scan, always in that order; status is no_findings,
no_findings_outside_exceptions, findings, generated, observed,
not_checked or error, and count is set for findings, generated and
observed. Details are redacted like error reports.
In a package directory, the unsafe and IDOR/BOLA scans cover its src and the
src of every workspace member below it, as listed by cargo metadata; in a
directory without src, such as a virtual workspace root, the IDOR/BOLA scan
walks every src tree below it. The source scans (unsafe syntax, IDOR/BOLA
routes and listener bindings) do not follow symlinked files or directories and
skip target/ and .git/. A walk
stops at 64 directory levels or 250,000 entries; reaching either bound is
reported as a finding, so the scan fails as incomplete instead of passing. The
route and listener scans skip each top-level #[cfg(test)] item (such as
mod tests; or an inline test module) on its own; code after it is still
scanned.
cargo audit and the SBOM read the project’s own Cargo.lock. In a workspace
member, which has none, they read the lockfile of the workspace root that
cargo metadata reports, and cargo audit receives it as --file; the SBOM
then lists every package of that workspace lockfile.
SBOM components come from Cargo.lock. Only crates.io packages receive the
plain pkg:cargo/<name>@<version> purl; a package from another registry adds a
repository_url qualifier, a git package adds a vcs_url qualifier with the
locked commit, and path or workspace packages (including the application) have
no purl. Every component that is not from crates.io carries a
rullst:cargo:source property with its lockfile source, or local.
SECURITY_COMPLIANCE.md and sbom-cyclonedx.json are written in the current
directory and replace a previous regular file. Because an audit may run on an
untrusted checkout, the command refuses to write either file through a symlink.
Security report (--report)
The report maps each check to OWASP ASVS 5.0.0
(release v5.0.0_release) Level 1 requirement IDs and section names, and
lists the Level 1 requirements no check relates to as NOT EVALUATED, by
chapter. A header states that it does not replace a manual review or a
penetration test; it never reports PASS or a certification.
| Check id | What it reads | ASVS 5.0.0 |
|---|---|---|
security_headers | Server::new or a header layer in src; [security] csp/coep in Rullst.toml | V3.4.1 (L1); V3.4.3, V3.4.4, V3.4.6 (L2) |
csrf | Single-line write routes; Server, csrf_middleware, csrf_signed_webhook_paths | V3.5.1 (L1) |
cookies | Set-Cookie-shaped string literals, cookie builders, csrf_same_site | V3.3.1 (L1); V3.3.2, V3.3.4 (L2) |
auth_rate_limit | Authentication write routes; a rate limiter or LoginGuard | V6.3.1 (L1) |
committed_secrets | Files from git ls-files (not target/, binaries, symlinks or files over 2 MiB) | V13.3.1 (L2), chapter V13 |
dependency_audit | cargo audit with the --audit-ignore exceptions | V15.2.1 (L1) |
idor | The --idor route-classification scan | V8.2.1, V8.2.2 (L1) |
a11y_img_alt, a11y_form_labels, a11y_html_lang | html! bodies and templates under src/ and templates/ | WCAG 2.2 1.1.1; 1.3.1, 3.3.2, 4.1.2; 3.1.1 |
Each check reports no_findings, no_findings_outside_exceptions,
findings, not_checked or error (shown as NO FINDINGS, FINDINGS, …
in Markdown and HTML) with a one-line fix and a link to the guide. A
committed-secret finding shows only the file, the line and the first four
characters of the value followed by …. The personal-data inventory
(#[privacy], #[orm(encrypted)], SecretString, #[orm(masked)], and
review for unclassified fields named like email, phone, cpf, cnpj, ssn,
birth, address, document or ip) is observed and never changes the exit
status.
Exit status: 1 when any check reports findings or error; 0 otherwise,
including when checks are not_checked (no src/, no Git work tree, no
cargo-audit). The terminal prints a grouped ✓/!/✗ summary, plain under
NO_COLOR, CI, TERM=dumb or a non-terminal stdout.
Formats: Markdown has one GitHub table per section; HTML is one
self-contained file (inline CSS with light and dark schemes, no script, no
external asset, every value escaped); JSON is the rullst.cli-audit-report.v1
document below, without ANSI sequences:
{
"schema_version": "rullst.cli-audit-report.v1",
"generated_at": "2026-10-08T12:00:00+00:00",
"tool": { "name": "cargo-rullst", "version": "13.0.0" },
"notice": "This report records bounded static checks for a reviewer. ...",
"standard": { "name": "OWASP Application Security Verification Standard", "version": "5.0.0", "level": 1, "source": "https://github.com/OWASP/ASVS/releases/tag/v5.0.0_release", "level1_requirements": 70, "level1_mapped": 7, "mapping_note": "..." },
"accessibility_standard": "WCAG 2.2",
"exit_non_zero": true,
"checks": [
{ "id": "csrf", "group": "security", "title": "CSRF", "status": "findings", "count": 1, "exceptions": [], "detail": "...", "fix": "...", "docs": "https://rullst.github.io/Rullst/book/security-report.html#csrf", "asvs": [{ "id": "V3.5.1", "section": "V3.5 Browser Origin Separation", "level": 1 }], "asvs_chapter": null, "wcag": [], "findings": [{ "file": "src/lib.rs", "line": 12, "message": "...", "preview": null }] }
],
"personal_data": { "status": "observed", "count": 2, "review": 1, "detail": "...", "docs": "...", "asvs_chapter": "V14 Data Protection", "fields": [{ "model": "User", "field": "email", "file": "src/models/user.rs", "line": 8, "classification": "review", "encrypted": false }] },
"not_evaluated": [{ "chapter": "V1", "name": "Encoding and Sanitization", "status": "not_evaluated", "whole_chapter": true, "requirements": ["V1.2.1", "..."] }]
}
checks always holds the ten ids of the table above, in that order. A new
key or a changed meaning needs a new schema_version.
Limits: every check is a bounded static heuristic. Routes are recognized
only when declared on one line; limiter, guard and header evidence is
project-wide and does not prove a route is mounted behind it; untracked files
and Git history are not scanned for secrets; templates are not rendered and
includes are not followed. The source walks share the audit bounds (no
symlinks, 64 levels, 250,000 entries) and an incomplete walk turns the source
checks into error. Files over 2 MiB are not read.
cargo rullst hook:install
Installs managed pre-commit and commit-msg wrappers. The first runs
cargo fmt --all -- --check, strict workspace Clippy, and
cargo rullst audit --idor; the second enforces Conventional Commits while
accepting the subjects Git itself writes for merges (Merge branch,
Merge remote-tracking branch, Merge tag, Merge pull request, …),
reverts (Revert "...") and fixup!/squash!/amend! commits. Existing
active hooks are moved to explicit .rullst-original backups and invoked first,
while reinstalling the managed wrappers is idempotent. The command supports
linked worktrees, fails clearly outside a Git worktree, and refuses a backup
collision instead of overwriting it. When core.hooksPath (local, global or
system Git configuration, as used by Husky or shared hook directories) selects
another directory, it fails before writing, because Git would never run wrappers in the
default hooks directory; call the checks from that hook manager instead. These local hooks are bypassable by design;
protected CI remains authoritative.
cargo rullst doctor
Runs grouped health checks and shows each one as ✓ (pass), ! (warning),
✗ (failed) or · (information, optional or not applicable). Every warning and
failure carries a fix (one line, or a short configuration snippet) and a link
to its group below. The command
exits with status 1 when any check failed; warnings do not fail it.
--fix installs missing rustfmt/clippy components with
rustup component add rustfmt clippy, then checks again. Outside a project
only the toolchain, project, security (cargo-audit) and disk groups appear.
Checks are local and bounded: tool probes are --version calls run in
parallel, the database probe is a 2-second TCP connect (no credentials are
sent) and a SQLite file is opened read-only. Kani is only looked up as a
cargo-kani executable (in Cargo’s bin directory or on PATH), never run:
cargo kani starts its first-time download and toolchain installation when it
is not set up yet. Values from .env are measured,
never printed.
Doctor: toolchain
rustc (✗ when missing or older than the 1.96.0 MSRV), Cargo, rustfmt and
clippy (fixable with --fix), Git, the wasm32-unknown-unknown target when
the project has src/islands, and one informational line for the optional
tools (cargo-deny, cargo-geiger, cargo-mutants, Kani, cargo-llvm-cov, Docker).
On Linux, toolchain.linker (v13) is a ! hint, never a failure, when no
fast linker is selected: no -fuse-ld=mold, -fuse-ld=lld (or
--ld-path= naming one of them) and no linker entry in [build], the host’s
[target.<triple>] or a [target.'cfg(..)'] table of any .cargo/config.toml
(or legacy .cargo/config) from the working directory up, or of
$CARGO_HOME/config.toml (~/.cargo by default), and none in RUSTFLAGS,
CARGO_ENCODED_RUSTFLAGS, CARGO_BUILD_RUSTFLAGS or the host’s
CARGO_TARGET_<TRIPLE>_RUSTFLAGS/_LINKER. It says whether mold or ld.lld
is on PATH and shows the snippet to add, for example:
[target.aarch64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
If the file already has that table, add the two flags to its rustflags
instead of repeating the table. The check is skipped on other operating systems
and on x86_64-unknown-linux-gnu with Rust 1.90 or newer, where rustc already
links with its bundled LLD by default.
Doctor: project
The enclosing Rullst project (a Cargo.toml that depends on rullst) and its
rullst requirement compared with the CLI’s major version; an older project
suggests cargo rullst upgrade --dry-run.
Doctor: config and .env
Whether .env parses, which keys listed in .env.example are set neither in
.env nor in the process environment (names only), whether RULLST_ENV
(or APP_ENV) names a known environment, and whether Rullst.toml is valid.
Doctor: database
The primary database from DATABASE_URL (environment, then .env, then
[database].url in Rullst.toml): a SQLite file must exist or be creatable;
PostgreSQL and MySQL/MariaDB must accept a TCP connection (✗ otherwise).
REDIS_URL is probed the same way (! when unreachable). Turso/remote and
mock_* endpoints are not contacted.
Doctor: migrations
The src/migrations/m*.rs files and, for a local SQLite database, which of them
the ORM migrations table has not applied yet (cargo rullst db:migrate).
For server databases run cargo rullst db:status.
Doctor: security baseline
cargo-audit installed, .env neither tracked nor unignored by Git (✗ when
tracked), APP_KEY set to at least 32 characters and not a template
placeholder, and a Cargo.lock for reproducible builds.
Doctor: disk space
Free space on the filesystem of the project (or the working directory): ✗ below 1 GiB, ! below 5 GiB. Not measured on Windows yet.
cargo rullst doctor --json prints one document on standard output and keeps
the exit status rules:
{
"schema": "rullst.cli-doctor.v1",
"schema_version": "rullst.cli-doctor.v1",
"cli_version": "13.0.0",
"ok": false,
"summary": { "pass": 11, "warn": 5, "fail": 1, "info": 2 },
"groups": [
{
"id": "database",
"title": "Database",
"checks": [
{
"id": "database.primary",
"title": "Primary database",
"status": "fail",
"detail": "PostgreSQL at 127.0.0.1:5432: connection refused",
"fix": "Start the server (for example `docker compose up -d`) or fix the URL in .env",
"docs": "https://rullst.github.io/Rullst/book/cli_reference.html#doctor-database"
}
]
}
]
}
status is pass, warn, fail or info; fix and docs are null for
passing checks. Group ids are toolchain, project, config, database,
migrations, security and disk; check ids (toolchain.rustc,
config.env_keys, security.app_key, …) are stable, but a check appears
only when it applies.
cargo rullst academy:doctor
Evaluates the twelve requirements of ProductionPreset::academy() (identity,
school membership, entitlements, object authorization, tenant isolation,
assessments, score events, durable automation and audit, content safety,
privacy and distributed abuse controls) against the declarations in a
rullst.academy-evidence.v1 file passed with --evidence <file>. It reads
only that file and never inspects the project, so it needs no Academy
scaffold: v13 removed the complete Academy scaffold, and the LMS example
(the former LMS starter) has no evidence file. Without --evidence every requirement is
NOT_EVALUATED.
Each requirement is reported as PASS, FAIL, SKIPPED or NOT_EVALUATED.
Unknown fields or requirements, a repeated requirement and a PASS without a
non-empty evidence reference are rejected. --json prints
rullst.academy-diagnostic.v1 with certification (always false),
contract_satisfied and each requirement’s status and declared_evidence.
The command exits with status 1 until every requirement has an evidenced
PASS. Evidence strings are caller declarations, not verified proof; see the
Academy production-boundary diagnostic.
🛠️ Quick CLI Cheat Sheet
# Create a new project (the wizard asks for the starter, database and features)
cargo rullst new my_app
# Reverse-engineer ORM models from an existing database
cargo rullst generate:models --driver postgres --url "postgres://user:pass@localhost:5432/mydb"
# Statically inspect routes, models, or schemas in the terminal
cargo rullst inspect route
cargo rullst inspect model
# Open the local Studio compatibility UI on 127.0.0.1:5555
cargo rullst studio
# Map the project (AGENTS.md, .llms.txt) for your editor's or terminal's AI assistant
cargo rullst generate:ai-context
# Write the Dockerfile, compose.yaml and Caddyfile for a Linux VM (Docker or Podman)
cargo rullst deploy