Skip to Content
Developer DocsFrontendRouting & Layouts

Routing and Layouts

AEGIS uses the Next.js App Router with route groups to separate public pages (login) from authenticated dashboard pages. This page documents the complete routing structure, layout hierarchy, and authentication middleware.

Layout Hierarchy

RootLayout (app/layout.tsx) ├── ThemeProvider (next-themes, dark default) │ └── AuthProvider (React context for user state) │ └── TooltipProvider (shadcn/ui tooltips) │ ├── LoginPage (/login) │ └── DashboardLayout (app/(dashboard)/layout.tsx) │ ├── Sidebar (collapsible navigation) │ ├── Topbar (tenant info, density toggle, theme, user) │ ├── Toaster (sonner toast notifications) │ └── DashboardShell │ ├── DensityProvider (compact/comfortable/spacious) │ ├── CommandPalette (Cmd+K) │ └── PageTransition (Framer Motion) │ └── {page content}

Root Layout

Located at src/app/layout.tsx, the root layout wraps the entire application:

export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en" suppressHydrationWarning> <body className={`${inter.variable} ${jetbrainsMono.variable} antialiased`}> <ThemeProvider attribute="class" defaultTheme="dark" enableSystem> <AuthProvider> <TooltipProvider>{children}</TooltipProvider> </AuthProvider> </ThemeProvider> </body> </html> ); }

Key details:

  • Fonts: Inter (body text) and JetBrains Mono (monospace/code) loaded via next/font/google.
  • Theme: Dark by default, with system theme detection enabled.
  • Auth: AuthProvider wraps all pages so both login and dashboard pages can access auth state.
  • Metadata: Title is “AEGIS” (app/layout.tsxdescription: "Agent Execution, Graph Intelligence & State").

Root Page

The root page at src/app/page.tsx immediately redirects to the compliance dashboard:

import { redirect } from "next/navigation"; export default function Home() { redirect("/compliance"); }

Dashboard Layout

The (dashboard) route group at src/app/(dashboard)/layout.tsx wraps all authenticated pages:

export default function DashboardLayout({ children }: { children: React.ReactNode }) { return ( <div className="flex h-screen overflow-hidden"> <Sidebar /> <div className="flex-1 flex flex-col overflow-hidden"> <Topbar /> <main className="flex-1 overflow-auto bg-background p-6 flex flex-col"> <DashboardShell>{children}</DashboardShell> </main> </div> <Toaster richColors position="bottom-right" /> </div> ); }

The layout uses a horizontal flex layout: collapsible sidebar on the left, main content area on the right with topbar + scrollable content.

The DashboardShell component provides:

  • DensityProvider: manages compact/comfortable/spacious display modes, stored in localStorage.
  • CommandPalette: global Cmd+K navigation overlay.
  • PageTransition: Framer Motion AnimatePresence for smooth page transitions keyed on pathname.

Page Routes

PathFileDescription
/app/page.tsxRedirects to /compliance
/loginapp/login/page.tsxEmail/password login page (public)
/invite/[token]app/invite/[token]/page.tsxInvite redemption — validate token, set password, log in (public, R42b)
/complianceapp/(dashboard)/compliance/page.tsxCompliance dashboard (default landing)
/compliance/[entityId]/[domain]app/(dashboard)/compliance/[entityId]/[domain]/page.tsxEntity compliance workspace (three-panel layout)
/filingsapp/(dashboard)/filings/page.tsxFiling review queue (filing-class HITL checkpoints)
/approvalsapp/(dashboard)/approvals/page.tsxAgent approvals queue (non-filing HITL checkpoints)
/flaringapp/(dashboard)/flaring/page.tsxFlaring dashboard with R-32 tracking
/eventsapp/(dashboard)/events/page.tsxEvents timeline
/events/[eventId]app/(dashboard)/events/[eventId]/page.tsxEvent detail view
/conversationsapp/(dashboard)/conversations/page.tsxAgent chat with SSE streaming
/entityapp/(dashboard)/entity/page.tsxRedirects to the explorer’s first tab (first registry entity type, same ordering as the tab bar; falls back to /entity/well if the registry is unreachable)
/entity/[type]app/(dashboard)/entity/[type]/page.tsxEntity list by type
/entity/well/[id]app/(dashboard)/entity/well/[id]/page.tsxWell detail view
/entity/facility/[id]app/(dashboard)/entity/facility/[id]/page.tsxFacility detail view
/entity/[type]/[id]app/(dashboard)/entity/[type]/[id]/page.tsxGeneric entity detail view
/skill-builderapp/(dashboard)/skill-builder/page.tsxAuthor’s “My Skills” list (owner-scoped via ?mine=true) with review status
/skill-builder/newapp/(dashboard)/skill-builder/new/page.tsxCreate a skill (author lands pending_review)
/skill-builder/[skillId]app/(dashboard)/skill-builder/[skillId]/page.tsxAuthor skill editor (shared SkillEditor, authorView)
/configuration/entity-typesapp/(dashboard)/configuration/entity-types/page.tsxEntity type definitions
/configuration/entity-types/[typeId]app/(dashboard)/configuration/entity-types/[typeId]/page.tsxEntity type detail/edit
/configuration/relationship-typesapp/(dashboard)/configuration/relationship-types/page.tsxRelationship type definitions
/configuration/relationship-types/[id]app/(dashboard)/configuration/relationship-types/[id]/page.tsxRelationship type detail
/configuration/event-typesapp/(dashboard)/configuration/event-types/page.tsxEvent type definitions
/configuration/event-types/newapp/(dashboard)/configuration/event-types/new/page.tsxCreate new event type
/configuration/event-types/[typeId]/editapp/(dashboard)/configuration/event-types/[typeId]/edit/page.tsxEdit event type
/configuration/event-types/[typeId]/detection-rules/[ruleId]nested pageDetection rule editor
/configuration/promptsapp/(dashboard)/configuration/prompts/page.tsxPrompt namespace list
/configuration/prompts/[namespaceKey]nested pageNamespace detail (templates, settings, tiers, access)
/configuration/prompts/[ns]/[slug]nested pageTemplate detail with version sidebar
/configuration/prompts/[ns]/[slug]/versions/draftnested pageVersion editor with validation panel
/configuration/prompts/[ns]/[slug]/versions/historynested pageVersion history table
/configuration/prompts/[ns]/[slug]/versions/comparenested pageSide-by-side version comparison
/configuration/prompts/approvalsapp/(dashboard)/configuration/prompts/approvals/page.tsxPrompt approval queue
/configuration/skillsapp/(dashboard)/configuration/skills/page.tsxAll skills tenant-wide + review queue (admin)
/configuration/skills/newapp/(dashboard)/configuration/skills/new/page.tsxCreate a skill (admin lands approved)
/configuration/skills/[skillId]/editapp/(dashboard)/configuration/skills/[skillId]/edit/page.tsxAdmin skill editor (shared SkillEditor)
/configuration/routersapp/(dashboard)/configuration/routers/page.tsxSkill Routers list: recruitment taxonomy for message-driven selection, with an unrouted-skills banner (approved skills unreachable via selection) and admin router creation
/configuration/routers/[routerId]app/(dashboard)/configuration/routers/[routerId]/page.tsxRouter detail: edit blurb / retire-reactivate (no delete — soft-deactivation only), mappings table with per-skill render state, disambiguation description versions, and a read-only “what the LLM sees” payload preview
/configuration/platform-settingsapp/(dashboard)/configuration/platform-settings/page.tsxPlatform Settings (admin-only): tenant LLM runtime config — default/fallback model from the curated picker, per-execution budgets, daily token/cost caps, and per-user budget overrides (user picker fed by GET /api/v1/auth/users). Reads/writes /api/v1/settings*; blank fields inherit the env defaults, and the sources map from GET /settings labels each effective value tenant or default
/configuration/capabilitiesapp/(dashboard)/configuration/capabilities/page.tsxCore capability catalog (the tool/action primitives skills and rules bind to)
/configuration/catalogsapp/(dashboard)/configuration/catalogs/page.tsxCatalogs & Categories admin
/configuration/evalsapp/(dashboard)/configuration/evals/page.tsxEval Runs dashboard (baseline-able regression runs; reads eval_runs/eval_case_results)
/configuration/policiesapp/(dashboard)/configuration/policies/page.tsxR37d — Compliance Policies: the admin-owned mandatory rule floor. Lists mandatory rules with their applies_to selector; create/edit via the shared RuleForm (structured condition + live match preview). Skill authors see these read-only in the skill editor’s Rules tab (two sections: locked “Mandatory (enforced)” with provenance over the skill’s own rules). R37e makes the “This skill’s rules” section add/edit/delete on both editor surfaces via the same RuleForm in skill-rule mode (no applies_to, advisory forced, writes through the skill-nested author path); a non-admin author’s rule write reopens the skill’s review, and circumvention lint warnings render inline, at save time, and in the ReviewDialog
/adminapp/(admin)/admin/page.tsxAdmin home (separate (admin) route group, its own layout)
/admin/usersapp/(admin)/admin/users/page.tsxR42d — Users & Roles admin: list/create users (invite links), edit roles, deactivate/reactivate, one-time password-reset links (/api/v1/auth/admin/*)

The Skill Builder and Configuration › Skills routes share one editor (components/skills/SkillEditor) and create form (SkillCreateForm); the two route trees are thin wrappers differing only in basePath, breadcrumb, and the authorView flag. Skill authoring authz is state-machine based (R37a): a non-admin create lands pending_review and any non-admin edit reopens review, so agents cannot load unapproved skill code. Dry-run (R37b) is reachable from the code-block editor for a skill’s author or an admin, and is not review-gated.

The sidebar is defined in src/components/sidebar.tsx and features:

  • Collapsible with animated width transition (52px collapsed, 240px expanded). State is persisted to localStorage under aegis-sidebar-collapsed.
  • Active indicator — a blue vertical bar animates between items using Framer Motion layoutId.
  • Sections: primary navigation items, a divider, and a “Configuration” section with nested children.
  • Role-gated Configuration (R42d): the Configuration section renders when useAuth().user?.roles includes admin or power_user. Two entries stay admin-only (adminOnly): Prompt Approvals and Platform Settings. Non-admin authors also reach the top-level Skill Builder entry. This is nav-gating only — backend routes enforce the real authorization.

Navigation items:

Compliance /compliance (ShieldCheck icon) Filings /filings (ClipboardList icon) Approvals /approvals (CheckCircle icon) Flaring /flaring (Flame icon) Events /events (CalendarClock icon) Conversations /conversations (MessageSquare icon) Entity Explorer /entity (Landmark icon) Skill Builder /skill-builder (Hammer icon) --- divider --- Configuration (admin or power_user) Entity Types /configuration/entity-types (LayoutGrid icon) Relationship Types /configuration/relationship-types (GitBranch icon) Event Types /configuration/event-types (Zap icon) Capabilities /configuration/capabilities (Zap icon) Prompts /configuration/prompts (FileCode icon) Skills /configuration/skills (Layers icon) Skill Routers /configuration/routers (Route icon) Compliance Policies /configuration/policies (ShieldCheck icon) Catalogs & Categories /configuration/catalogs (BookOpen icon) Prompt Approvals /configuration/prompts/approvals (CheckCircle icon, admin-only) Platform Settings /configuration/platform-settings (SlidersHorizontal icon, admin-only) Eval Runs /configuration/evals (FlaskConical icon)

The sidebar footer displays the tenant branding label (the tenant_branding Platform Setting; the seeded RRC tenant shows “RRC District 08 — Permian Basin”, and a blank setting shows no footer line — E0.3).

Authentication Middleware

The middleware at src/middleware.ts protects all dashboard routes:

const PUBLIC = ["/login", "/_next", "/favicon.ico"]; export function middleware(request: NextRequest) { const { pathname } = request.nextUrl; if (PUBLIC.some((p) => pathname.startsWith(p))) return NextResponse.next(); const token = request.cookies.get("aegis_token")?.value; if (!token) { const url = new URL("/login", request.url); url.searchParams.set("redirect", pathname); return NextResponse.redirect(url); } return NextResponse.next(); } export const config = { matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"], };

Key behavior:

  • Public paths (/login, /_next, /favicon.ico) are excluded from auth checks.
  • If no aegis_token cookie is found, the user is redirected to /login with a redirect query parameter to return them to their original destination after login.
  • The middleware only checks for the presence of a cookie — actual token validation happens at the API Gateway when backend requests are made.

Login Page

The login page at src/app/login/page.tsx provides an email + password form:

  1. User enters their email and password.
  2. The login() function in lib/api.ts sends a POST to /api/v1/auth/token (same-origin, routed through the gateway to auth-service).
  3. On success, auth-service sets the httpOnly aegis_token cookie via Set-Cookie (HttpOnly; Secure; SameSite=Lax). The response body carries only {user_id, roles, email, display_name} — no access_token (R42b).
  4. AuthContext is hydrated from that response body. There is no localStorage persistence — a reload re-hydrates identity from GET /api/v1/auth/me.
  5. The user is redirected to their original path (the redirect query param) or /conversations.

For local development, log in with admin@aegis.local / aegis-dev-admin (the seeded bootstrap admin). Accounts are admin-provisioned — the primary flow is the Users & Roles admin UI at /admin/users, which issues one-time invite links (the invitee sets their own password via /invite/[token]); the auth_service.create_user CLI is a break-glass fallback. There is no self-serve signup.

Topbar

The topbar (src/components/topbar.tsx) displays:

  • Tenant info: “Permian Basin Energy LLC” with tenant label.
  • Search: Cmd+K shortcut button that triggers the command palette.
  • Density toggle: Three-mode toggle (compact, comfortable, spacious) for adjusting UI density.
  • Admin link: External link to /admin.
  • User info: Role badge (Admin/Operator), user ID, theme toggle, and sign-out button.
Last updated on