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