# Architect — F-003 HTTP foundation and request context done -> work/artifacts/F-003/architect.md ## Deliverables - specs/F-003-http-foundation/SPEC.md - specs/F-003-http-foundation/DESIGN.md - specs/F-003-http-foundation/TASKS.md - specs/F-003-http-foundation/TESTS.md ## Key decisions 1. **pino** for JSON structured logging: Fastify ecosystem standard, fast, boring. Fastify's own logger stays OFF (`logger: false`); logging happens through explicit onRequest/onResponse hooks so there is exactly one log line per request and zero hidden log paths. 2. **zod** for the input validation hook: explicit `parseJson(schema, body)` calls inside handlers. No decorators, no magic binding. F-003 ships the hook; first real consumer is F-005 identity. 3. **request_id policy**: propagate incoming `x-request-id` only when it matches `^[A-Za-z0-9._-]{1,128}$` (header-injection defense); otherwise `crypto.randomUUID()`. Response header `x-request-id` on every response; error envelopes also embed requestId. 4. **Error envelope v2** (additive): `{ error: { statusCode, code, message }, requestId }`. Codes: NOT_FOUND, VALIDATION_ERROR, INTERNAL_ERROR, or upstream Fastify error codes for known 4xx. >=500 always answers the generic `Internal Server Error`; stack goes to logs tagged with requestId, never to the client. 5. **Logger injection**: `buildApp({ logger? })` — tests capture logs through an in-memory destination. No transports, no env sniffing in tests. ## Boundaries - Cross-cutting code lives in `shared` and `infrastructure/logging`; composition wiring only in `src/app`. Modules (health) untouched. ## Open risks - Envelope change touches F-001 test expectations (additive; tests will be updated in build).