Lithair v0.9.0 ships two changes that came directly from a real application being built on top of it: a security fix that closes a session-gate bypass in with_handler, and a new with_model_ref API that lets background workers write to models without sacrificing authentication.

What shipped

Security fix — with_handler now respects with_models_require_session(true) (PR #87, closes #86)

When you call with_models_require_session(true), every auto-generated /api/* endpoint is gated behind session auth. That worked correctly for routes registered via with_model. It silently didn’t work for routes registered via with_handler.

The root cause: with_handler used raw with_route calls that bypassed the model_infos pipeline — the same pipeline that wires the session gate. Any consumer who migrated from with_model to with_handler for programmatic access was unknowingly running without auth, even with the gate explicitly set.

Fix: with_handler now pushes a deferred gate-applier closure into an external_handler_gates vec. At serve() time, all closures run — applying the session gate to every with_handler-registered handler. Order of builder calls doesn’t matter; the gate is applied after the full builder chain resolves.

438 tests, including new invariants covering the with_handler path specifically.

Programmatic write API — with_model_ref::<T>() (PR #88, closes #85)

Before v0.9.0, the only way to write to a model programmatically (from a background job, OAuth callback, or external sync task) was with_handler — which lost the session gate, or manual Arc gymnastics. The consumer needed to write directly to a model from a sync worker running alongside the HTTP server.

with_model_ref solves this cleanly:

let (builder, mail_handler) = LithairServer::new()
    .with_models_require_session(true)
    .with_model_ref::<Mail>("./data/mails", "/api/mails")
    .await?;

// mail_handler: Arc<DeclarativeHttpHandler<Mail>>
// HTTP clients still go through session auth
// Background sync worker uses mail_handler directly:
mail_handler.apply_replicated_item(new_mail).await?;

The returned Arc<DeclarativeHttpHandler<T>> handles apply_replicated_item, apply_replicated_update, apply_replicated_delete. The session store is wired internally — no manual plumbing. HTTP clients still hit the session gate; the Arc holder bypasses it by design (it’s your own process).

442 tests.

Why it matters

Both changes come from a real consumer hitting real walls. The session gate bypass was a correctness bug that could silently open an API that was supposed to be private. The programmatic API closes the last gap between “fits in RAM, runs as a single binary” and “can do anything a traditional server does.”

The pattern — same-day fix, same-day release, crates.io published — is becoming a reliable characteristic of how this project handles production feedback.

What’s next

The next tractable item from the consumer-feedback queue is native in-process event subscription (#70): an EventListener trait that reacts to model mutations without the HTTP round-trip. Same pattern as with_model_ref — Rust trait, registered on the builder, fan-out alongside SSE clients. Lightweight indexing or fan-out without a self-issued HTTP connection.