# Phase 2 — Integration & dependency **Depends on:** Phase 0 · **Can run in parallel with:** Phase 1 · **Blocks:** Phase 3 · **Size:** one session ## Goal Make the client↔server dependency a **thing you can read in one place**, and re-sync the contract layer with the code it stopped tracking on 2026-07-13. Right now the seam is real but scattered: envelope shape and casing in one contract doc, cookies and refresh in `client/CLAUDE.md`, CORS in a refinement report, `NEXT_PUBLIC_API_URL` in `.env` files, the container topology in `DEPLOY.md` and `docker-compose.yml`, and the OTP relay in its own README. Nobody can answer "what does the client actually need from the server?" without reading six files. --- ## Inputs - `docs/integration/openapi/swagger.v1.json` — the **fresh** snapshot from Phase 0, plus the added/removed endpoint diff Phase 0 wrote into `docs/_plan/open-contradictions.md`. - [dev/contracts/](../../dev/contracts/) — 18 domain files + 2 convention files + the stale snapshot. - [dev/shared-working-context/frontend/requests/for-backend.md](../../dev/shared-working-context/frontend/requests/for-backend.md) — 67 REQs; many *are* contract amendments that were delivered but never folded back into the domain docs. - [dev/shared-working-context/reports/mocks-registry.md](../../dev/shared-working-context/reports/mocks-registry.md) — which seams are mocked. - [DEPLOY.md](../../DEPLOY.md) · [docker-compose.yml](../../docker-compose.yml) · [deploy/Caddyfile](../../deploy/Caddyfile) - [telegram-otp-bot/README.md](../../telegram-otp-bot/README.md) + [INTEGRATION-PROMPT.md](../../telegram-otp-bot/INTEGRATION-PROMPT.md) - `client/.env.development`, `client/.env.production`, `server/src/API/Baya.Web.Api/appsettings*.json` - Code, for the seam itself: `client/src/lib/api/`, `client/src/services/*/`, the server's `ApiResult` envelope, CORS setup, and auth middleware. ## Outputs ``` docs/integration/ index.md THE seam, one page: what crosses the wire and what each side owes the other api-contract.md envelope, casing, pagination, errors, idempotency, auth headers/cookies domains/ 22 files, one per service domain — refreshed against the live swagger openapi/ swagger.v1.json (from Phase 0) README.md how to regenerate, when it was last taken, endpoint count config-matrix.md every env var / appsettings key, both projects + docker + the bot topology.md the runtime dependency graph ``` Also updated: [DEPLOY.md](../../DEPLOY.md) — fix the stale `user-secrets` reference and link to `docs/integration/topology.md`. It stays at root and stays the deploy *procedure*. --- ## Steps ### 1. Write `index.md` first — the seam on one page Before touching the per-domain detail, answer these in one page: - **Transport**: HTTP/JSON over `NEXT_PUBLIC_API_URL`; where gRPC exists and whether it is used. - **The envelope**: `ApiResult` — `{ isSuccess, statusCode, message, requestId, data }`, payload always under `data`, `requestId` is a W3C trace id (refinement-phase-9). Client unwraps in `clientFetch`/`unwrap()`. - **Casing**: JSON bodies camelCase; URL segments snake_case. (REQ-001 settled this — confirm against the live swagger, don't trust the doc.) - **Pagination**: `{ items, total, page, pageSize }`; the server binds `pageSize` case-insensitively (REQ-010). - **Errors**: status codes, machine-readable error codes (REQ-003), what the client does with 401 / 403 / 5xx / network. - **Auth**: the cookie set, JWE opacity to the client, silent refresh, session rotation and reuse-detection, `/me` and `me/select_role`, role hydration. - **Idempotency**: which endpoints require `Idempotency-Key` and what the client generates. - **Money**: IRR on the wire, Toman at the UI boundary — link to `docs/rules/shared/`. - **What the server owes the client** and **what the client owes the server**, as two short lists. ### 2. Refresh the 22 domain contracts against the live swagger For each domain, compare the contract doc against `swagger.v1.json` and the handler code. Mark each endpoint: `matches` · `drifted (describe)` · `undocumented (in code, not in contract)` · `phantom (in contract, not in code)`. Fold in the delivered REQs — 17+ were delivered in refinement-phase-3 and amended shapes that the domain docs still describe the old way. **The domain doc is the thing that should be true; the REQ ledger is a change log.** After this phase, a reader should never need to read the REQ file to know the current shape. Note the domain-file cleanup: there is both a `messaging.md` (851 B stub) and a `messaging-notifications-admin.md` (10.6 KB) — merge. Align the file set with the client's 22 `services/` domains so the mapping is one-to-one where it can be. Anything you cannot verify from swagger or code: mark `UNVERIFIED:` and add a row to Phase 4's input list. Do not guess a shape. ### 3. Write `config-matrix.md` One table: **key · where it's set (appsettings / .env / docker-compose / Caddyfile) · consumed by · required? · default · notes**. Cover at minimum: - `NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_NESHAN_KEY`, and the rest of the client's `NEXT_PUBLIC_*` - `IdentitySettings:SecretKey` / `:Encryptkey` - `Seams:FieldEncryption:Key` / `:HashKey` — **flag as load-bearing and immutable** - the connection strings (app DB + log DB), and that the DB is remote and **not** containerised - `OpenTelemetry:Otlp:Endpoint`, the health endpoints (`/healthz/live`, `/healthz/ready`) - the seam selectors that choose real vendor adapters vs mocks (refinement-phase-8) - the Telegram OTP bot's token/chat config - CORS origins, and the Caddy hostnames `balinyaar.ir` / `api.balinyaar.ir` State plainly, once, that config lives in files and `dotnet user-secrets` is not used — and that this is a deliberate pre-launch trade with live credentials in git (link to `DEPLOY.md` "Going to Production" and to Phase 5's `pre-launch.md`). ### 4. Write `topology.md` The runtime dependency graph — a mermaid diagram plus a short table: ``` browser -> Caddy (caddy_net) -> client container (Next.js) -> server container (ASP.NET) -> server container -> remote SQL Server (not containerised) -> telegram-otp-bot (OTP relay) -> object storage / external rails (per seam config) ``` For each edge: what flows over it, what breaks if it is down, and where it is configured. This is the "dependency between projects, documented in a proper place" deliverable. ### 5. Point the old locations at the new one `dev/contracts/README.md` gets a one-line "moved to `docs/integration/`" banner (Phase 6 archives the folder; until then, don't leave two live copies competing). --- ## Verification - [ ] Every endpoint in `swagger.v1.json` appears in exactly one `docs/integration/domains/*.md`, or is listed in that file's "undocumented, intentionally" section with a reason. - [ ] No `phantom` endpoints remain undeclared — each is either removed from the doc or listed as "planned, not built" with a backlog reference for Phase 4. - [ ] `config-matrix.md` accounts for every key in `appsettings.Production.json`, `docker-compose.yml`, and both `.env` files — diff mechanically, don't eyeball. - [ ] `DEPLOY.md` no longer instructs anyone to use `user-secrets`. - [ ] `index.md` fits on one screen-and-a-half and answers the seam question without a single click. ## Definition of done A frontend agent can build against the API by reading `docs/integration/index.md` plus one domain file, and a deploy question is answered by `topology.md` + `config-matrix.md` without opening `docker-compose.yml`. ## Handoff _(filled in by the agent that runs this phase — especially: the drift list, since Phase 4 turns the `phantom` and `drifted` rows into backlog items)_