Skip to main content

Frontend Overview

The Proxima Console frontend is a single-page application built with React 18, TypeScript 5, Tailwind CSS, and shadcn/ui components. It provides the operator-facing UI for managing infrastructure across client environments. The application is bundled with Vite and served as static files behind nginx in production.

Technology Stack

  • Framework: React 18 with TypeScript 5
  • Styling: Tailwind CSS with shadcn/ui component library
  • Data Fetching: TanStack React Query for caching, background refetching, and deduplication
  • Build Tool: Vite
  • Testing: Vitest + React Testing Library (unit), Playwright + Chromium (E2E browser tests)
  • Linting: ESLint + Prettier
  • Code Splitting: Route-level lazy loading with React.lazy + Suspense

Communication Model

The frontend communicates with the backend primarily via REST API calls. Data freshness is maintained through React Query's refetchInterval at varying frequencies depending on the page context. Two real-time channels supplement REST:

  • Server-Sent Events (SSE) — AI Chat streams assistant responses over SSE (use-chat-stream.ts sends Accept: text/event-stream).
  • WebSocket — used only for the interactive web terminal. The full-page terminal (/hosts/:hostId/terminal) connects to /api/v1/terminal/ws (and /terminal/ws/join for shared sessions). This is the single WebSocket exception documented in the SPEC; no other feature uses WebSockets.

Theme

The application uses a light-only design system called "Clean Authority" — authority through restraint, standing apart from the dark dashboards that dominate the space. There is no dark mode (darkMode is removed from the Tailwind config; dark: prefixes are never used).

  • Background: Near-white slate (hsl(240, 17%, 98%)#f8f8fa)
  • Neutrals: Blue-tinted slate (hsl(240, ...)) — never pure gray
  • Accent: Copper #a8522d (hsl(18, 58%, 42%)), reserved for brand identity and AI features and used sparingly — there is no cyan
  • Typography: Geist (UI) and Geist Mono (code, hostnames, IPs), loaded via Google Fonts in index.html
  • Elevation: Borders only — no box-shadows, no glassmorphism, no glow effects
  • Cards: rounded-lg border bg-card; interactive cards shift border-color toward the copper accent on hover (no translate, no shadow)
  • Tables: Uppercase column labels in a faint slate, --card row background, --border-subtle dividers
  • Border radius: 6px base (--radius)

All color tokens are CSS custom properties (HSL) defined in frontend/src/index.css and mapped through frontend/tailwind.config.js. Status/severity → Tailwind class maps, polling intervals, and throttle constants are centralized in src/lib/design-tokens.ts. The full token reference lives in the repo at docs/standards/design-system.md.

Authentication

The frontend uses JWT-based authentication with the backend. On each request, the API client attaches the access token as a Bearer header. Tokens are stored in localStorage under proxima_access_token, proxima_refresh_token, and proxima_session_id (used to identify the current session in the session management UI).

Token Lifecycle

  • Proactive refresh: Before each API request, the client checks if the access token expires within 2 minutes. If so, it refreshes the token automatically.
  • 401 retry: If a request returns 401, the client attempts a single token refresh and retries the request. Concurrent refresh calls are deduplicated with a module-level promise lock.
  • Session expiry: If refresh fails, the client dispatches a CustomEvent("auth:session-expired") which triggers automatic logout and redirect to /login.

AuthContext + useAuth

The AuthProvider component wraps the app inside BrowserRouter and provides the useAuth() hook:

PropertyTypeDescription
userAuthUser | nullCurrent authenticated user
clientAccessRecord<string, string[]> | nullClient-to-permissions map from /auth/me
isSuperAdminbooleanWhether user bypasses all permission checks
isAuthenticatedbooleanWhether a user is logged in
isLoadingbooleanTrue during initial token validation on mount
login(email, password)Promise<LoginResult>Returns { kind: "success" } or { kind: "mfa", mfaToken }
loginWithMFA(mfaToken, code)Promise<void>Complete MFA challenge
loginWithGoogle(code)Promise<void>Exchange Google one-time auth code
logout()Promise<void>Server logout + clear tokens + reset state
refreshUser()Promise<void>Re-fetch /auth/me

usePermissions

The usePermissions() hook provides permission checking based on the authenticated user's clientAccess map:

  • can(permission, clientId?) — super admin returns true; otherwise checks if the user has the permission for the given client (or any client if clientId is omitted)
  • accessibleClients — array of client IDs the user has access to (null for super admins, meaning all)
  • isSuperAdmin — convenience passthrough

Route Guards

ComponentBehavior
ProtectedRouteRedirects to /login if not authenticated. Redirects to /setup-mfa if authenticated but MFA is not enabled. Saves the current location in router state for post-login redirect. Shows a spinner during isLoading.
PublicRouteRedirects to the saved location (or /clients) if already authenticated. Shows a spinner during isLoading.
MFASetupRouteOnly renders when authenticated and mfa_enabled === false. Redirects to /login if not authenticated, /clients if MFA already enabled.

Auth Pages

PageRouteDescription
LoginPage/loginEmail/password form. Conditionally shows "Sign in with Google" when GET /auth/config returns google_sso_enabled: true. Redirects to /login/mfa on MFA challenge.
MFAVerifyPage/login/mfa6-digit TOTP code input with "Use a recovery code" toggle. Reads mfaToken from router state.
MFASetupPage/setup-mfaTwo-step mandatory MFA setup flow. See MFA Setup Page.
GoogleCallbackPage/auth/google/callbackReads code from URL params, exchanges it via loginWithGoogle().
ForgotPasswordPage/forgot-passwordEmail input. Always shows success message (no enumeration).
ResetPasswordPage/reset-passwordReads token from URL params. New password + confirm, 12-character minimum.
AcceptInvitePage/accept-invite?token=...Set password form. Reads token from URL search params. Auto-logs in on success.

User Menu

The AppHeader includes a dropdown menu (right side) with the user's initials avatar, display name, and email. The "Sign out" item calls logout() from useAuth().

The sidebar (AppSidebar) uses usePermissions() together with the useFeatures() feature flags to control navigation visibility. Items are organized into labelled groups, and a group is hidden entirely when none of its items are visible:

Assets

  • Projectsclients:read, for super admins or users with access to zero/multiple clients (single-client users instead see an Environments link pointing directly at their client)
  • Hostshosts:read
  • Agentsagents:read
  • Clusters and Databasesassets:read

Observability

  • Alertsalerts:read and the alerts feature flag
  • Changeschanges:read and the changes feature flag
  • Logslogs:read and the logs feature flag
  • Integrationsintegrations:read

Automation

  • Runbooksrunbooks:read and the runbooks feature flag
  • AI Chatchat:read and the ai_chat feature flag

Service Desk (all gated by servicedesk:read and the service_desk feature flag)

  • Home, Requests, Projects, Metrics

Admin navigation lives separately under AdminLayout (driven by src/components/layout/admin-nav.ts), not the main sidebar. Its items include Users (users:read), Teams (teams:read), Roles (roles:read), Service Accounts (users:read), Credentials (credentials:read), Webhook Sources (webhooks:read), Alert Config (alertsources:read), Compliance (compliance:read), Audit Log (audit:read), Access Audit (roles:read), and Terminal Sessions (terminal_sessions:read).

The sidebar footer displays the user's initials, name, and email. The user dropdown in AppHeader includes a "Profile & Security" link and a "Send Feedback" item that opens the feedback dialog.

Feedback Widget

The in-app feedback widget allows authenticated users to submit bugs, feature requests, UX issues, and suggestions directly from the console. It is accessible via:

  • User menu: "Send Feedback" item in the AppHeader dropdown
  • Keyboard shortcut: Ctrl+Shift+F (or Cmd+Shift+F on macOS)

The FeedbackDialog component provides:

  • Category selector: Bug Report, Feature Request, UX Issue, Suggestion
  • Title (required, max 200 characters) and description (required, max 2000 characters)
  • Paste-to-attach screenshot: paste an image from clipboard (Ctrl+V) to attach a PNG screenshot (max 5MB). A thumbnail preview is shown with a remove button.
  • Auto-populated page URL: the current browser URL is included automatically

On submit, the frontend calls POST /api/v1/feedback. If Jira is configured on the backend, a Jira issue is created with labels (console-feedback, category) and the screenshot is attached as a file. A success toast shows the Jira issue key (e.g. "Feedback submitted — PDD-42"). If Jira is not configured, the backend logs the feedback to structured logs and returns success without a Jira key.

The dialog state is managed via FeedbackContext in RootLayout, exposed through the useFeedback() hook.

Features

Collapsible Sidebar Layout

The application uses a collapsible sidebar navigation with a breadcrumb header. Navigation items are organized into groups (e.g., "Infrastructure" section with Clients and Hosts). Active items are highlighted with a left border accent in the primary color. The sidebar can be collapsed to maximize content area.

Projects List Page

The UI labels this page Projects (the underlying resource is the "client"); the rest of this catalog uses "client" when referring to the data model.

Route: /clients

Displays all managed clients (projects) with a view toggle between a list (table) view and a card grid view. List is the default; the view preference is persisted in the URL (?view=card) for bookmarkable links. Each card/row shows the project name, environment count, and host count. The page uses React Query with a 60-second refetch interval. An Add button opens a dialog form for creating new projects with auto-generated slugs.

Client Detail Page

Shows a single client's environments with inline host lists beneath each environment. Environment names are clickable links to the Environment Detail Page. Provides a quick overview of infrastructure distribution across environments. Host lists use React Query with a 30-second refetch interval (POLLING.CLIENT_HOSTS).

Tabs:

  • Environments (default) — environment cards with inline host lists
  • Credentials — credential management table with CRUD operations (see Credentials Tab)

CRUD operations:

  • Edit Client — opens a dialog with name, slug, and metadata fields (slug auto-generated from name unless manually edited)
  • Delete Client — confirmation dialog, redirects to clients list on success
  • Add Environment — dialog with name and slug fields
  • Edit/Delete Environment — icon buttons on each environment card

Credentials Tab

The Credentials tab displays a sortable table of all credentials scoped to the client. Columns: Name, Type (badge), Environment ("All" for client-wide), Revision, Updated. Each row has an Actions dropdown (Edit, Delete) gated by credentials:write and credentials:delete permissions. A Create Credential button opens the CredentialFormDialog.

CredentialFormDialog (create and edit modes):

  • Name — required text input
  • Type selector — dropdown populated from GET /credentials/types registry. Disabled in edit mode (type is immutable). Supports: postgresql, redis, nginx, docker, generic
  • Provider — always "local" (Vault Transit), disabled
  • Environment selector — optional scoping to a specific environment ("All environments" default)
  • Dynamic fields — rendered based on the selected type's field definitions. Required fields marked with *. Secret fields use password inputs with show/hide toggle. Each field has a tooltip when the type definition includes help text. In edit mode, existing secret values are not shown; fields display "(currently set)" and only need to be filled to update
  • Generic type — key-value pair editor with Add/Remove buttons. All values treated as secrets
  • Security warnings — real-time regex-based warnings for privileged users in DSN fields (postgres, root, admin patterns). Amber alert banners with specific remediation messages
  • Permissions hint — type-specific guidance below the type selector (e.g., "Recommended: pg_monitor role" for PostgreSQL)
  • Auto-test (edit mode only) — debounced 800ms connectivity test fires when all required fields are filled. Shows spinner/success/error status badge
  • Docs link — links to the least-privilege credential setup guide

Delete confirmation uses an AlertDialog warning that deletion may affect agent configs referencing the credential.

Environment Detail Page

Route: /clients/:clientId/environments/:envId

Displays a single environment with aggregated metrics across all hosts. Breadcrumbs link back to the parent client.

Sections:

  • Header — environment name, slug, and creation date
  • Stats row — total host count, online count, offline count
  • Tabs:
    • Metrics (default) — Aggregate Metrics Panel with the same dashboard layout as host metrics (CPU, memory, disk, network, load) but showing AVG-aggregated values across all hosts in the environment. Includes summary cards, time range selector, and "Averaged across N hosts" subtitle.
    • Hosts — table with hostname, IP, OS, CPU/memory/disk capacity and live utilization percentages, status badge, and last seen time. Each row links to the host detail page. Utilization data fetched via useBatchHostLatestMetrics (calls /hosts/metrics/latest).
    • Changes — recent change events for all hosts in the environment (permission-gated via changes:read)
    • Agent Config — environment-level agent configuration panel for managing base configs across all hosts in the environment (see Agent Config UI)

Uses four queries: getEnvironment (environment detail), getClient (breadcrumb name), getEnvironmentHosts (host list with 30-second polling), and useBatchHostLatestMetrics (per-host utilization for online hosts).

Host Inventory Page

A feature-rich inventory view displaying all hosts across all clients and environments. The page supports two view modes (list and card), search, sorting, column management, host pinning, and inline display of agent labels and user tags. All preferences are persisted in localStorage via useHostInventoryPreferences.

Search is handled by the GlobalSearchBar in the page header (simple mode by default). Typing filters hosts inline by hostname, IP, OS, agent labels, and user tags. The search term is stored in the URL (?search=) so browser back/forward preserves the filter. Toggle to PQL mode for advanced infrastructure queries.

Toolbar: The HostInventoryToolbar composes display controls in a responsive layout:

  • Client/Environment filters — dropdowns narrowing by client and environment, persisted in URL search params (?client=uuid&env=uuid)
  • Tabs — "All Hosts" and "Pinned" tabs; Pinned tab shows only pinned hosts with an empty state when nothing is pinned
  • Labels/Tags toggles — expand/collapse inline badge display for agent labels and user tags
  • Column Manager — popover for showing/hiding table columns (Hostname always visible, preferences persisted)
  • View Toggle — switch between list (table) and card (responsive grid) views
  • Sort Control — popover for selecting sort field and direction

List view columns show combined capacity and utilization:

ColumnExampleDescription
CPU4 (72.5%)Core count + colored utilization percentage
Memory8.0 GB (55.0%)Total memory + colored utilization percentage
Disk100.0 GB (80.2%)Total disk + colored utilization percentage

Utilization percentages are color-coded: green (below 70%), amber (70–89%), red (90%+). Sortable column headers use arrow icons. Pinned hosts sort to top with a visual separator. When labels/tags are expanded, a secondary row renders below each host with muted gray badges (labels) and primary-colored badges (tags), both with "+N more" truncation.

Card view renders a responsive grid (4/2/1 columns at xl/sm/mobile breakpoints). Each HostCard shows hostname link, status badge, IP, OS, color-coded metric badges, and optionally label/tag badges. Pinned and unpinned sections are separated by a divider.

Metrics are fetched in a single batch request at the page level using useBatchHostLatestMetrics, which calls GET /api/v1/hosts/metrics/latest with all online host IDs. Tags are fetched via useBatchHostTags (gated by tag expansion toggle). A Last Updated indicator shows how recently data was refreshed.

Host Detail Page

Displays detailed system information for a single host with full breadcrumbs (Clients > ClientName > EnvName > Hostname, all clickable) and a host actions dropdown (Activate/Deactivate, Revoke Agent, Delete Host with confirmation dialogs). On mobile screens, the sidebar collapses to a grouped <Select> dropdown for better usability.

Sidebar Navigation: The host detail page uses a grouped left sidebar (replacing the earlier flat tab bar) with 6 section groups:

GroupSections
OverviewSystem
InventoryServices, Packages, Open Ports, Network, Users, Hardware, GPU
MonitoringMetrics, Processes, Containers, Integrations
SecurityOverview, Scheduled Tasks
ChangesChanges (permission-gated), Logs (permission-gated)
ConfigAgent

The sidebar component (HostDetailSidebar) filters items by permission — groups with no visible items are hidden entirely.

Content sections:

SectionContent
System (default)System information panel with virtualization, OS, kernel details. Includes an Agent Config Status card showing sync state (In Sync/Out of Sync/Unknown), desired config version, active collectors, override count, and a "Manage Config" button that navigates to the Agent Config section.
ServicesAll systemd services with real statuses (running, exited, stopped, failed, starting, stopping). Services are sorted with unhealthy first. The "Unhealthy" filter excludes running, active, and exited services. Status badges are color-coded: green (running), muted (stopped/exited), red (failed), amber (starting/stopping).
MetricsInteractive time-series dashboard for system metrics (retry button on chart errors)
ContainersDocker containers with name, image, state badges, status, port mappings, and container IDs. "Running Only" toggle filter and "Filter Similar" action by image.
Processeshtop-like process table with sortable columns (CPU%, MEM%, RSS, THR, PID) defaulting to CPU descending, color-coded usage bars (green/blue/amber/red by utilization), 30-second auto-refresh with live indicator, process name with command subtitle, user, state badge. Server-side pagination (50 per page) and client-side search by name, command, user, or PID.
IntegrationsPer-host collector status with ok/error/unknown badges and version badges
PackagesInstalled packages with versions
PortsOpen/listening ports
NetworkNetwork adapters with IP addresses
UsersSystem users and their properties
ChangesWatched files table (path, type, SHA-256, sensitive badge, last seen) and recent change events list with severity and event type badges
HardwareCPU, disk, and filesystem details
SecuritySub-tabbed panel with 3 tabs: Firewall (rules with search, target filters, pagination), Certificates (sorted by expiry with color-coded expiry badges), Security Config (grouped key-value settings). Tabs with 0 items are hidden; defaults to the first tab with data.
Agent ConfigHost-level agent configuration panel (see Agent Config UI)

All inventory sections (Services, Packages, Ports, Network, Users, Processes) share common UX patterns: debounced search (300ms via useDebounce), pagination, and distinct empty states — "No X discovered"/"No process data available" when no data exists vs. "No X match your filters"/"No processes match your search" when filters produce no results.

System Information panel: Virtualization type and role are displayed inline in the main grid alongside OS, kernel, architecture, etc. Cloud provider/region/zone and labels are shown as separate subsections when present.

Metrics Dashboard

The Metrics tab provides a Datadog/Grafana-inspired dashboard with all metrics visible at once in a scrollable view.

Layout:

  • Summary stat cards — CPU, Memory, Disk, Load Average with sparkline backgrounds
  • Area charts — CPU usage, memory usage, disk usage with gradient fills
  • Network charts — Side-by-side Sent/Received in 2-column grid
  • Load averages — Overlaid 1m/5m/15m as multi-line chart
  • Grouped sections — CPU, Memory, Disk, Network, Load with section titles

Dashboard Context: Host, integration, and aggregate metric panels all wrap their content with a DashboardProvider (Inner/Outer component pattern) that provides two shared React contexts:

  • CrosshairContext — Hovering on one chart shows a dashed reference line on all sibling charts at the same timestamp. Uses a 60ms throttle (Grafana EventBus pattern). Each chart publishes hover via useCrosshair(), other charts render a ReferenceLine.
  • DashboardTimeRangeContext — Manages the time range as string expressions (from/to — e.g. "now-3h" / "now" for relative, ISO strings for absolute/zoomed). Computes rangeHours, startISO/endISO, and isZoomed. Provides setTimeRange, shiftRange, zoomTo, resetZoom. Persisted to URL search params (?from=now-1h&to=now) for shareable dashboard links.

Time Range Picker: A Grafana-style TimeRangePicker popover with searchable quick presets (Last 15 minutes through Last 30 days, Today, Yesterday), absolute datetime-local inputs for custom ranges, and recently used ranges (localStorage). Shift arrows move the window by half its duration. A shared MetricsToolbar component renders the picker, title, loading spinner, and refresh button consistently across all dashboard panels.

Drag-to-zoom: Click and drag on any chart to zoom into a specific time range. A ReferenceArea overlay provides visual feedback during the drag. The zoom is committed to the shared context, so all charts zoom together. A "Reset Zoom" button returns to the relative range. Auto-refresh is disabled during zoom to prevent reset.

Series toggle: Click a legend item to isolate (solo) that series. Ctrl/Meta/Shift+click toggles individual series without affecting others. A "Reset View" button restores all series. Toggle state persists in localStorage.

Chart gap detection: When data points are missing (gap > 2x expected interval), lines disconnect instead of drawing across gaps. The fillTimeGaps() utility inserts null placeholders that Recharts renders as line breaks.

Network metric aggregation: Network metrics are fetched without a labels_key parameter, letting the backend aggregate across all network interfaces server-side.

Data fetching: The useMetricDashboard hook fetches all metrics in parallel using React Query, with 60-second auto-refresh (paused during zoom). Network metrics skip the series discovery step (no per-interface breakdown needed). The hook accepts startISO/endISO from the dashboard context for zoom support.

Integration Metrics Dashboard

The Integration Detail Page includes a Metrics tab with a dedicated dashboard for integration-specific metrics (e.g., PostgreSQL, Nginx). The dashboard is driven by per-integration config objects in integration-metrics.ts.

Layout:

  • Summary cards — Key metrics for the integration (e.g., active connections, cache hit ratio, deadlocks, replication lag)
  • Chart groups — Metrics organized by category with configurable chart types (area, line), layout (full/half width), and overlay mode
  • Collector picker — When multiple collectors of the same type exist on a host, a dropdown lets the operator switch between instances

Collector label filtering: Integration metrics are filtered by the collector=<name> label prefix. The useIntegrationMetricDashboard hook discovers series with matching labels and fetches data for each. A fallback path handles pre-label-injection metrics (empty labels key).

Per-metric overrides: Individual metrics within a group can override the group's derivative and chartType settings. For example, the "Deadlocks & Replication" group renders deadlocks as a rate-of-change line chart and replication lag as an area chart.

Tooltip formatting: Byte-valued metrics (detected by metric name containing "bytes") automatically format as KB/MB/GB in chart tooltips across all chart types (single-series, multi-series, and overlay).

Integration Config Tab

The Integration Detail Page also includes a Config tab that displays runtime configuration parameters collected by the agent (e.g., PostgreSQL pg_settings, Nginx nginx -T). The tab shares the same host/collector picker as the Metrics tab.

Config table columns:

ColumnDescription
ParameterConfiguration parameter name (monospace font)
ValueCurrent value — rendered with type-specific formatting for known Nginx config keys (see below), monospace for all others
UnitUnit of measurement (e.g., 8kB, kB) or - if unitless
SourceWhere the value was set (displayed as a badge, e.g., configuration file, default)
CategorySettings category

Parameters are sorted alphabetically. When no configuration data has been collected yet, an empty state message is shown with a note that config is refreshed every 5 minutes.

Category sub-tabs: When multiple categories exist (e.g., Docker config with "Daemon", "Images", "Volumes"), the table renders sub-tabs using Radix Tabs. Common prefixes are stripped from tab labels (e.g., "Docker / Daemon" becomes "Daemon"). The Category column is hidden when sub-tabs are active since category is implicit from the selected tab.

Structured rendering for Nginx config:

Known Nginx config keys are rendered with rich formatting instead of plain comma-separated strings:

KeyRendering
server_namesIndividual outline badges for each domain name
upstreamsArrow-separated pairs (server → upstream), one per line
upstream_backendsGrouped display — upstream name as a header with backend servers listed below
ssl_certificatesStructured cards with server name, cert path, and a color-coded expiry badge (red = expired, amber = ≤30 days, green = >30 days)
ssl_cert_earliest_expiryStandalone color-coded expiry badge

All other config keys render with the existing monospace behavior.

Change Detection Pages

The change detection UI provides visibility into file integrity monitoring events and external change events (from webhook sources) across the infrastructure.

Changes Page

Route: /changes

A global timeline of all change events across all hosts. Features server-side pagination via URL search params and a filter toolbar with three select dropdowns:

  • Severity — Filter by critical, warning, or info
  • Event Type — Filter by file_created, file_modified, file_deleted, file_metadata_changed, push, merge_request, tag, release, pipeline_completed, pipeline_failed, workflow_completed, workflow_failed, sync, sync_failed, health_degraded
  • Source — Filter by event source (agent, gitlab, github, deployment)

Each event row shows a severity badge, event type badge, summary text, host link, source, and relative timestamp. Clicking a row navigates to the change detail page. Data is polled every 30 seconds.

Change Detail Page

Route: /changes/:changeId

Displays a single change event with:

  • Header — Summary text, severity badge, event type badge, timestamp
  • Event Metadata card — Source, event type, severity, created timestamp, with links to host, client, and environment when available
  • Details card — JSONB details rendered as key-value pairs (excluding diff_text)
  • Diff viewer — When the event has a diff_text in its details, renders a unified diff with red/green line coloring and line numbers

File Versions Page

Route: /hosts/:hostId/files/:fileId

Displays the version history for a single watched file:

  • File metadata header — File path, file type badge, sensitive badge, owner, mode
  • Versions table — Version number (with "latest" badge on newest), SHA-256 hash (truncated), file size, owner:group, mode, created timestamp
  • Click-to-diff — Clicking a version row shows its unified diff in a card below the table

Host Changes Tab

The Changes tab on the Host Detail Page has two sections:

  • Watched Files — Table of files being monitored on this host (path, type, SHA-256, sensitive badge, last seen). Click a file row to navigate to its File Versions page.
  • Recent Changes — List of recent change events for this host using the shared ChangeEventRow component.

Change Detection Components

ComponentLocationPurpose
SeverityBadgecomponents/changes/Color-coded badge for severity levels (critical=red, warning=amber, info=blue)
EventTypeBadgecomponents/changes/Descriptive badge for event types (Created, Modified, Deleted, Metadata Changed)
ChangeEventRowcomponents/changes/Reusable timeline row with severity, event type, summary, host link, and relative time
DiffViewercomponents/changes/Renders unified diff text with red/green line coloring, line numbers, and monospace font
ChangeFilterscomponents/changes/Filter toolbar with severity, event type, and source select dropdowns
HostChangesTabcomponents/changes/Host detail tab combining watched files table and recent changes list

MFA Setup Page

Route: /setup-mfa

Users who log in without MFA enabled are automatically redirected here by ProtectedRoute. The page implements a two-step mandatory MFA enrollment flow:

Step 1 — Verify:

  • Calls authMFASetup() on mount to generate a TOTP secret
  • Displays a QR code (QRCodeSVG from qrcode.react, 200×200) for authenticator app scanning
  • Shows the secret key for manual entry
  • 6-digit TOTP code input with pattern validation
  • "Verify & Enable" button (disabled until code is 6 digits) → calls authMFAConfirm(code)

Step 2 — Recovery Codes:

  • Displays 8 recovery codes in a 2×4 monospace grid
  • "Copy codes" button (clipboard API)
  • "I have saved these recovery codes" checkbox
  • "Continue" button (disabled until checkbox checked) → calls refreshUser() → navigates to /clients

Recovery codes come from the setup response (not the confirm response). The confirm endpoint returns { message: string } only.

The AppHeader includes a GlobalSearchBar component between breadcrumbs and the user menu (hidden on mobile screens and on the /search page to avoid duplicate bars). It supports two modes, switchable via a toggle button:

Simple mode (default):

  • Placeholder — "Search hosts by name, IP, OS... Ctrl+K"
  • On the Hosts page — Filters hosts inline as you type (live keystroke callbacks via onSimpleSearch). Search state is stored in the URL (?search=) so browser back/forward preserves the filter.
  • On other pages — Pressing Enter navigates to /hosts?search={query}
  • No autocomplete or PQL validation — Keeps the input fast and distraction-free

Advanced mode (PQL):

  • Placeholder — "Find anything... Ctrl+K"
  • Real-time PQL validation — Debounced (300ms) calls to POST /api/v1/search/validate display a visual error indicator when the query has syntax errors
  • Autocomplete — Context-aware suggestions for fields, operators, values, keywords, and saved searches
  • Navigation — Pressing Enter navigates to /search?q={encoded_query}
  • PQL help popover — Quick reference for operators, examples, and field prefixes

Shared features (both modes):

  • Keyboard shortcutCtrl+K (or Cmd+K on macOS) focuses the search input from anywhere on the page
  • Toggle button — Shows "PQL" in simple mode / "Simple" in advanced mode. Mode preference is persisted in localStorage via useSearchMode hook.

Search Results Page

Route: /search?q={pql_query}&page=1

Displays PQL search results with paginated host cards. The page reads the q parameter from URL search params and fetches results via searchHosts().

Layout:

  • PQL input — Monospace text input with search icon and submit button, pre-filled from the URL q parameter
  • Results count — e.g. "42 hosts found"
  • Host cardsSearchHostCard components showing hostname (link to host detail), status badge, IP address, OS, architecture, agent version, and tag badges (truncated at 5 with "+N more")
  • Pagination — Previous/Next buttons with page counter
  • Save search — Button in the header opens SaveSearchDialog to save the current query
  • Empty state — When no query, shows PQL syntax help with example queries

Save Search Dialog

The SaveSearchDialog is a modal form for saving PQL queries:

FieldRequiredDescription
NameYesDisplay name for the saved search
QueryRead-only display of the PQL query being saved
DescriptionNoOptional description of what the search finds
Share with teamNoToggle to make the search visible to all users

On save, the dialog validates the query via createSavedSearch() and invalidates the saved searches React Query cache.

Admin Pages

The frontend includes a full admin UI for managing users, teams, roles, service accounts, and viewing audit logs. All admin pages are under the /admin/* route prefix and require appropriate RBAC permissions.

Users Page

Route: /admin/users (requires users:read)

Table layout displaying all users with columns: Name (linked to detail page), Email, Status (badge: active/invited/disabled), MFA (badge: enabled/disabled), Auth Provider, Last Login, and an Actions dropdown menu.

Actions dropdown:

  • Edit — opens UserFormDialog in edit mode
  • Resend Invite — visible only for invited status users
  • Reset Password — visible only for active status users
  • Enable/Disable — toggles user status

"Invite User" button opens UserFormDialog in create mode (email, display name, super admin toggle).

User Detail Page

Route: /admin/users/:userId (requires users:read)

Displays user information card (email, status, auth provider, MFA status, last login, created at) with Edit/Disable action buttons. Below the info card:

  • Client Assignments table — shows directly assigned clients with role, plus "Assign Client" dialog (UserClientDialog) with client and role pickers
  • Team Memberships table — read-only display of teams the user belongs to with their role in each team

Roles Page

Route: /admin/roles (requires roles:read)

Card grid displaying roles with name, description, permission count, and a system badge for built-in roles. System roles show a lock icon and cannot be edited or deleted. "Create Role" button opens RoleFormDialog with an interactive permission matrix (resource×action grid). A "Compare" toggle enables selecting 2-3 roles for side-by-side comparison with diff highlighting. See Roles & Permissions UX for full details.

Role Detail Page

Route: /admin/roles/:roleId (requires roles:read)

Displays role information with a read-only permission matrix (resource×action grid) and a "Used By" section showing which users and teams are assigned this role, with source indicators (direct vs team-inherited). Edit/Delete buttons are hidden for system roles. See Roles & Permissions UX for full details.

Teams Page

Route: /admin/teams (requires teams:read)

Card grid with team name, description, member count, and client count. "Create Team" button opens TeamFormDialog (name, description).

Team Detail Page

Route: /admin/teams/:teamId (requires teams:read)

Two sections:

  • Members table — user name/email, role, and actions (change role, remove). "Add Member" button opens TeamMemberDialog with user and role pickers (role dropdown shows permission preview on hover).
  • Clients table — client name with remove action. "Add Client" button opens TeamClientDialog with client picker.

Access Audit Page

Route: /admin/access-audit (requires roles:read)

Two-tab page for reviewing who has access to what. "By User" tab: select a user to see all their client access with role, source (direct/team), and expandable permission details. "By Client" tab: select a client to see all users with access. See Roles & Permissions UX for full details.

Service Accounts Page

Route: /admin/service-accounts (requires users:read)

Table with columns: Name, API Key Prefix (monospace), Role, Status (active/inactive badge), Last Used, Expires, and Actions (activate/deactivate, delete). Activate/deactivate sends the full required fields (name, role_id, is_active) to the backend PUT endpoint.

Critical UX: When creating a service account, the expiration date is required (calendar date picker, max 1 year from today). The API key is shown exactly once in an ApiKeyRevealDialog with a copy button. The dialog warns that the key cannot be retrieved again.

Credentials Page

Route: /admin/credentials (requires credentials:read)

Cross-client credential management table with columns: Name, Type (badge), Client, Environment, Provider, Last Rotated, Revision, and Actions (edit, delete). Supports text search filtering across name, type, and client fields. "Create Credential" button opens CredentialFormDialog in create mode (see Credentials Tab for dialog details).

Unlike the client-scoped Credentials Tab, this page shows credentials across all clients the user has access to, making it useful for administrators managing credentials at scale.

Webhook Sources Page

Route: /admin/webhook-sources (requires webhooks:read)

Table with columns: Name, Source Type (color-coded badge: purple GitLab, slate GitHub, orange ArgoCD), Environment, Status (enabled/disabled badge), Created, and Actions dropdown (edit, rotate token, delete). Supports text search filtering across name and source type.

Key actions:

  • Create opens WebhookSourceFormDialog with fields for name, source type, client, environment, secret, and host mapping (JSON textarea)
  • Rotate Token generates a new token and displays it in WebhookTokenDisplay (one-time display with copy button and source-specific setup instructions)
  • Delete requires confirmation

After creation, WebhookTokenDisplay shows the webhook URL and plaintext token with a warning that the token cannot be retrieved again.

Audit Log Page

Route: /admin/audit-log (requires audit:read)

Read-only filterable table with columns: Timestamp, Actor (email), Action, Resource Type, Resource ID, IP Address. Rows are expandable to show the full details JSON payload.

Filters:

  • Actor email (text input)
  • Action type (select)
  • Resource type (select)
  • Date range (from/to date inputs)

Profile & Security Page

Route: /profile (no admin permission required — any authenticated user)

Three sections:

  1. Change Password — form with current password, new password, and confirm new password fields
  2. Two-Factor Authentication — shows MFA status (enabled/disabled badge). When enabled, provides a "Disable MFA" form requiring the current account password for confirmation. When disabled, notes that MFA will be required on next login.
  3. Active Sessions — table showing device label (with user agent fallback), IP address, last active time, and expiry. The current session is identified via session_id stored in localStorage and displays a green "Current" badge. Revoking the current session or using "Revoke All" triggers a confirmation dialog warning that the user will be signed out, then calls logout() automatically. Non-current sessions can be revoked without confirmation.

Admin Dialog Components

ComponentPurpose
UserFormDialogCreate/edit user (email, display name, super admin toggle)
UserClientDialogAssign user to client with role picker
RoleFormDialogCreate/edit role with grouped permission checkboxes
TeamFormDialogCreate/edit team (name, description)
TeamMemberDialogAdd team member (user picker + role picker)
TeamClientDialogAdd client to team (client picker)
ServiceAccountFormDialogCreate service account (name, description, role, calendar date picker for required expiry — max 1 year)
ApiKeyRevealDialogOne-time API key display after service account creation
WebhookSourceFormDialogCreate/edit webhook source (name, type, client, environment, secret, host mapping JSON)
WebhookTokenDisplayOne-time webhook URL + token display with copy buttons and source-specific setup instructions

AgentStatusBadge Component

A reusable badge component that renders the agent's connection status with color-coded indicators. Status classes are provided by the centralized getStatusClasses() function from design tokens:

  • Online — Agent is reporting normally
  • Offline — Agent has not reported within the expected window (set to offline by the stale agent detector)
  • Degraded — Agent is reporting but with issues
  • Unknown — Status cannot be determined

Agents / Fleet Page

Route: /agents (requires agents:read)

Fleet management view for installed agents: version distribution, online/offline status, and self-update rollouts. Supports canary rollouts of new agent versions with pause/resume/abort controls. Backend→agent commands are keyed by agent_id.

AI Chat

Route: /chat (requires chat:read and the ai_chat feature flag)

Conversational interface backed by the MCP server. Assistant responses stream over Server-Sent Events (use-chat-stream.ts), not WebSocket. Chat always uses the platform LLM key (per-client BYOK keys are not yet implemented).

Web Terminal & Session Recordings

  • Full-page terminal — Route /hosts/:hostId/terminal (HostTerminalPage). An interactive shell rendered with xterm.js, connected over /api/v1/terminal/ws — the single WebSocket exception. The pc CLI offers the same access from the command line, including non-interactive exec.
  • Join shared session — Route /terminal/join/:sessionId (JoinSessionPage) connects a second viewer to a live session via /terminal/ws/join.
  • Recording playback — Route /recordings/:sessionId (RecordingPlaybackPage) replays a recorded terminal session. Sessions are also auditable from the admin Terminal Sessions page (/admin/terminal-sessions).

Runbooks

  • Runbooks list/runbooks (requires runbooks:read and the runbooks feature flag)
  • Runbook editor/runbooks/new and /runbooks/:runbookId (RunbookEditorPage)
  • Execution detail/runbooks/executions/:executionId (RunbookExecutionDetailPage) shows a step-by-step execution timeline.

Alerts

  • Alerts page/alerts (requires alerts:read and the alerts feature flag). A firing-alert count badge in the sidebar polls every 30s.
  • Alert detail/alerts/:alertGroupId (AlertDetailPage).
  • Alert configuration lives under admin at /admin/alert-config (alertsources:read).

Logs

Route: /logs (requires logs:read and the logs feature flag)

Centralized log search across the fleet (backed by VictoriaLogs), including agent self-shipped logs.

Databases & Clusters

  • Clusters/clusters and /clusters/:clusterId (requires assets:read)
  • Databases/databases, /databases/:databaseId, and /databases/clusters/:clusterId (requires assets:read)

Inventory and detail views for database clusters and individual databases discovered across client environments.

Service Desk

Routes under /service-desk (requires servicedesk:read and the service_desk feature flag): Home (/service-desk), Requests (/service-desk/requests), a Kanban board (/service-desk/kanban), request creation (/service-desk/new), ticket detail (/service-desk/tickets/:issueKey), Projects (/service-desk/projects), and Metrics (/service-desk/metrics). Org/project mappings are configured under admin (/admin/service-desk).

Briefing Page

Route: / (the authenticated index/landing page)

A daily operational briefing summarizing fleet health, recent changes, and notable events across accessible clients.

Custom Hooks

useHostLatestMetrics

Fetches the most recent utilization values (CPU, memory, disk) for a single host by making 3 parallel per-metric API calls. Used on the host detail page. Accepts an enabled parameter — when false (e.g., agent is offline), the query is skipped entirely to avoid unnecessary API calls.

useBatchHostLatestMetrics

Fetches the most recent utilization values (CPU, memory, disk) for multiple hosts in a single API call to GET /api/v1/hosts/metrics/latest. Used on the hosts list page to avoid N+1 queries — returns a Record<hostId, { cpu, memory, disk }> map. The query key is stabilized with sorted host IDs to prevent unnecessary refetches when array order changes.

useMetricDashboard

Fetches all dashboard metrics for a host in parallel. Discovers available metric names, then fetches data for each. Network metrics skip series discovery and fetch aggregated data directly. Uses React Query with 60-second refetch interval. Accepts an options parameter with startISO, endISO, and refetchInterval to integrate with DashboardContext zoom (absolute range overrides relative rangeHours, polling paused during zoom).

useAggregateMetricDashboard

Fetches aggregated metrics for an environment or client scope. Mirrors useMetricDashboard but routes API calls to the aggregate endpoints (/environments/:id/metrics or /clients/:id/metrics). Accepts an AggregateMetricScope discriminated union that selects the correct API functions at runtime. Uses React Query with 60-second refetch interval. Accepts an options parameter with startISO, endISO, and refetchInterval to integrate with DashboardContext zoom.

useIntegrationMetricDashboard

Fetches integration-specific metrics for a host, filtered by collector label. Discovers series with the collector=<name> prefix, fetches data for each metric in the integration config, and supports per-metric derivative calculation. Falls back to empty labels key when no collector-labeled series exist (backward compatibility). Uses React Query with 60-second refetch interval. Accepts an options parameter with startISO, endISO, and refetchInterval to integrate with DashboardContext zoom.

useSearchMode

Persists the user's search mode preference (simple vs PQL) in localStorage (proxima:search-mode). Returns { mode, setMode, toggle }. Used by GlobalSearchBar to remember the last-used mode across sessions.

useSearchSuggestions

Provides context-aware autocomplete suggestions for the PQL search input. Fetches field names, operators, and values from the backend based on the cursor position in the query. Debounced to avoid excessive API calls during typing.

usePagination

A hook for offset-based table pagination, providing page state, navigation callbacks, and derived values (total pages, can-go-prev/next).

useDebounce

A generic debounce hook that delays updating a value until a specified timeout (default 300ms). Used by all inventory tables to debounce search input, preventing excessive re-filtering on every keystroke.

useNetworkStatus

Detects online/offline network state using useSyncExternalStore with navigator.onLine and online/offline browser events. When the connection is restored, it shows a toast notification and invalidates all React Query caches to trigger a refresh. The AppHeader component uses this hook to display an amber "You are offline" banner.

CRUD Dialog Components

Infrastructure dialogs are located in src/components/crud/, admin dialogs in src/components/admin/:

ComponentLocationPurpose
ClientFormDialogcrud/Create/edit clients with name, slug (auto-generated via toSlug), and optional JSON metadata fields
EnvironmentFormDialogcrud/Create/edit environments with name and slug fields
DeleteConfirmDialogcrud/Reusable confirmation dialog for destructive actions (AlertDialog-based)
UserFormDialogadmin/Create/edit user (email, display name, super admin toggle)
UserClientDialogadmin/Assign user to client with role picker
RoleFormDialogadmin/Create/edit role with grouped permission checkboxes
TeamFormDialogadmin/Create/edit team (name, description)
TeamMemberDialogadmin/Add team member (user picker + role picker)
TeamClientDialogadmin/Add client to team (client picker)
ServiceAccountFormDialogadmin/Create service account (name, description, role, calendar date picker for required expiry — max 1 year)
ApiKeyRevealDialogadmin/One-time API key display after service account creation

Auto-slug generation: When creating a new entity, the slug field is auto-populated from the name using the toSlug utility (lowercase, strip special characters, spaces to hyphens). Once the user manually edits the slug, auto-generation stops. In edit mode, auto-generation is disabled by default.

Slug validation: Both ClientFormDialog and EnvironmentFormDialog validate slugs inline with the pattern /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/. Invalid slugs show an inline error message with aria-invalid and aria-describedby for accessibility. The submit button is disabled until the slug is valid.

Mutation feedback: All mutations use React Query useMutation with automatic cache invalidation on success. Contextual toast notifications (via sonner) include entity names (e.g., Client "Acme Corp" deleted instead of generic Client deleted). Post-delete operations navigate back to the parent list page.

Common Components

ComponentPurpose
ErrorBoundaryReact error boundary that catches uncaught render errors and displays a fallback UI
ErrorAlertReusable error display with message and optional retry button
PageTitleConsistent page header with title, description, and optional action slot
TablePaginationPrev/next pagination controls with page indicator (<nav> landmark for a11y)
NotFoundPage404 page with link back to clients
LastUpdatedShows relative time since last data refresh (e.g., "Updated 15s ago"), re-renders every 10s
ResponsiveTableTable wrapper with overflow-x-auto, right-edge fade gradient when content overflows, and role="region" for a11y
UtilizationDisplayShared components for host utilization rendering: HostUtilizationCell, UtilizationValue, UtilizationLegend, and getUtilizationColor — used by both HostsPage and EnvironmentDetailPage

Host Inventory Components

The HostsPage serves as an orchestrator, delegating rendering to specialized components in src/components/inventory/:

ComponentPurpose
HostListViewEnhanced table with sortable column headers, pin icons, and expandable label/tag rows
HostCardViewResponsive card grid (1/2/4 columns) with pinned/unpinned divider
HostCardIndividual host card with metric badges, status, labels, and tags
HostInventoryToolbarComposes search, filters, tabs, and display controls
HostSystemInfoSystem info panel with labels, tags, cloud metadata
ViewToggleList/Card icon toggle with active/inactive states
ColumnManagerPopover for showing/hiding table columns (Hostname always visible)
SortControlPopover for selecting sort field and direction
HostLabelBadgesMuted gray badges for agent labels with "+N more" truncation
HostTagBadgesPrimary-colored badges for user tags with "+N more" truncation

useHostInventoryPreferences

Manages all localStorage-persisted preferences (view mode, visible columns, pinned hosts, sort field/direction, label/tag expansion) via a single proxima:host-inventory-prefs key. Provides individual setters and toggle functions.

useBatchHostTags

Fetches tags for multiple hosts in parallel using useQueries. Gated by enabled prop (only fetches when tags are expanded). Shares React Query cache keys with HostTagEditor on the host detail page.

Utilities

UtilityModulePurpose
formatByteslib/formatConverts byte values to human-readable sizes (KB, MB, GB)
formatDatelib/formatFormats timestamps to locale-appropriate date strings
formatDateTimelib/formatFormats timestamps to locale-appropriate date + time strings
formatRelativeTimelib/formatConverts timestamps to relative descriptions (e.g., "5m ago")
formatChartTimelib/formatFormats timestamps for chart X-axis labels
fillTimeGapslib/metrics-utilsInserts null placeholders for chart gap detection
mergeTimeSeriesByTimestamplib/metrics-utilsMerges multiple series into a single dataset for multi-line charts
toSluglib/utilsConverts a name to a URL-safe slug (lowercase, hyphens, no special chars)
queryKeyslib/query-keysFactory for type-safe React Query cache keys
POLLINGlib/design-tokensCentralized polling interval constants
THROTTLElib/design-tokensCentralized throttle constants (e.g., crosshair sync)
getStatusClasseslib/design-tokensReturns Tailwind classes for agent status colors
getSeverityClasseslib/design-tokensReturns Tailwind classes for change event severity colors (critical/warning/info)
SEVERITY_COLORSlib/design-tokensSeverity color tokens mapping (text, bg, border classes)
buildOverlayDatacomponents/monitoring/overlay-utilsBuilds overlay chart data from metric groups — shared across host and aggregate metric panels
parseTimeExpressionlib/time-rangeParses Grafana-style expressions (now-3h, now/d, now-1d/d) into Date objects
resolveTimeRangelib/time-rangeResolves a (from, to) expression pair into { start, end } Date objects
formatTimeRangeLabellib/time-rangeFormats time range for display (e.g. "Last 3 hours", "Feb 20 to Feb 22")
computeRangeHourslib/time-rangeMaps resolved duration to nearest TimeRangeHours bucket (1/6/24/168)

Agent Config UI

The Agent Config UI provides management interfaces for the 8 agent configuration types (collectors, intervals, labels, healthcheck, change_detection, log_collection, agent_settings, watch_files) at both environment and host levels.

Shared Components (components/agent-config/):

  • ConfigSection — collapsible section with source badge (inherited/host override/merged/not configured/configured), Override and Remove Override buttons for host-level editing
  • ConfigJsonEditor — raw JSON textarea with live validation, Prettify button, and Save
  • SyncBanner — config version sync status indicator (In Sync/Out of Sync/Unknown) comparing heartbeat-reported version against desired version
  • AgentConfigStatusCard — compact card for the host overview showing sync status, active collectors, override count, and "Manage Config" link

Section Form Components (components/agent-config/sections/): Each config type has a typed form component with appropriate field controls — duration inputs for intervals, toggle + path lists for change detection, key-value editors for labels, card grid with inline expand for collectors (type-specific fields per collector type), file source lists for log collection, etc. All sections share a common prop interface (data, onChange, onSave, readOnly, isPending) and support a form/JSON toggle.

Environment Panel (EnvironmentAgentConfigPanel): Renders all 8 config sections as directly editable forms. No sync banner or inheritance indicators — this is the base config level.

Host Panel (HostAgentConfigPanel): Renders effective (merged) config with per-section source indicators. Inherited configs are read-only; users can click "Override" to create a host-level copy, then edit and save. "Remove Override" reverts to the inherited environment config. Includes a sync banner comparing the agent's heartbeat config version against the desired version.

Responsive Design

The frontend adapts to mobile and desktop viewports using the useIsMobile() hook (768px breakpoint):

  • Tables — Wrapped in ResponsiveTable for horizontal scroll with a fade gradient edge indicator
  • Host Detail Sidebar — HostDetailPage renders a grouped <Select> dropdown on mobile instead of the left sidebar navigation
  • Charts — Metric charts use 200px height on mobile, 250px on desktop
  • Metric controls — Time range picker and refresh button in a shared MetricsToolbar component

Accessibility

  • ARIA labels — All icon-only buttons have aria-label attributes (e.g., "Edit environment", "Delete host")
  • Form validation — Invalid inputs use aria-invalid and aria-describedby pointing to error messages
  • Navigation landmarksTablePagination uses <nav aria-label="Table pagination">; ResponsiveTable uses role="region" with a label
  • Offline banner — Uses role="alert" for screen reader announcement
  • Empty states — Inventory tables distinguish between "no data" and "no filter results" with a clear action button

TypeScript Types

Frontend TypeScript types are auto-generated from the backend swagger spec, ensuring compile-time safety and keeping types in sync with Go structs automatically.

Generation Pipeline

swagger annotations → make swagger → swagger.json (Swagger 2.0)
→ swagger2openapi → OpenAPI 3.0 → openapi-typescript → generated.ts
→ adapter files re-export with clean names

Run make swagger && make generate-types after changing swagger annotations. A CI job (lint:types) verifies generated types are fresh on every MR.

Type Files

FileDescription
types/generated.tsAuto-generated from the swagger spec (tens of thousands of lines covering all API schemas). Do not edit.
types/api.tsAdapter: domain models (Host, Service, etc.), response wrappers, request types
types/auth.tsAdapter: AuthUser, LoginResponse, MFASetupResponse, AuthSession, etc.
types/admin.tsAdapter: AdminUser, Role, Team, ServiceAccount, audit log types
types/search.tsAdapter: SavedSearch, SearchFieldInfo, SearchValidation, etc.
types/metrics.tsFrontend-only MetricSeries type
types/briefing.tsAdapter: morning/ops briefing types
types/chat.tsAdapter: AI chat message and session types
types/feedback.tsAdapter: user feedback types
types/fleet.tsAdapter: agent fleet self-update types
types/kubernetes.tsAdapter: Kubernetes cluster and workload types

How Adapters Work

The generated types have three issues that adapters fix:

  1. All fields optional — Swagger spec lacks required arrays, so adapters use Required<> to make all response fields non-optional.
  2. Ugly names — Generated names like components["schemas"]["github_com_..."] are re-exported as clean names (Client, Host, etc.).
  3. No nullability — Go pointer fields (*string) serialize as null, but Swagger 2.0 has no nullable keyword. Adapters use a Nullable<T, K> helper to mark specific fields as T | null.

Types not in the swagger spec (generic wrappers like ApiResponse<T>, frontend-only types like MFAChallenge) stay hand-written in the adapter files. Request types with mixed required/optional fields also stay hand-written for correctness.