# Server frameworks & overlapping dependencies — sanctioned choices

Status: adopted 2026-07-16 (audit R-17,
`docs/audits/MONOREPO_ARCHITECTURE_AUDIT_2026-07-16.md`). Enforcement is
review-time until lint automation lands.

## HTTP server frameworks

The workspace currently ships four (fastify 114 packages, hono 67, express 19,
NestJS 16). For **new** services:

| Framework   | Status        | Use for                                                                                                                                                        |
| ----------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **fastify** | ✅ sanctioned | Platform/backend services (the majority pattern; `@lilith/fastify-core` exists)                                                                                |
| **hono**    | ✅ sanctioned | Edge runtimes, BFFs, lightweight gateways                                                                                                                      |
| express     | 🧊 frozen     | No new services. Existing 19 packages maintained in place; migrate opportunistically when a service is being rewritten anyway                                  |
| NestJS      | 🧊 frozen     | No new services. The 16 existing packages are also the only reason `emitDecoratorMetadata`/`experimentalDecorators` are on in the global tsconfig (audit R-15) |

## HTTP clients

Use native `fetch` via **`@oshun/http-client`** (timeout, retry,
circuit-breaker, SSRF guard, tracing). `axios` (6 packages) and `node-fetch` (3)
are frozen for new code.

## Overlapping utility dependencies — resolution policy

| Overlap                                          | Policy                                                                                                                                                                                                                                                                                                                                      |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `uuid` vs `nanoid` vs hand-rolled                | New code uses **`@oshun/ids`** (uuid/ulid/prefixedId). `uuid`/`nanoid` frozen for new code; migrate via the R-10 rollout                                                                                                                                                                                                                    |
| `bcrypt` vs `bcryptjs`                           | **Do not blind-swap.** The 6 `bcryptjs` users are auth-critical services; hash formats are compatible ($2a/$2b) but native `bcrypt` adds a compile step to their container builds. Swap per-service with its test suite + image build verified — not mechanically                                                                           |
| `crypto-js`                                      | **Load-bearing in React Native** (`apps/lilith/mobile` encryption-at-rest; Hermes has no `node:crypto`, and on-device ciphertext must remain decryptable). Any migration (e.g. to `@noble/ciphers`) must ship a data-format migration for existing installs. `apps/lilith/svc-offline` shares formats with mobile. Not a mechanical removal |
| `@noble/*` vs `hash.js`/`crypto-js` for NEW code | New cryptographic code uses `node:crypto` (services) or `@noble/*` (cross-platform)                                                                                                                                                                                                                                                         |

The audit's R-17 originally framed `bcryptjs`/`crypto-js` as simple removals;
investigation during P2 execution (2026-07-16) showed both carry
runtime/deployment constraints, recorded above so nobody "cleans them up"
mechanically.
