Skip to main content
doc0blade47/mem0documentation home
Sign in

Dashboard Views

Last updated Oct 11, 2026100% grounded
Relevant source files

The following files were used as context for generating this wiki page:

Overview

The administrative dashboard provides a centralized Next.js interface for managing self-hosted instances, monitoring runtime memory operations, inspecting stored entities, and configuring system-level parameters through authenticated API query hooks and state management providers. Sources: server/dashboard/src/app/setup/page.tsx, server/dashboard/src/hooks/use-api-query.ts

The dashboard layout combines a fixed sidebar shell container managed by Redux layout state with collapsible navigation groups, instance branding, and a top-bar control header. The primary navigation shell uses NavWrapper to compute responsive positioning based on whether isSidebarCollapsed is active, utilizing layout widths defined by SIDEBAR_WIDTH and COLLAPSED_SIDEBAR_WIDTH. A toggle button rendered in the top header triggers the toggleSidebar Redux action, altering both the fixed container width and the adjacent main content offset.

Sources: server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx:31-51

Note

When the sidebar is collapsed, collapsible triggers and textual labels (such as group titles and user email identifiers) are hidden, while tooltips are automatically passed to SidebarMenuButton components to display the item titles on hover.

Sources: server/dashboard/src/app/root/dashboard/components/main-nav.tsx:44-64

The MainNav component organizes dashboard links into three distinct functional sections: ACTIVITY, CLOUD FEATURES, and ACCOUNT. Each section groups route definitions that map to Lucide icons and match the current router path via usePathname.

SectionTitleURLIcon
ACTIVITYRequests/dashboard/requestsActivity
ACTIVITYMemories/dashboard/memoriesGalleryVerticalEnd
ACTIVITYEntities/dashboard/entitiesUsers
CLOUD FEATURESCategories/dashboard/categoriesTags
CLOUD FEATURESWebhooks/dashboard/webhooksWebhookIcon
CLOUD FEATURESAnalytics/dashboard/analyticsChartLine
CLOUD FEATURESExport/dashboard/exportFolderInput
ACCOUNTAPI Keys/dashboard/api-keysKeyRound
ACCOUNTConfiguration/dashboard/configurationWrench
ACCOUNTSettings/dashboard/settingsSettings
{[
  {
    title: "Requests",
    url: "/dashboard/requests",
    icon: Activity,
    active: pathname === "/dashboard/requests",
  },
  {
    title: "Memories",
    url: "/dashboard/memories",
    icon: GalleryVerticalEnd,
    active: pathname === "/dashboard/memories",
  },
  {
    title: "Entities",
    url: "/dashboard/entities",
    icon: Users,
    active: pathname === "/dashboard/entities",
  },

Sources: server/dashboard/src/app/root/dashboard/components/main-nav.tsx:65-83, server/dashboard/src/app/root/dashboard/components/main-nav.tsx:132-152, server/dashboard/src/app/root/dashboard/components/main-nav.tsx:199-217

User Profile and Dropdown Menu

At the base of the expanded sidebar shell, NavWrapper renders a user profile widget connected to useAuth. Clicking the user trigger opens a DropdownMenu providing direct navigation to user settings and a logout handler.

<DropdownMenu>
  <DropdownMenuTrigger asChild>
    <button className="flex items-center gap-2 w-full text-left hover:bg-surface-default-secondary-hover rounded-md p-1.5 transition-colors">
      <div className="grid size-7 place-items-center rounded-md bg-surface-default-tertiary text-onSurface-default-secondary text-xs font-semibold shrink-0">
        {user?.name?.charAt(0).toUpperCase() || "?"}
      </div>
      <div className="flex flex-col min-w-0">
        <span className="typo-body-xs text-onSurface-default-primary truncate">
          {user?.name}
        </span>
        <span className="typo-caption-sm text-onSurface-default-tertiary truncate">
          {user?.email}
        </span>
      </div>
    </button>
  </DropdownMenuTrigger>
  <DropdownMenuContent
    align="start"
    side="top"
    className="w-56 font-fustat bg-surface-default-primary border-memBorder-secondary"
  >
    <div className="px-2 py-1.5">
      <p className="typo-body-sm text-onSurface-default-primary">
        {user?.name}
      </p>
      <p className="typo-body-xs text-onSurface-default-tertiary">
        {user?.email}
      </p>
    </div>
    <DropdownMenuSeparator className="bg-memBorder-primary" />
    <DropdownMenuItem
      asChild
      className="typo-body-sm text-onSurface-default-primary hover:bg-surface-default-tertiary-hover focus:bg-surface-default-tertiary-hover cursor-pointer"
    >
      <Link href="/dashboard/settings">
        <Settings className="size-4 mr-2" />
        Settings
      </Link>
    </DropdownMenuItem>
    <DropdownMenuSeparator className="bg-memBorder-primary" />
    <DropdownMenuItem
      onClick={logout}
      className="typo-body-sm text-onSurface-default-primary hover:bg-surface-default-tertiary-hover focus:bg-surface-default-tertiary-hover cursor-pointer"
    >
      <LogOut className="size-4 mr-2" />
      Log out
    </DropdownMenuItem>
  </DropdownMenuContent>
</DropdownMenu>

Sources: server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx:82-130

Memories Inspection and Deletion

Data Table Rendering and Pagination

The MemoriesPage component retrieves records from the backend API using useApiQuery, querying MEMORY_ENDPOINTS.BASE with an optional user_id query parameter and a maximum fetch limit (MEMORY_FETCH_LIMIT) set to 1000. The raw response extracts results via res.data?.results ?? res.data ?? []. When total records reach or exceed MEMORY_FETCH_LIMIT, an UpgradeBanner renders with a cloud upgrade CTA. Client-side pagination slices the fetched list using a fixed PAGE_SIZE of 20 records per page.

const PAGE_SIZE = 20;
// Keep in sync with ALL_MEMORIES_LIMIT in server/main.py.
const MEMORY_FETCH_LIMIT = 1000;
 
export default function MemoriesPage() {
  const [userId, setUserId] = useState("");
  const [selectedMemory, setSelectedMemory] = useState<Memory | null>(null);
  const [memoryToDelete, setMemoryToDelete] = useState<Memory | null>(null);
  const [page, setPage] = useState(0);
  const apiUrl = process.env.NEXT_PUBLIC_API_URL || "";
 
  const {
    data: memories = [],
    isLoading,
    refetch,
  } = useApiQuery<Memory[]>(
    async () => {
      const params = userId.trim()
        ? { user_id: userId.trim(), top_k: MEMORY_FETCH_LIMIT }
        : { top_k: MEMORY_FETCH_LIMIT };
      const res = await api.get(MEMORY_ENDPOINTS.BASE, { params });
      const raw = res.data?.results ?? res.data ?? [];
      return Array.isArray(raw) ? raw : [];
    },
    { errorToast: "Failed to load memories", initialData: [] },
  );
 
  const totalPages = Math.ceil(memories.length / PAGE_SIZE);
  const paginatedMemories = memories.slice(
    page * PAGE_SIZE,
    (page + 1) * PAGE_SIZE,
  );

Sources: server/dashboard/src/app/root/dashboard/memories/page.tsx:29-60

The table columns display specific memory attributes mapped from the Memory interface:

Column LabelKeyWidthRender Behavior
Contentmemory400Clamped to 2 lines (line-clamp-2 text-sm)
Useruser_id100Raw string rendering
Agentagent_id100Raw string rendering
Createdcreated_at120Formatted via date-fns (MMM d, yyyy) or --
const columns = [
  {
    key: "memory" as keyof Memory,
    label: "Content",
    width: 400,
    render: (value: string) => (
      <span className="line-clamp-2 text-sm">{value}</span>
    ),
  },
  { key: "user_id" as keyof Memory, label: "User", width: 100 },
  { key: "agent_id" as keyof Memory, label: "Agent", width: 100 },
  {
    key: "created_at" as keyof Memory,
    label: "Created",
    width: 120,
    render: (value: string) =>
      value ? format(new Date(value), "MMM d, yyyy") : "--",
  },
];

Sources: server/dashboard/src/app/root/dashboard/memories/page.tsx:79-97

Note

Pressing the Enter key while typing inside the User ID filter input resets the pagination index to 0 and triggers an immediate query refetch.

  <Input
    placeholder="Filter by User ID (optional)"
    value={userId}
    onChange={(e) => setUserId(e.target.value)}
    onKeyDown={(e) => {
      if (e.key === "Enter") {
        setPage(0);
        refetch();
      }
    }}
    className="w-64"
  />
</div>

Sources: server/dashboard/src/app/root/dashboard/memories/page.tsx:114-126

Inspection Sheet and Deletion Flow

Clicking any row in the DataTable assigns the selected Memory object to local state via onRowClick, activating the inspection Sheet component. The sheet displays metadata and details for the selected memory item while highlighting the active row with a background utility class (bg-surface-default-tertiary).

<Card className="border-memBorder-primary overflow-hidden">
  <DataTable
    data={paginatedMemories}
    columns={columns}
    getRowKey={(row) => row.id}
    onRowClick={(row) => setSelectedMemory(row)}
    getRowClassName={(row) =>
      selectedMemory?.id === row.id
        ? "bg-surface-default-tertiary"
        : undefined
    }
  />
</Card>

Sources: server/dashboard/src/app/root/dashboard/memories/page.tsx:152-164, server/dashboard/src/app/root/dashboard/memories/page.tsx:195-208

Deletion actions execute against MEMORY_ENDPOINTS.BY_ID(memoryToDelete.id) using api.delete. Success responses trigger a toast notification with a "success" variant, clear the inspection sheet if the deleted memory was currently open, reset memoryToDelete, and invoke refetch(). Errors are intercepted by getErrorMessage and displayed via a destructive toast.

const handleDelete = async () => {
  if (!memoryToDelete) return;
  try {
    await api.delete(MEMORY_ENDPOINTS.BY_ID(memoryToDelete.id));
    toast({ title: "Memory deleted", variant: "success" });
    if (selectedMemory?.id === memoryToDelete.id) setSelectedMemory(null);
    setMemoryToDelete(null);
    void refetch();
  } catch (error) {
    toast({
      title: "Failed to delete memory",
      description: getErrorMessage(error),
      variant: "destructive",
    });
  }
};

Sources: server/dashboard/src/app/root/dashboard/memories/page.tsx:62-77

Entity Exploration and Graph Cleanup

Entities List Presentation and Metadata

The EntitiesPage component queries ENTITY_ENDPOINTS.BASE via useApiQuery to fetch a collection of Entity records. While loading, it renders a TableSkeleton component with 5 rows and 5 columns. If the resulting entity array is empty, it displays an EmptyState component indicating that entities appear once memories are stored with a user_id, agent_id, or run_id. Otherwise, it wraps a DataTable component inside a styled Card element.

export default function EntitiesPage() {
  const [entityToDelete, setEntityToDelete] = useState<Entity | null>(null);
 
  const {
    data: entities = [],
    isLoading,
    refetch,
  } = useApiQuery<Entity[]>(
    async () => {
      const res = await api.get<Entity[]>(ENTITY_ENDPOINTS.BASE);
      return res.data ?? [];
    },
    { errorToast: "Failed to load entities", initialData: [] },
  );

Sources: server/dashboard/src/app/root/dashboard/entities/page.tsx:20-33, server/dashboard/src/app/root/dashboard/entities/page.tsx:102-121

The table structures entity records using the following column schema:

Column LabelKeyWidthRender Behavior
Typetype100Rendered as a capitalized outline Badge
IDid280Monospace truncated text (font-mono text-sm truncate)
Memoriestotal_memories100Right-aligned numeric counter
Last Activeupdated_at140Formatted via date-fns (MMM d, yyyy) or --
Actionsid40Ghost icon button rendering a Trash2 trigger
const columns = [
  {
    key: "type" as keyof Entity,
    label: "Type",
    width: 100,
    render: (value: Entity["type"]) => (
      <Badge variant="outline" className="capitalize">
        {value}
      </Badge>
    ),
  },
  {
    key: "id" as keyof Entity,
    label: "ID",
    width: 280,
    render: (value: string) => (
      <span className="font-mono text-sm truncate">{value}</span>
    ),
  },
  {
    key: "total_memories" as keyof Entity,
    label: "Memories",
    width: 100,
    align: "right" as const,
  },
  {
    key: "updated_at" as keyof Entity,
    label: "Last Active",
    width: 140,
    render: (value: string | null) =>
      value ? format(new Date(value), "MMM d, yyyy") : "--",
  },
  {
    key: "id" as keyof Entity,
    label: "",
    width: 40,
    render: (_: string, row: Entity) => (
      <Button
        variant="ghost"
        size="icon"
        onClick={() => setEntityToDelete(row)}
        className="size-7"
      >
        <Trash2 className="size-3.5 text-onSurface-danger-primary" />
      </Button>
    ),
  },
];

Sources: server/dashboard/src/app/root/dashboard/entities/page.tsx:53-100

Note

The EntityType definition strictly restricts entity classification values to three types: "user", "agent", and "run".

export type EntityType = "user" | "agent" | "run";

Sources: server/dashboard/src/types/api.ts:36-36

Entity Deletion and Cleanup Flow

Clicking the trash icon button on any entity row sets that specific Entity object into the entityToDelete local state variable. This action opens the DeleteConfirmationModal component, which displays a warning description stating that all memories associated with the entity will be permanently removed.

  key: "id" as keyof Entity,
  label: "",
  width: 40,
  render: (_: string, row: Entity) => (
    <Button
      variant="ghost"
      size="icon"
      onClick={() => setEntityToDelete(row)}
      className="size-7"
    >
      <Trash2 className="size-3.5 text-onSurface-danger-primary" />
    </Button>
  ),
},

Sources: server/dashboard/src/app/root/dashboard/entities/page.tsx:86-99, server/dashboard/src/app/root/dashboard/entities/page.tsx:123-131

Confirming deletion triggers the handleDelete function, which issues an HTTP DELETE request to ENTITY_ENDPOINTS.BY_ID(entityToDelete.type, entityToDelete.id). The endpoint dynamically formats the URL path using encodeURIComponent(id) to handle identifiers safely. On success, a toast notification displays "Entity deleted" with a "success" variant, the deletion modal state is cleared, and refetch is invoked to update the table data.

const handleDelete = async () => {
  if (!entityToDelete) return;
  try {
    await api.delete(
      ENTITY_ENDPOINTS.BY_ID(entityToDelete.type, entityToDelete.id),
    );
    toast({ title: "Entity deleted", variant: "success" });
    setEntityToDelete(null);
    void refetch();
  } catch (error) {
    toast({
      title: "Failed to delete entity",
      description: getErrorMessage(error),
      variant: "destructive",
    });
  }
};

Sources: server/dashboard/src/app/root/dashboard/entities/page.tsx:35-51, server/dashboard/src/utils/api-endpoints.ts:30-34

Warning

If the deletion request fails, getErrorMessage(error) extracts the server error detail or fallback message, displaying it inside a destructive toast notification.

} catch (error) {
  toast({
    title: "Failed to delete entity",
    description: getErrorMessage(error),
    variant: "destructive",
  });
}

Sources: server/dashboard/src/app/root/dashboard/entities/page.tsx:44-50, server/dashboard/src/lib/error-message.ts:3-19

API Key Management and Dialogs

Table Listing and Column Configuration

The ApiKeysPage component renders a data table displaying existing API keys retrieved from the backend. The underlying data query utilizes useApiQuery targeting API_KEY_ENDPOINTS.BASE, falling back to an empty array if no keys are returned. When three or more keys exist, an UpgradeBanner element is displayed above the table layout.

const {
  data: keys = [],
  isLoading,
  refetch,
} = useApiQuery<ApiKey[]>(
  async () => {
    const res = await api.get<ApiKey[]>(API_KEY_ENDPOINTS.BASE);
    return res.data ?? [];
  },
  { errorToast: "Failed to load API keys", initialData: [] },
);

Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:37-47, server/dashboard/src/app/root/dashboard/api-keys/page.tsx:206-210

The table structures key records using the following column schema:

Column LabelKeyWidthRender Behavior
Labellabel150Plain text label
Keykey_prefix120Monospace code snippet rendering prefix with ellipsis (...)
Createdcreated_at120Formatted via date-fns (MMM d, yyyy)
Last Usedlast_used_at120Formatted via date-fns or rendered as "Never"
Actionsid40Ghost icon button rendering a Trash2 trigger to assign keyToRevoke
const columns = [
  { key: "label" as keyof ApiKey, label: "Label", width: 150 },
  {
    key: "key_prefix" as keyof ApiKey,
    label: "Key",
    width: 120,
    render: (value: string) => (
      <code className="text-xs font-mono">{value}...</code>
    ),
  },
  {
    key: "created_at" as keyof ApiKey,
    label: "Created",
    width: 120,
    render: (value: string) => format(new Date(value), "MMM d, yyyy"),
  },
  {
    key: "last_used_at" as keyof ApiKey,
    label: "Last Used",
    width: 120,
    render: (value: string | null) =>
      value ? format(new Date(value), "MMM d, yyyy") : "Never",
  },
  {
    key: "id" as keyof ApiKey,
    label: "",
    width: 40,
    render: (_: string, row: ApiKey) => (
      <Button
        variant="ghost"
        size="icon"
        onClick={() => setKeyToRevoke(row)}
        className="size-7"
      >
        <Trash2 className="size-3.5 text-onSurface-danger-primary" />
      </Button>
    ),
  },
];

Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:90-128

Note

The ApiKey TypeScript interface defines properties for id, label, key_prefix, created_at, and an optional last_used_at timestamp.

export interface ApiKey {
  id: string;
  label: string;
  key_prefix: string;
  created_at: string;
  last_used_at: string | null;
}

Sources: server/dashboard/src/types/api.ts:10-16

Creation Dialog Modals and Clipboard Copying

Clicking the "Create Key" button opens a Radix-backed Dialog component controlled by createOpen state. The dialog content transitions between two distinct states based on whether newKey has been populated by a successful creation response.

return (
  <div className="space-y-4">
    <div className="flex items-center justify-between">
      <h1 className="text-xl font-semibold font-fustat">API Keys</h1>
      <Dialog open={createOpen} onOpenChange={handleDialogClose}>
        <DialogTrigger asChild>
          <Button size="sm">
            <Plus className="size-4 mr-1" /> Create Key
          </Button>
        </DialogTrigger>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Create API Key</DialogTitle>
          </DialogHeader>

Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:130-143

When newKey is empty, the modal presents a form input for the newLabel string and a disabled-until-typed "Create" action button. Executing handleCreate posts to API_KEY_ENDPOINTS.BASE, assigns the returned plain-text secret to newKey, and triggers a table refetch. Once generated, the view swaps to display the raw key inside a read-only input paired with a CopyToClipboard component that temporarily toggles check icon feedback.

const handleCreate = async () => {
  try {
    const res = await api.post<ApiKeyCreateResponse>(API_KEY_ENDPOINTS.BASE, {
      label: newLabel,
    });
    setNewKey(res.data.key);
    void refetch();
  } catch (error) {
    toast({
      title: "Failed to create key",
      description: getErrorMessage(error),
      variant: "destructive",
    });
  }
};

Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:49-63, server/dashboard/src/app/root/dashboard/api-keys/page.tsx:144-201

Warning

Closing the creation dialog via handleDialogClose completely resets newKey, newLabel, and copy states back to their defaults.

const handleDialogClose = (open: boolean) => {
  if (!open) {
    setNewKey("");
    setNewLabel("");
    setCopied(false);
  }
  setCreateOpen(open);
};

Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:81-88

Revocation and Toast Feedback Handling

Revoking an API key is initiated by clicking the trash action icon on a table row, which stores the target entity in keyToRevoke. Triggering handleRevoke issues an HTTP DELETE request to API_KEY_ENDPOINTS.BY_ID(keyToRevoke.id), clears the revocation target, triggers a data refetch, and fires a success toast.

const handleRevoke = async () => {
  if (!keyToRevoke) return;
  try {
    await api.delete(API_KEY_ENDPOINTS.BY_ID(keyToRevoke.id));
    toast({ title: "API key revoked", variant: "success" });
    setKeyToRevoke(null);
    void refetch();
  } catch (error) {
    toast({
      title: "Failed to revoke key",
      description: getErrorMessage(error),
      variant: "destructive",
    });
  }
};

Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:65-79, server/dashboard/src/utils/api-endpoints.ts:21-24

The dashboard error handling architecture maps exception responses across operations using specific utility functions. The following sequence traces execution paths for dialog rendering, error serialization, and toast notification delivery:

  1. ApiKeysPage → DialogContent → DialogOverlay: Renders modal dialog layers with fixed positioning and background overlay styling. Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:140-140, server/dashboard/src/components/ui/dialog.tsx:17-39
  2. ApiKeysPage → useApiQuery → run → getErrorMessage: Intercepts query failures, extracts axios error details, and formats fallback strings. Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:37-47, server/dashboard/src/hooks/use-api-query.ts:31-38, server/dashboard/src/lib/error-message.ts:3-16
  3. ApiKeysPage → useApiQuery → run → toast: Dispatches formatted error notifications through the Sonner toast wrapper interface. Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:37-47, server/dashboard/src/hooks/use-api-query.ts:39-45, server/dashboard/src/components/ui/use-toast.ts:16-35

Sources: server/dashboard/src/app/root/dashboard/api-keys/page.tsx:37-47, server/dashboard/src/app/root/dashboard/api-keys/page.tsx:140-140, server/dashboard/src/components/ui/dialog.tsx:17-39, server/dashboard/src/hooks/use-api-query.ts:31-45, server/dashboard/src/lib/error-message.ts:3-16, server/dashboard/src/components/ui/use-toast.ts:16-35

Configuration and System Settings

Provider Configuration Forms

The server configuration view provides administrative forms for managing LLM and embedding providers alongside user profile and credential controls.

The configuration page (ConfigurationPage) loads effective server settings from MEMORY_ENDPOINTS.CONFIGURE via useApiQuery and fetches supported provider lists from MEMORY_ENDPOINTS.CONFIGURE_PROVIDERS. The form manages provider identifiers, model names, and API keys for both LLM and embedding backends, restricting editing capabilities to users with administrator privileges (isAdmin). When saving, provider parameters are packaged into a versioned payload (version: "v1.1") and posted back to the configure endpoint.

type BundledProviders = {
  llm: string[];
  embedder: string[];
};

Sources: server/dashboard/src/app/root/dashboard/configuration/page.tsx:27-30, server/dashboard/src/app/root/dashboard/configuration/page.tsx:41-57, server/dashboard/src/app/root/dashboard/configuration/page.tsx:71-98

Important

Selecting a new LLM provider automatically resets the associated API key state to an empty string to prevent credential leaks across provider boundaries. Sources: server/dashboard/src/app/root/dashboard/configuration/page.tsx:131-134

User Profile and Password Mutations

The settings view (SettingsPage) manages user account details and password rotation through authenticated endpoints. Profile updates are submitted via AUTH_ENDPOINTS.ME using HTTP PATCH requests when input values differ from existing user state and contain non-whitespace characters. Password changes require verification of the current password, matching confirmation values, and a minimum length of 8 characters for the new password before submission to AUTH_ENDPOINTS.CHANGE_PASSWORD.

Endpoint ConstantPathMethodPurpose
AUTH_ENDPOINTS.ME/auth/mePATCHUpdates authenticated user name and email attributes
AUTH_ENDPOINTS.CHANGE_PASSWORD/auth/change-passwordPOSTRotates the user account password given current credentials
MEMORY_ENDPOINTS.CONFIGURE/configureGET / POSTRetrieves or persists server LLM and embedding provider settings
MEMORY_ENDPOINTS.CONFIGURE_PROVIDERS/configure/providersGETFetches lists of bundled LLM and embedder providers

Sources: server/dashboard/src/app/root/dashboard/configuration/page.tsx:43-52, server/dashboard/src/app/root/dashboard/configuration/page.tsx:97-97, server/dashboard/src/app/root/dashboard/settings/page.tsx:43-46, server/dashboard/src/app/root/dashboard/settings/page.tsx:71-74, server/dashboard/src/utils/api-endpoints.ts:6-7, server/dashboard/src/utils/api-endpoints.ts:15-16

Requests Audit Telemetry

Telemetry Data Fetching and Metrics

The requests audit log view retrieves access logs from the self-hosted instance through REQUEST_ENDPOINTS.BASE (/requests) with a maximum query limit defined by REQUEST_LOG_LIMIT (200 records). Incoming ApiRequestLog entities are mapped via normalizeLog into structured RequestLog objects comprising unique identifiers, creation timestamps, HTTP methods, paths, status codes, latency measurements in milliseconds, and authentication source types.

type RequestLog = {
  id: string;
  createdAt: string;
  method: string;
  path: string;
  statusCode: number;
  latencyMs: number;
  authType: string;
};

Sources: server/dashboard/src/app/root/dashboard/requests/page.tsx:17-27, server/dashboard/src/app/root/dashboard/requests/page.tsx:71-81, server/dashboard/src/utils/api-endpoints.ts:26-28

Client-side calculations derive summary metrics from fetched logs including total request counts, successful requests (statusCode < 400), overall success percentage rates, and average latency values. Pagination divides the data stream into chunks governed by PAGE_SIZE (20 items per view). Sources: server/dashboard/src/app/root/dashboard/requests/page.tsx:28-28, server/dashboard/src/app/root/dashboard/requests/page.tsx:103-114

Note

Resetting the pagination page index to zero (setPage(0)) occurs automatically whenever the manual refresh button is triggered alongside query refetching. Sources: server/dashboard/src/app/root/dashboard/requests/page.tsx:188-192

Method and Status Tag Styling

Badge presentation utilizes distinct color styles based on HTTP request verbs and numerical response status codes. The getMethodClassName helper inspects normalized method strings case-insensitively, assigning sky-blue styles to POST, amber tones to PUT and PATCH, rose styling to DELETE, and fallback primary borders to unrecognized verbs.

HTTP MethodBadge Theme Styling
POSTSky blue (border-sky-200 bg-sky-50 text-sky-700)
PUT, PATCHAmber (border-amber-200 bg-amber-50 text-amber-700)
DELETERose (border-rose-200 bg-rose-50 text-rose-700)
Other / DefaultSurface secondary (border-memBorder-primary bg-surface-default-secondary)

Sources: server/dashboard/src/app/root/dashboard/requests/page.tsx:42-54

Similarly, getStatusClassName evaluates numeric HTTP response codes to apply error-severity styling: status codes of 500 and above render in rose tones, codes from 400 to 499 apply amber styling, and successful codes below 400 render with emerald green borders and background tints. Sources: server/dashboard/src/app/root/dashboard/requests/page.tsx:30-40

Authentication Source Inspection

The audit table inspects the authType property on incoming request entries to identify the verification mechanism used by the client. The getAuthLabel utility maps internal authentication identifiers to human-readable labels for presentation in the telemetry data grid.

Raw Auth Type ValueDisplay Label
bearerJWT
api_keyAPI Key
admin_api_keyAdmin Key
disabledDisabled
Any other value--

Sources: server/dashboard/src/app/root/dashboard/requests/page.tsx:56-69

Locked Features and Commercial Mockups

Analytics Feature Wrapper

The self-hosted dashboard presents usage insights behind commercial wrappers via the LockedPage component. The analytics view specifies utmMedium="dashboard-locked-analytics", rendering a description ("Track memory operations, latency, and usage patterns over time.") above an interactive visual mockup.

export default function AnalyticsPage() {
  return (
    <LockedPage
      title="Analytics"
      description="Track memory operations, latency, and usage patterns over time."
      previewContent={<AnalyticsMockup />}
      utmMedium="dashboard-locked-analytics"
    />
  );
}

Sources: server/dashboard/src/app/root/dashboard/analytics/page.tsx:45-54

The AnalyticsMockup layout organizes metrics into two primary structural blocks: a top metric summary grid displaying statistical cards, followed by a time-series operations chart container.

Stat LabelMock ValueCard Container Style
Total Operations"12,847"border-memBorder-primary
Avg Latency"142ms"border-memBorder-primary
Success Rate"99.7%"border-memBorder-primary

Sources: server/dashboard/src/app/root/dashboard/analytics/page.tsx:9-24

The operations chart renders a 200-pixel-tall flex container mapping twelve discrete height percentages ([40, 65, 45, 80, 55, 90, 70, 85, 60, 95, 75, 50]) to individual column elements styled with bg-surface-default-brand rounded-t. Sources: server/dashboard/src/app/root/dashboard/analytics/page.tsx:30-39

Note

All interactive inputs, buttons, and form controls within locked feature mockups are explicitly disabled (disabled) to indicate that the underlying feature requires a commercial license upgrade. Sources: server/dashboard/src/app/root/dashboard/webhooks/page.tsx:16-38, server/dashboard/src/app/root/dashboard/export/page.tsx:21-49

Webhooks and Export Mockup Configurations

The webhooks locked view uses utmMedium="dashboard-locked-webhooks" and presents a card containing an endpoint URL input field, a disabled checkbox list of event types, a masked signing secret field, and a submit button.

Event NameCheckbox Initial State
memory.createdChecked (true, disabled)
memory.updatedUnchecked (false, disabled)
memory.deletedUnchecked (false, disabled)
search.performedUnchecked (false, disabled)

Sources: server/dashboard/src/app/root/dashboard/webhooks/page.tsx:21-31

Similarly, the export locked view sets utmMedium="dashboard-locked-export" and exposes format options alongside a preview schema output box.

{
  "memories": [
    {
      "id": "mem_abc123",
      "content": "User prefers dark mode",
      "user_id": "user_1",
      "created_at": "2026-04-10T12:00:00Z"
    }
  ],
  "total": 1247
}

Sources: server/dashboard/src/app/root/dashboard/export/page.tsx:34-44

The export format selector lists three format buttons (JSON, CSV, and Pydantic Schema), where the JSON button receives an active border highlight class (border-memPurple-300). Sources: server/dashboard/src/app/root/dashboard/export/page.tsx:16-26

Was this page helpful?