Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

36. Assisted framework upgrades

Rullst v12 introduced a bounded upgrade transaction for existing applications. The goal is to make the safe, repeatable part a single command while refusing to guess about application data or security policy.

The transaction steps below were written for 12.1.0; check publication status. The v12 line reached end of life on 16 October 2026, so a v12 application moves to v13 with the v13 CLI. The v13 source rules and the rule classification are described in v12 to v13: source findings and belong to the 13.0.0-alpha.4 prerelease. Install the exact release and complete the application-specific validation below before any production rollout.

What the command can guarantee

cargo rullst upgrade can inventory the Cargo workspace, update the Rullst release train, apply compiler suggestions, require cargo check, and restore the files it controls after a failure. It cannot prove that a database upgrade, authorization rule, provider integration or deployment still behaves correctly.

The automatic transaction owns only:

  • versioned Rullst dependencies in exact Cargo workspace manifests;
  • the root Cargo.lock produced by Cargo resolution;
  • Rust edits proposed by cargo fix;
  • a cargo check --workspace --all-targets --locked gate using the application’s selected features.

The 12.1 updater writes managed requirements as =VERSION, preserving the explicitly selected release instead of allowing Cargo to choose a later patch or minor version. cargo fix resolves the candidate lockfile, and the final check must use that resolution unchanged. Review these pins before restoring any broader dependency-update policy in your application.

It never runs migrations, changes secrets, invents tenant/ownership policy, opens Nexus or Studio, contacts application providers, or marks the result production-ready.

1. Prepare the application

Create a branch, make the worktree reviewable, record the old test result, and back up every database. Prove the database backup can be restored before changing the framework.

Install the exact CLI from the same release train as the target framework. For a v12 application, that is the v13 prerelease (Cargo never selects a prerelease on its own):

cargo install cargo-rullst --version =13.0.0-alpha.4 --locked --force

The framework command does not update its own executable. This matters for v5: the already-published v5 CLI cannot gain the v12 migration engine retroactively, and the v13 CLI upgrades only from v12 or later. A v5 application first moves to v12 with the end-of-life 12.x CLI (cargo install cargo-rullst --version 12.1.0 --locked --force, as the steps below show), then to v13. Run each CLI inside the application.

2. Inspect without writing

cargo rullst upgrade --dry-run

The plan shows every dependency edit and source finding. MUST-CHANGE (BLOCKER in the 12.x CLI) means a known old API or option requires a source change; REVIEW means the code uses an API, feature or configuration whose behaviour changed and must be revalidated. Neither label means that unreported code is automatically safe.

For automation, request JSON:

cargo rullst upgrade --dry-run --json > upgrade-plan.json

The root object uses schema_version: "rullst.upgrade-plan.v1" and identifies the rule catalog, exact target, manifest changes, detected source majors, findings, automatic scope and mandatory manual gates. Consumers must reject an unknown schema version rather than silently interpreting it as v1. The v13 CLI extends v1 additively (see the v13 fields); consumers must ignore keys they do not know.

Use an explicit target to make the selected published version visible:

cargo rullst upgrade --to 13.0.0-alpha.4 --dry-run

Other targets must exist in the registry and belong to the installed CLI’s major train, so a CLI never applies rules it does not carry: a v12 CLI cannot plan a v13 migration, and the v13 CLI cannot downgrade a project.

3. Apply the transaction

After resolving or accepting every finding:

cargo rullst upgrade

Before the first write, the CLI snapshots Cargo workspace manifests, the root lockfile and Rust sources under:

target/rullst-upgrades/<UTC-run-id>/
├── files/
├── index.tsv
├── report.md
└── report.json

It then edits the TOML while preserving comments and relative order, runs compiler-provided fixes and executes the Cargo check gate. A failing gate restores the controlled files automatically and returns a non-zero status.

To deliberately keep a partial result for diagnosis:

cargo rullst upgrade --keep-on-failure

To restore a persisted snapshot after that mode or after an interrupted run:

cargo rullst upgrade \
  --restore target/rullst-upgrades/<UTC-run-id>

Restore accepts only a path-validated snapshot inside the current project’s target/rullst-upgrades directory. cargo clean deletes target, so retain a normal version-control commit or copy a needed diagnostic report before cleaning.

Recovery boundaries and 12.1.0 hardening

Stop editors, watchers and other writers before restoring. File recovery does not undo build-script/test side effects or database/external-service changes. Keep an independent version-control backup; the directory under target is not a substitute for one. A filesystem error during application can leave some files restored and others unchanged, so always review the result.

Since 12.1.0 the CLI preflights the entire backup, stages every replacement before writing originals, and rejects linked or malformed paths. It limits indexes to 8 MiB/100,000 entries, each snapshot to 64 MiB and the total to 512 MiB. Disk failure while staging leaves originals intact; failure during the later per-file apply reports progress and retains the backup. This is not an all-files atomic commit and does not defend against hostile concurrent filesystem changes. These improvements are not in 12.0.0.

4. Finish a v5 to v12 migration

This step uses the v12 CLI. The v13 CLI accepts only v12 and v13 projects, so upgrade older applications to v12 first and then to v13.

The v5 README used attribute-style routing and a server builder with no router or port. The scanner reports these markers instead of applying a global text replacement. Replace the old shape:

#[routes]
fn home() -> Response {
    // ...
}

Server::new()
    .route("/", get(home))
    .run()
    .await;

with an explicit v12 router and typed error propagation:

use rullst::{Server, response::Html, routes};

async fn home() -> Html<&'static str> {
    Html("Hello from v12")
}

#[rullst::runtime::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let app = routes![get("/" => home)];
    Server::new(app).run(3000).await?;
    Ok(())
}

Then follow the complete v5 → v12 guide, including feature selection, disposable database migration/rollback, explicit Nexus and Studio boundaries, provider validation and authorization negatives.

5. Run the application-owned gates

At minimum:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo build --release

Also test the actual production feature set, restore/migrate/rollback against a production-shaped database copy, cross-user and cross-tenant denials, proxy/TLS identity, CSRF/CORS, Nexus/Studio exposure and every configured live provider.

v12 to v13: source findings

The v13 CLI carries the rullst-upgrade-rules-v4 catalog. It parses the application instead of matching text: Rust files with syn (so comments, doc comments and string literals never match an API rule, and macro bodies such as html! and routes! are read token by token), Cargo manifests with a TOML parser, and a few generated project files (.gitignore, .dockerignore, .cargo/config.toml, Foundry.toml, docker-compose.prod.yml, Kubernetes YAML, an Omni shell under omni-app/, rullst-client.ts, Dockerfile, .env/.env.example keys and release scripts). Files are read without following symbolic links; a finding records a rule and a line, never file contents. A Rust file that does not parse, is not UTF-8 or is larger than 2 MiB is listed as NOT SCANNED.

Each finding has a stable code (V13-...), a kind, a location, a one-line message and the first-column title of its row in the v13 migration guide:

Source findings (rullst-upgrade-rules-v4): 1 must-change, 2 review
  MUST-CHANGE src/views/home.rs:12 [V13-HTML-DYNAMIC-EVENT-HANDLER] a dynamic value in an `on*`/`hx-on*` attribute of `html!` no longer compiles
      migration-v13 row: `html!` event handlers
  REVIEW src/main.rs:64 [V13-RENDER-PAGE-LANGUAGE] `render_page` declares `lang="pt-BR"`; English pages should use `render_page_with_lang`
      migration-v13 row: Starter page language
  • must-change: a compile-breaking or API-shape change that the code uses (for example a dynamic onclick={...} in html!, a belongs_to without foreign_key, a struct-level #[sqlx(rename_all)] on a model, a match on FieldKind without a wildcard, a utoipa 5 dependency used with Studio).
  • review: the code uses an API, feature or configuration whose behaviour changed (for example Model::all(), ValidatedJson, the Mail facade, render_page, a generated .gitignore without the SQLite journal files).

A review rule is reported once per file (its first location); a must-change rule at every location. The rule classification lists which migration rows have rules. Findings never block cargo rullst upgrade, whose cargo check gate still restores the project when the code does not compile. The gate’s error names the must-change findings and repeats the guidance of V13-CAPITAL-REMOVED and V13-MAIL-REMOVED findings, which name removed APIs but are review findings. update project verify, review and apply refuse a preparation that has must-change findings and carry review findings in the plan.

Rules by release

Each rule records the release that introduced the change it reports. The rules for the 12.x to 13 changes date from 13.0.0-alpha.1; a rule added later, or reworded because a later release changed what it reports, carries that release:

ReleaseRules
13.0.0-alpha.1The other 95 rules
13.0.0-alpha.2V13-HTML-MARKUP-RETURN, V13-HTML-RAW-MARKUP, V13-ORM-CONSTRAINT-ERRORS, V13-TEXT-TIMESTAMPS, V13-POLYGLOT-FEATURE
13.0.0-alpha.3V13-CONNECT-GITHUB-EMAIL, V13-ORM-TRANSACTION-ERRORS, V13-ORM-SKIP-MANAGED-COLUMNS, V13-ORM-DEFAULT-FEATURES (reworded: rullst-orm has no default driver), V13-RULLST-DEFAULT-FEATURES, V13-OMNI-REMOVED, V13-OFFLINE-SYNC-REMOVED
13.0.0-alpha.4V13-DEPLOY-TARGETS-REMOVED, V13-DEPLOY-GENERATORS-REMOVED, V13-CLI-SELF-UPDATE-REMOVED, V13-CLI-AI-REMOVED

A rule is dated by the first release tag whose catalog contains its code (git show v13.0.0-alpha.2:cargo-rullst/src/generators/build/upgrade/rules/catalog.rs, or git log -S'"V13-..."' on that file), checked against the CHANGELOG entry that names it; a new rule takes the next release. A unit test checks that every rule names one of these releases.

The plan takes the application’s Rullst version to be the oldest version that a Rullst requirement in the workspace manifests admits (=13.0.0-alpha.3, or 13.0.0-alpha.1, a caret requirement) or that Cargo.lock holds. When that version is a 13.0 prerelease, only the rules introduced after it apply: an alpha.3 application gets the alpha.4 rules, an alpha.2 application the seven alpha.3 rules and the alpha.4 rules, and an alpha.1 application the alpha.2, alpha.3 and alpha.4 rules, because the earlier changes are already in the version it runs. The plan says what it skipped:

Source findings (rullst-upgrade-rules-v4): 0 must-change, 1 review
  Rules: introduced after 13.0.0-alpha.2, the Rullst version this project uses; earlier rules skipped: 100 (findings hidden: 3); --all-rules applies every rule

A v12 application, or one whose version cannot be read (path dependencies without a version and no Cargo.lock), gets every rule. --all-rules applies every rule to a prerelease application too, for example to review the 12.x to 13 changes again. Project preparations (cargo rullst update) always apply every rule, because their must-change findings gate execution.

JSON plan fields

--dry-run --json keeps every rullst.upgrade-plan.v1 key. Each element of source_findings keeps path, line, code (the stable rule id), severity (BLOCKER or REVIEW) and message, and adds:

KeyMeaning
kindmust-change or review
introduced_inThe release that introduced the rule’s change, such as 13.0.0-alpha.3
migration_rowFirst-column title of the row in migration-v13.md
migration_urlThe published table of the v13 migration guide

The root object adds finding_counts (must_change, review), unscanned_sources (project-relative paths), migration_guide and rule_selection: current_version (the application’s Rullst version, or null when it cannot be read), all_rules (--all-rules, and always true in project preparations), skipped_rules (rules introduced in or before current_version that were not applied) and hidden_findings (the findings those rules would have added). A consumer that ignores unknown keys reads the plan as before; findings of skipped rules are absent from source_findings and finding_counts.

Fixing the findings

Each finding names its rule and the first-column title of its row in the v13 migration guide, which says what changed and how to adapt. Fix them by hand, or with your editor’s or terminal’s AI assistant given the plan, the migration row and the project’s AGENTS.md (cargo rullst generate:ai-context); review every edit it proposes. cargo rullst ai upgrade, which proposed such fixes in earlier 13.0 prereleases, was removed in 13.0.0-alpha.4.

A practical order:

  1. cargo rullst upgrade --dry-run and read every finding with its migration row.
  2. Fix findings that also compile against v12 (most review items and the html!, ORM and Nexus must-change items), then run the v12 tests.
  3. cargo rullst upgrade. When a fix needs a v13 API (for example utoipa 6 with Studio), run cargo rullst upgrade --keep-on-failure, fix the kept state, and finish with cargo rullst upgrade (no further version edits; it validates the result).
  4. Run the application-owned gates below.

Rehearsal on generated starters

A rehearsal generates starters with a published CLI, points their Rullst dependencies at a checkout of the next version through [patch.crates-io] (the registry requirements stay, so the upgrade edits them as it would for a published release), runs cargo rullst upgrade with the newer CLI and then each project’s build, tests and Clippy.

13.0.0-alpha.2 to 13.0.0-alpha.3

The October 2026 rehearsal used the published v13.0.0-alpha.2 Linux binary, checked with sha256sum -c cli-checksums-x86_64-unknown-linux-gnu.txt from the same release, and a CLI built from main; the projects built with Rust 1.99.0. Because main still carried the alpha.2 version, the rehearsal exported it (git archive), set every package to 13.0.0-alpha.3 in that copy, patched the projects to the copy and passed --to 13.0.0-alpha.3. The published alpha.3 CLI targets its own version, so --to is not needed there.

./cargo-rullst-13.0.0-alpha.2-x86_64-unknown-linux-gnu rullst new a2-saas --default \
  --blueprint saas --database sqlite --skip-initial-migration
cargo build && cargo test            # on 13.0.0-alpha.2; commit Cargo.lock
# append [patch.crates-io] entries for every rullst package in Cargo.lock, commit
cargo rullst upgrade --dry-run --to 13.0.0-alpha.3
cargo rullst upgrade --to 13.0.0-alpha.3
# edit what the findings ask for, then
cargo build && cargo test && cargo clippy --all-targets -- -D warnings

Blank, Blank --api, SaaS and LMS starters built on alpha.2 without warnings, and 3, 3, 7 and 11 tests passed. Against the alpha.3 candidate:

StarterMust-changeReviewcargo rullst upgradeManual editsalpha.3 build, tests and Clippy
Blank00passed; changed only Cargo.toml and Cargo.locknoneno warnings; 3 passed; clean
Blank --api01 (V13-HOT-RELOAD-MACHINE-ENDPOINTS)passed; changed only Cargo.toml and Cargo.locknoneno warnings; 3 passed; clean
SaaS04 (V13-ORM-SKIP-MANAGED-COLUMNS; V13-REFERRER-NO-REFERRER in two files, V13-MAIL-FACADE-CONFIG)passed; changed only Cargo.toml and Cargo.lock#[orm(skip)] to #[orm(db_default)] on User::is_admin and User::session_epochno warnings; 7 passed; clean
LMS06 (V13-ORM-SKIP-MANAGED-COLUMNS; V13-LMS-RECORD-PROGRESS in three files, V13-REFERRER-NO-REFERRER, V13-MAIL-FACADE-CONFIG)passed; changed only Cargo.toml and Cargo.lockthe same User editno warnings; 11 passed; clean

V13-ORM-SKIP-MANAGED-COLUMNS is the only finding about an alpha.3 change. The other findings locate APIs whose behaviour changed between 12.x and 13.0.0-alpha.2, so they also appear on a new alpha.2 or alpha.3 starter and need no edit in an application that already runs alpha.2. After the User edit the SaaS and LMS dry runs report 3 and 5 findings, all of that kind. The rehearsal’s CLI applied every rule; the plan now skips the rules an alpha.2 application already runs with (see Rules by release), so its default dry run lists only V13-ORM-SKIP-MANAGED-COLUMNS for SaaS and LMS and nothing for Blank and Blank --api, and --all-rules lists the findings in the table.

An HTTP smoke test of each upgraded starter (db:migrate, then the server in development) passed. In SaaS and LMS, registration stored is_admin = 0 and session_epoch = 0 from the column defaults, “Sign out everywhere” raised the epoch and the earlier cookie was refused, signing in again worked and the loopback Nexus answered 200. Blank and the JSON API answered / and /health, and the API’s bearer POST /api/messages returned 201 (401 without the token).

The same starters generated by the alpha.3 CLI differ from the alpha.2 ones only in the User fields above, in the localized account mail and APP_NAME of the password controller and .env and the checked reset-link lifetime of src/models/password_reset.rs (the “Account mail language and product name” and “Generated reset link lifetime” rows; existing projects keep their code) and in the SaaS users migration, which imports through rullst::db instead of rullst_orm (both compile). The alpha.2 to alpha.3 checklist turns this into steps for a real application.

Limits: SQLite only; the starters use none of the other APIs that alpha.3 removes or changes (Omni, offline-sync, default features, Orm::transaction, mock Connect credentials), so their rules ran only in the catalog’s unit tests; no live mail, billing or OAuth provider was called.

Earlier rehearsals: v12 to v13

.github/rehearse-v12-upgrade.sh is a manual helper (not a CI job) for the v12 to v13 path; its header documents the arguments. In October 2026 it and the published 12.3.0 binary upgraded Blank, Blog, SaaS and LMS starters of the 12.0.0, 12.1.2 and 12.3.0 CLIs to the 13.0.0-alpha.1 workspace with review findings only (5 to 14 per starter, for example V13-GITIGNORE-DATABASES, V13-MODEL-ALL and V13-HEALTH-PROBES) and no source edit, with one exception: the 12.3 SaaS starter still names LemonSqueezyProvider, so cargo fix failed and the project was restored until the Lemon Squeezy branches of src/controllers/billing_controller.rs and the BILLING_STORE_ID lines of .env and .env.example were removed, as V13-CAPITAL-REMOVED describes (that edit also compiles on 12.3). Starters of the 12.0 CLI also carry the build files listed in the migration guide. Those counts predate the alpha.2 and alpha.3 rules and were not re-measured.

v12 → v13 rule classification

Every row of the v13 migration guide is classified for rullst-upgrade-rules-v4:

  • (a) must-change: a compile-breaking or API-shape change detectable in application source (12 rows);
  • (b) review: changed behaviour worth reviewing when the application uses the affected API, feature or configuration, which the rules locate (100 rows);
  • (c) none: no application impact, or not detectable in the application (CLI behaviour, generator output for new projects, opt-in features, fixes of inputs that previously failed) (99 rows).

A row with both a must-change and a review part counts in both. The catalog has 114 rules. A row can map to several rules and a rule to several rows; when a row changes in a later release, update this table and the rule together.

Migration rowClassRulesNotes
Existing runtime APIs(c) none—General statement; the session-schema migration is the next row
Account/session registry(b) reviewV13-AUTH-RECOVERY-MIGRATE
Private object storage(c) none—Opt-in storage-s3 candidate
R2 public URLs(b) reviewV13-R2-PUBLIC-URL
Tenant local storage(c) none—Only a tenant directory below a regular file changes the error; not located
Recoverable Live UI(c) none—Opt-in module
Distributed tracing(b) reviewV13-OTLP-ENVIRONMENT
Core queue(b) reviewV13-QUEUE-SEMANTICSUnindexed 12.x Redis failures are not located
Core scheduler, cache and realtime(b) reviewV13-PRESENCE-COUNTINGThe TenantCache key encoding is not located
Core memory cache sweeps(c) none—Internal sweep cadence; reads unchanged
Studio queue monitor(b) reviewV13-QUEUE-DRIVER-PREVIEWS
Studio API playground(a) must-changeV13-STUDIO-UTOIPA-6Must-change when a manifest declares utoipa < 6 or utoipa-axum < 0.3
Studio local access and table view(b) reviewV13-STUDIO-TABLE-VALUES
LMS blueprint(c) none—Generator output; existing applications keep their code
Consumer generators(c) none—Generator commands
html! event handlers(a) must-changeV13-HTML-DYNAMIC-EVENT-HANDLER
html! returns Markup(a) must-changeV13-HTML-MARKUP-RETURN, V13-HTML-RAW-MARKUPLocated in function signatures and RawHtml calls, including html! bodies; a String collected from html! output, an #[island] call or Live::mount output is reported where RawHtml wraps it
Core request validation(b) reviewV13-VALIDATION-STATUS
Core feature flags and scheduler(b) reviewV13-SCHEDULER-WEEKDAYSA/B splits: the two feature-flag rules below; the bucket reassignment depends on flag configuration and is not located
Memory feature-flag splits(b) reviewV13-MEMORY-FEATURE-SPLITS
Database feature-flag splits(b) reviewV13-DB-FEATURE-SPLITS
Security headers(b) reviewV13-REFERRER-NO-REFERRER
Core WAF signatures(c) none—Only requests that previously failed with 403 change
Core security header precedence(c) none—Explicit header values in handlers and inner layers are not located
AI provider streaming and stop reasons(b) reviewV13-AI-GEMINI-STOP-REASONS
AI image-beacon guardrail(c) none—Guardrail hardening without an application API change
AI RAG tenant tags(b) reviewV13-AI-RAG-TENANT-TAGS
Anthropic provider output(b) reviewV13-AI-ANTHROPIC-OUTPUT
OpenAI provider output(b) reviewV13-AI-OPENAI-OUTPUT
Ollama host(c) none—Only values that previously failed change meaning
AI chat memory keys on MySQL/MariaDB(b) reviewV13-AI-CHAT-MEMORY-KEYSThe database backend is not located
Core CSRF on HEAD(c) none—HEAD previously failed with 500
Hot-reload machine endpoints(b) reviewV13-HOT-RELOAD-MACHINE-ENDPOINTS
Core JSON PII masking(b) reviewV13-PII-MASKING
Core PII masking of range responses(b) reviewV13-PII-MASKING
Security DLP, RASP and honeypot(b) reviewV13-SECURITY-DLP-HONEYPOT
Security audit log lines(b) reviewV13-SECURITY-AUDIT-LOG-LINES
Connect generic OIDC(b) reviewV13-OIDC-OPTIONAL-NAME
Age assurance(c) none—Opt-in APIs and generator
Optional consent and export(c) none—Opt-in generator
Project context(c) none—CLI generator
Typed API generation(c) none—Opt-in CLI profile
CLI generator output(b) reviewV13-TS-CLIENT-REGENERATEOther generator changes affect new output only
CLI generator robustness(b) reviewV13-TS-CLIENT-REGENERATE, V13-GITIGNORE-DATABASESOther generator changes affect new output only
ORM database-managed columns(b) reviewV13-ORM-SKIP-MANAGED-COLUMNSReports #[orm(skip)] on is_admin/session_epoch without #[sqlx(skip)], as alpha.2 starters declared them
ORM dirty tracking(c) none—Opt-in RowSnapshot field
ORM partial updates(b) reviewV13-ORM-PARTIAL-UPDATE
ORM derive checks(a) must-changeV13-ORM-BELONGS-TO-KEY, V13-ORM-IGNORED-RELATION-KEY, V13-ORM-SEARCHABLE-TABLE
ORM queries and cache(b) reviewV13-ORM-SQLX-JSON-SERIALIZE, V13-ORM-QUERY-CACHE-KEYCount, chunk-order and stream changes are not located
ORM query-cache index(b) reviewV13-ORM-CACHE-PREFIX
ORM Redis model hashes(b) reviewV13-ORM-REDIS-HASHES
ORM outbox keys on MySQL/MariaDB(b) reviewV13-OUTBOX-MYSQL-KEYSThe database backend is not located
ORM audit payloads on MySQL/MariaDB(b) reviewV13-ORM-AUDIT-PAYLOADSAuditable models and audit-table setup; the database backend is not located
Turso migrations(b) reviewV13-TURSO-ROLLBACK-DRIFT
Schema table names on PostgreSQL(b) reviewV13-SCHEMA-PG-TABLE-CASE
Legacy SecretString ciphertext(c) none—Older values become readable; no source change
SecretString client input(b) reviewV13-SECRET-STRING-CLIENT-INPUT
ORM protected values and SecretString serialization(b) reviewV13-ORM-PROTECTED-VALUESEncrypted and masked model fields, and SecretString fields of serialized structs and models
Offline Redis mock(b) reviewV13-REDIS-MOCK-TIE-ORDER
SQLite DSN paths(c) none—Only stray files of earlier versions
Auto-healing diagnostics(b) reviewV13-AUTO-HEALING-DIAGNOSTICS
ORM migrations(c) none—Existing migrations keep their behaviour; db:seed refusing pending migrations is command behaviour
ORM transactional migrations and the pool(c) none—within_transaction() is a v13 API, so v12 migrations are unaffected
ORM instance mutations(b) reviewV13-ORM-MISSING-ROWPlain delete() calls are not located
ORM encryption key settings(c) none—Only .env entries that the process environment lacks change; .env is not read
ORM constraint errors(b) reviewV13-ORM-CONSTRAINT-ERRORSCode that matches DatabaseError
ORM transaction errors(b) reviewV13-ORM-TRANSACTION-ERRORSOrm::transaction calls in files that mention DatabaseError; cargo check reports closure error types without From<rullst_orm::Error>
ORM query values(c) none—Only exhaustive matches on RullstValue break, and cargo check reports them
ORM date/time columns(b) reviewV13-TEXT-TIMESTAMPSString created_at/updated_at fields of SQLx models; whether the table’s migration calls timestamps() is not checked
Schema builder constraints(c) none—Only Column struct literals break, and cargo check reports them
ORM sandbox tests(a) must-changeV13-ORM-SANDBOX-TESTFlags every sandbox test as review; cargo check reports the signatures that no longer compile
ORM model SQLx options(a) must-changeV13-ORM-SQLX-STRUCT-OPTION
ORM trashed scopes(b) reviewV13-ORM-ONLY-TRASHED
ORM bulk soft deletes(b) reviewV13-ORM-TRASHED-DELETE-ALL
ORM joined scopes(b) reviewV13-ORM-SQL-TEXT
ORM chunk bounds(b) reviewV13-ORM-CHUNK-BOUNDS
ORM eager pivot order(c) none—The order was unspecified before
PersonalData reports(b) reviewV13-PERSONAL-DATA-REPORT
Nexus derive(a) must-changeV13-NEXUS-PRIMARY-KEY, V13-NEXUS-FIELD-KIND-NUMBERThe field-kind rule is a review
Nexus composite keys(c) none—Opt-in composite keys; only RegistryEntry struct literals break, and cargo check reports them
Nexus dates and times(c) none—Derived field kinds change without a source edit; comparisons with FieldKind::DateTime are not located
Nexus access in the starters(c) none—Generator output; alpha.2 #[orm(skip)] fields are reported under ORM database-managed columns
Nexus credentials(b) reviewV13-NEXUS-DOTENV
Nexus panel(a) must-changeV13-NEXUS-FIELD-KIND-MATCHOther panel changes are runtime behaviour without an application API
CLI migration catalog(c) none—CLI
CLI upgrade downgrades(c) none—CLI
CLI Foundry service environment(b) reviewV13-FOUNDRY-SERVICE
CLI Foundry build artifact(b) reviewV13-FOUNDRY-SERVICE
CLI generators(b) reviewV13-MFA-CLIENT-SECRETOther changes affect new generator output only
CLI operations(b) reviewV13-FOUNDRY-SERVICEScript exit statuses are not located
CLI diagnostics and side effects(c) none—CLI
CLI home screen(c) none—CLI
CLI security audit(c) none—CLI
CLI dashboard audit(c) none—CLI
Core deferrable jobs(c) none—Opt-in APIs
Core scheduler time zones(c) none—Opt-in scheduler-tz feature
Dash N+1 warning(c) none—CLI
CLI security report(c) none—CLI
Generated security tests(c) none—Generator output for new projects
Blank JSON API starter(c) none—Generator output; existing API projects keep their behaviour
CLI audit lockfile(c) none—CLI
RASP shell signatures(c) none—Request matching; not detectable in source
CLI footprint report(c) none—New CLI command; renamed green (CLI ai removed, footprint renamed green row)
CLI dev/dash shutdown(c) none—CLI
CLI dev --ts-sync(c) none—CLI
CLI dash live metrics(c) none—CLI
Core development telemetry(b) reviewV13-DEV-TELEMETRY-ROUTE
Core development error console(c) none—Development-only page; handle_autofix callers are not located
CLI ai fix and ai review(c) none—Added and removed within the 13.0 prereleases
CLI project updates on SELinux(c) none—CLI
CLI pkg in a virtual workspace(c) none—CLI
CLI new repository(c) none—CLI
CLI generated validator dependency(c) none—Generator output; cargo check reports a missing validator dependency
CLI make:model imports(c) none—Generator output for new models
CLI dockerize and generate:buildah(c) none—Affected generated files are not distinguishable
CLI errors and exit codes(c) none—CLI output and scripts
CLI doctor(c) none—CLI
CLI output additions(c) none—CLI
SaaS plan gates(c) none—Opt-in generated module
Capital providers and NFS-e removed(b) reviewV13-CAPITAL-REMOVEDCompile-breaking; reported for review because a name such as FiscalError can also be an application type; also locates BILLING_PROVIDER=lemonsqueezy and BILLING_STORE_ID in .env and .env.example
Capital quota keys on MySQL/MariaDB(b) reviewV13-CAPITAL-QUOTA-KEYSSqlQuotaStore/SqlQuotaBackend; the database backend is not located
Capital zero tier limit(b) reviewV13-CAPITAL-ZERO-TIER
Capital provider subscription IDs(c) none—Rejects dot-only identifiers before a request
Capital invoice amounts(c) none—Only amounts below one minor unit change
Messaging outbox relay key(b) reviewV13-OUTBOX-RELAY-KEY
Messaging encrypted SQLite startup(c) none—Correct keyrings are unaffected
Messaging Redis Streams candidate(c) none—No 12.x release contains the adapter
Mail providers removed(b) reviewV13-MAIL-REMOVEDCompile-breaking; also locates removed MAIL_DRIVER values and settings in Rust strings, .env, .env.example and Rullst.toml
Mail driver default(b) reviewV13-MAIL-FACADE-CONFIG
Mail sender(b) reviewV13-MAIL-FACADE-CONFIG
Mail attachment inspection(b) reviewV13-MAIL-ATTACHMENT-INSPECTION
Mail link checks(c) none—Depends on message content
Mail facade settings(b) reviewV13-MAIL-FACADE-CONFIG
Mail queued attachments(b) reviewV13-MAIL-QUEUED-ATTACHMENTSMail::init_queue, Mail::enqueue/enqueue_for_tenant and register_mail_handler
Mail SES addresses(c) none—Requests SES rejected now succeed
Mail Resend scheduling(b) reviewV13-MAIL-RESEND-SCHEDULE
Mail tracking recipient(b) reviewV13-MAIL-TRACKING-RECIPIENT
Mail plain-text fallback(b) reviewV13-MAIL-TEXT-FALLBACK
Labs runner(a) must-changeV13-LABS-RUNNER-REMOVED
Education candidates(c) none—New v13 packages, first published in 13.0.0-alpha.1
Labs package(c) none—New package in the release inventory
Media S3 storage(c) none—New opt-in feature of a package first published in 13.0.0-alpha.1
Generated billing settings(b) reviewV13-BILLING-PROJECT-SETTINGS
Application templates(c) none—Generator output
Starter migrations(b) reviewV13-SQLITE-ONLY-SEED-TIME
ERP starter access(b) reviewV13-ERP-DASHBOARD-ACCESS
LMS lesson progress(b) reviewV13-LMS-PROGRESS-KEY
LMS concurrent progress saves(b) reviewV13-LMS-RECORD-PROGRESS
LMS lesson media in Nexus(b) reviewV13-LMS-MEDIA-KINDS
Blog robots and sitemap(b) reviewV13-BLOG-ROBOTS-SITEMAP
Generated login and registration(b) reviewV13-CREDENTIAL-RATE-LIMIT
SaaS user JSON(b) reviewV13-PASSWORD-HASH-HIDDEN
Registration timestamps(b) reviewV13-EMPTY-TIMESTAMPS
Billing row timestamps(b) reviewV13-EMPTY-TIMESTAMPS
Registration lengths(b) reviewV13-UTF16-LENGTHS
Revocable login sessions(c) none—Additive APIs and generator output
Password change and reset(c) none—Additive APIs and generator output
VPS deploy proxy(b) reviewV13-VPS-DEPLOY-PROXY
Blank database status(b) reviewV13-MODEL-ALL
Portfolio profile(b) reviewV13-PORTFOLIO-PROFILE
Blog and Portfolio pages(b) reviewV13-PAGE-CSP-NONCE
Generated linker configuration(b) reviewV13-LINKER-CONFIG
Build files from the 12.0 CLI(b) reviewV13-MSVC-FASTLINK, V13-CARGO-LOCK-IGNORED, V13-DOCKER-UNLOCKED-BUILDProjects generated by the 12.0 CLI
ERP orders and stock(b) reviewV13-ERP-STORE-ORDER
Blog and ERP reads(b) reviewV13-MODEL-ALL
Blog and ERP page bounds(b) reviewV13-PAGE-BOUNDS
Docker build context(b) reviewV13-DOCKER-CONTEXT
Kubernetes Ingress(b) reviewV13-K8S-INGRESS-TLS
Starter health probes(b) reviewV13-HEALTH-PROBES
Kubernetes and Buildah names(b) reviewV13-K8S-NAMES
Pre-compressed static assets(c) none—CLI
Static asset caching(c) none—Runtime default; not detectable in source
Generated release profile(c) none—Generator output for new projects
Generated container runtime(c) none—Generator output for new projects
Interactive new --api(c) none—CLI
CLI new wizard(c) none—CLI
Minimal default features(b) reviewV13-RULLST-DEFAULT-FEATURES, V13-ORM-DEFAULT-FEATURESReports rullst and rullst-orm dependencies that keep their defaults and select no driver
Generated rullst-orm dependency(c) none—Generator output; such a dependency is reported under Minimal default features
ORM adapter aggregate features(a) must-changeV13-POLYGLOT-FEATUREDependency features and [features] entries such as "rullst/orm-polyglot"
Omni and offline sync removed(a) must-change, (b) reviewV13-OFFLINE-SYNC-REMOVED, V13-OMNI-REMOVEDThe feature in dependency features and [features] entries; the shell’s omni-app/tauri.conf.json or omni-app/src/lib.rs, and cargo rullst omni/make:omni in workflows, root scripts, Makefile and justfile
Starter page language(b) reviewV13-RENDER-PAGE-LANGUAGE
Repository examples(c) none—Repository examples, not applications
Windows paths in CLI and console output(c) none—CLI output
Consumer scaffolds under symlinked directories(c) none—CLI
Core HSTS default(c) none—Runtime default; not detectable in source
Artisan and .env(c) none—std::env::var calls in migrations and seeders are not located
Core WAF in development(c) none—Development-only behaviour
CSRF token on static-looking paths(c) none—Only requests that previously received no token change
Multipart CSRF refusal(c) none—Only the refusal text changes; tests that compare it are not located
Secure cookies over HTTPS(c) none—Runtime behaviour; not detectable in source
CSRF cookie on cacheable responses(c) none—Cache-Control values set by handlers are not located
Connect mock credentials(c) none—Credentials come from configuration and are not located
Connect mock authorization URL(c) none—Mock mode only
Account mail language and product name(c) none—Generator output; existing projects keep their code
Production log defaults(c) none—Runtime defaults; not detectable in source
Connect GitHub email(b) reviewV13-CONNECT-GITHUB-EMAILGithubProvider and Connect::driver("github", ...)
Account mail link lifetime(c) none—Only reset lifetimes that previously failed change; the link wording is not located
Generated reset link lifetime(c) none—Generator output; existing projects keep their code
CLI deploy targets removed(b) reviewV13-DEPLOY-TARGETS-REMOVEDfly.toml, render.yaml, railway.json, railway.toml, and deploy --platform fly|railway|render in workflows, root scripts, Makefile and justfile; introduced in 13.0.0-alpha.4
SQLite database in the deployed image(c) none—Generator output for new Dockerfiles; an existing Dockerfile without DATABASE_URL is not located, and cargo rullst deploy warns when it keeps one; introduced in 13.0.0-alpha.4
CLI Foundry, Nix, Buildah and gRPC generators removed(b) reviewV13-DEPLOY-GENERATORS-REMOVEDFoundry.toml, a nixify flake.nix, build_buildah.sh, src/grpc/mod.rs, and the removed commands in workflows, root scripts, Makefile and justfile; introduced in 13.0.0-alpha.4
CLI self-update removed(b) reviewV13-CLI-SELF-UPDATE-REMOVEDrullst update check|verify|stage|install, or update guided with --scope/--root, in workflows, root scripts, Makefile and justfile; introduced in 13.0.0-alpha.4
CLI ai removed, footprint renamed green(b) reviewV13-CLI-AI-REMOVEDrullst ai or rullst footprint in workflows, root scripts, Makefile and justfile; introduced in 13.0.0-alpha.4
Core error console assistant panel(c) none—Development-only page; handle_autofix response readers are not located; introduced in 13.0.0-alpha.4
CLI starters moved to the examples(c) none—CLI behaviour; applications generated from the removed starters keep their code; introduced in 13.0.0-alpha.4
ORM derive dependencies(c) none—Fix of code that previously failed to compile; a tracing or tokio dependency kept for the derive still works; introduced in 13.0.0-alpha.4
ORM FromRow and SQLx macros(a) must-changeV13-ORM-FROM-ROW, V13-SQLX-MACROSModels deriving FromRow beside Orm (any path, separate derive attributes included) unless #[orm(custom_from_row)]; SQLx derives on other structs and enums and sqlx::…::query!-family macros when no manifest enables SQLx’s derive/macros (or its default features); introduced in 13.0.0-alpha.4
Nexus AI assistant feature(b) reviewV13-NEXUS-AI-FEATUREA rullst dependency with nexus/nexus-tz but not nexus-ai, or rullst-nexus without default features and without ai; introduced in 13.0.0-alpha.4
Generated starter dependencies(c) none—Generator output for new projects; an unused tracing-subscriber in an existing project keeps working; introduced in 13.0.0-alpha.4
TLS crypto provider(c) none—Dependency features and runtime provider defaults; the optional sqlx feature change is not located; introduced in 13.0.0-alpha.4

Planned simpler update experience

Since 13.0.0-alpha.4 the CLI never downloads or replaces its own binary. Install a newer CLI with Cargo and its exact version (cargo install cargo-rullst --version =VERSION --locked). At most once a day, in an interactive terminal, the CLI prints that command when crates.io has a newer release of its major; see the CLI reference. The 12.1.0 to 13.0.0-alpha.3 self-updater (update check, update verify, update stage and update install) was removed; the project commands below remain.

cargo rullst update project prepare --project PATH --json copies the Git working directory into private storage and edits only the candidate’s versioned workspace dependencies. It preserves dirty and untracked source, tracked deletions and the root lockfile, including a legacy ignored lockfile. Compare before/ and candidate/ at the reported location and review preparation.json. Builds/tests are not executed and neither execution nor application is authorized. See the preparation limits and exclusions. Use update project verify --prepared PATH --dry-run to inspect its validation commands. Only after reviewing the trusted project, use --allow-project-code to authorize lockfile resolution, locked checks and tests in a fresh private copy. This is not a sandbox; tests inherit your environment and can have external effects. See the verification options and limits. Then update project review --verified PATH --json revalidates the verification record and shows the full dependency diff with a review digest. Use the returned verified_directory; the digest grants no apply authority. Explicit update project apply --verified PATH --approved-review SHA256 applies only the reviewed manifests/root lockfile. The matching recover command restores only that operation and refuses unrelated edits.

The safe-update priority proposes one guided flow for project preparation, validation and approved application. cargo rullst update guided --to VERSION --project PATH composes those commands. Each approval defaults to no and follows its complete review. Version, directories and digests are carried between steps; separate prompts govern trusted project execution/network and original-file application. --offline keeps verification local. Since 13.0.0-alpha.4 the flow no longer installs a CLI (--scope and --root were removed): install the target CLI with Cargo first. See the guided command. File recovery does not replace database backups or application acceptance tests, and updating the CLI alone never updates a deployed application.

The 12.1.0 update mechanism does not implement v13 migrations. The v13 CLI (13.0.0-alpha.4 prerelease) carries its own rule catalog (v12 to v13: source findings); update guided offers a project update only within the installed CLI’s major.

Is this unique?

No. Assisted upgrades are an established framework practice: Rails documents interactive bin/rails app:update, Angular provides ng update, and Dart offers preview/apply analysis fixes through dart fix. Microsoft’s .NET Upgrade Assistant also analyzed and changed projects, although Microsoft now marks it deprecated in favor of its modernization tooling.

Rullst’s useful distinction is the bounded composition: Cargo-workspace-aware TOML edits, a version-selected framework rule catalog, human and JSON plans, controlled snapshots, default rollback, explicit recovery and Cargo gates in one CLI flow. This is a testable design choice, not evidence that Rullst is the first or universally the best updater.

Official references: