# Port allocation Canonical port plan for both stacks (tenant + SuperAdmin). Every port is env-configurable; defaults below are non-colliding so both stacks can run simultaneously on a single workstation. ## Dev ports | Port | Service | Stack | Default env var | Config file | |------|---------------------|------------|--------------------------|------------------------------------------| | 3000 | Tenant client (Vite) | tenant | `VITE_PORT` | `client/vite.config.ts` | | 3001 | Tenant API (Express) | tenant | `PORT` | `server/src/config/index.js` | | 3002 | SuperAdmin API (Express) | superadmin | `SUPERADMIN_PORT` | `superadmin/server/src/index.js` | | 3003 | SuperAdmin client (Vite) | superadmin | `VITE_PORT` (client) | `superadmin/client/vite.config.ts` | The four dev ports (3000, 3001, 3002, 3003) are non-colliding by default. Vite dev servers run with `strictPort: true`. If a port is already in use, they fail fast rather than silently picking a different port — so a collision is loud, not silent. ## Cross-stack proxy targets | From / stack | To / stack | Default | Override env var | |------------------------|-------------------------------|------------------------------------|----------------------| | Tenant client `/api` | Tenant API | `http://127.0.0.1:3001` | `VITE_API_TARGET` | | Tenant client `/uploads` | Tenant API | `http://127.0.0.1:3001` | `VITE_API_TARGET` | | Tenant client `/ws`, `/socket` | Tenant API (WS) | `ws://127.0.0.1:3001` | `VITE_WS_TARGET` | | SuperAdmin client `/api` | SuperAdmin API | `http://localhost:3002` | `SUPERADMIN_PORT` + `SUPERADMIN_SERVER_HOST` | | Tenant client license JWKS | SuperAdmin API | `/api/v1/auth/jwks` (relative; defaults via Vite proxy are NOT used) | `VITE_SUPERADMIN_JWKS_URL` | The JWKS endpoint is only exposed by the SuperAdmin server. The tenant client must point at the SuperAdmin origin explicitly — set `VITE_SUPERADMIN_JWKS_URL` to a full URL such as `http://localhost:3002/api/v1/auth/jwks` (or any reverse-proxied path) when both stacks are running. If the URL is relative (starts with `/`) the tenant axios instance is used; if it is absolute (`http://` / `https://`) a one-off `fetch()` is used instead so cross-origin requests work. ## Running both stacks simultaneously The default ports are chosen so the two stacks do not collide. Open four terminals (or use a terminal multiplexer): ```bash # Terminal 1 — tenant API on :3001 cd server npm run dev # Terminal 2 — tenant client on :3000 (proxies /api → :3001) cd client npm run dev # Terminal 3 — SuperAdmin API on :3002 cd superadmin/server npm run dev # Terminal 4 — SuperAdmin client on :3003 (proxies /api → :3002) cd superadmin/client npm run dev ``` You can also run any one stack in isolation — the defaults match the one-stack commands documented in `AGENTS.md`. ## Changing ports Each port is overridable via an env var. Set the env var before the `npm run dev` (or `npm start`) command, or persist it in a `.env` file (see the per-stack `.env.example`). ### Tenant API ```bash # server/.env PORT=3101 ``` Then start with `cd server && npm run dev`. The client must be told where the API lives: ```bash # client/.env (or inline) VITE_API_TARGET=http://127.0.0.1:3101 VITE_WS_TARGET=ws://127.0.0.1:3101 VITE_PORT=3100 ``` ### Tenant client (Vite) ```bash # client/.env VITE_PORT=3100 # Vite dev port VITE_API_TARGET=http://127.0.0.1:3101 VITE_WS_TARGET=ws://127.0.0.1:3101 ``` ### SuperAdmin API ```bash # superadmin/server/.env SUPERADMIN_PORT=3202 ``` ### SuperAdmin client (Vite) ```bash # superadmin/client/.env VITE_PORT=3203 # Vite dev port SUPERADMIN_PORT=3202 # must match the SuperAdmin API SUPERADMIN_SERVER_HOST=http://localhost ``` ### Tenant → SuperAdmin JWKS ```bash # client/.env — relative path (tenant axios instance) VITE_SUPERADMIN_JWKS_URL=/api/v1/auth/jwks # client/.env — absolute cross-origin (one-off fetch()) VITE_SUPERADMIN_JWKS_URL=http://localhost:3002/api/v1/auth/jwks ``` In production, point this at a reverse-proxied origin that fronts the SuperAdmin API (e.g. `https://superadmin.example.com/api/v1/auth/jwks`). ## Collision detection Both Vite configs use `strictPort: true`. If a port is already bound, the dev server exits with a clear error instead of silently picking a different port — so a collision is loud, not hidden. Both Express servers bind to the configured `PORT` / `SUPERADMIN_PORT`; if the port is unavailable, Node throws `EADDRINUSE` at startup. Default values give every service a unique port, so merely changing nothing is the recommended setup. Override only when you need to work around a port already in use on the host.