# 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).
