karbon-framework
Karbon is a batteries-included Rust web framework built on Axum. MySQL and PostgreSQL, JWT auth, CSRF, background jobs, events, i18n, and more — out of the box.
Setup
# Cargo.toml
[]
= { = "karbon-framework", = "0.1" }
= { = "0.8", = ["runtime-tokio-rustls", "mysql", "chrono"] }
# .env
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=myapp
DB_USER=root
DB_PASSWORD=
JWT_SECRET=change-me-to-a-random-secret
use App;
async
For PostgreSQL, swap the feature:
= { = "karbon-framework", = "0.1", = false, = ["postgres"] }
= { = "0.8", = ["runtime-tokio-rustls", "postgres", "chrono"] }
Controllers
Define routes with proc macros. The #[require_role] attribute auto-injects role checks.
use ;
use AppState;
use AuthGuard;
use State;
use IntoResponse;
use Json;
// Mount in your router:
Entities & CrudRepository
Implement CrudRepository to get free CRUD methods. Add SOFT_DELETE = true for soft deletion.
use FromRow;
use Serialize;
use CrudRepository;
// Now you can:
let post = find_by_id.await?;
let post = find_by_slug.await?;
let all = find_all.await?;
let n = count.await?;
delete.await?; // sets deleted_at = NOW()
restore.await?; // sets deleted_at = NULL
force_delete.await?; // actual DELETE
// Dynamic WHERE:
let posts = find_all_where.await?;
Insertable & Updatable
Derive macros that generate type-safe INSERT/UPDATE queries.
use Insertable;
// auto-adds created_at = NOW()
let id = NewPost .insert.await?; // returns last_insert_id
use Updatable;
// auto-adds updated_at = NOW()
UpdatePost .update.await?;
// → UPDATE posts SET title=?, updated_at=? WHERE id=?
Transactions
let mut tx = state.db.begin.await?;
NewPost .insert.await?;
NewComment .insert.await?;
tx.commit.await?;
// If anything fails, the transaction is automatically rolled back on drop
Authentication & Security
JWT
use ;
let jwt = new;
let token = jwt.generate?;
let claims = jwt.verify?;
AuthGuard (Axum extractor)
async
Role Hierarchy
ROLE_SUPER_ADMIN → ROLE_ADMIN → ROLE_REDACTEUR, ROLE_MODERATEUR → ROLE_USER
A user with ROLE_SUPER_ADMIN passes auth.require_role("ROLE_ADMIN").
Password Hashing
use Password;
let hash = hash?; // Argon2id
let ok = verify?; // also supports bcrypt legacy
Middleware
All built-in and applied automatically by App::serve():
| Middleware | Description |
|---|---|
| Compression | gzip/brotli on all responses |
| Request ID | X-Request-Id UUID on every request/response |
| Graceful shutdown | Clean shutdown on Ctrl+C / SIGTERM |
Available as opt-in layers:
use ;
let app = new
.route
.layer
.layer;
| Middleware | Description |
|---|---|
csrf_protection |
Double-submit cookie (SameSite=Strict, constant-time comparison) |
RateLimitLayer::per_minute(n) |
Per-IP rate limiting |
maintenance_mode |
Returns 503 globally when enabled |
request_logger |
Logs method, path, status, duration |
Background Jobs
In-process job queue with configurable workers and automatic retry.
use ;
use Future;
use Pin;
use Duration;
let queue = new; // 4 concurrent workers
queue.push.await;
Event System
Async pub/sub for decoupling logic.
use ;
let bus = new;
// Register handlers
bus..await;
bus..await;
// Emit — all handlers run concurrently
bus.emit.await;
WebSocket
Helpers for Axum WebSocket upgrade.
use websocket_handler;
use ;
async
// In your router:
new.route
i18n
Simple translation system with interpolation and locale fallback.
use I18n;
let mut i18n = new;
i18n.add_translations;
i18n.add_translations;
i18n.t; // → "Utilisateur introuvable"
i18n.t_locale; // → "User not found"
i18n.t_with; // → "Bienvenue David !"
// Load from JSON:
i18n.load_json?;
Validation
25+ built-in constraints for input validation.
use *;
use *;
use *;
// String constraints
new.min.max.validate?;
new.validate?;
new.validate?;
new.validate?;
// Password strength
strong.validate?;
// Async validation (database checks)
use AsyncValidator;
new
.unique
.exists
.validate
.await?;
Pagination
use ;
// In a handler — params come from query string: ?page=1&per_page=20&sort=id&order=desc&search=hello
async
// Response: { "data": [...], "meta": { "page": 1, "per_page": 20, "total": 42, "total_pages": 3, "has_next": true, "has_prev": false } }
Database Seeder
use ;
use DbPool;
use Pin;
use Future;
;
run_seeders.await?;
Testing
use TestApp;
async
async
async
File Uploads
use ;
let config = UploadConfig ;
let manager = new;
let file = manager.save.await?;
// Magic bytes validated, SVG sanitized, path traversal prevented
// Returns: SavedFile { filename, path, size, mime_type }
ImgResizer — On-the-fly Image Processing
Automatic image resizing, format conversion, and caching. Just add one line to your router.
use ImgResizer;
// Quick setup
app.nest_service;
// Advanced config
app.nest_service;
URL format
/files/r/{spec}/{path}
| URL | Effect |
|---|---|
/files/r/320x180/uploads/photo.jpg |
Resize fit 320x180 |
/files/r/640x0/uploads/photo.jpg |
Width 640, auto height |
/files/r/0x400/uploads/photo.jpg |
Height 400, auto width |
/files/r/320x180_cover/uploads/photo.jpg |
Cover crop (fill + crop) |
/files/r/320x180_stretch/uploads/photo.jpg |
Stretch to exact size |
/files/r/800x600.webp/uploads/photo.jpg |
Convert to WebP |
/files/r/320x180_q75/uploads/photo.jpg |
JPEG quality 75% |
/files/r/320x180_gray/uploads/photo.jpg |
Grayscale filter |
/files/r/320x180_blur3/uploads/photo.jpg |
Gaussian blur (sigma 3) |
/files/r/800x600_cover_q90_gray.webp/path.jpg |
All combined |
Query params: ?anchor=bottom-right for crop anchor in cover mode.
Image processor (programmatic)
use ;
new
.resize
.mode
.anchor
.jpeg
.grayscale
.sharpen
.watermark
.process?;
Features: resize (fit/cover/stretch/width/height), crop, rotate, flip, blur, brightness, contrast, grayscale, sharpen, watermark with opacity/scale/position, WebP/PNG/JPEG/GIF output, decompression bomb protection, upscale guard, disk cache with auto-invalidation.
Full documentation
CLI, project scaffolding, and full docs: github.com/larevuegeek/karbon
License
AGPL-3.0-or-later — Copyright (C) 2026 LaRevueGeek