Files
poimen-memory/tasks/M3.5.1-http-server.md
T
Story Crater Bot 631cbfa3e9 feat: complete M0.1-M0.4 phases
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
2026-08-22 23:13:42 -07:00

4.1 KiB
Raw Blame History

M3.5.1 — HTTP server + router, Kong auth hook, metrics

Field Value
Phase M3.5 — Distributed API Layer
Size M — 13 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

  1. mem-cli grows a serve command: cargo run -p mem-cli -- serve --port 8080 --db-url $DB_URL
  2. Choose framework: actix-web (stable, high perf) or axum (newer, composable). Decision required — pick one and document the choice.
  3. Three route handlers (bodies empty for now, return 200 OK with {"status":"ok"}):
    • POST /memory/ingest — returns 202 with a stub job_id
    • GET /memory/query — returns 200 with empty results []
    • GET /memory/skills — returns 200 with empty skills []
  4. Request logger middleware — every request logs method, path, status, latency in one line (not pretty-printed).
  5. Metrics middleware — track latency histogram per route (p50/p95/p99 in microseconds), request count, error count.
  6. 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
  7. CORS: disable (agents are internal cluster; no browser requests expected)
  8. Health check: GET /health returns 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 testtests/it_http_server.rs:

  1. a1_server_startsHttpServer::new(...).run() succeeds, port is open.
  2. a2_health_check — GET /health returns 200 and body contains "ok".
  3. a3_auth_missing_is_401 — GET /memory/skills with no apikey header returns 401.
  4. a4_auth_wrong_is_401 — GET /memory/skills with apikey: wrong returns 401.
  5. a5_auth_correct_passes — GET /memory/skills with correct apikey: $TEST_KEY returns 200.
  6. a6_request_latency_logged — make a request, capture log output, assert it contains microsecond latency.
  7. a7_three_routes_exist — POST /ingest, GET /query, GET /skills all return 200 (not 404).
  8. 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 from actix-web or axum that 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