Phase 15 adds AegisAI's browser application. It is a Next.js and TypeScript client for the existing FastAPI API; it does not duplicate business rules, document access policy, or RBAC in the browser.
The frontend is a product interface for authentication, document work, retrieval, grounded chat, and administration. FastAPI remains the sole authority for identity, authorization, document access, job lifecycles, audit events, and generated answers.
Browser
|
v
Next.js application :3000
| no provider, database, or OpenAI secrets
v
FastAPI API :8000 ----------> PostgreSQL, Redis/Celery, Qdrant, providers
The Next.js server is the browser-facing backend-for-frontend boundary. Browser code calls only same-origin Next.js routes. Those routes forward requests to FastAPI with the current AegisAI access token. The browser never receives the refresh token and never calls PostgreSQL, Redis, Qdrant, OpenAI, or SSO providers directly.
The initial local Compose deployment uses http://localhost:3000 for Next.js
and http://localhost:8000 for FastAPI. The API URL is server-only
configuration, not a NEXT_PUBLIC_* value.
- A successful local login produces AegisAI's existing access and refresh tokens at FastAPI.
- The Next.js server stores them in HttpOnly,
SameSite=Laxcookies. CookieSecureis enabled outside local HTTP development. - A proxy route sends the short-lived access token to FastAPI as a Bearer token. On an authentication failure it may make exactly one server-side refresh-token exchange, rotate both cookies, and retry the original request.
- Logout calls FastAPI's existing refresh-token revocation endpoint, then clears both browser cookies even if the backend session has already expired.
- No token is stored in
localStorage,sessionStorage, client-side state, URL query parameters, logs, or error messages.
The current FastAPI SSO callback returns a token response rather than redirecting to a frontend route. Phase 15 will add a narrowly scoped callback handoff so the Next.js server can establish the same HttpOnly session after a verified SSO login. It will preserve the backend's signed state, PKCE, nonce, verified-email, account-linking, and inactive-user protections.
| Route | Audience | Primary FastAPI contract | Notes |
|---|---|---|---|
/login |
Unauthenticated users | POST /auth/login, GET /auth/sso/{provider} |
Form login, SSO entry points, safe error feedback. |
/register |
Unauthenticated users | POST /auth/register |
Account creation; no automatic elevated role. |
/documents |
documents:read |
GET /documents |
Paginated readable-document workspace. |
/documents/upload |
documents:write |
POST /documents |
Multipart upload with the backend's type and size validation. |
/documents/[id] |
Direct document read access | Document, extraction, chunks, job, and indexing endpoints | Metadata, lifecycle, source locations, and permitted actions. |
/search |
documents:read |
POST /retrieval/search |
Semantic results with controlled metadata filters only. |
/chat |
documents:read |
POST /chat/stream |
Incremental SSE answer, terminal citations, and safe errors. |
/admin |
Relevant admin permissions | GET /admin/overview |
Navigation is capability-aware; FastAPI remains authoritative. |
/admin/users, /admin/documents, /admin/jobs, /admin/audit |
Relevant admin permissions | Existing /admin/* and /audit-events endpoints |
Each screen requests only its necessary backend capability. |
An unknown or unauthorized document must remain indistinguishable in the UI:
the document route renders a generic unavailable state for FastAPI 404 rather
than attempting to infer whether another user owns it.
Every data screen implements these explicit states:
| State | User-visible behavior | Rules |
|---|---|---|
| Loading | Skeleton or concise progress indicator | Do not show stale data as current without identifying it. |
| Empty | Explain that no permitted resources match | Offer only actions the user is allowed to take. |
| Validation error | Show field-level safe backend validation message | Preserve non-secret user input. |
| Unauthorized | Redirect unauthenticated users to login; show a capability-safe denied state for 403 |
Never claim an administrator role locally. |
| Unavailable | Show retry guidance for 5xx, retrieval, chat, or processing outages |
Do not expose stack traces, provider errors, token data, or raw SSE frames. |
| Mutation success | Refresh the authoritative server state | Do not optimistically invent document/job lifecycle transitions. |
Document processing status is a backend-owned lifecycle. The frontend can poll the existing job and indexing-status endpoints while a document is pending or running; it must stop after a terminal state and respect cancellation/deletion.
- TypeScript request and response types are derived manually from the published FastAPI/OpenAPI contract and covered by focused tests; frontend types do not create a competing contract.
- The proxy allow-lists known API paths and methods. It does not accept arbitrary target URLs, request headers, or external redirects from the browser.
- Uploads stream as multipart form data without reading whole documents into browser JavaScript memory. The FastAPI upload limit remains authoritative.
- Chat consumes only the documented SSE events:
answer_delta,citations,done, anderror. Citations are rendered from the terminal verified event, not guessed from generated answer text. - The browser may hide unavailable navigation for usability, but every request
handles FastAPI
401,403, and404; hiding a link is never authorization. - The frontend does not persist chat transcripts in Phase 15. The current per-turn client history remains bounded by FastAPI's Phase 11 validation.
Phase 15 does not add a new identity provider, browser-held API keys, direct provider calls, a database schema for UI preferences, persisted chat history, tenant selection, audit-event editing, or alternate authorization semantics. Observability and production browser deployment hardening are Phase 16 and later work.
- 15.1 Frontend contract and screen map
- 15.2 Next.js runtime and Docker integration
- 15.3 Typed API client and server-managed session
- 15.4 Authentication and SSO browser flows
- 15.5 Document workspace
- 15.6 Search and grounded chat
- 15.7 Administration workspace
- 15.8 Tests, Compose verification, and documentation
This checkpoint introduces no runnable frontend. Verify the contract before implementation:
- Confirm every planned screen maps to an existing FastAPI route in
/docs. - Confirm the user-facing permissions in the route map match the backend's RBAC requirements.
- Confirm the frontend will use server-only
API_BASE_URLconfiguration and HttpOnly cookies, with noNEXT_PUBLIC_*token or provider-secret setting. - Keep
docker compose up --build --force-recreateas the canonical backend verification command until the frontend service is added in 15.2.
The frontend/ application is a strict TypeScript Next.js App Router project.
Its initial landing page is intentionally static: it proves the browser runtime
without prematurely implementing client-side authentication or a direct API
call. The /login link is the reserved entry point for 15.4.
The frontend Dockerfile installs reproducible dependencies from
package-lock.json, type-checks and builds the application, then copies only
the Next.js standalone output into its runtime image. Compose publishes the
frontend at port 3000 and supplies API_BASE_URL=http://backend:8000 only to
the frontend container. That value is server-only and is not exposed to browser
JavaScript.
From the repository root, run:
docker compose up --build --force-recreateThen open http://localhost:3000. The AegisAI landing page should appear and
its Sign in link should currently lead to an expected 404; the login page
is introduced in 15.4. Confirm http://localhost:8000/health remains healthy.
lib/server/api-client.ts is the server-only FastAPI client. Its initial
allow-list contains only the session contracts needed at this stage:
GET /auth/me and POST /auth/refresh. Each call disables caching, supplies
the Bearer token only from server code, applies a bounded timeout, and maps
unexpected backend response details to a safe error.
lib/server/session.ts is the server-only token boundary. It stores AegisAI's
access and refresh tokens in separate HttpOnly, SameSite=Lax cookies. They
are never returned from GET /api/session. When an access token receives a
401, the session resolver makes one refresh request, rotates both cookies,
and retries GET /auth/me. A rejected refresh clears both cookies and returns
an anonymous session; an availability failure remains a safe 503 rather than
silently logging a user out.
SESSION_COOKIE_SECURE is explicitly false only in local HTTP Compose. It
must be true behind HTTPS. SESSION_REFRESH_COOKIE_MAX_AGE_SECONDS controls
only browser persistence and must not outlive FastAPI's refresh-token policy;
FastAPI continues to validate the actual refresh token.
Run the frontend checks:
cd frontend
npm test
npm run typecheck
npm run buildThen start the stack and inspect the safe anonymous session response:
docker compose up --build --force-recreate
curl http://localhost:3000/api/sessionExpected response before 15.4 creates a browser session:
{"authenticated":false}/login and /register submit only to same-origin Next.js route handlers.
Those handlers call FastAPI, store a successful token pair in HttpOnly cookies,
and return only safe user data. /api/auth/logout asks FastAPI to revoke the
refresh token and clears cookies in all cases. Session refresh remains owned by
the 15.3 resolver.
SSO buttons navigate through an allow-listed Next.js route to FastAPI's
existing provider flow. Set SSO_FRONTEND_REDIRECT_URL=http://localhost:3000
in backend/.env for browser SSO. After backend verification, FastAPI sets the
same HttpOnly session cookies and redirects to /login?provider=...; tokens
never appear in the redirect URL or page body. Leave that setting blank only
for the existing API-only SSO callback contract.
docker compose up --build --force-recreate- Open
http://localhost:3000/register, create an account, then sign in at/login. - Run
curl http://localhost:3000/api/session; it should reportauthenticated: truewith user data, but no tokens. - Send
POST /api/auth/logoutfrom the browser or DevTools, then repeat the session call; it should returnauthenticated: false. - For a configured provider, set
SSO_FRONTEND_REDIRECT_URLas above and add FastAPI's existing callback URL (for example,http://localhost:8000/auth/sso/google/callback) to the provider registration. Complete sign-in and verify the browser returns to/loginwithout tokens in its URL.
The workspace uses same-origin Next.js routes for upload, list, detail, reprocess, rename, deletion, and direct-access grants. FastAPI remains authoritative for every permission and lifecycle transition. A missing or denied document renders the same unavailable state.
/search calls the Phase 10 retrieval API through an authenticated server
route and labels scores as similarity, not certainty. /chat forwards the
Phase 11 SSE stream without buffering it. The browser renders answer text only
from answer_delta, citations only from the verified terminal citations
event, and safe text from error; it never calls OpenAI directly.
After Docker Desktop is running:
docker compose up --build --force-recreate- Sign in at
http://localhost:3000/login, upload a supported fixture at/documents, and wait for its processing/indexing status to become ready. - Rename and reprocess it from
/documents/{id}. Confirm FastAPI—not the UI— rejects an invalid lifecycle transition or unauthorized request. - Search from
/search; a ready matching document should return chunk text and its similarity score. Stop Qdrant or remove embedding credits to verify the safe unavailable state. - Ask a matching question at
/chat. The answer should stream, followed by citations. Ask an unrelated question to verify the backend's grounded, insufficient-context behavior. No OpenAI key or JWT should appear in browser storage, page source, or URLs.
The frontend exposes /admin, /admin/users, /admin/rbac,
/admin/documents, and /admin/operations. Each calls a same-origin route
handler that forwards only the current server-held access token. Navigation is
for usability only: FastAPI still evaluates the precise existing permission for
every overview, user, RBAC, document, job, and audit request.
Sign in as an administrator and open each administration URL above. Confirm the overview counts, user status change, role/permission summaries, global document metadata, job states, and audit-event records. Sign in as a user without the corresponding permissions and confirm each API call is denied or reports a safe unavailable state without leaking data.
Run the browser-independent quality gate from frontend/:
npm test
npm run typecheck
npm run buildThen, with Docker Desktop running, use the repository-root command below for the final end-to-end verification:
docker compose up --build --force-recreateConfirm the API health endpoint, sign in through the frontend, upload and process a document, search it, receive a cited chat answer, and visit each administration page as an administrator and a non-administrator. The phase is complete only after that Compose/browser workflow succeeds.
Compose verification completed successfully on the local stack: the backend
ran 198 tests, applied migrations, served a healthy /health response, and
the frontend served its landing page at port 3000.