rustango 0.60.7

Batteries-included web framework for Rust: ORM + migrations + auto-admin + multi-tenancy + audit log + auth (sessions, JWT, OAuth2/OIDC, HMAC) + APIs (ViewSet, OpenAPI auto-derive, JSON:API) + jobs (in-mem + Postgres) + email + media (S3 / R2 / B2 / MinIO + presigned uploads + collections + tags) + production middleware (CSRF, CSP, rate-limiting, compression, idempotency, etc.).
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
<p align="center">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ujeenet/rustango/main/docs/rustango_dark.png">
    <img src="https://raw.githubusercontent.com/ujeenet/rustango/main/docs/rustango_light.png" alt="Rustango β€” the batteries-included web framework for Rust" width="640">
  </picture>
</p>

# Rustango

**A batteries-included web framework for Rust: declare a model once, and get an ORM, auto-migrations, an auto-admin, multi-tenancy, and a REST API out of it.**

**Runs on [axum](https://github.com/tokio-rs/axum) and [tokio](https://tokio.rs).** Handlers are plain axum handlers and everything Rustango adds is a `tower` layer or an `axum::Router`, so any axum extractor, middleware or crate from that ecosystem drops straight in.

One `#[derive(Model)]` is the whole contract β€” from it Rustango emits typed queries, migration diffs, admin screens, serializers, and CRUD endpoints. A tri-dialect ORM, first-class auth, and every standard middleware ship in the box: all opt-out via cargo features, and all working on **Postgres, MySQL, and SQLite** from the same source.

πŸ“š **Docs:** [rustango.com](https://rustango.com) Β· [in-repo guides](https://github.com/ujeenet/rustango/tree/main/docs/) Β· [API reference](https://docs.rs/rustango)
🌍 **Also in:** [Deutsch](https://github.com/ujeenet/rustango/tree/main/docs/de/) Β· [EspaΓ±ol](https://github.com/ujeenet/rustango/tree/main/docs/es/) Β· [FranΓ§ais](https://github.com/ujeenet/rustango/tree/main/docs/fr/) β€” every published guide, not a subset.
🍳 **Cookbook:** [`cookbook_blog/COOKBOOK.md`](https://github.com/ujeenet/rustango/blob/main/crates/rustango/examples/cookbook_blog/COOKBOOK.md) β€” a runnable, test-backed recipe for every feature below.

---

## Install

```toml
[dependencies]
# Postgres (default)
rustango = "0.60"

# SQLite β€” file-backed or in-memory
rustango = { version = "0.60", default-features = false, features = ["sqlite", "tenancy", "admin", "manage"] }

# MySQL 8+
rustango = { version = "0.60", default-features = false, features = ["mysql", "tenancy", "admin", "manage"] }
```

Every capability is a cargo feature you can turn off. Renaming the dep works too β€” `#[derive(Model)]` resolves the crate root via `proc-macro-crate`, so `orm = { package = "rustango", version = "0.60" }` needs no extra wiring.

**Moving between versions?** Rustango is `0.x`, so a minor bump is allowed to break things and several have. [UPGRADING.md](https://github.com/ujeenet/rustango/blob/main/UPGRADING.md) has the per-version notes and a checklist β€” including the two that bite regardless of version: a session secret that can stop a booting app, and a generated system migration that has to reach production.

## An app on SQLite in 30 lines

```rust
use std::sync::Arc;
use axum::{routing::get, Extension, Json, Router};
use rustango::core::Model as _;
use rustango::server::AppBuilder;
use rustango::sql::{Auto, FetcherPool, Pool};
use rustango::Model;

#[derive(Model, Debug, Clone, serde::Serialize)]
#[rustango(table = "demo_user")]
pub struct User {
    #[rustango(primary_key)] pub id: Auto<i64>,
    #[rustango(max_length = 80)] pub name: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    AppBuilder::from_env().await?            // reads DATABASE_URL
        .bootstrap(&[User::SCHEMA]).await?   // CREATE TABLE IF NOT EXISTS
        .api(Router::new().route("/users", get(list)))
        .serve("0.0.0.0:8080").await
}

async fn list(Extension(pool): Extension<Arc<Pool>>) -> Json<Vec<User>> {
    Json(User::objects().fetch(&pool).await.unwrap())
}
```

```sh
# Backend selection lives in your Cargo.toml β€” see Install above.
# `cargo run --features sqlite` would name a feature of *your* crate, not rustango's.
DATABASE_URL='sqlite:./var/app.db?mode=rwc' cargo run
```

The **same code** boots on Postgres with `DATABASE_URL=postgres://…` or MySQL with `DATABASE_URL=mysql://…` β€” no changes. Every SQLite connection turns on sensible defaults automatically (`PRAGMA foreign_keys = ON`, `journal_mode = WAL` for file-backed DBs, `busy_timeout = 5s`).

## Why Rustango

- **One ORM, three backends.** Models, queries, migrations, relations, and aggregates emit correct SQL for Postgres, MySQL 8+, and SQLite from the same code.
- **Batteries included.** Auth (sessions + JWT + OAuth2/OIDC + HMAC + API keys + TOTP), an auto-admin, multi-tenancy, caching, background jobs, email, file storage, signals, i18n, an MCP server, and OpenAPI β€” not add-ons, in the box.
- **Declare it, don't wire it.** A project scaffolder (`cargo rustango new`), `make:*` generators, a `manage` CLI, `#[derive(Model)]` / `#[derive(ViewSet)]` / `#[derive(Serializer)]`, and admin config blocks β€” an app is described in attributes, not assembled by hand.
- **Opt-out, not opt-in.** Everything is a cargo feature. A JSON-only API binary compiles out the admin, templates, and tenancy entirely.

---

## Table of contents

- [Quick start](#quick-start)
- [The ORM](#the-orm)
- [Migrations](#migrations)
- [Auto-admin](#auto-admin)
- [APIs β€” ViewSets, Serializers, JWT, OpenAPI](#apis--viewsets-serializers-jwt-openapi)
- [HTML views & forms](#html-views--forms)
- [Multi-tenancy](#multi-tenancy)
- [Authentication & permissions](#authentication--permissions)
- [Security middleware](#security-middleware)
- [Caching, email, storage, jobs](#caching-email-storage-jobs)
- [Signals, i18n, MCP](#signals-i18n-mcp)
- [The `manage` CLI](#the-manage-cli)
- [Configuration](#configuration)
- [Testing](#testing)
- [Features](#features)
- [Documentation](#documentation)

---

## Quick start

```bash
cargo install cargo-rustango

cargo rustango new myblog                   # default: ORM + admin
cargo rustango new myapi --template api      # JSON-only, no admin
cargo rustango new shop --template tenant    # multi-tenancy + operator console
```

```bash
cd myblog
cp .env.example .env                         # edit DATABASE_URL
docker compose up -d postgres                # starts Postgres only
cargo run -- makemigrations                  # generate migrations from your models
cargo run -- migrate                         # apply pending migrations
cargo run                                    # http://localhost:8080
```

For autoreload during development: `cargo watch -x run` (or [`bacon`](https://github.com/dtolnay/bacon) `run`).

Add an app and a model:

```bash
cargo run -- startapp blog                   # scaffolds src/blog/ with a starter model + admin block + smoke test
```

```rust
use rustango::{Auto, Model};
use chrono::{DateTime, Utc};

#[derive(Model, Clone)]
#[rustango(
    table = "posts",
    display = "title",
    admin(list_display = "id, title, published_at", search_fields = "title, body"),
    audit(track = "title, body"),
    index("published_at, author_id"),
)]
pub struct Post {
    #[rustango(primary_key)]
    pub id: Auto<i64>,
    #[rustango(max_length = 200)]
    pub title: String,
    pub body: String,
    pub author_id: i64,
    #[rustango(auto_now_add)]
    pub published_at: Auto<DateTime<Utc>>,
}
```

```bash
cargo run -- makemigrations                  # generates migration JSON from the model diff
cargo run -- migrate                         # applies it
cargo run -- make:viewset PostViewSet --model Post
cargo run -- make:serializer PostSerializer --model Post
```

Full walkthrough: [getting started](https://github.com/ujeenet/rustango/blob/main/docs/getting-started.md) Β· [scaffolding](https://github.com/ujeenet/rustango/blob/main/docs/scaffolding.md).

---

## The ORM

`#[derive(Model)]` registers a struct in a global inventory and emits typed query, save, and `FromRow` code. The query builder is lazy and chainable (`.filter()`, `.exclude()`, `.order_by()`, `.annotate()`, `.select_related()`) β€” nothing hits the database until you `.fetch()` β€” reverse relations batch through `sql::fetch_with_prefetch_pool(qs, "fk_col", &pool)`, and the **same code runs on all three backends** through the `Pool` enum.

```rust
// Filter, order, paginate
let recent = Post::objects()
    .filter("published_at__lt", Utc::now())
    .exclude("status", "draft")
    .order_by(&[("published_at", true)])   // true = DESC
    .limit(20)
    .fetch(&pool).await?;

// Aggregate (scalar): .values(&[]) β†’ one row, no GROUP BY
let stats = Post::objects()
    .values(&[])
    .annotate("total", AggregateExpr::Count(None))
    .annotate("avg_views", AggregateExpr::Avg("view_count"))
    .compile()?;
```

Supported: every field type (ints, floats, `String`, `bool`, `DateTime`/`Date`, `Uuid`, `Json`, `Decimal`, plus PG-only `Array`/`Range`/`HStore`/`Vector`/`Geometry`), nullable `Option<T>`, `Auto<T>` primary keys, `ForeignKey<T>` / one-to-one / many-to-many, generic FKs + composite-key FKs (ContentTypes), soft-delete, `unique_together` / `index_together`, container-level default scopes, subquery/`EXISTS` filters, bulk insert/update, transactions, and raw SQL escape hatches. `EXPLAIN` works on any queryset.

πŸ“– [ORM guide](https://github.com/ujeenet/rustango/blob/main/docs/orm.md) Β· [models](https://github.com/ujeenet/rustango/blob/main/docs/models.md) Β· [runnable ORM recipes](https://github.com/ujeenet/rustango/blob/main/crates/rustango/examples/cookbook_blog/COOKBOOK.md)

## Migrations

`makemigrations` diffs your models against the last migration snapshot and emits JSON operations; `migrate` applies pending ones, and `downgrade` rolls back. Schema changes (create/alter/drop tables, columns, indexes, constraints, composite FKs) are auto-detected; data migrations are hand-authored with `sql` + `reverse_sql`. `embed_migrations!("migrations")` bakes them into the binary.

```bash
cargo run -- makemigrations
cargo run -- migrate
cargo run -- downgrade                       # roll back the last migration
```

πŸ“– [Adopt an existing schema](https://github.com/ujeenet/rustango/blob/main/docs/manage.md) with `manage inspectdb` β€” it emits `#[derive(Model)]` source for every table.

## Auto-admin

An `admin(...)` block on a model gives you a full CRUD admin β€” `list_display`, `list_filter`, `search_fields`, `date_hierarchy`, `fieldsets`, ordering, pagination, and bulk actions β€” with zero hand-written views:

<p align="center">
  <img src="https://raw.githubusercontent.com/ujeenet/rustango/main/docs/img/admin.png" alt="The auto-admin: a Posts list with filter facets, search, bulk actions, and pagination β€” all from one admin(...) block" width="760">
</p>

Also included: a token-driven **theme system** with dark mode and per-tenant branding (logo/colors via the pluggable `Storage` trait β€” S3/R2/B2/MinIO/local), inline child editing (`register_admin_inline!`), a per-write **audit trail** with JSON diffs, a users/roles/permissions RBAC surface, a self-serve change-password page, and session invalidation on password rotation.

πŸ“– [Admin guide](https://github.com/ujeenet/rustango/blob/main/docs/admin.md)

## APIs β€” ViewSets, Serializers, JWT, OpenAPI

`#[derive(ViewSet)]` gives you full REST CRUD β€” list (page or cursor pagination), retrieve, create (one row or a whole array in a single POST), update, partial update, destroy (soft when the model opts in) β€” with per-action permission gates:

```rust
#[derive(ViewSet)]
#[viewset(
    model = Post,
    fields = "id, title, body, author_id, published_at",
    filter_fields = "author_id, status",
    search_fields = "title, body",
    ordering = "-published_at",
    page_size = 20,
    permissions(list = "post.view", create = "post.add", update = "post.change", destroy = "post.delete"),
)]
pub struct PostViewSet;

let app = Router::new().merge(PostViewSet::router("/api/posts", pool.clone()));
```

`#[derive(Serializer)]` is a declarative JSON faΓ§ade over a model (read-only / write-only / renamed / computed `method` fields, per-field `validate`, nested FK serialization, and `many` collections). JWT ships a full lifecycle (issue with custom claims, verify without a DB hit, refresh, re-check permissions, revoke/blacklist). OpenAPI 3.1 auto-derives from your serializers + viewsets. ViewSets answer in rustango's own shape (a `{count, page, results, …}` list envelope and `{error, message, status}` errors); `jsonapi` and `problem_details` are adapters you apply in your own handlers. The HTTP `QUERY` method (RFC 10008) is supported for body-carrying reads.

πŸ“– [ViewSets](https://github.com/ujeenet/rustango/blob/main/docs/viewsets.md) Β· [serializers](https://github.com/ujeenet/rustango/blob/main/docs/serializers.md) Β· [JWT](https://github.com/ujeenet/rustango/blob/main/docs/auth-jwt-api.md) Β· [OpenAPI](https://github.com/ujeenet/rustango/blob/main/docs/openapi.md) Β· [QUERY method](https://github.com/ujeenet/rustango/blob/main/docs/query-method.md)

## HTML views & forms

Class-based views (`ListView`, `DetailView`, `CreateView`, `UpdateView`, `DeleteView`) render Tera templates with pagination, filters, bulk actions, FK-display, and business-validation hooks. `ModelForm`-style forms parse and validate against a model (auto-skipping DB-populated fields), aggregate per-field errors, and emit an insert query. Every view router with a POST route checks the CSRF token.

πŸ“– [HTML views](https://github.com/ujeenet/rustango/blob/main/docs/html-views.md)

## Multi-tenancy

`Cli::tenancy().run()` boots an operator console, a per-tenant admin, and host-based request dispatch on **any backend**. Tenants resolve from subdomain, header, path, or port via a resolver chain; each `Org` picks its isolation strategy:

| Mode | Backends | What it does | When |
|---|---|---|---|
| **`database`** (default) | PG, MySQL, SQLite | A dedicated database (or SQLite file) per tenant, one cached pool each. | Enterprise B2B, compliance, sharding β€” anything on MySQL/SQLite. |
| **`schema`** | **Postgres only** | All tenants share one DB, one per PG schema, one shared pool with `SET search_path` per request. | High-N SaaS on PG (500+ small tenants) where connection counts bite. |

Database-mode is the default and works identically everywhere; schema-mode is a Postgres-only pool optimization. Set `schema` on MySQL/SQLite and the framework returns a clear error pointing you back to database-mode.

πŸ“– Runnable walkthrough: [cookbook Ch. 5 β€” Multi-tenancy](https://github.com/ujeenet/rustango/blob/main/crates/rustango/examples/cookbook_blog/COOKBOOK.md#chapter-5--multi-tenancy)

## Authentication & permissions

Pluggable auth backends (model / API-key / JWT β€” first to recognize the credential wins), argon2id password hashing, typed permission helpers (codename-based, superuser bypass), sessions, TOTP/2FA, API keys, and signed URLs (magic links / time-bounded file downloads).

πŸ“– [passwords](https://github.com/ujeenet/rustango/blob/main/docs/auth-passwords.md) Β· [sessions](https://github.com/ujeenet/rustango/blob/main/docs/auth-sessions.md) Β· [backends](https://github.com/ujeenet/rustango/blob/main/docs/auth-backends.md) Β· [API keys](https://github.com/ujeenet/rustango/blob/main/docs/auth-api-keys.md) Β· [decorators](https://github.com/ujeenet/rustango/blob/main/docs/auth-decorators.md) Β· [flows](https://github.com/ujeenet/rustango/blob/main/docs/auth-flows.md)

## Security middleware

One hardened middleware chain: request IDs, access logging, rate limiting (in-process or distributed via cache), CORS presets, security-header presets + custom/staged CSP, CSP report endpoint, IP allow/block, CSRF, and per-account lockout. `manage check --deploy` runs an automated pre-ship audit.

πŸ“– [Security guide](https://github.com/ujeenet/rustango/blob/main/docs/security.md) Β· [middleware catalog](https://github.com/ujeenet/rustango/blob/main/docs/middleware.md)

## Caching, email, storage, jobs

- **Caching** β€” in-memory / Redis backends, `get_or_set` memoization, and per-view response caching (`CachePageLayer`). [caching](https://github.com/ujeenet/rustango/blob/main/docs/caching.md)
- **Email** β€” a renderer + `Mailable` + job-backed delivery pipeline with pluggable backends. [email](https://github.com/ujeenet/rustango/blob/main/docs/email.md)
- **Storage & media** β€” pluggable `Storage` (S3/R2/B2/MinIO/local), `Media` rows, presigned uploads, collections, and tags. [files](https://github.com/ujeenet/rustango/blob/main/docs/files.md)
- **Background jobs** β€” in-memory or DB queue (`FOR UPDATE SKIP LOCKED` on PG/MySQL 8+, transaction-bounded `UPDATE … RETURNING` on SQLite) plus scheduled tasks. [jobs](https://github.com/ujeenet/rustango/blob/main/docs/jobs.md)

## Signals, i18n, MCP

- **Signals** β€” model lifecycle (`pre_save` / `post_save` / `pre_delete` / `post_delete`) and request lifecycle (`request_started` / `request_finished` / `got_request_exception`).
- **i18n** β€” `Translator` is a `gettext`-style translation API: per-locale catalogs, base-language fallback, `{name}` placeholders, CLDR pluralization, plus a DB-override layer and live admin translation editor. [i18n](https://github.com/ujeenet/rustango/blob/main/docs/i18n.md)
- **MCP server** β€” the `mcp` feature turns an app into a Model Context Protocol server: AI agents authenticate as tenant-scoped identities and call your framework-exposed tools over JSON-RPC 2.0. [mcp](https://github.com/ujeenet/rustango/blob/main/docs/mcp.md)

## The `manage` CLI

Your app's binary doubles as its admin CLI β€” `cargo run -- <cmd>`. Migrations (`makemigrations` / `migrate` / `inspectdb`), scaffolders (`startapp` / `make:viewset` / `make:serializer`), system commands (`check` / `check --deploy` / `dbshell`), and β€” with the `tenancy` feature β€” operator/tenant/superuser provisioning and recovery verbs.

πŸ“– [manage reference](https://github.com/ujeenet/rustango/blob/main/docs/manage.md)

## Configuration

Layered config: a `<env>_settings.toml` pipeline (`default.toml` β†’ `<env>_settings.toml` β†’ `RUSTANGO__*` environment variables), typed sections, compile-time feature reflection, and a deploy audit. Everything has a sensible default; override only what you need.

## Testing

A `TestClient` drives the router as a tower service (no socket), a `RequestFactory` builds requests, and fixtures seed data. The cookbook's ~150 tests run against live Postgres, MySQL, and SQLite.

πŸ“– [testing](https://github.com/ujeenet/rustango/blob/main/docs/testing.md)

---

## Features

Always on: the ORM, the query builder, the SQL layer and migrations.
Everything else is a cargo feature.

`default = ["postgres", "batteries"]` turns on most of the list below.
The ones it does not, opt in by name: `mysql`, `sqlite`, `tenancy`,
`csrf`, `sso`, `admin-sso`, `passkey`, `cache-redis`, `cache-page`,
`email-smtp`, `mcp`, `testkit` and `test_utils`.

**Backends** β€” pick one; every framework surface works the same on all three.

| Feature | |
|---|---|
| `postgres` | PostgreSQL, TLS included. The default. |
| `mysql` | MySQL 8.0+. |
| `sqlite` | SQLite 3.35+, with WAL and foreign keys on. |

**Data & storage**

| Feature | |
|---|---|
| `casts` | Typed field conversions. |
| `media` | Media library with collections and tags. |
| `storage` | File storage abstraction. |
| `storage-s3` | S3-compatible backend. |
| `storage-azure` | Azure Blob Storage backend. |
| `uploads` | Multipart upload handling. |
| `signed_url` | Expiring signed URLs. |

**Web**

| Feature | |
|---|---|
| `runserver` | The development server. |
| `manage` | `cargo run -- <verb>` CLI dispatcher. |
| `template_views` | Generic CRUD view handlers. |
| `forms` | Forms framework with multi-error validation. |
| `sessions` | Server-side sessions. |
| `compression` | Response compression. |
| `sse` | Server-sent events. |
| `websocket` | WebSocket support. |
| `http-client` | Outbound HTTP client. |

**APIs**

| Feature | |
|---|---|
| `serializer` | Typed JSON serializers. |
| `openapi` | OpenAPI schemas generated from serializers. |
| `jwt` | JWT with refresh, blacklist and custom claims. |
| `api_keys` | API key authentication. |
| `hmac-auth` | HMAC request signing. |
| `oauth2` | OAuth2 provider. |
| `webhook` | Webhook registration. |
| `webhook-delivery` | Delivery with retries. |

**Admin**

| Feature | |
|---|---|
| `admin` | The auto-admin site. |
| `admin-sso` | OIDC single sign-on for admins. |

**Auth & security**

| Feature | |
|---|---|
| `auth_flows` | Login, logout, password reset. |
| `passwords` | Password hashing and validators. |
| `totp` | TOTP two-factor authentication. |
| `passkey` | WebAuthn / passkeys. |
| `sso` | OIDC single sign-on for app users. |
| `csrf` | CSRF middleware for form POSTs. |
| `csp-nonce` | Per-response CSP nonces. |
| `secrets` | Secret management and rotation. |

**Operations**

| Feature | |
|---|---|
| `cache` | Cache framework. |
| `cache-redis` | Redis backend. |
| `cache-page` | Whole-page response caching. |
| `jobs` | In-process background job queue. |
| `jobs-postgres` | Database-backed queue, surviving restarts. |
| `scheduler` | Fixed-interval tasks. |
| `signals` | Model lifecycle signals. |
| `email` | Email framework. |
| `email-smtp` | SMTP transport. |
| `notifications` | User notifications. |
| `config` | Layered settings and deploy audit. |

**Multi-tenancy**

| Feature | |
|---|---|
| `tenancy` | Tenant registry, per-tenant databases, operator console. Schema mode is PostgreSQL only. |

**Tooling**

| Feature | |
|---|---|
| `mcp` | Model Context Protocol server for AI agents. |
| `testkit` | Schema builders and model factories for tests. |
| `test_utils` | Test-only constructors for downstream crates. |

Internationalisation, signals, content types, permissions and the audit
log need no feature flag. `cargo rustango new --help` prints the opt-in
list as the scaffolder sees it.

---

## Documentation

- **Guides & tutorials**: <https://rustango.com>
- **Runnable cookbook**: [`cookbook_blog/COOKBOOK.md`](https://github.com/ujeenet/rustango/blob/main/crates/rustango/examples/cookbook_blog/COOKBOOK.md) β€” a test-backed recipe for every feature, on all three backends.
- **In-repo guides** ([`docs/`](https://github.com/ujeenet/rustango/tree/main/docs/)): [getting started](https://github.com/ujeenet/rustango/blob/main/docs/getting-started.md) Β· [models](https://github.com/ujeenet/rustango/blob/main/docs/models.md) Β· [ORM](https://github.com/ujeenet/rustango/blob/main/docs/orm.md) Β· [migrations & CLI](https://github.com/ujeenet/rustango/blob/main/docs/manage.md) Β· [admin](https://github.com/ujeenet/rustango/blob/main/docs/admin.md) Β· [viewsets](https://github.com/ujeenet/rustango/blob/main/docs/viewsets.md) Β· [serializers](https://github.com/ujeenet/rustango/blob/main/docs/serializers.md) Β· [auth](https://github.com/ujeenet/rustango/blob/main/docs/auth-flows.md) Β· [security](https://github.com/ujeenet/rustango/blob/main/docs/security.md) Β· [middleware](https://github.com/ujeenet/rustango/blob/main/docs/middleware.md) Β· [caching](https://github.com/ujeenet/rustango/blob/main/docs/caching.md) Β· [email](https://github.com/ujeenet/rustango/blob/main/docs/email.md) Β· [files](https://github.com/ujeenet/rustango/blob/main/docs/files.md) Β· [jobs](https://github.com/ujeenet/rustango/blob/main/docs/jobs.md) Β· [i18n](https://github.com/ujeenet/rustango/blob/main/docs/i18n.md) Β· [MCP](https://github.com/ujeenet/rustango/blob/main/docs/mcp.md) Β· [testing](https://github.com/ujeenet/rustango/blob/main/docs/testing.md) Β· [glossary](https://github.com/ujeenet/rustango/blob/main/docs/glossary.md)
- **API reference**: <https://docs.rs/rustango>
- **Changelog**: [`CHANGELOG.md`](https://github.com/ujeenet/rustango/blob/main/CHANGELOG.md)

## Contributing

Git hooks (fmt + secret/debris scan on pre-commit; `cargo check --all-features` + clippy + lib tests on pre-push) install with `bin/install-hooks.sh` (sets `git config core.hooksPath .githooks`). Please run `cargo fmt` and `cargo clippy --all-targets` before opening a PR.

## License

Licensed under either of

- Apache License, Version 2.0 ([`LICENSE-APACHE`](https://github.com/ujeenet/rustango/blob/main/LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([`LICENSE-MIT`](https://github.com/ujeenet/rustango/blob/main/LICENSE-MIT) or <http://opensource.org/licenses/MIT>)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.