# portal Next.js 16 customer area + backstage. > Part of the **Breakpilot Platform**. For the big picture see [`platform/docs`](https://gitea.meghsakha.com/platform/docs): > [Architecture](https://gitea.meghsakha.com/platform/docs/src/branch/main/PLATFORM_ARCHITECTURE.md) · > [Infrastructure](https://gitea.meghsakha.com/platform/docs/src/branch/main/INFRASTRUCTURE.md) · > [Product Integration Spec](https://gitea.meghsakha.com/platform/docs/src/branch/main/PRODUCT_INTEGRATION_SPEC.md) · > [Implementation Plan](https://gitea.meghsakha.com/platform/docs/src/branch/main/IMPLEMENTATION_PLAN.md) ## What this is Next.js 16 customer area + backstage. Scaffolded under milestone M5.1. See [`platform/docs`](https://gitea.meghsakha.com/platform/docs) for the full architecture context. **Plane:** Control **Owner:** @sharang **Status:** pre-alpha **Linked milestone:** [M5.1](https://gitea.meghsakha.com/platform/docs/src/branch/main/IMPLEMENTATION_PLAN.md) ## Run locally ```bash # Prerequisites: Node 20+, pnpm 9+, the dev stack running. # 1. Bring up Keycloak + Postgres + Redis (separate clone): cd /path/to/platform/orca-platform && make dev-up # 2. Run tenant-registry (separate clone): cd /path/to/platform/tenant-registry && make dev # 3. Run this app: make install # pnpm install --frozen-lockfile make dev # next dev on http://localhost:3000 # Or hit a real tenant immediately: # open http://acme.localhost:3000 → redirects to Keycloak → back to /acme/dashboard ``` Seed login (from the dev-stack realm): `test@breakpilot.dev` / `test`. `make test` / `make lint` / `make typecheck` / `make build` run vitest / eslint / tsc / next build respectively. Env vars live in `.env.example`. Copy to `.env.local` for local overrides (gitignored). ## Surface | Route | Renders | |---|---| | `http://localhost:3000/` | Apex landing — pointer to tenant subdomains | | `http://.localhost:3000/` | Middleware rewrites to `/[slug]/` → redirects to `/[slug]/dashboard` | | `http://.localhost:3000/dashboard` | OIDC-gated dashboard; signed-out users see "Sign in with Keycloak" | | `http://backstage.localhost:3000/` | (Skeleton) backstage route — rewritten to `/__backstage__/*` | | `/api/auth/[...nextauth]` | Auth.js v5 endpoints (callback, signin, signout, jwt) | ## Architecture notes - **Host → slug routing**: `src/middleware.ts` parses `Host` header via `parseHost()` (in `src/lib/host.ts`) and rewrites the request path to `//...`. URL bar stays unchanged. Apex hosts and unknown subdomains fall through unmodified. - **Tenant context**: `src/app/[slug]/layout.tsx` fetches the tenant from `tenant-registry` (`src/lib/tenant-registry.ts`). 404 → `notFound()`; HTTP errors bubble up. - **Auth**: `src/auth.ts` is the Auth.js v5 config — Keycloak provider, tenant-context claims (`tenant_id`, `tenant_slug`, `org_roles`, `products`, `plan`, `tenant_status`) propagated via JWT/session callbacks. Real RBAC enforcement lands in M5.2 / M10.1. ## Deployment | Env | URL | How | |---|---|---| | dev | `http://localhost:3000` | `make dev` | | stage | `https://portal.stage.breakpilot.com` | auto on merge to `main` | | prod | `https://portal.breakpilot.com` | manual: tag `vX.Y.Z` + sign-off | Rollback: `orca rollout undo portal --env={{env}}`. ## Observability - Traces, logs, metrics: [SigNoz](https://signoz.meghsakha.com) — service name `portal` - Audit events: Tenant Registry `/audit` (Retraced-shape schema) - On-call: `oncall@breakpilot.com` · runbook at `platform/docs/runbooks/portal.md` ## Contributing See [`CONTRIBUTING.md`](./CONTRIBUTING.md). TL;DR: branch from main, open a PR, 1 review + green CI, squash-merge. ## License Proprietary — all rights reserved. Copyright (c) 2026 Sharang Parnerkar and Benjamin Boenisch. See [`LICENSE`](./LICENSE).