Roadmap

Ingenium is alpha (v0.0.1). The API is mostly settled but still subject to change before 0.1.0. This page tracks what's shipped, what's known-broken, and what's planned. Authoritative changelog lives in the source repo.

Shipped in v0.0.1

Core surface:

  • riftex() app factory with lazy-composed middleware pipeline and app.compose() pre-warm.
  • Router() with prefix mounting, nested routers, and deterministic precedence (static > param > wildcard).
  • IngeniumContext with params, query, headers, state, status/header setters, and terminal writers (json / text / html / send / redirect / stream).
  • Lazy body parsers - json (with optional Zod-like schema + maxBytes), text, urlencoded, buffer, stream.
  • Handler return-value reflection (object → JSON, string → text/html, Buffer → octet-stream, Readable → stream, undefined → 204).
  • Error class hierarchy (RiftexError and friends) with default JSON error boundary; app.onError override + re-throw delegation.
  • Express compatibility shim (expressCompat) for pure-function middleware (cors, helmet, etc.) + detect-and-throw guard for known-broken middleware (multer, express-session, compression, body-parser).
  • Plugin system - app.register(plugin, opts?) with lifecycle hooks (onRoute, onCompose, onRequest, onResponse, onError) and per-request decorators.

Transports:

  • Node HTTP adapter with app.listen(port, host?) returning { port, close }.
  • HTTP/2 and HTTP/2 cleartext (h2c) transports.
  • WebSocket and SSE transports sharing the same dispatch entry.
  • Bun adapter (riftexpress-bun) for Bun.serve() with a Web-Streams ↔ node:stream bridge.

Production primitives:

  • Static file middleware - ETag, If-None-Match, range requests, MIME detection, HEAD support, index / extensions / dotfiles / maxAge options.
  • Rate limiting - sliding-window middleware with pluggable store (in-memory ships in core).
  • Sessions - cookie + server-side stores.
  • CSRF protection - native riftex.csrf(opts) middleware, double-submit cookie or session-synchronizer pattern, HMAC-SHA-256 signed, timing-safe verification.
  • CORS - origin allowlist, preflight caching, credentials handling.
  • Trust proxy - X-Forwarded-* and Forwarded parsing with explicit-hop counting.
  • Body parsing - schema-validated JSON with hard maxRequestBytes cap enforced at the transport layer (default 2 MiB).
  • Per-request timeout (requestTimeoutMs) with RiftexTimeoutError (503) and late-write protection via per-context _epoch counter.
  • Graceful shutdown - drain in-flight requests, refuse new ones.

Auth + integrations:

  • JWT middleware - HS256/384/512, RS256/384/512, PS256/384/512, ES256/384/512, plus JWKS support with in-flight request coalescing. 'none' rejected unconditionally.
  • API-key middleware.
  • Idempotency-Key middleware - skip-caching 5xx by default via IdempotencyOptions.cacheable.
  • OpenAPI generation, RFC 7807 application/problem+json errors, content negotiation, reverse-proxy helpers, cron + background jobs primitives.

Hardening:

  • Header injection guard - \r / \n in header names or values throws RiftexHeaderInjectionError (500, code HEADER_INJECTION).
  • ctx.json() safety - circular refs, BigInt, and unserializable values throw RiftexUnserializableError (500, code UNSERIALIZABLE_RESPONSE) instead of bubbling a generic JSON.stringify TypeError. safeJsonStringify helper exported for lenient mode.

Tooling:

  • CLI scaffolder - riftex new <name> [--bun|--minimal].
  • ADR docs covering load-bearing decisions (radix-trie router, lazy composition with dirty bit, return-value reflection, context pool, compat shim scope).

Known issues

  • Compat shim long-tail beyond the known-broken list - middleware that own res.end outside the four detected ones (multer, express-session, compression, body-parser) silently misbehave. Workaround: use the native equivalents listed above. See Express compatibility shim.
  • No ctx.query.parse(schema) yet - only body validation has the schema affordance. See Schema validation.
  • ExtractParams doesn't narrow constrained params - :id(\\d+) stays string. The router doesn't yet honor inline constraints at runtime; types and runtime have to land together.
  • Static middleware doesn't honor If-Modified-Since - only If-None-Match. See Static files.

Not production-ready for multi-instance deploys

The in-memory stores for sessions, idempotency, and rate-limit don't share state across pods. Redis-backed adapters are the next P0.

Deferred to next session

  • Native rate-limit, session, and idempotency Redis stores.
  • Plugin scoping (Fastify-style sub-app affinity) - today plugin hooks/decorators apply app-wide.
  • Standard Schema integration - first-class support so ctx.body.json(schema) accepts TypeBox / Valibot / ArkType natively, not just the { parse(input): T } duck-type that already covers Zod.
  • Native multipart / file upload (ctx.body.multipart()) so multer-shaped use cases stop needing a hand-rolled parser.
  • Full benchmark matrix on CI - pinned versions, isolated CPU pinning, Bun in the same matrix, multi-payload scenarios, RSS tracking.

Open questions

  • Lazy compose dirty-bit cost under heavy mutation. Apps that register routes per-request will recompose on every request. Cap it, warn after N recomposes per minute, or expose a freeze() toggle for production?
  • Compat shim long-tail strategy. Aim for "covers the top 20 middleware on npm" with documented gaps, or stay minimal and route everyone to native ports?
  • Standard Schema vs { parse } duck-type. Promote the schema arg to StandardSchemaV1 (more ergonomic) or keep it duck-typed (keeps the core dep-free)?

Where to next?