M0.1 - Cargo workspace + crate skeletons - 6-crate workspace with correct dependency direction - CI/CD pipeline with GitHub Actions - Integration tests verifying build and dependency structure M0.2 - Domain types and sha256 identity - Level (L0, L1, L2) enum with proper serde formatting - Role enum (User, Assistant, ToolResult, System) - Record, Chunk, and MemoryNode domain types - Content-hash identity system ensuring rebuild idempotence - Newtypes (ProjectId, QueryId, RunId) with validation - Round-trip serde tests for all types M0.3 - RecordSource trait + ChunkPolicy - RecordSource trait for streaming record sources - Chunk policy with token budgets and boundary modes - TokenCounter trait with CharsOverFourCounter stub - Chunking stream that respects budgets without splitting records - VecSource for testing - Integration tests verifying lossless chunking and budget adherence M0.4 - Tokenizer-backed chunk sizing - Vendored Qwen2 tokenizer with hash verification - QwenTokenCounter implementing proper token counting - Hash guard that fails on modified tokenizer - mem tokens CLI subcommand for token counting - Integration tests with known string counts, hash guards, and budget verification Total: 19 integration tests passing, all phases verified to compose correctly Workspace builds cleanly with no clippy warnings
4.1 KiB
4.1 KiB
M3.5.1 — HTTP server + router, Kong auth hook, metrics
| Field | Value |
|---|---|
| Phase | M3.5 — Distributed API Layer |
| Size | M — 1–3 days |
| Status | ⬜ Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | M3.5.2, M3.5.3, M3.5.5, M3.5.6 |
Goal
HTTP facade for homelab gateway. Three routes (/ingest, /query, /skills), async background tasks, request metrics. Auth hook validates Kong apikey: header. Stateless — no business logic here, just request demultiplexing.
Architecture
Kong (api.riotpiao.com)
↓ apikey validation
HTTP Server (Rust httpd, actix-web or axum)
↓ route dispatch
/ingest (async) /query (sync) /skills (read-only)
Steps
mem-cligrows aservecommand:cargo run -p mem-cli -- serve --port 8080 --db-url $DB_URL- Choose framework: actix-web (stable, high perf) or axum (newer, composable). Decision required — pick one and document the choice.
- Three route handlers (bodies empty for now, return 200 OK with
{"status":"ok"}):POST /memory/ingest— returns 202 with a stubjob_idGET /memory/query— returns 200 with empty results[]GET /memory/skills— returns 200 with empty skills[]
- Request logger middleware — every request logs method, path, status, latency in one line (not pretty-printed).
- Metrics middleware — track latency histogram per route (p50/p95/p99 in microseconds), request count, error count.
- Kong auth hook:
- Extract
apikey:header (case-insensitive header name, exact value match against stored key) - If missing or unrecognized → 401 with
{"error":"unauthorized","reason":"missing apikey header"} - Pass apikey to request context so handlers can log which key made the request
- Extract
- CORS: disable (agents are internal cluster; no browser requests expected)
- Health check:
GET /healthreturns 200{"status":"ok","uptime_seconds":N}
Acceptance
- Server starts without errors
- Health check responds
- Three routes defined and callable
- Auth middleware rejects missing apikey (401)
- Request logger emits latency per request
- Metrics collected (observable via endpoint or in-process)
Verify
Harness: Integration tests against a live server instance started in each test.
Integration test — tests/it_http_server.rs:
a1_server_starts—HttpServer::new(...).run()succeeds, port is open.a2_health_check— GET /health returns 200 and body contains"ok".a3_auth_missing_is_401— GET /memory/skills with no apikey header returns 401.a4_auth_wrong_is_401— GET /memory/skills withapikey: wrongreturns 401.a5_auth_correct_passes— GET /memory/skills with correctapikey: $TEST_KEYreturns 200.a6_request_latency_logged— make a request, capture log output, assert it contains microsecond latency.a7_three_routes_exist— POST /ingest, GET /query, GET /skills all return 200 (not 404).a8_metrics_collected— inspect metrics middleware state after request, assert latency histogram contains sample.
Command: cargo test -p mem-cli http_server
False pass:
- Auth check only verified on one endpoint. Test all three separately — a route without middleware does not inherit it.
- Metrics collected but never asserted. A metrics middleware that silently fails still compiles.
- Latency logged in milliseconds. The real metric needs microseconds (or the paper's 5000-token chunk at 812ms latency dominates the timing, and p99 becomes meaningless).
Traps
- Actix-web's
.service()does not inherit middleware registered outside a scope; scope middleware applies only to routes inside that scope. - Header name case matters for Kong's key-auth;
apikey:is lowercase. tokio::runtime::Runtime::new()in tests blocks on network if used naively — use test utilities fromactix-weboraxumthat spawn the server in a background thread.- Metrics registered at startup are easy to forget to increment. Middleware must actually call the metrics update, not just define it.
Background: DESIGN.md § Distributed API Layer