Files
baya-monorepo/dev/shared-working-context/reports/refinement-phase-0-report.md
T
2026-07-12 01:09:11 +03:30

6.1 KiB

Refinement Phase 0 — Local end-to-end bring-up & the integration seam — Report (2026-07-12)

What was built

Removed the three hard integration blockers so the client and server actually talk on one machine, and proved it with one real authenticated round-trip. Plumbing only — no business logic, money path, auth crypto, or envelope shape changed.

  • CORS (§3.1). New Baya.WebFramework/ServiceConfiguration/CorsServiceExtension.csAddCorsPolicies(configuration) builds the named policy BalinyaarWebClient from Cors:AllowedOrigins (string array), defaulting to http://localhost:3000 when unset. Allows exactly the four headers the client sends (Authorization, Content-Type, Accept-Language, Idempotency-Key) + AllowAnyMethod(); no AllowCredentials() (the client uses a bearer header, not a cookie). Registered in the service chain and app.UseCors(CorsServiceExtension.PolicyName) placed after UseRouting() and before UseRateLimiter() so pre-flight OPTIONS is answered before the limiter/auth run.
  • Local database story (§3.2). server/docker-compose.yml rewritten to a single-purpose SQL Server 2022 (Developer edition) on localhost:1433 with a dev-only MSSQL_SA_PASSWORD + a named volume + healthcheck. (The old compose referenced an unbuildable bobby-baya app image and exposed 1435.) The committed appsettings.json / appsettings.Development.json connection strings (SqlServer + logDb) are now non-working placeholders (Password=SET_VIA_USER_SECRETS_OR_ENV); the real local value comes from dotnet user-secrets. Added <UserSecretsId>baya-web-api</UserSecretsId> to the API csproj (it was missing — user-secrets weren't actually wired before).
  • Development-only OTP retrieval (§3.3). GET /api/v1/dev/last_otp/{phone} (DevController) returns the most recent OTP for a phone so a browser / e2e flow can log in without an SMS gateway. Backed by a new Development-only DevOtpStore + DevCapturingSmsSender (an ISmsSender decorator that captures the code then delegates to the log-only LoggingSmsSender), wired only in Development by AddDevelopmentOtpCapture(). The endpoint 404s in every non-Development environment and the capture is not even registered there — two independent guarantees it can't leak a code. It does not weaken the otp rate-limit or the per-phone resend window.
  • Client env & runbook (§3.4). Verified client/.env.development already has NEXT_PUBLIC_API_URL = https://localhost:5002 (matches the API's bound URL) — no client code changed, no mock flag flipped. Wrote dev/post-phase/refinement/RUNBOOK.md — the copy-pasteable "run the whole app locally" procedure (dev-cert trust, compose, user-secrets, both run commands, OTP-from-logs / dev-endpoint, DevTools verification, troubleshooting).

What is now testable (and exactly how)

  • Automated (in the suite, +11 tests → 369 total): 3 new Baya.Test.Api integration tests (CorsAndDevBringUpTests): a pre-flight OPTIONS /api/v1/auth/request_otp from http://localhost:3000 reflects Access-Control-Allow-Origin: http://localhost:3000; the same from a foreign origin gets no allow-origin header; GET /api/v1/dev/last_otp/... returns 404 in the (non-Development) test host. Plus 8 Baya.Test.Foundation unit tests (DevOtpBringUpTests) covering the store's capture/latest/ spelling-insensitive lookup + the decorator's capture-and-still-delegate behaviour.
  • Manual (the §7 proof): follow RUNBOOK.mddocker compose up -d, set the user-secret, dotnet run, npm run dev, open /fa/login, request an OTP, read the code from the server console (or GET /api/v1/dev/last_otp/{phone}), submit → land on the customer home with 200s on /auth/request_otp, /auth/verify_otp, /api/v1/me and no CORS error. Negative check: remove app.UseCors(...) → the same flow fails with a CORS error.

What is mocked / waiting on a real service

  • No new seam. The existing ISmsSender (LoggingSmsSender) stays the interim OTP channel (logs the code); its mocks-registry.md row is updated to note the Development-only DevCapturingSmsSender + /dev/last_otp affordance. Real SMS is Refinement Phase 8.
  • The DevOtpStore / DevCapturingSmsSender / /dev/last_otp endpoint are a Development-only dev affordance, not a seam (per the phase §4) — superseded by real SMS in Phase 8.

Contracts

  • None produced. This phase ships plumbing (CORS middleware) + a Development-only diagnostic endpoint the frontend does not consume as a contract, so no dev/contracts/domains/*.md was written and the swagger.v1.json snapshot was not regenerated (the only new path is the dev-only helper Phase 8 removes — regenerating would add churn for a path no client binds to). Auth remains the one real domain the frontend consumes, unchanged.

Docs updated

  • server/CLAUDE.md "Startup wiring" — added AddCorsPolicies(config) to the registration list, the Development-only AddDevelopmentOtpCapture() note, and CORS in the pipeline order (after routing, before the rate limiter). Project map — noted the Development-only Dev controller.
  • dev/post-phase/refinement/RUNBOOK.md — new local-run runbook.
  • dev/shared-working-context/reports/mocks-registry.mdISmsSender row updated.

Follow-ups for later phases

  • Phase 1 — local-dev demo seed (nurses/variants/search rows) so discovery/booking aren't empty on the real path.
  • Phase 4 — flip the 21 USE_*_MOCK flags (this phase changed none).
  • Phase 5 — rotate the leaked remote sa credentials + the dev-grade IdentitySettings keys (still committed as dev placeholders here); set real production Cors:AllowedOrigins.
  • Phase 8 — real SMS gateway; removes the /dev/last_otp helper + the DevCapturingSmsSender decorator.
  • Note for deployed envs: Cors:AllowedOrigins must list the real web origin(s); an empty array falls back to the localhost dev origin (safe — a real user's origin won't match, so cross-origin is effectively denied until configured).