Relevant source files
The following files were used as context for generating this wiki page:
- server/dashboard/src/app/root/dashboard/memories/page.tsx
- server/dashboard/src/app/setup/page.tsx
- server/dashboard/src/app/root/dashboard/api-keys/page.tsx
- server/dashboard/src/app/root/dashboard/entities/page.tsx
- server/dashboard/src/app/root/dashboard/configuration/page.tsx
- server/dashboard/src/app/root/dashboard/requests/page.tsx
- integrations/opencode-plugin/opencode-mem0.ts
- server/dashboard/src/app/root/dashboard/components/main-nav.tsx
- mem0-ts/src/oss/src/memory/index.ts
- server/dashboard/src/app/root/dashboard/settings/page.tsx
- server/main.py
- integrations/n8n-nodes-mem0/nodes/Mem0/Mem0.node.ts
- server/dashboard/src/app/root/dashboard/analytics/page.tsx
- server/routers/entities.py
- integrations/antigravity-plugin/core/memory_core.py
- mem0/memory/main.py
- integrations/agent-plugin-core/python/memory_core.py
- server/dashboard/src/app/root/dashboard/components/nav-wrapper.tsx
- cli/node/src/commands/memory.ts
- server/dashboard/src/app/root/dashboard/webhooks/page.tsx
- cli/node/src/commands/entities.ts
- server/dashboard/src/utils/api-endpoints.ts
- server/dashboard/src/app/root/dashboard/export/page.tsx
- server/dashboard/src/types/api.ts
- mem0/client/main.py
- cli/node/src/backend/platform.ts
- server/dashboard/src/components/ui/dialog.tsx
- server/dashboard/src/components/ui/use-toast.ts
- server/dashboard/src/hooks/use-api-query.ts
- server/dashboard/src/lib/error-message.ts
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
Navigation and Shell Layout
Sidebar and Shell Structure
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
Navigation Item Routing and Categories
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.
| Section | Title | URL | Icon |
|---|---|---|---|
| ACTIVITY | Requests | /dashboard/requests | Activity |
| ACTIVITY | Memories | /dashboard/memories | GalleryVerticalEnd |
| ACTIVITY | Entities | /dashboard/entities | Users |
| CLOUD FEATURES | Categories | /dashboard/categories | Tags |
| CLOUD FEATURES | Webhooks | /dashboard/webhooks | WebhookIcon |
| CLOUD FEATURES | Analytics | /dashboard/analytics | ChartLine |
| CLOUD FEATURES | Export | /dashboard/export | FolderInput |
| ACCOUNT | API Keys | /dashboard/api-keys | KeyRound |
| ACCOUNT | Configuration | /dashboard/configuration | Wrench |
| ACCOUNT | Settings | /dashboard/settings | Settings |
{[
{
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 Label | Key | Width | Render Behavior |
|---|---|---|---|
| Content | memory | 400 | Clamped to 2 lines (line-clamp-2 text-sm) |
| User | user_id | 100 | Raw string rendering |
| Agent | agent_id | 100 | Raw string rendering |
| Created | created_at | 120 | Formatted 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 Label | Key | Width | Render Behavior |
|---|---|---|---|
| Type | type | 100 | Rendered as a capitalized outline Badge |
| ID | id | 280 | Monospace truncated text (font-mono text-sm truncate) |
| Memories | total_memories | 100 | Right-aligned numeric counter |
| Last Active | updated_at | 140 | Formatted via date-fns (MMM d, yyyy) or -- |
| Actions | id | 40 | Ghost 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 Label | Key | Width | Render Behavior |
|---|---|---|---|
| Label | label | 150 | Plain text label |
| Key | key_prefix | 120 | Monospace code snippet rendering prefix with ellipsis (...) |
| Created | created_at | 120 | Formatted via date-fns (MMM d, yyyy) |
| Last Used | last_used_at | 120 | Formatted via date-fns or rendered as "Never" |
| Actions | id | 40 | Ghost 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:
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-39ApiKeysPage→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-16ApiKeysPage→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 Constant | Path | Method | Purpose |
|---|---|---|---|
AUTH_ENDPOINTS.ME | /auth/me | PATCH | Updates authenticated user name and email attributes |
AUTH_ENDPOINTS.CHANGE_PASSWORD | /auth/change-password | POST | Rotates the user account password given current credentials |
MEMORY_ENDPOINTS.CONFIGURE | /configure | GET / POST | Retrieves or persists server LLM and embedding provider settings |
MEMORY_ENDPOINTS.CONFIGURE_PROVIDERS | /configure/providers | GET | Fetches 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 Method | Badge Theme Styling |
|---|---|
POST | Sky blue (border-sky-200 bg-sky-50 text-sky-700) |
PUT, PATCH | Amber (border-amber-200 bg-amber-50 text-amber-700) |
DELETE | Rose (border-rose-200 bg-rose-50 text-rose-700) |
| Other / Default | Surface 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 Value | Display Label |
|---|---|
bearer | JWT |
api_key | API Key |
admin_api_key | Admin Key |
disabled | Disabled |
| 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 Label | Mock Value | Card 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 Name | Checkbox Initial State |
|---|---|
memory.created | Checked (true, disabled) |
memory.updated | Unchecked (false, disabled) |
memory.deleted | Unchecked (false, disabled) |
search.performed | Unchecked (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
Related
On This Page
OverviewNavigation and Shell LayoutSidebar and Shell StructureNavigation Item Routing and CategoriesUser Profile and Dropdown MenuMemories Inspection and DeletionData Table Rendering and PaginationInspection Sheet and Deletion FlowEntity Exploration and Graph CleanupEntities List Presentation and MetadataEntity Deletion and Cleanup FlowAPI Key Management and DialogsTable Listing and Column ConfigurationCreation Dialog Modals and Clipboard CopyingRevocation and Toast Feedback HandlingConfiguration and System SettingsProvider Configuration FormsUser Profile and Password MutationsRequests Audit TelemetryTelemetry Data Fetching and MetricsMethod and Status Tag StylingAuthentication Source InspectionLocked Features and Commercial MockupsAnalytics Feature WrapperWebhooks and Export Mockup ConfigurationsRelated