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

Rullst Nexus

rullst-nexus is a server-rendered administrative panel for models that implement NexusModel, either manually or through #[derive(Nexus)]. The derive infers primitive widgets and accepts explicit semantic metadata such as kind = "textarea" and kind = "enum", options = "draft, published". Nexus provides registered-model CRUD, search, pagination, semantically validated form widgets, bounded selected-record delete/deactivate actions, telemetry, a security view and an optional AI query page (the ai feature of rullst-nexus, nexus-ai on the rullst facade; see AI assistant). The current interface uses server-side HTML and HTMX; Wasm islands, drag-and-drop media management and automatic relationship discovery described by older documentation were not implemented. They remain worthwhile separate features, but must not be presented as current behavior.

Build a protected panel

Nexus fails closed: try_build() requires an explicit validated access policy. The generated-app helper permits credential-free access only in debug builds and only for a loopback peer proven by Axum ConnectInfo. Release builds require NEXUS_ADMIN_USERNAME and a unique NEXUS_ADMIN_PASSWORD of at least 16 characters, each read from the process environment or else the working directory’s .env (never loaded into the process environment). A malformed or unreadable .env fails with NexusBuildError::InvalidDotenv.

#![allow(unused)]
fn main() {
use rullst_nexus::{Nexus, NexusAuthPolicy};

fn build() -> Result<axum::Router, Box<dyn std::error::Error>> {
let access = NexusAuthPolicy::local_development_or_basic_from_env()?;
let nexus = Nexus::new()
    .with_auth_policy(access)
    .with_brand("Application Admin")
    // .register::<User>()
    .try_build()?;

let app = axum::Router::new().nest("/nexus", nexus);
Ok(app)
}
}

That is the plain-Axum form. On a rullst::Router, as in the generated starters, mount it with .nest_axum("/nexus", nexus): rullst::Router::nest accepts only another rullst::Router, and there is no with_state (pass shared state with .layer(Extension(state))). A release build without the credentials makes local_development_or_basic_from_env() return NexusBuildError::MissingCredential; the Nexus guide shows how to start the application without Nexus in that case, as the Portfolio and ERP examples do.

An application with its own login can instead select NexusAuthPolicy::application(authorizer): its NexusAuthorizer sees each request and answers Allow (with the audit actor ID), SignIn (redirect to the login page, or 401 with HX-Redirect for fragments and mutations), Deny (403) or an error (503). The SaaS starter and the LMS example use it, through local_development_or_application, for signed-in users whose users.is_admin is true; see Using the application’s sign-in.

The serving boundary must preserve the socket address, for example with Axum’s into_make_service_with_connect_info::<SocketAddr>(). Basic Auth additionally requires the application-owned NexusVerifiedTls capability inserted only after validating a trusted TLS terminator, or an HTTPS report accepted by Core’s TrustedProxyLayer from a listed proxy network with trust_forwarded_proto enabled. The same layer (Server::trusted_proxies) gives each client behind the proxy its own lockout bucket. Never derive either from an untrusted forwarded header.

NexusAuthPolicy::protect_router can apply the same administrator boundary to application-owned operational routes, as the ERP example does for its whole back office: the dashboard and the inventory mutations.

Capability boundary

  • Implemented: explicit model registration and a compile-tested derive; server-rendered tables/forms; bounded/validated registry metadata; parameterized and allowlisted SQL identifiers; bound record values; server-side pair/byte limits and semantic validation for Boolean, enum, JSON, number, date, e-mail and HTTP(S) URL fields; CRUD, search, pagination, sort and batch operations, also for composite primary keys; typed date/time writes that native date/time columns accept; CSRF middleware; fail-closed loopback/Basic access and per-request access through the application’s own sign-in; bounded Basic Auth failure throttling; opt-in exact text-column tenant scope across every built-in read and mutation, derived only from a trusted Core TenantContext; and opt-in transaction-coupled minimized mutation audit with a fixed cross-database schema and bounded export API.
  • Batch boundary: at most 1,000 explicitly selected IDs; deactivation is available only for a writable Boolean is_active or active field.
  • Application responsibility: identity, tenant membership and domain policy; model/field authorization for global models and custom routes; database privileges; trusted proxy and TLS configuration; secret rotation; immutable external audit delivery, retention and backup; schema/type compatibility (on PostgreSQL Nexus casts number, relation, Boolean, date and time writes, but editable JSON and enums need text columns); and ownership rules beyond the explicit tenant column. The built-in audit table is transaction-coupled in the same database, not append-only or tamper-evident, and records committed mutations rather than rejected attempts. Multiline intent and variants from an unrelated Rust enum require explicit #[nexus] metadata; the struct derive does not invent them.
  • Not implemented: a generic NexusLayer, automatic ORM schema reflection, automatic HasMany/BelongsTo widgets, full rich-media management and a shared-production authentication service. These ideas may be implemented when they have typed contracts and proportional tests.

For a complete model example and the local/release access flow, see Rullst Nexus: Explicit Admin CMS.