Use base + dev override:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --buildOpen:
- Frontend: http://localhost:5173
- Backend API: http://localhost:8001/api/health
- Frontend from another device in same network:
http://<YOUR_LAN_IP>:5173
Find your LAN IPv4 on Windows:
ipconfigUse the IPv4 Address value (for example 192.168.1.25) in URL:
http://192.168.1.25:5173
Use base + prod override:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --buildThis profile keeps your current local dev flow unchanged and adds a separate public setup for mentor access.
On the backend host:
copy .env.mentor.example .envFill real values in .env:
JWT_SECRET_KEYCORS_ALLOW_ORIGINSwith your Netlify URLCORS_ALLOW_ORIGIN_REGEXwith your Netlify site patternAPP_ENV=prod- production-safe auth settings:
CAPTCHA_PROVIDER,AUTH_COOKIE_SECURE,AUTH_COOKIE_SAMESITE
Start only backend dependencies + API:
docker compose up -d --build postgres redis backendHealth check:
curl http://<BACKEND_PUBLIC_HOST>:8000/api/healthdocker exec -w /app usc-backend python scripts/seed_demo.pyRepo already includes netlify.toml:
- base:
frontend - build:
npm ci && npm run build - publish:
dist
In Netlify project settings, set required environment variable:
VITE_API_BASE=https://<BACKEND_PUBLIC_HOST>/api
Optional:
VITE_MAP_STYLE_URL,VITE_MAP_DEFAULT_LAT,VITE_MAP_DEFAULT_LNG,VITE_MAP_DEFAULT_ZOOMVITE_SENTRY_*
Open Netlify URL and test:
- login with demo account
- products/orders/analytics
- AI screen
- order create flow
Important: Netlify hosts only frontend. PostgreSQL/Redis/FastAPI must stay on your backend host.
Add --profile observability to include Prometheus (and Grafana in dev):
docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile observability up -d --buildOpen:
- Prometheus: http://localhost:9090
- Grafana (dev): http://localhost:3000
docker compose downdocker compose up -d --build backend frontend- Compose files:
docker-compose.yml= base services and safe defaults (no dev-only hot reload in backend runtime)docker-compose.dev.yml= local dev overrides (uvicorn --reload, frontendnpm run dev, bind mounts)docker-compose.prod.yml= production-like overrides
- Frontend proxy in dev points to backend via
VITE_PROXY_TARGET=http://backend:8000. - Backend cache uses Redis via
REDIS_URL=redis://redis:6379/0in compose env. - Demo seeding is disabled by default (
AUTO_SEED_DEMO=false) to avoid data mutation on every restart. JWT_SECRET_KEYis required by compose and must be provided from your local environment.- Frontend container no longer runs
npm installon every start.
Before running compose, define a strong local JWT_SECRET_KEY (do not commit real secrets):
$env:JWT_SECRET_KEY = "replace-with-at-least-32-random-characters"Recommended minimum:
- length: 32+ characters
- high entropy random string (letters + numbers + symbols)
- never commit to git
Refresh tokens are now expected to rotate through an HttpOnly cookie.
Important backend env values:
AUTH_REFRESH_COOKIE_NAMEAUTH_COOKIE_PATHAUTH_COOKIE_DOMAINAUTH_COOKIE_SAMESITEAUTH_COOKIE_SECURE
Recommended defaults:
- local dev with Vite proxy:
AUTH_COOKIE_SAMESITE=lax,AUTH_COOKIE_SECURE=false - HTTPS public deployment:
AUTH_COOKIE_SECURE=true - cross-origin frontend/backend over HTTPS:
AUTH_COOKIE_SAMESITE=noneandAUTH_COOKIE_SECURE=true
Frontend uses MapLibre GL JS with a free OpenFreeMap style by default:
VITE_MAP_STYLE_URL=https://tiles.openfreemap.org/styles/libertyVITE_MAP_DEFAULT_LAT=42.8746VITE_MAP_DEFAULT_LNG=74.5698VITE_MAP_DEFAULT_ZOOM=12
This gives an interactive delivery map in checkout without paid providers.
Attribution:
- OpenStreetMap contributors: https://www.openstreetmap.org/copyright
- OpenFreeMap: https://openfreemap.org/
If map tiles are temporarily unavailable, checkout still works with manual lat/lng input.
Run with dev profile:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --buildAcceptance checklist:
- Open Cart screen.
- Open checkout block.
- Click on map and ensure marker appears.
- Ensure
lat/lnginputs sync with selected point. - Click geolocation and ensure point is updated.
- Deny geolocation permission and ensure fallback/error text is shown.
- Create order.
- Open order details and verify
commentcontains[geo:lat,lng].
- Continue checkout using manual
lat/lngfields. - Verify frontend env values:
VITE_MAP_STYLE_URL,VITE_MAP_DEFAULT_LAT,VITE_MAP_DEFAULT_LNG,VITE_MAP_DEFAULT_ZOOM. - Open browser devtools and check failed tile/style requests.
- Try another style URL, then restart frontend container.
- Backend metrics endpoint:
GET /api/metrics(Prometheus format). - Request correlation: backend returns
x-request-idresponse header and logs it. - Important metric families:
http_requests_totalhttp_request_duration_secondsauth_login_attempts_totalrate_limit_hits_totaldb_query_failures_total
Alert hints:
- sustained growth of 5xx statuses in
http_requests_total - spikes in
rate_limit_hits_total - p95 latency growth in
http_request_duration_seconds
Backend env:
SENTRY_DSN_BACKENDSENTRY_ENVIRONMENTSENTRY_RELEASESENTRY_TRACES_SAMPLE_RATE
Frontend env:
VITE_SENTRY_DSN_FRONTENDVITE_SENTRY_ENVIRONMENTVITE_SENTRY_RELEASEVITE_SENTRY_TRACES_SAMPLE_RATE
If DSN is empty, Sentry stays disabled.
docker exec -w /app usc-backend python scripts/seed_demo.pyOr set AUTO_SEED_DEMO=true in backend environment when you explicitly need auto-seed on startup.
Bootstrap local virtualenv once:
powershell -ExecutionPolicy Bypass -File scripts/bootstrap_backend_venv.ps1Run backend:
cd backend
.\.venv_local\Scripts\Activate.ps1
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000 --reloaddocker compose ps
docker compose logs -f backend
docker compose logs -f frontend
docker exec usc-redis redis-cli pingExpected Redis response: PONG.
Daily full backup + manual pre-release backup is the MVP default policy.
Commands:
- Backup (Linux/macOS):
bash scripts/db/backup.sh - Backup (Windows):
powershell -ExecutionPolicy Bypass -File scripts/db/backup.ps1 - Restore (Linux/macOS):
bash scripts/db/restore.sh backups/<file>.dump - Restore (Windows):
powershell -ExecutionPolicy Bypass -File scripts/db/restore.ps1 -DumpFile backups/<file>.dump - Smoke restore on temp DB:
- Linux/macOS:
bash scripts/db/smoke_restore.sh backups/<file>.dump - Windows:
powershell -ExecutionPolicy Bypass -File scripts/db/smoke_restore.ps1 -DumpFile backups/<file>.dump
- Linux/macOS:
Detailed rollback instructions: docs/db-rollback-runbook.md.
- Backend unit/API tests:
pytest backend/tests -q --ignore=backend/tests/integration
- Backend integration tests (PostgreSQL required):
alembic upgrade headpytest backend/tests/integration -q
- Frontend unit/component tests:
cd frontendnpm run test:ci
- E2E golden path (Playwright):
cd frontendnpm run e2e:ci
GitHub Actions workflow: .github/workflows/ci.yml (single required pipeline CI).
- Centralized audit log is written to
audit_eventforauth,orders,deliveriesactions. POST /api/orders/create/supportsIdempotency-Keyheader:- same key + same payload (within 24h) returns the same response;
- same key + different payload returns
409 IDEMPOTENCY_CONFLICT.
- Strict order statuses:
PENDING,CONFIRMED,DELIVERING,PARTIALLY_DELIVERED,DELIVERED,CANCELLED,FAILED
- Strict delivery statuses:
ASSIGNED,PICKED_UP,ON_THE_WAY,PARTIALLY_DELIVERED,DELIVERED,FAILED,CANCELLED
- Returns are disabled in MVP:
POST /api/orders/{id}/returns/returns501 RETURNS_DISABLED_IN_MVP.
- Notifications are persistent in DB:
notification_eventnotification_user_state(per-userread/unread)
- Notifications API:
GET /api/notifications/->{ items, unread_count }POST /api/notifications/{id}/read/POST /api/notifications/read_all/
- Profile update API:
PATCH /api/profile/me/updates user fields and active company fields (when user is member).
- Detailed setup and phone connection guide:
docs/codex_remote.md
- Quick commands from project root:
- Bootstrap:
.\scripts\codex_remote\bootstrap.ps1 - Start:
.\scripts\codex_remote\start.ps1 - Stop:
.\scripts\codex_remote\stop.ps1
- Bootstrap: