Documentation

Backend roadmap

Wedge: reactive full-stack web — see STRATEGY.md.
Track: Backend (this document) — unlocks full-stack reactivity (likely the true signature).
Prior track: Frontend (FRONTEND_ROADMAP.md)
Master checklist: TaskList.md

Live Graph (DB → wire → pixel) is the reactive spine; this track is the HTTP/app framework that lets you build real servers without hand-rolling every IF path EQUALS.


Thesis

Luke apps should declare routes, binds, and sessions the same way they declare reactive cells — declarative surface, native Build cost, no glue frameworks.


Gap status

Gap Status Beachhead
Parameterized SQL (? binds) dbExecBind / dbQueryBind + LIST of TEXT; TLS conn pool + stmt cache + WAL
Networked DB (Postgres / libpq) std/pg — Slipstream default — NETWORK_DB_ROADMAP.md, numbers in BACKEND_BENCHMARKS.md
Exam ops dashboard Exam/ — Luke watching itself (request log → IVM → live Material UI)
Path params + match httpMatch path, "/user/:id", params
Method-aware dispatch SERVE ROUTES ON server WITH n codegen from ROUTES + HANDLE
Query string → MAP httpQueryMap
Headers / cookies httpHeader / httpCookie / httpSetCookie
JSON body 🟡 httpBody + jsonParse (stdlib sugar later)
Form body (application/x-www-form-urlencoded) httpFormMap + FORM / VALIDATE FORMform_*_error / form_*_ok cells
Auth / session / login 🟡 std/auth — Argon2id, sessions, CSRF, scoped WATCH, SECRET, FLOW, LIMIT/remaining, REVEAL, WHO SAW SINCE, SCRUB TO access
Middleware / filters 🟡 REQUIRE LOGIN / REQUIRE CSRF; MIDDLEWARE ORDER AUTH THEN RATE LIMIT (order compile check)
Declarative route table syntax ROUTES / HANDLE / LINK TO / SERVE ROUTES — broken link / missing HANDLE / SECRET without auth = compile error
SSE channel auth / backpressure 🟡 PUSH WATCH of SECRET requires FOR CURRENT USER; scheduler lanes = backpressure story
Password reset / 2FA / OAuth 🟡 FLOW + VERIFY BY CODE\|TOTP\|OAUTH wrapper only — no invented providers (interop/conformance)
Declarative LIMIT + reactive remaining 🟡 LIMIT login TO N PER … + login.remaining + REFRESH LIMIT
Migrations / schema helpers SCHEMA / ENSURE SCHEMA; MIGRATION / MIGRATE / REWIND + luke_schema_migrations
C10K concurrency (pool / evented I/O) SO_REUSEPORT multi-loop + per-loop pools; keep-alive 100k; idle timeouts; graceful SIGTERM — see DEPLOY.md
HTTP/1.1 keep-alive / chunked / streaming keep-alive reuse; httpChunkOpen/httpChunk/httpChunkEnd; SSE unchanged
TLS Reverse proxy (Caddy/nginx) — no invented in-process TLS; LUKE_TRUST_PROXY + httpClientIp

Auth rules (non-negotiable)

  1. No homegrown crypto — passwords via libsodium Argon2id (crypto_pwhash_str / _verify); randomness via randombytes_buf; compares via sodium_memcmp; audit chain via crypto_generichash.
  2. Password = hash, not encryption — plaintext never stored; “auto encrypt everything” is out of scope (key management is the hard part).
  3. Secure path together — hash + timing-safe verify + session cookie (HttpOnly, SameSite=Lax; LUKE_AUTH_SECURE=1 adds Secure) + CSRF. Not hash-only.
  4. Live Graph + authWATCH … FOR CURRENT USER binds user_id = ? per request (no shared IVM across tenants).
  5. Auth-as-typesSECRET on an unscoped path is a compile error; FLOW DONE without VERIFY is a compile error; declassify via REVEAL. See AUTH.md.
  6. Whole-stack compile gates (beachheads) — broken LINK TO, SECRET route without auth, middleware order inversion, OAuth without VERIFY BY OAUTH, unknown SCHEMA types → compile error.

Examples: auth_unit.luke, auth_api.luke, auth_scoped.luke, auth_secret_ok.luke, auth_flow_ok.luke, auth_lang_ok.luke, backend_lang_ok.luke, backend_routes_serve.luke, backend_form_errors.luke, backend_migrate_ok.luke (+ negatives backend_routes_bad_*.luke, backend_mw_bad_order.luke, backend_flow_oauth_bad.luke).


War cry surface (this beachhead)

import std/serverimport std/sqlitefn health(req: Request) {  httpReply(req, 200, "text/plain", "ok")}fn show_user(req: Request) {  let path = httpPath(req)  var params: map = {}  httpMatch(path, "/user/:id", params)  let id = params["id"]  httpReply(req, 200, "text/plain", id)}raw "ROUTES DO"raw "GET \"/ok\" HANDLE health"raw "GET \"/user/:id\" AS INTEGER HANDLE show_user"raw "END ROUTES"let server = httpListen(8799)raw "SERVE ROUTES ON server WITH 8"

Form validation feeds reactive cells (BIND in UI without a second declaration):

raw "FORM login DO"raw "HAS email AS EMAIL"raw "HAS password AS PASSWORD"raw "END FORM"raw "VALIDATE FORM login FROM params"raw "# → form_login_email_error / form_login_ok cells"

Migrations are versioned UP/DOWN SQL (rewind = apply DOWN):

MIGRATION app DO  VERSION 1 UP "CREATE TABLE items(id INTEGER PRIMARY KEY)" DOWN "DROP TABLE items"END MIGRATIONMIGRATE app ON db TO 1REWIND app ON db TO 0

Examples: examples/build/sql_bind.luke, examples/build/backend_api.luke, examples/build/backend_routes_serve.luke.


Sequencing

  1. Secure data path — parameterized binds (done); migrate demos off string-concat SQL.
  2. Request shape — match + query map + headers/cookies + form cells (done); richer JSON helpers next.
  3. Session / auth — opaque sid cookie + DB table (beachhead); do not invent OAuth/TOTP providers — wrap textbook flows only.
  4. Declarative routesROUTES + HANDLE + SERVE ROUTES codegen (done); no clever auto-dispatch invention.
  5. Schema migrate/rewindMIGRATION / MIGRATE / REWIND (done).
  6. Hardening — middleware depth, request limits, SSE auth, structured errors.