AkurAI Build
Menu

AkurAI-Build

public

Latest change 2a1193541cf008c406be9f347e44150a58a66130 - docs: preserve the architecture review, backlog, and schema policy by Olafur Bui

# Review 3/10 — HTTP Server Architecture (src/server.rs, src/main.rs)

## Overall
Solid, security-conscious design: graceful shutdown handles SIGINT+SIGTERM (server.rs:1497), 1 MiB `DefaultBodyLimit` (server.rs:240), DB writes/searches offloaded via `spawn_blocking` (server.rs:898, 935, 955), strict percent-decoding of paths/queries, correct static-mode precompression negotiation with q-value parsing (server.rs:470–530), health vs. DB-backed ready split (server.rs:750, 779–791), and static/native security-header middlewares. Findings below are prioritized improvements, not a rewrite.

## Findings

1. **P1 — No request timeouts or connection limits** (server.rs:250, 288). `axum::serve` runs with hyper defaults: no header-read, request, or idle timeout, and no cap on concurrent connections. A slowloris client (slow headers or 1-byte/s bodies within the 1 MiB limit) holds sockets indefinitely; graceful shutdown will also wait on them. Add `tower_http::timeout::TimeoutLayer` (or hyper-util builder timeouts) plus a `GlobalConcurrencyLimitLayer` / listener-level connection cap.

2. **P1 — Blocking DB call on the async executor in the readiness probe** (server.rs:750–751; db.rs:145–148, 367–371). `native_ready` calls `Database::check_ready()` inline, which locks a `std::sync::Mutex<Connection>` and runs a SQLCipher query. While a vector search holds that mutex inside `spawn_blocking`, every `/api/ready` request parks a tokio worker thread on the mutex. Use `spawn_blocking`, or `try_lock` with fast 503 on contention.

3. **P1 — Per-request router clone and template recompilation in production** (server.rs:976, 1031–1046). Every page request deep-clones the `PageRouter` (`state.pages.as_ref().clone()` — Vec/BTreeMap structure copies) and `render_native_template` rebuilds a full minijinja `Environment`, re-adding and recompiling every shared template plus the page. `PageRouter::scan` already validates compilation (server.rs:660–670); build one `Environment` per router at scan time (store in `Arc`) and render from it, and borrow the router instead of cloning.

4. **P2 — Search gate permit released on client disconnect while the query still runs** (server.rs:927–940). `_permit` lives in the handler future; if the client aborts, the future drops and the permit is freed, but the `spawn_blocking` search keeps running. A second search can then start, defeating the single-flight semaphore and contending on the connection mutex. Move the permit into the `spawn_blocking` closure so it is held for the query's actual lifetime.

5. **P2 — Fallback page handler accepts every HTTP method** (server.rs:969). `render_page` renders full pages for POST/PUT/DELETE/etc. Static mode gets this right (405 + `Allow: GET, HEAD`, server.rs:418–425); mirror that check in the native fallback (and in `public_asset`, server.rs:794, though `get()` routing already restricts it).

6. **P2 — `If-None-Match` comparison is non-conformant** (server.rs:453–457, 807–811). Exact string equality: `If-None-Match: *` never yields 304, and RFC 9110 weak comparison (ignore `W/` prefix) is not applied, so a client echoing a weak-tagged variant against static mode's strong ETag revalidates unnecessarily. Handle `*` and strip `W/` before comparing.

7. **P2 — Dev-mode `PageRouter::scan` performs blocking filesystem I/O in the async handler** (server.rs:970–974). Full directory walk + file reads + template compiles per request on a runtime thread. Dev-only, but wrapping in `spawn_blocking` is cheap and keeps the runtime honest.

8. **P2 — Blanket `CompressionLayer` in native mode** (server.rs:241). All responses, including preloaded `/assets/*` bytes, are re-compressed per request (CPU per hit, no caching of compressed output), while the API's tiny JSON gains little. Consider precompressed variants like static mode, or `compress_when` predicates. Minor related note: if signal-handler installation fails, `shutdown_signal` resolves immediately and triggers shutdown (server.rs:1497–1512) — prefer `pending()` on install failure.

## Verdict
No blockers; P1 items 1–3 are the highest-value hardening/perf fixes before heavier production traffic.