Files
web/docs/adr/index.md

22 lines
1.4 KiB
Markdown
Raw Normal View History

feat(web): initial implementation — server, mw, httputil, health (v1.0.0) Introduces code.nochebuena.dev/einherjar/web — the HTTP transport layer of the Einherjar framework. Absorbs httpserver, httpmw, and httputil from micro-lib, replacing gorilla/mux with chi, adopting SecurityBag-native middleware, and centralizing error handling through a single httputil.Error function. server: - Server interface — embeds lifecycle.Component and chi.Router - Config struct (EINHERJAR_SERVER_* env vars); DefaultConfig - New(logger, cfg, opts...) Server; WithMiddleware option - Binds TCP synchronously in OnStart; logs "server: listening" on success - Graceful shutdown within ShutdownTimeout on OnStop mw: - Recover — catches panics, returns 500, logs at Error - RequestID — injects UUID v7 (UUID v4 fallback) into context and X-Request-ID header - RequestLogger — structured access log per request - CORS / CORSAllowAll — chi-based, applied only when origins non-empty - IPRateLimit / UserRateLimit — pluggable RateLimiterStore interface - InMemoryRateLimiterStore — token-bucket backed by golang.org/x/time/rate; background goroutine evicts stale entries every 5 minutes - StatusRecorder — wraps ResponseWriter to capture HTTP status code httputil: - Handle[Req, Res] / HandleNoBody[Res] / HandleEmpty[Req] — generic handler adapters - Error(logger, w, r, err) — derives log level from status (≥500→Error, 4xx→Warn, 499→Info); writes standardized JSON body; logz enriches *xerrors.Err automatically - JSON(w, status, v) / NoContent(w) — response helpers - HandlerFunc adapter type health: - NewHandler / NewHandlerWithConfig — runs all Checkable checks concurrently; returns JSON {status, components} with per-component latency and error - Config struct (EINHERJAR_HEALTH_CHECK_TIMEOUT, default 5s) Root factory: - web.New(logger, cfg...) Server — composes Recover+RequestID+RequestLogger+CORS in outermost-first order; CORS applied only when AllowedOrigins non-empty - server.Server interface and web/server/identifiable.go: embeds observability.Identifiable; ModulePath and ModuleVersion read via runtime/debug.ReadBuildInfo() — prints in launcher banner
2026-05-29 15:48:11 +00:00
# ADR Index — web
Module-level architecture decisions for `code.nochebuena.dev/einherjar/web`.
For framework-wide decisions see the
[Einherjar docs repository](https://code.nochebuena.dev/einherjar/docs).
No module-level ADRs for v1.0.0 — all design decisions were consistent with
existing framework principles (ADR-001 through ADR-003 from `core`, framework
ADRs 001004 from `docs`). No contested choices required a record.
Decisions worth noting (not ADR-worthy individually):
| Decision | Outcome | Rationale |
|---|---|---|
| `RateLimiterStore` as interface (not concrete) | Interface only; `InMemoryRateLimiterStore` as default | Allows `cache-valkey` to satisfy it via duck typing without cross-module import |
| Fail-open rate limiting | Request allowed on store error | Availability preferred over hard enforcement during infrastructure degradation |
| `last_seen` excluded | Not in v1.0.0 | Application-domain concern; not transport-level middleware |
| UUID v7 for request IDs | v7 with v4 fallback | Time-ordered IDs sort chronologically in logs; fallback ensures generation never fails |
| `observability.Checkable` not redefined | Imported from contracts | Starters implement contracts directly; no web import needed by db/cache starters |
| Background goroutine for in-memory eviction | `time.Ticker` goroutine | Avoids `worker` module dependency; in-memory store is self-contained |