Every decision in Lithair has a reason. This page explains the philosophy behind the code, the features, and the choices that make Lithair what it is.
You start a server. You see a wall of text. You have no idea if it's normal, if something is misconfigured, or where to look when something breaks.
Lithair takes a different approach: every log has a purpose. At startup, you see exactly what's configured, what's loaded, how much memory is used. At runtime, you see cache hits, auth events, data operations — with timing.
Visual prefixes make scanning instant. Timing data makes performance visible.
And log levels are respected: info gives you the overview,
debug gives you the details, trace gives you everything.
The goal
When something goes wrong at 3am, your logs should tell you what happened without having to reproduce the issue.
🚀 Lithair Website - Simple & Declarative
==========================================
📁 Data: ./blog_data
🌐 Port: 3007
🔐 RBAC: Declarative (Admin, Editor, Viewer)
📦 Models: Article (DeclarativeModel with auto CRUD)
🎨 Frontend: Astro + mdBook docs (public/)
🛡️ Admin: /secure-a1b2c3
INFO ✅ RBAC configured with 3 roles
INFO ✅ 3 demo users created
INFO ✅ Sessions: ./blog_data/sessions
INFO ✅ Articles: ./blog_data/articles
INFO ✅ Admin routes protected declaratively INFO 🔄 Initializing memory-first cache for 'public/'
(extensions: [html, css, js, svg, png, woff2])
INFO ✅ Cache initialized in 23ms
(47 items loaded, 2.34MB/100MB memory - 2.3%)
TRACE 🎯 Cache HIT '/index.html' in 0.003ms
(age: 4m 23s)
DEBUG ❌ Cache MISS '/unknown.css' in 0.001ms
(total: 1,247 hits, 3 misses)
WARN ⚠️ Memory usage is high (82.1%),
consider increasing CACHE_MAX_MEMORY_MB INFO ✅ User logged in: admin as Admin
INFO 👋 User logged out: editor (session: a3f2...)
DEBUG 🔍 SCC2 Data Access: Retrieved 12 articles
from persistent storage Lithair does a lot. It handles your HTTP, your data, your auth, your persistence, your admin. When you do everything, everything must be tested.
Unit tests validate individual components. Integration tests verify the full stack. And Gherkin scenarios describe expected behavior in human-readable language, so you can read and validate what the system is supposed to do without reading Rust.
This is the DevOps mindset: the CI pipeline is not a nice-to-have. It's the safety net that lets you ship with confidence. Every PR is tested. Every feature has coverage. Every security-sensitive path is verified.
The goal
If it's not tested, it doesn't exist. If a scenario is not in Gherkin, the behavior is not guaranteed. The tests ARE the specification.
Feature: Authentication
As a user of a Lithair application
I want authentication to be secure by default
Scenario: Login with valid credentials
Given a user "admin" with role "Admin" exists
When I POST to /auth/login with valid credentials
Then the response status should be 200
And I receive a session token
And the session is stored in memory
Scenario: Admin route requires authentication
Given I am not authenticated
When I access /admin/
Then I am redirected to /admin/login/
And no admin content is leaked
Scenario: MFA enforcement for Admin role
Given a user "admin" with MFA required
When I login without TOTP code
Then I receive "mfa_required" status
And the session is NOT fully activated #[test]
fn test_path_validation_security() {
// Valid paths
assert!(
validate_and_normalize_path("docs/index.md", "docs")
.is_ok()
);
// Directory traversal attacks — MUST fail
assert!(
validate_and_normalize_path("../etc/passwd", "docs")
.is_err()
);
assert!(
validate_and_normalize_path(
"docs/../../../etc/passwd", "docs"
).is_err()
);
} #[test]
fn test_declarative_permission_checker() {
let checker = DeclarativePermissionChecker
::from_definitions(vec![
("Admin", vec!["*"]),
("Editor", vec!["ArticleRead", "ArticleWrite"]),
("Viewer", vec!["ArticleRead"]),
]);
// Admin has wildcard — everything passes
assert!(checker.has_permission("Admin", "ArticleDelete"));
// Editor: read + write, NOT delete
assert!(checker.has_permission("Editor", "ArticleWrite"));
assert!(!checker.has_permission("Editor", "ArticleDelete"));
// Viewer: read ONLY
assert!(!checker.has_permission("Viewer", "ArticleWrite"));
} CI pipeline roadmap: Every PR triggers unit tests, integration tests, and Gherkin scenario validation. Security-sensitive paths (auth, path traversal, RBAC boundaries) have dedicated test suites. Coverage reports are enforced, not optional.
Your HTML, CSS, JS, images — everything is read from disk once at startup, stored in SCC2, and served directly from memory for every request after that.
Traditional web servers (Nginx, Apache) read files from disk for every request. They use OS-level caching (page cache) which helps, but you're still going through the kernel's filesystem layer, file descriptors, and system calls.
Lithair skips all of that. At startup, files are loaded into a lock-free concurrent HashMap (SCC2). Serving a file is a direct memory lookup — no syscall, no file descriptor, no kernel involvement.
For a typical frontend (50 files, 2-3 MB), the memory cost is negligible. The performance gain is not.
1. Startup
All files in ./public are read, MIME-typed, and stored in SCC2. Configurable max file size (default 10MB) and total memory (default 100MB).
2. Request
URL path is mapped to a key in SCC2. Direct memory read. Response includes x-cache: HIT header with timing.
3. Hot reload (dev mode)
File watcher detects changes and updates SCC2 in-place. No restart needed during development.
4. Configuration
CACHE_MAX_MEMORY_MB,
CACHE_MAX_FILE_SIZE_MB,
CACHE_ENABLE_HOT_RELOAD — all via environment variables.
| Traditional (disk) | Lithair (memory) | |
|---|---|---|
| Latency | 5-50ms | 0.001-0.5ms |
| Syscalls per request | open + read + close | 0 |
| File descriptors | 1 per request | 0 |
| Cold start cost | None (lazy) | ~23ms (preload all) |
Most admin panels rely on client-side redirects and token checks. Lithair protects the admin at the server level with declarative route guards, randomized paths, and multi-factor authentication.
Predictable paths
/admin, /dashboard, /wp-admin — bots scan these 24/7. You're relying on your auth layer to handle a constant stream of attacks.
Client-side guards
A JavaScript redirect to /login is not security. The HTML, JS, and API endpoints are still reachable. An attacker doesn't need the UI to exploit the API.
No MFA by default
Admin accounts with password-only auth are the #1 attack vector. MFA is always "we'll add it later" and later never comes.
Randomized admin path
RS_ADMIN_PATH=random generates a unique path like /secure-a1b2c3 on each deploy. Bots can't find what they can't predict.
Server-side route guards
Declarative guards protect URL patterns at the HTTP layer. No valid session? The server returns a redirect — no admin content, no API access, no HTML leak.
MFA enforced per role
Admin role requires TOTP by default. Editor can opt in. 5 endpoints auto-generated, compatible with Google Authenticator, Authy, 1Password.
RBAC on every operation
Field-level permissions checked on every API call. An Editor can write articles but not delete them. A Viewer can read but not modify. All enforced automatically.
// 🛡️ DECLARATIVE ROUTE GUARD
// Protect admin routes — server-side, no client JS needed
.with_route_guard(RouteGuard {
protected_prefix: "/admin".into(),
login_path: "/admin/login".into(),
session_cookie: "lithair_session".into(),
})
// 🔐 MFA required for Admin, optional for Editor
.with_mfa_totp(MfaConfig {
issuer: "Lithair Blog".into(),
required_for: vec!["Admin".into()],
optional_for: vec!["Editor".into()],
}) In a traditional database, an UPDATE overwrites the previous value. It's gone. Forever. In Lithair, every change is an event that's appended to a log. Nothing is ever lost.
A user edits their profile. You run UPDATE users SET name = 'new' WHERE id = 1.
The old name is gone. If you need to know what it was yesterday? You can't. Unless you built
a separate audit system, which most people don't.
Now multiply that by every field, every model, every user. You have a database that shows the current state but has zero memory of how it got there.
Event sourcing flips this: instead of storing the current state, you store every change that ever happened. The current state is simply the result of replaying all events.
Complete audit trail
Every change tracked with timestamp, user, old value, new value. Not a separate audit table — it's the data model itself.
Time-travel
Reconstruct any entity's state at any point in history. #[lifecycle(versioned = 3)] keeps the last 3 versions automatically.
Safe replication
Events are stored in .raftlog files that any node can replay. This is how Raft consensus keeps nodes in sync.
Zero-cost recovery
Snapshots consolidate events into current state. On restart: load latest snapshot + replay remaining events into SCC2. Milliseconds, not minutes.
// Traditional database: current state only
UPDATE articles SET title = 'New Title' WHERE id = 'abc';
// Old title? Gone. Who changed it? Unknown. When? No idea.
// Lithair: every change is an event
{ "type": "ArticleUpdated",
"id": "abc",
"field": "title",
"old": "Old Title",
"new": "New Title",
"user": "editor",
"timestamp": "2026-02-23T09:15:42Z" }
// Old title? "Old Title". Who? "editor". When? 09:15:42. Lithair is open source. Read the code, run it, break it, contribute.
Code that explains itself
Too many open-source projects treat comments as optional. You open a file, see a function, and have to reverse-engineer why it exists, what it does, and what happens if you change it.
In Lithair, every module, every function, every non-obvious decision is documented inline. Not boilerplate comments that restate the code. Real explanations of intent, trade-offs, and consequences.
Yes, AI helps write Lithair. That's precisely why the comments matter. AI-assisted code without context is a liability. AI-assisted code with clear documentation is an asset anyone can audit, understand, and maintain.
The goal
Zero back-and-forth between the code and external documentation. If you're reading a file, you have everything you need to understand it.