-
VXLAN — L2-over-L3 оверлей для RouterOS 7.x
+
VXLAN — L2-over-L3 оверлей для RouterOS 7.x
VXLAN переносит L2 через IP-сеть. VNI определяет сегмент; шифрование зависит от настроенного транспорта.
diff --git a/backend/scripts/acceptance/ui-readonly-audit.mjs b/backend/scripts/acceptance/ui-readonly-audit.mjs
new file mode 100644
index 00000000..b4265d26
--- /dev/null
+++ b/backend/scripts/acceptance/ui-readonly-audit.mjs
@@ -0,0 +1,137 @@
+import fs from 'node:fs';
+import assert from 'node:assert/strict';
+import { chromium, expect } from '@playwright/test';
+import axe from 'axe-core';
+
+// Deliberately read-only. The fixture stays outside Git; no response fixtures are used.
+const credentials = JSON.parse(fs.readFileSync(process.env.MMAPP_PORTAL_FIXTURE || 'tmp/portal195-private.json'));
+const output = process.env.MMAPP_UI_AUDIT_OUTPUT || 'tmp/ui260-audit.json';
+assert(!fs.existsSync(output), 'Never overwrite an acceptance report');
+const origin = 'http://localhost:3000';
+const routes = (process.env.MMAPP_UI_AUDIT_ROUTES || '/dashboard,/servers,/uptime,/traffic,/statistics,/wireguard,/ipsec,/gre,/vxlan,/ospf,/bgp,/filters,/backups,/certificates,/containers,/alerts,/settings,/releases').split(',');
+assert(routes.every(path => /^\/[a-z-]+$/.test(path)));
+const requiredReads = {
+ '/dashboard': ['/api/servers', '/api/uptime/overview', '/api/filters/rules', '/api/bgp/catalog', '/api/internet-path/latest', '/api/filters/gre-tunnels', '/api/wireguard', '/api/traffic/servers', '/api/vxlan'],
+ '/servers': ['/api/servers'], '/uptime': ['/api/uptime/overview'], '/traffic': ['/api/traffic/servers'],
+ '/statistics': ['/api/statistics'], '/wireguard': ['/api/wireguard'], '/ipsec': ['/api/ipsec'],
+ '/gre': ['/api/gre/tunnels'], '/vxlan': ['/api/vxlan'], '/ospf': ['/api/ospf/catalog'],
+ '/bgp': ['/api/bgp/catalog'], '/filters': ['/api/filters/rules'], '/backups': ['/api/backups'],
+ '/certificates': ['/api/certificates/state'], '/containers': ['/api/containers/state'], '/alerts': ['/api/alerts'],
+ '/users': ['/api/users'], '/firewall': ['/api/firewall/all'], '/probes': ['/api/servers'], '/terminal': ['/api/servers'],
+ '/data-collection': ['/api/scheduler/status'], '/recursive-routes': ['/api/servers'],
+ '/route-optimizer': ['/api/servers', '/api/filters/rules', '/api/uptime/speed-probes'],
+ '/network-map': ['/api/servers', '/api/filters/gre-tunnels', '/api/uptime/speed-probes'],
+ '/ip-ranges': ['/api/evobgp/settings'], '/asns': ['/api/evobgp/settings'], '/communities': ['/api/evobgp/settings'], '/domains': ['/api/evobgp/settings'],
+};
+const report = {
+ date: '2026-10-15', block: process.env.MMAPP_UI_AUDIT_BLOCK || '12.5.260', origin, realPortalUiLogin: true,
+ responseMocks: false, deviceWritesPermitted: false, cases: [], blockedWrites: [], readOnlyPosts: [],
+ bounds: ['Default rendered state only; data can still be refreshing', 'Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals', 'Automated WCAG-tagged checks, not manual screen-reader certification'],
+};
+const browser = await chromium.launch({ channel: 'msedge', headless: true });
+const tags = ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'];
+try {
+ for (const theme of ['dark', 'light']) {
+ const context = await browser.newContext();
+ await context.addInitScript(value => {
+ localStorage.setItem('rl-theme', value);
+ window.__acceptanceMetrics = { lcp: null, cls: 0, longTasks: 0, longTaskMs: 0 };
+ for (const type of ['largest-contentful-paint', 'layout-shift', 'longtask']) {
+ try {
+ new PerformanceObserver(list => {
+ for (const e of list.getEntries()) {
+ const m = window.__acceptanceMetrics;
+ if (type === 'largest-contentful-paint') m.lcp = e.startTime;
+ if (type === 'layout-shift' && !e.hadRecentInput) m.cls += e.value;
+ if (type === 'longtask') { m.longTasks++; m.longTaskMs += e.duration; }
+ }
+ }).observe({ type, buffered: true });
+ } catch { /* Browser may not expose every observer. */ }
+ }
+ }, theme);
+ const page = await context.newPage();
+ await page.goto(origin + '/dashboard');
+ await page.getByLabel('Email', { exact: true }).fill(credentials.email);
+ await page.getByLabel('Пароль', { exact: true }).fill(credentials.password);
+ await page.getByRole('button', { name: 'Войти', exact: true }).click();
+ await expect(page.getByRole('navigation', { name: 'Путь страницы' })).toBeVisible({ timeout: 30000 });
+ await page.route('**/api/**', async route => {
+ const r = route.request();
+ if (!['GET', 'HEAD', 'OPTIONS'].includes(r.method())) {
+ const path = new URL(r.url()).pathname;
+ // These POST handlers were inspected: DNS resolution, remote GET catalog,
+ // and a computed OSPF preview. None applies device configuration.
+ const readQuery = r.method() === 'POST' && (path === '/api/network/resolve-hosts' || path === '/api/evobgp/catalog' || /^\/api\/servers\/\d+\/ospf\/optimize\/preview$/.test(path));
+ if (process.env.MMAPP_UI_AUDIT_ALLOW_READ_POSTS === 'true' && readQuery) {
+ report.readOnlyPosts.push({ theme, path });
+ await route.continue();
+ return;
+ }
+ report.blockedWrites.push({ theme, method: r.method(), path: new URL(r.url()).pathname });
+ await route.abort('blockedbyclient');
+ } else await route.continue();
+ });
+ for (const path of routes) for (const width of [1440, 320]) {
+ const item = { theme, path, width, pageErrors: [], apiErrors: [] };
+ const successfulReads = new Set();
+ const onError = error => item.pageErrors.push(error.name);
+ const onResponse = response => {
+ const u = new URL(response.url());
+ if (u.origin === origin && u.pathname.startsWith('/api/') && response.status() >= 400) item.apiErrors.push({ path: u.pathname, status: response.status() });
+ if (u.origin === origin && response.status() === 200 && response.request().method() === 'GET') successfulReads.add(u.pathname);
+ };
+ page.on('pageerror', onError); page.on('response', onResponse);
+ try {
+ await page.setViewportSize({ width, height: 1000 });
+ const start = Date.now();
+ await page.goto(origin + path, { waitUntil: 'domcontentloaded', timeout: 35000 });
+ await expect(page.locator('#main-content')).toBeVisible({ timeout: 30000 });
+ await expect(page.getByRole('navigation', { name: 'Путь страницы' })).toBeVisible();
+ item.mainVisibleMs = Date.now() - start;
+ if (requiredReads[path]) {
+ await expect.poll(() => requiredReads[path].every(endpoint => successfulReads.has(endpoint)), { timeout: 35000 }).toBe(true);
+ item.requiredReads = requiredReads[path];
+ item.dataResponsesMs = Date.now() - start;
+ }
+ await page.evaluate(async () => {
+ await document.fonts.ready;
+ await new Promise(resolve => setTimeout(resolve, 1200));
+ await Promise.race([
+ Promise.allSettled(document.getAnimations().filter(a => a.effect?.getComputedTiming().iterations !== Infinity).map(a => a.finished)),
+ new Promise(resolve => setTimeout(resolve, 1500)),
+ ]);
+ });
+ item.layout = await page.evaluate(() => ({
+ overflow: document.documentElement.scrollWidth > innerWidth + 1,
+ mainOverflow: document.querySelector('#main-content').scrollWidth > document.querySelector('#main-content').clientWidth + 1,
+ theme: document.documentElement.classList.contains('dark') ? 'dark' : 'light',
+ pendingSkeletons: document.querySelectorAll('#main-content [data-slot="skeleton"]').length,
+ }));
+ assert.equal(item.layout.theme, theme);
+ item.performance = await page.evaluate(() => {
+ const n = performance.getEntriesByType('navigation')[0];
+ return { ...window.__acceptanceMetrics, ttfbMs: n.responseStart - n.requestStart, domInteractiveMs: n.domInteractive, durationMs: n.duration };
+ });
+ await page.addScriptTag({ content: axe.source });
+ item.violations = await page.evaluate(async values => {
+ const result = await window.axe.run({ include: ['#main-content', '[aria-label="Основная навигация"]'] }, { runOnly: { type: 'tag', values } });
+ return result.violations.map(v => ({ id: v.id, impact: v.impact, targets: v.nodes.map(n => n.target) }));
+ }, tags);
+ item.auditCompleted = true;
+ } catch (error) { item.failure = { name: error.name, message: error.message.slice(0, 250) }; }
+ finally {
+ page.off('pageerror', onError); page.off('response', onResponse);
+ report.cases.push(item);
+ fs.writeFileSync(output, JSON.stringify(report, null, 2) + '\n');
+ console.log(JSON.stringify({ theme, path, width, violations: item.violations?.map(v => v.id), overflow: item.layout?.overflow, failure: item.failure?.name }));
+ }
+ }
+ await context.close();
+ }
+ report.completed = true;
+ report.passed = report.blockedWrites.length === 0 && report.cases.every(c => c.auditCompleted && !c.layout.overflow && !c.layout.mainOverflow && c.violations.length === 0 && c.pageErrors.length === 0 && c.apiErrors.length === 0);
+ if (!report.passed) process.exitCode = 1;
+} finally {
+ fs.writeFileSync(output, JSON.stringify(report, null, 2) + '\n');
+ await browser.close();
+}
diff --git a/components/data-grids/servers-data-grid.tsx b/components/data-grids/servers-data-grid.tsx
index edc80d42..59d67766 100644
--- a/components/data-grids/servers-data-grid.tsx
+++ b/components/data-grids/servers-data-grid.tsx
@@ -7,6 +7,7 @@ import {
} from "@tanstack/react-table"
import type { Server, ServerType } from "@/lib/data"
import { StatusBadge } from "@/components/status-badge"
+import { Badge, type BadgeProps } from "@/components/reui/badge"
import { Flag } from "@/components/flag"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
@@ -49,10 +50,10 @@ const TYPE_LABELS: Record
= {
"home-router": "Home Router",
}
-const TYPE_STYLES: Record = {
- "jump-host": "bg-violet-500/10 text-violet-400 border-violet-500/20",
- "exit-node": "bg-sky-500/10 text-sky-400 border-sky-500/20",
- "home-router": "bg-emerald-500/10 text-emerald-400 border-emerald-500/20",
+const TYPE_VARIANTS: Record = {
+ "jump-host": "primary-light",
+ "exit-node": "info-light",
+ "home-router": "success-light",
}
function rosVer(os: string): number {
@@ -63,13 +64,13 @@ function rosVer(os: string): number {
function TypeBadge({ type }: { type: ServerType }) {
const icon =
- type === "jump-host" ?
- : type === "exit-node" ?
- :
+ type === "jump-host" ?
+ : type === "exit-node" ?
+ :
return (
-
+
{icon}{TYPE_LABELS[type]}
-
+
)
}
diff --git a/components/form-kit/segmented-control.tsx b/components/form-kit/segmented-control.tsx
index b47e159a..cf0ce5d8 100644
--- a/components/form-kit/segmented-control.tsx
+++ b/components/form-kit/segmented-control.tsx
@@ -37,7 +37,7 @@ function SegmentedControl({
>
{option.label}
{option.count != null && (
- {option.count}
+ {option.count}
)}
))}
diff --git a/components/server-tile-rail.tsx b/components/server-tile-rail.tsx
index 7e5fde08..1c07b816 100644
--- a/components/server-tile-rail.tsx
+++ b/components/server-tile-rail.tsx
@@ -35,6 +35,10 @@ import { LayersIcon, SearchIcon, ServerIcon } from "lucide-react"
/** Preview: https://reui.io/preview/base/list-9 · https://reui.io/docs/components/base/icon-tile · https://reui.io/docs/components/base/frame · https://reui.io/docs/components/base/badge */
export const ALL_SERVERS_ID = "all"
+// Device URL changes can remount the page. Keep only the current keyboard intent,
+// and never restore it after the user has focused another control.
+let pendingKeyboardFocus: { path: string; id: string; source: Element | null; until: number } | null = null
+
export interface ServerTileItem {
id: string
name: string
@@ -185,7 +189,15 @@ function ServerTileRail({
useEffect(() => {
const viewport = listRef.current?.closest('[data-slot="scroll-area-viewport"]')
viewport?.scrollTo({ top: 0 })
- listRef.current?.querySelector('[aria-selected="true"]')?.scrollIntoView({ block: "nearest" })
+ const selected = listRef.current?.querySelector('[aria-selected="true"]')
+ selected?.scrollIntoView({ block: "nearest" })
+ const pending = pendingKeyboardFocus
+ if (pending && (pending.until < performance.now() || pending.path !== window.location.pathname)) {
+ pendingKeyboardFocus = null
+ } else if (pending && pending.id === resolvedId && selected?.getClientRects().length) {
+ if (document.activeElement === document.body || document.activeElement === pending.source) selected.focus({ preventScroll: true })
+ pendingKeyboardFocus = null
+ }
}, [pageIndex, resolvedId, q, pageSize])
const visibleIds = new Set(table.getRowModel().rows.map(row => row.id))
const visibleGroups = groups.map(group => ({ ...group, items: group.items.filter(item => visibleIds.has(item.id)) })).filter(group => group.items.length > 0)
@@ -200,6 +212,19 @@ function ServerTileRail({
return ids
}, [filtered, showAllTile])
+ const tabStopId = activeId && selectableIds.includes(activeId)
+ ? activeId
+ : showAllTile ? ALL_SERVERS_ID : table.getRowModel().rows.find(row => isSelectable(row.original))?.id
+
+ function selectWithKeyboard(id: string) {
+ if (id === resolvedId) {
+ listRef.current?.querySelector('[aria-selected="true"]')?.focus()
+ return
+ }
+ pendingKeyboardFocus = { path: window.location.pathname, id, source: document.activeElement, until: performance.now() + 10000 }
+ onSelect(id)
+ }
+
function moveSelection(delta: number) {
if (selectableIds.length === 0) return
const idx = selectableIds.indexOf(resolvedId)
@@ -208,7 +233,7 @@ function ServerTileRail({
? delta > 0 ? 0 : selectableIds.length - 1
: Math.min(selectableIds.length - 1, Math.max(0, idx + delta))
const nextId = selectableIds[nextIdx]
- if (nextId) onSelect(nextId)
+ if (nextId) selectWithKeyboard(nextId)
}
function handleListKeyDown(event: KeyboardEvent) {
@@ -225,13 +250,13 @@ function ServerTileRail({
}
if (event.key === "Home") {
event.preventDefault()
- if (selectableIds[0]) onSelect(selectableIds[0])
+ if (selectableIds[0]) selectWithKeyboard(selectableIds[0])
return
}
if (event.key === "End") {
event.preventDefault()
const last = selectableIds[selectableIds.length - 1]
- if (last) onSelect(last)
+ if (last) selectWithKeyboard(last)
}
}
@@ -283,8 +308,6 @@ function ServerTileRail({
className="flex flex-col gap-0.5 p-1.5"
role="listbox"
aria-label="Серверы"
- aria-activedescendant={activeId ? `${railId}-server-tile-${activeId}` : undefined}
- tabIndex={0}
onKeyDown={handleListKeyDown}
>
{showAllTile ? (
@@ -292,6 +315,7 @@ function ServerTileRail({
id={`${railId}-server-tile-${ALL_SERVERS_ID}`}
item={allItem}
selected={resolvedId === ALL_SERVERS_ID}
+ tabStop={tabStopId === ALL_SERVERS_ID}
onSelect={() => onSelect(ALL_SERVERS_ID)}
showCount={showCount}
showType={false}
@@ -315,6 +339,7 @@ function ServerTileRail({
id={`${railId}-server-tile-${item.id}`}
item={item}
selected={resolvedId === item.id}
+ tabStop={tabStopId === item.id}
onSelect={() => onSelect(item.id)}
showCount={showCount}
showType={showType}
@@ -354,6 +379,7 @@ function ServerTileButton({
id,
item,
selected,
+ tabStop,
onSelect,
showCount,
showType,
@@ -363,6 +389,7 @@ function ServerTileButton({
id: string
item: ServerTileItem
selected: boolean
+ tabStop: boolean
onSelect: () => void
showCount: boolean
showType: boolean
@@ -385,6 +412,7 @@ function ServerTileButton({
type="button"
role="option"
aria-selected={selected}
+ tabIndex={tabStop ? 0 : -1}
title={item.title ?? hostname}
disabled={!selectable}
onClick={onSelect}
diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md
index a508c84b..c7321332 100644
--- a/docs/ux-ui-external-acceptance.md
+++ b/docs/ux-ui-external-acceptance.md
@@ -1,6 +1,6 @@
# UX/UI: протокол внешней приёмки 12.5
-Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт.
+Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт.
IPsec78: реальный PSK responder established, mode-config198.18.78.10 и online=true подтверждены API; точные собственные объекты удалены. Сертификатный/DN online, late failure и history restore остаются открытыми. [Отчёт](ux-ui/acceptance/ipsec-mode-config-2026-10-09.json), [журнал](ux-ui-progress.md).
@@ -18,6 +18,27 @@ OSPF84: real GRE Full adjacency/private routes/Ping3/3 и4 live PC/320px URL/bro
BFD85: Up/Full/own route/Ping,4 live UI и loss/withdrawal/recovery приняты; exact own cleanup8 коллекций×2 passed. Withdrawal5,245ms не подтверждает sub-second BFD timing; этот вопрос открыт. Runtime routing83 без новой сборки. [Отчёт](ux-ui/acceptance/ospf-bfd-loss-2026-10-09.json).
+## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15)
+
+Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json).
+
+ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены.
+
+TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах.
+
+Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены.
+
+Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json).
+
+Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe.
+
+| Референс | Схема и экран |
+| --- | --- |
+| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) |
+| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) |
+
+Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными.
+
## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15)
По сохранённому разрешению254 выполнен тот же exact mmt253-8e62a941.pngs.top на104.165.244.7. Перед началом снят свежий приватный native/export/SSL/files/SQL baseline; own certificate/files/jobs/DNS0, Cloudflare token/zone/account проверены. Renewal временно выключен. Подготовленный backend258 активирован; frontend251/Portal/DB сохранены, backend healthy. [Развёртывание](ux-ui/acceptance/acme259-deployment.json).
diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md
index b35a8861..5cf697f8 100644
--- a/docs/ux-ui-plan.md
+++ b/docs/ux-ui-plan.md
@@ -1,8 +1,29 @@
# План реализации UX/UI по пятичасовым окнам
-Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт.
+Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт.
-Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-14. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
+Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-15. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
+
+## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15)
+
+Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json).
+
+ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены.
+
+TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах.
+
+Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены.
+
+Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json).
+
+Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe.
+
+| Референс | Схема и экран |
+| --- | --- |
+| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) |
+| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) |
+
+Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными.
## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15)
@@ -314,27 +335,27 @@ Production backend/frontend ux-backups237 собраны и развёрнуты
Следующий238 фиксирует уже выполненное длительное REST-наблюдение. Затем — S3 restore через реальный UI в пределах собственного bridge и retention. Полная конфигурация CHR, arbitrary PKI, другие ОС, public IPv6, multi-instance и внешние jobs не приняты этим блоком.
-## Актуальная точка продолжения — 2026-10-14
+## Актуальная точка продолжения — 2026-10-15
Этапы0–11 завершены в локальных границах журнала. История выполненных внешних блоков и доказательства сохранены в [журнале](ux-ui-stage-12.md) и [протоколе](ux-ui-external-acceptance.md). Реальная Telegram доставка201 и Portal SSO195–199 приняты в описанных границах. Multi-instance, внешние аккаунты/клиентские ОС и оставшиеся профильные сетевые ограничения открыты.
По поручению2026-10-09 работать укрупнёнными разделами, последовательно на текущей модели: карта возможностей → исправления → профильные тесты → build/deploy → собственные реальные сценарии → cleanup → один локальный коммит без push. Проверять квоту после завершённого раздела; незавершённые проверки явно переносить в журнал.
-## Остаток приёмки по группам — 2026-10-14
+## Остаток приёмки по группам — 2026-10-15
-Этапы0–11 завершены в локальных границах. Этап12.5 открыт: ниже7 групп проверок, а не7 обещанных окон или точное число тестов. Детали и исторические ограничения сохраняются в [матрице](ux-ui-remaining-acceptance.md).
+Этапы0–11 завершены в локальных границах. Этап12.5 открыт. Ниже семь групп охвата; это не семь обязательных новых этапов и не обещание числа окон. Принятые сценарии не запускать заново без изменения кода или найденного риска. Для расширения стенда отдельно нужны перечисленные условия.
-| Группа | Что уже подтверждено | Оставшийся результат / что требуется |
+| Группа | Подтверждено | Открытый объём / условие |
| --- | --- | --- |
-| REST и достоверность данных | Ошибки OSPF/BFD/BGP/GRE/WAN215–221 явные; latest API smoke222 прошёл | Причина единичных native401/500 неизвестна. Короткое независимое наблюдение возможно сейчас; оно не даст SLA |
-| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux, private IPv4/ULA и synthetic IPv6/IPFIX; Android full-tunnel/reconnect227 подтверждены пользователем | Windows/macOS/iOS VPN, расширенная Android приёмка, независимые WAN/multi-WAN и public IPv6; public IPv6 на текущем стенде отсутствует |
-| Масштаб и длительная устойчивость | Контролируемые lock/dedupe/loss/recovery и короткие native проверки | Несколько backend/worker/writers, длительный rekey/REST/traffic, physical disconnect и maximum throughput |
-| Внешние хранилища и jobs | Локальные API/errors/RBAC/owned SQL; реальные Portal1.3.1/Telegram195–201 и Selectel S3/client/API/ownSQL228–229 | Native S3 backup/restore/live UI/retention; provider/container/Gitea/MaxMind jobs с соответствующими endpoints/аккаунтами; private/paid credentials не выдумывать |
-| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP сценарии и точные границы записаны | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/policy/path-length и длительные переходы; BFD-only gate гарантией не стал |
-| Общий UX, доступность и производительность | Полная UI192 исторически; latest backend25/UI52, scoped contrast/live218 и keyboard206 | Финальная свежая UI-регрессия перед выпуском, профильный performance и ручная accessibility/screen-reader приёмка; scoped axe не полный WCAG |
-| Границы выпуска | План/доказательства/датированные блоки сохранены; коммиты локальные | Определить требуемые конфигурации и принять результат в этих границах; невыполненные варианты остаются явными ограничениями |
+| REST и достоверность данных | Нативные наблюдения236/238; ограничение shared REST pools257,12 параллельных чтений двух CHR и25 backend-профилей прошли | Длительное наблюдение после transport fix; историческая причинность всех401/500 и сравнение версий RouterOS полностью не доказаны |
+| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux; Android StrongSwan full-tunnel и reconnect227 подтверждены пользователем | Windows/macOS/iOS и другие клиенты; независимые WAN/multi-WAN. Публичного IPv6 на предоставленном стенде нет — проверка требует другого подключения |
+| Масштаб и устойчивость | Контролируемые PG lock/dedupe/loss/recovery, отдельные multi-process сценарии194/200/201 | Несколько полноценных backend/worker со всеми writers, длительный rekey/traffic, физические потери WAN/DB и maximum throughput |
+| Хранилища и внешние jobs | Реальные Portal/Telegram195–201, Selectel S3/limited bridge restore/retention228–240, GeoIP mirror242; контейнеры248; production ACME250 и реальное renewal/rotation259 с trusted TLS/cleanup | Large native export/migration buckets/multi-instance S3; CA revocation отдельно не принят. Gitea publishing CI не запускался: действует no push. Direct licensed MaxMind отсутствует в текущей реализации |
+| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP в записанных сценариях, включая private IPv6 | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/path-length, независимые внешние peers и длительные переходы. BFD-only gate не гарантирован |
+| UX, доступность и производительность | UI226, ARIA мастеров251, новая проверка260 текущего runtime описана выше | Исправления260 и72 live состояния приняты; оставшиеся12 экранов и ручная screen-reader приёмка отдельно. Synthetic timing не field CWV, scoped axe не полная WCAG сертификация |
+| Границы выпуска | Датированный журнал, протоколы и локальные коммиты без push | Подготовить итоговый перечень принятых конфигураций/исключений для решения о выпуске; лабораторный успех не объявлять приёмкой всех конфигураций |
-Portal offline JWT действует доexp после logout/role change; это принятая граница198, а не мгновенная revocation. Controlled Telegram unknown/retry201 не доказывает physical WAN lost ACK. User screenshots и vendor ReUI/theme сохранены.
+Portal offline JWT действует доexp после logout/role change; это записанная граница198, не мгновенная revocation. Controlled Telegram unknown/retry201 не доказывает физическую потерю ACK в WAN. Архивные записи ниже отражают состояние на свою дату; актуальный статус задают эта таблица и верхняя точка продолжения.
## Цель и обязательные требования
diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md
index bc79ae33..1a7d79d9 100644
--- a/docs/ux-ui-progress.md
+++ b/docs/ux-ui-progress.md
@@ -1,6 +1,6 @@
# UX/UI: журнал реализации
-Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт.
+Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт.
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
@@ -36,7 +36,28 @@
| 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 |
| 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 |
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
-| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная12.3.83; внешняя12.5 открыта, блоки1–259 в записанных границах. Реальная ротация/renewal259 и полный откат приняты. Runtime backend ux-certificates258/frontend ux-certificates251. Общая backend-регрессия257 и профиль сертификатов258 прошли. Следующий260 — актуальный остаток и профильная итоговая приёмка. |
+| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная12.3.83; внешняя12.5 открыта, блоки1–260 в записанных границах. Ротация/renewal259 и откат приняты; общий UI260:72 live states passed. Runtime backend258/frontend260. Следующий261 — оставшиеся12 экранов и итоговые границы. |
+
+## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15)
+
+Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json).
+
+ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены.
+
+TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах.
+
+Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены.
+
+Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json).
+
+Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe.
+
+| Референс | Схема и экран |
+| --- | --- |
+| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) |
+| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) |
+
+Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными.
## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15)
diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md
index 596ef729..ae3a89b6 100644
--- a/docs/ux-ui-remaining-acceptance.md
+++ b/docs/ux-ui-remaining-acceptance.md
@@ -1,10 +1,31 @@
-# Оставшаяся внешняя приёмка — 2026-10-14
+# Оставшаяся внешняя приёмка — 2026-10-15
-Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт.
+Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт.
-Это актуальная матрица после 12.5.259. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
+Это актуальная матрица после 12.5.260. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
-Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-certificates258`, frontend `ux-certificates251`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
+Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-certificates258`, frontend `ux-accessibility260-final`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
+
+## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15)
+
+Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json).
+
+ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены.
+
+TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах.
+
+Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены.
+
+Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json).
+
+Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe.
+
+| Референс | Схема и экран |
+| --- | --- |
+| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) |
+| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) |
+
+Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными.
## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15)
@@ -316,21 +337,21 @@ Production backend/frontend ux-backups237 собраны и развёрнуты
Следующий238 фиксирует уже выполненное длительное REST-наблюдение. Затем — S3 restore через реальный UI в пределах собственного bridge и retention. Полная конфигурация CHR, arbitrary PKI, другие ОС, public IPv6, multi-instance и внешние jobs не приняты этим блоком.
-## Остаток приёмки по группам — 2026-10-14
+## Остаток приёмки по группам — 2026-10-15
-Этапы0–11 завершены в локальных границах. Этап12.5 открыт: ниже7 групп проверок, а не7 обещанных окон или точное число тестов. Детали и исторические ограничения сохраняются в [матрице](ux-ui-remaining-acceptance.md).
+Этапы0–11 завершены в локальных границах. Этап12.5 открыт. Ниже семь групп охвата; это не семь обязательных новых этапов и не обещание числа окон. Принятые сценарии не запускать заново без изменения кода или найденного риска. Для расширения стенда отдельно нужны перечисленные условия.
-| Группа | Что уже подтверждено | Оставшийся результат / что требуется |
+| Группа | Подтверждено | Открытый объём / условие |
| --- | --- | --- |
-| REST и достоверность данных | Ошибки OSPF/BFD/BGP/GRE/WAN215–221 явные; latest API smoke222 прошёл | Correlated236:384 samples/40 diagnostics без отказов; current policies/address/load исправны.828/304 retained REST sessions и vendor inactive-session fix — гипотеза; 238:763.844s/624samples/24diagnostics все200, API records изменились без отказа; причинность401/500 и version control остаются открытыми |
-| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux, private IPv4/ULA и synthetic IPv6/IPFIX; Android full-tunnel/reconnect227 подтверждены пользователем | Windows/macOS/iOS VPN, расширенная Android приёмка, независимые WAN/multi-WAN и public IPv6; public IPv6 на текущем стенде отсутствует |
-| Масштаб и длительная устойчивость | Контролируемые lock/dedupe/loss/recovery и короткие native проверки | Несколько backend/worker/writers, длительный rekey/REST/traffic, physical disconnect и maximum throughput |
-| Внешние хранилища и jobs | Portal/Telegram195–201; native S3230, persistence235, missing UX237, bridge-only live restore239, isolated retention240; real GeoIP mirror242; availability243 | Large native export только HTTP fixture; migration buckets/multi-instance. Containers:feature=true, package не обнаружен,/container400 — нужны package/lifecycle. ACME:no token/zone, нужен test-domain/scoped Cloudflare/staging. Gitea:local dry-run принят; publishing CI не запускался (no push). Direct licensed MaxMind не реализован |
-| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP сценарии и точные границы записаны | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/policy/path-length и длительные переходы; BFD-only gate гарантией не стал |
-| Общий UX, доступность и производительность | Полная UI192 исторически; latest backend25/UI52, scoped contrast/live218 и keyboard206 | Свежая UI226 принята в записанных границах; профильный performance и ручная accessibility/screen-reader приёмка; scoped axe не полный WCAG |
-| Границы выпуска | План/доказательства/датированные блоки сохранены; коммиты локальные | Определить требуемые конфигурации и принять результат в этих границах; невыполненные варианты остаются явными ограничениями |
+| REST и достоверность данных | Нативные наблюдения236/238; ограничение shared REST pools257,12 параллельных чтений двух CHR и25 backend-профилей прошли | Длительное наблюдение после transport fix; историческая причинность всех401/500 и сравнение версий RouterOS полностью не доказаны |
+| Клиентские ОС и внешняя сеть | CHR↔CHR/Linux; Android StrongSwan full-tunnel и reconnect227 подтверждены пользователем | Windows/macOS/iOS и другие клиенты; независимые WAN/multi-WAN. Публичного IPv6 на предоставленном стенде нет — проверка требует другого подключения |
+| Масштаб и устойчивость | Контролируемые PG lock/dedupe/loss/recovery, отдельные multi-process сценарии194/200/201 | Несколько полноценных backend/worker со всеми writers, длительный rekey/traffic, физические потери WAN/DB и maximum throughput |
+| Хранилища и внешние jobs | Реальные Portal/Telegram195–201, Selectel S3/limited bridge restore/retention228–240, GeoIP mirror242; контейнеры248; production ACME250 и реальное renewal/rotation259 с trusted TLS/cleanup | Large native export/migration buckets/multi-instance S3; CA revocation отдельно не принят. Gitea publishing CI не запускался: действует no push. Direct licensed MaxMind отсутствует в текущей реализации |
+| Расширенные сетевые конфигурации | GRE/WG/VXLAN/IPsec/OSPF/BFD/BGP в записанных сценариях, включая private IPv6 | from-pool/EUI-64/multiple-address import/export, outer IPv6, произвольная PKI/OID/path-length, независимые внешние peers и длительные переходы. BFD-only gate не гарантирован |
+| UX, доступность и производительность | UI226, ARIA мастеров251, новая проверка260 текущего runtime описана выше | Исправления260 и72 live состояния приняты; оставшиеся12 экранов и ручная screen-reader приёмка отдельно. Synthetic timing не field CWV, scoped axe не полная WCAG сертификация |
+| Границы выпуска | Датированный журнал, протоколы и локальные коммиты без push | Подготовить итоговый перечень принятых конфигураций/исключений для решения о выпуске; лабораторный успех не объявлять приёмкой всех конфигураций |
-Portal offline JWT действует доexp после logout/role change; это принятая граница198, а не мгновенная revocation. Controlled Telegram unknown/retry201 не доказывает physical WAN lost ACK. User screenshots и vendor ReUI/theme сохранены.
+Portal offline JWT действует доexp после logout/role change; это записанная граница198, не мгновенная revocation. Controlled Telegram unknown/retry201 не доказывает физическую потерю ACK в WAN. Архивные записи ниже отражают состояние на свою дату; актуальный статус задают эта таблица и верхняя точка продолжения.
| Раздел | Подтверждено | Осталось / условие завершения |
| --- | --- | --- |
@@ -345,14 +366,14 @@ Portal offline JWT действует доexp после logout/role change; э
| BGP | Established83, exact /32 exchange/API/UI/administrative withdrawal92, BFD primary GRE loss/alternate/recovery93; basic native IPv6 eBGP/AFI/NLRI/Ping/withdrawal/API/UI155; native IPv6 BGP/BFD scoped withdrawal/recovery160; private4-byte ASN/as4/API/UI162 | Hold timer/TCP loss безBFD принят104; packet loss в переходе, разные внешние peers/WAN policy, basic BFDv6 принят160; IPv6 outer/public WAN/scale. Два логических пути используют тот же public WAN; physical redundancy не принята |
| Recursive routes | Production PUT/change/own history restore88, disabled guards, partial/unknown/applied outcomes, live формы/контекст | Автоматический rollback/retry, durable receipts и несколько экземпляров отсутствуют; PUT не является idempotent noop. Полная произвольная legacy catalog миграция не принята |
| Diagnostics/terminal | Allowlist/native reads, Ping/trace/route/MTU/DNS, actual avg-rtt, disabled guards, JWT/live PC/mobile89; real default memory tail/log limits/API/UI133 | DNS использует backend resolver; MTU — ограниченная оценка, не гарантированный максимум. Stop ожидания не доказывает остановку RouterOS команды; arbitrary CLI и physical disconnect не приняты |
-| Portal/интеграции | Portal1.3.1/issuer/login/RBAC/expiry/context195–199, actual Telegram201; Selectel S3 native/API/live limited restore/retention228–240; real GeoIP mirror242 | Offline JWT сохраняетдоступ доexp; S3 large-native/migration/multi-instance отдельно; containers lifecycle248 принят в записанных границах; ACME issuance250 принят; automatic renewal/CA revoke и remote Gitea publishing не приняты; direct licensed MaxMind не реализован. ClientOS/scale/WAN отдельно |
+| Portal/интеграции | Portal1.3.1/issuer/login/RBAC/expiry/context195–199, actual Telegram201; Selectel S3 native/API/live limited restore/retention228–240; real GeoIP mirror242 | Offline JWT сохраняетдоступ доexp; S3 large-native/migration/multi-instance отдельно; containers lifecycle248 принят в записанных границах; ACME issuance250 принят; automatic renewal/rotation259 приняты в конкретном сценарии; CA revoke и remote Gitea publishing не приняты; direct licensed MaxMind не реализован. ClientOS/scale/WAN отдельно |
| Общая приёмка | Локальная12.3.83; UI1244/context221/compiled984 parity139; профильные build/E2E/live проверки последующих блоков | Traffic initial live mock-id gate passed141; IPv6 special ranges/cache passed142; unknown RIPE coordinates passed143; IPv6 synthetic UDP/worker/SQL passed144; native CHR ULA/IPFIX/API/UI passed145; ICMPv6/IPv6 endpoint labels passed146; HTTP DTO validation passed147; scoped flow catalog/serverId guards passed148; strict monthly calendar passed149; Sessions labels passed150; application GRE static IPv6 lifecycle151 passed; WireGuard static IPv6 lifecycle152 и disabled SQL/API gates passed; OSPF/BFD boundary/warning153 passed; OSPFv3 routes154 accepted in recorded bounds; BGP IPv6 basic155 passed; bigint ID/SQL guards156 passed; общая UI1260/context221/backend24 regression157 passed; IPv6 BFD correlation158 profile/deployed passed; native OSPFv3/BFDv6 boundary/recovery159 passed; BGP IPv6/BFD withdrawal/recovery160 passed; selected BGP/alert unknown guards161 replay/profiles/deployed passed (SQL stability skipped); private4-byte ASN162 native/API/UI passed; duplicate BGP alert keys163 replay/profiles/deployed passed; nullable selected BGP164 replay/profiles/API/UI passed; collector165 parallel/current guards/read-only PG passed; positive partial/error SQL166/full24 with PG passed (optional network SQL not repeated); GRE/BGP freshness167/SQL8 passed; other source audit168 выявил identity/disabled/freshness gaps; локальный server/traffic identity guard169 passed без SQL/deploy, source/per-probe/freshness170/ownSQL16/compiled1008/deployed passed; source diagnostics171/42E2E/ownSQL metadata/live2 passed; monitoring history172/ownSQL26/compiled1020/deployed passed; Ping known/error173/ownHTTP-SQL8/85uniqueE2E/native4/4/deployed passed; traffic confidence174 baseline reproduced/local guard passed; owned SQL9/9358ms/deploy175/1024compiled/live smoke passed; legacy history/provenance open; compound RTT176 local/native replay2 passed, SQL10/deploy177/1024compiled/live smoke passed; history178/legacy provenance/current TTL/manual truth/SQL9+14/35unique E2E/native GET/deployed passed; live179 complete/disable-config/bounded fallback/ownSQL10/nativeSSE2/liveUI2 passed; volume180 exact/unit marker/ownSQL6/18E2E/API/liveUI/deployed passed; binding status181 native/ownSQL9/8E2E/deployed passed; peer atomicity182 ownSQL3/native/API/UI passed; peer failure warning183 ownSQL2/4E2E/deployed passed; inactive history184 ownSQL2/deployed passed; gap185/identity186/restore187/wave188/sourceguard189/confirmed peak190 passed; aggregate peaks191, request dedupe193, cross-process lock194/200, full regression192 (1286 unique E2E и25-й профиль privacy196 scoped), Portal195–199/Telegram201 passed; последующие изменения проверять отдельно; scoped axe не означает полную WCAG сертификацию |
Доказательства и точные ограничения: [полная история этапа12](ux-ui-stage-12.md), [внешний протокол](ux-ui-external-acceptance.md), [BGP93](ux-ui/acceptance/bgp-failover-2026-10-10.json), [паспорт стенда](ux-ui-test-server.md). Невыполненные пункты не являются все блокерами данного лабораторного сценария: для выпуска необходимо явно определить требуемые конфигурации и принять их либо согласовать ограничение.
-## Следующая последовательность — 2026-10-14
+## Следующая последовательность — 2026-10-15
-1. **Приёмка260:** обновить актуальный остаток без устаревших blockers; профильный UX/a11y/performance контроль текущего runtime. Реальная ротация и откат259 приняты.
+1. **Приёмка261:**12 оставшихся default screens с real Portal/API и точным read-only guard; профильный audit/layout/performance. Сертификаты259 и UI260 приняты в записанных границах.
2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания.
3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций.
diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md
index a4cabb6f..3868ea1a 100644
--- a/docs/ux-ui-stage-12.md
+++ b/docs/ux-ui-stage-12.md
@@ -1,6 +1,6 @@
# Этап 12: итоговая приёмка UX/UI
-Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт.
+Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт.
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
@@ -4152,6 +4152,27 @@ Real Portal/browser API, provider local verified: создан один job ро
Runtime backend ux-backup234/frontend ux-routing218 healthy; новое application image для volume-only fix не требовалось. Пароли CHR не менялись, S3 mutations0 в235, configs body/JWT не опубликованы, private local manifest удалён. Исходные10 missing local files не восстановлены и records не удалены. Force-recreate — не host reboot/power loss/fsync/DR приёмка. Далее236 — UX недоступных local files, затем version retention границы и оставшиеся7 группы матрицы; multi-instance guard не принимается. Этап12.5 открыт.
+## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15)
+
+Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json).
+
+ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены.
+
+TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах.
+
+Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены.
+
+Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json).
+
+Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe.
+
+| Референс | Схема и экран |
+| --- | --- |
+| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) |
+| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) |
+
+Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными.
+
## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15)
По сохранённому разрешению254 выполнен тот же exact mmt253-8e62a941.pngs.top на104.165.244.7. Перед началом снят свежий приватный native/export/SSL/files/SQL baseline; own certificate/files/jobs/DNS0, Cloudflare token/zone/account проверены. Renewal временно выключен. Подготовленный backend258 активирован; frontend251/Portal/DB сохранены, backend healthy. [Развёртывание](ux-ui/acceptance/acme259-deployment.json).
diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md
index 24936032..dd636915 100644
--- a/docs/ux-ui-test-server.md
+++ b/docs/ux-ui-test-server.md
@@ -1,6 +1,6 @@
# Тестовый сервер UX/UI — 2026-10-10
-Актуальное продолжение 2026-10-15: 259 — реальный first issue и scheduler renewal приняты: trusted TLS сменил SHA-256, один logical leaf, старые native ID/fingerprint/private-key сохранены в архиве. Native/export/SSL/SQL/DNS восстановлены, private fixtures удалены. Runtime backend258/frontend251 healthy. Следующий260 — актуализировать остаток и выполнить профильную приёмку UX/доступности/производительности; этап12.5 открыт.
+Актуальное продолжение 2026-10-15: 260 — исправлены общие focus/target-size/contrast и BGP320 tabs; production frontend ux-accessibility260-final развёрнут, backend258 healthy.72 live WCAG/layout состояния прошли,59 уникальных E2E прошли в записанных итерациях; synthetic timing сохранён. Следующий261 — оставшиеся12 экранов и точные границы приёмки; этап12.5 открыт.
| Параметр | Проверенное состояние |
| --- | --- |
@@ -14,7 +14,7 @@
| PostgreSQL | postgres:18-alpine,18.6;mmapp-test-postgres healthy;SELECT version() passed |
| Данные | Отдельный Compose project mmapp-test,том mmapp-test_mmapp-pgdata и каталог ./data |
| Доступ | PostgreSQL не публикуется;frontend3000/backend8000 привязаны к127.0.0.1 после запуска |
-| Образы приложения | mmapp-test/backend:ux-certificates258 и mmapp-test/frontend:ux-certificates251; backend healthy, проверено2026-10-15 в259 |
+| Образы приложения | mmapp-test/backend:ux-certificates258 и mmapp-test/frontend:ux-accessibility260-final; backend healthy, проверено2026-10-15 в260 |
Compose SHA256:3052bd6ab5d9865f97605cebfb5ff27a4410cd3357b81153a65a87fb7c92582f. OptionalBACKEND_IMAGE_TAG позволяетобновлятьbackendотдельно. Локальный и удалённый файлы совпали. Шаблоны: [Compose](../deploy/docker-compose.test.yml), [env без секретов](../deploy/env.test.example).
@@ -34,6 +34,27 @@ OSPF84: real GRE Full adjacency/private routes/Ping3/3 и4 live PC/320px URL/bro
BFD85: Up/Full/own route/Ping,4 live UI и loss/withdrawal/recovery приняты; exact own cleanup8 коллекций×2 passed. Withdrawal5,245ms не подтверждает sub-second BFD timing; этот вопрос открыт. Runtime routing83 без новой сборки. [Отчёт](ux-ui/acceptance/ospf-bfd-loss-2026-10-09.json).
+## 12.5.260 — общая доступность, клавиатура и профильная скорость UI (2026-10-15)
+
+Проведён read-only аудит18 разделов×2 темы×1440/320px:72 состояния текущего стенда/настоящий Portal UI, без response mocks. Baseline выявил23 состояния с axe нарушениями: недостаточная независимая pointer-area у focusable listbox-контейнера, контраст счётчиков SegmentedControl, raw type badges серверов и заголовка VXLAN. Document overflow0/JS errors0/API errors0; BGP320 имел2 внутренних переполнения main. Dashboard в baseline ещё содержал skeleton: его ранние timings не считаются готовностью данных. [Baseline](ux-ui/acceptance/ui260-baseline.json).
+
+ServerTileRail сохраняет существующие Item/ScrollArea/DataGrid/44px варианты: один Tab stop на option, стрелки/Home/End перемещают selection и DOM focus, включая переход через страницу50. Root больше не конкурирует с вложенными option за фокус/target. Первые2 новые keyboard tests обнаружили remount GrePageContent при смене server URL; короткий10s keyboard intent восстанавливает фокус на новом выбранном элементе, только если пользователь не сфокусировал другой control, и игнорирует скрытый rail. Настройки устройств/URL selection/API не изменены.
+
+TypeBadge переиспользует установленный ReUI Badge primary/info/success-light и text-foreground (как существующий rail), поскольку текущий dark success-foreground не прошёл4.5:1; vendor/style/theme не менялись, декоративные иконки скрыты от AT. У счётчиков снята opacity, заголовок VXLAN использует text-foreground. Вкладки BGP на узком экране имеют собственную горизонтальную прокрутку и не сжимаются. [Badge API](https://reui.io/docs/components/base/badge?ref=mcp), [preview](https://reui.io/components/badge?ref=mcp); audit checklist получен через MCP, выполнен в изменённых границах.
+
+Локально initial57 E2E:55 passed/2 keyboard failed; после исправления remount rerun6 (2 keyboard+4 GRE/VXLAN) passed. Затем добавлены2 contrast tests всех3 типов серверов; final shared4 (2 contrast+2 keyboard) passed. Это59 уникальных принятых случаев; повторные прогоны не прибавляются к числу сценариев; initial отказ сохранён в сводке. Проверены серверные операции/отказы/RBAC/формы, BGP data states, GRE/VXLAN layout и backup layout. TypeScript/scoped ESLint passed. Production frontend build passed,528 source files matched; финальный образ ux-accessibility260-final развёрнут, backend258/DB/Portal сохранены.
+
+Первый live recheck72 оставил2 color-contrast failures только на servers/dark; прочие70 прошли. После text-foreground final server recheck4 прошёл. Итоговое покрытие72 уникальных состояний объединено с явным происхождением результатов, а не выдано за76 разных случаев: axe WCAG2/2.1/2.2 tagged violations0, document/main overflow0, JS/API errors0, blocked writes0. Для data pages сначала ожидаются200 required endpoint responses; dashboard после этого skeleton0. Synthetic main-visible p50/p95=548/1052ms; required API response p50/p95=1033/2494ms. Это один Edge/headless lab run через SSH, без throttling, стандартный viewport snapshot; не field CWV/maximum load/полная ручная AT сертификация. [Полный протокол](ux-ui/acceptance/ui260-final.json), [сводка](ux-ui/acceptance/ui260-summary.json).
+
+Визуально просмотрены screenshots servers1440 и BGP320. На BGP320 подписи KPI ещё сокращаются; эту отдельную проблему читаемости включить в261 вместе с оставшимися экранами, не считать её проверенной axe.
+
+| Референс | Схема и экран |
+| --- | --- |
+| [ReUI Badge](https://reui.io/components/badge?ref=mcp) | Тип устройства → semantic variant → читаемая подпись; [servers1440](ux-ui/screens/ui260-servers.png) |
+| [ReUI Frame](https://reui.io/components/frame?ref=mcp) | Узкий экран → прокрутка вкладок → выбранный раздел; [BGP320](ux-ui/screens/ui260-bgp.png) |
+
+Устройства не перенастраивались; новых native объектов/DNS/ACME не создавали. Текущая матрица исправлена: containers248 и ACME issue250/renewal259 приняты, старое отсутствие пакета/токена больше не записано как blocker. Семь групп описывают охват, не обязательные семь новых этапов. Следующий261 —12 оставшихся default screens: users/firewall/probes/terminal/data-collection/recursive-routes/route-optimizer/network-map/ip-ranges/asns/communities/domains; затем актуальные границы выпуска. Новая реализация CA revoke/MaxMind или внешняя публикация не объявляются выполненными.
+
## 12.5.259 — реальная ротация сертификата и подтверждённый откат (2026-10-15)
По сохранённому разрешению254 выполнен тот же exact mmt253-8e62a941.pngs.top на104.165.244.7. Перед началом снят свежий приватный native/export/SSL/files/SQL baseline; own certificate/files/jobs/DNS0, Cloudflare token/zone/account проверены. Renewal временно выключен. Подготовленный backend258 активирован; frontend251/Portal/DB сохранены, backend healthy. [Развёртывание](ux-ui/acceptance/acme259-deployment.json).
diff --git a/docs/ux-ui/acceptance/ui260-baseline.json b/docs/ux-ui/acceptance/ui260-baseline.json
new file mode 100644
index 00000000..c10eefb7
--- /dev/null
+++ b/docs/ux-ui/acceptance/ui260-baseline.json
@@ -0,0 +1,2063 @@
+{
+ "date": "2026-10-15",
+ "block": "12.5.260",
+ "origin": "http://localhost:3000",
+ "realPortalUiLogin": true,
+ "responseMocks": false,
+ "deviceWritesPermitted": false,
+ "cases": [
+ {
+ "theme": "dark",
+ "path": "/dashboard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 703,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 16
+ },
+ "performance": {
+ "lcp": 668,
+ "cls": 0.18394311005015432,
+ "longTasks": 1,
+ "longTaskMs": 118,
+ "ttfbMs": 94.30000000074506,
+ "domInteractiveMs": 289,
+ "durationMs": 523.1999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/dashboard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 555,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 16
+ },
+ "performance": {
+ "lcp": 528,
+ "cls": 0.035963546874999995,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 54.5,
+ "domInteractiveMs": 222.5,
+ "durationMs": 493.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/servers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 553,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 672,
+ "cls": 0.000044211847752700614,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 97.30000000074506,
+ "domInteractiveMs": 239.69999999925494,
+ "durationMs": 471.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/servers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 536,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 548,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 108,
+ "domInteractiveMs": 207.60000000149012,
+ "durationMs": 480.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/uptime",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 832,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 772,
+ "cls": 0.01717699266975309,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.70000000298023,
+ "domInteractiveMs": 201.5,
+ "durationMs": 498.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/uptime",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 777,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 756,
+ "cls": 0.08757044687499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 53,
+ "domInteractiveMs": 151.5,
+ "durationMs": 386.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/traffic",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 563,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1488,
+ "cls": 0.025900711534288193,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 87,
+ "domInteractiveMs": 234,
+ "durationMs": 532.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/traffic",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 524,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 488,
+ "cls": 0.11047610429687499,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 57.599999997764826,
+ "domInteractiveMs": 203.80000000074506,
+ "durationMs": 444.0999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/statistics",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 633,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 804,
+ "cls": 0.05435100839120371,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 154.20000000298023,
+ "domInteractiveMs": 288.30000000074506,
+ "durationMs": 611.1999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/statistics",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 612,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 776,
+ "cls": 0.07823437187499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.400000002235174,
+ "domInteractiveMs": 205.39999999850988,
+ "durationMs": 586
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/wireguard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 590,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 900,
+ "cls": 0.00016905755690586422,
+ "longTasks": 1,
+ "longTaskMs": 54,
+ "ttfbMs": 95.10000000149012,
+ "domInteractiveMs": 239.79999999701977,
+ "durationMs": 470.0999999977648
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/wireguard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 526,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 524,
+ "cls": 0.00063807578125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.19999999925494,
+ "domInteractiveMs": 201.30000000074506,
+ "durationMs": 411.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ipsec",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 553,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 944,
+ "cls": 0.00006102382330246914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 98.40000000223517,
+ "domInteractiveMs": 233.5,
+ "durationMs": 440.30000000074506
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ipsec",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 564,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 560,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 51.399999998509884,
+ "domInteractiveMs": 216.19999999925494,
+ "durationMs": 486.5999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/gre",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 586,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 564,
+ "cls": 0.06258026005497686,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 98.09999999776483,
+ "domInteractiveMs": 271.30000000074506,
+ "durationMs": 478.4000000022352
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/gre",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 566,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 564,
+ "cls": 0.10702192187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 92.60000000149012,
+ "domInteractiveMs": 257.30000000074506,
+ "durationMs": 486.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/vxlan",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 553,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 548,
+ "cls": 0.12087190923996914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 99.19999999925494,
+ "domInteractiveMs": 234.5,
+ "durationMs": 432.5
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/vxlan",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 519,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 480,
+ "cls": 0.24465938125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.5,
+ "domInteractiveMs": 209.09999999776483,
+ "durationMs": 432.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ospf",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1113,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 752,
+ "cls": 0.0054106802782600315,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 154,
+ "domInteractiveMs": 291,
+ "durationMs": 722.5
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ospf",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 464,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 428,
+ "cls": 0.019630139453125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 54.899999998509884,
+ "domInteractiveMs": 140.80000000074506,
+ "durationMs": 350.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/bgp",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 552,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 540,
+ "cls": 0.008227206790123459,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 107.39999999850988,
+ "domInteractiveMs": 238.39999999850988,
+ "durationMs": 461.80000000074506
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/bgp",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 533,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": true,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 712,
+ "cls": 0.043276721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.69999999925494,
+ "domInteractiveMs": 211.5,
+ "durationMs": 427.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/filters",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 638,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 656,
+ "cls": 0.036411010079089506,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 96.89999999850988,
+ "domInteractiveMs": 239.39999999850988,
+ "durationMs": 458.5999999977648
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/filters",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 554,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 520,
+ "cls": 0.073080721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 60.599999997764826,
+ "domInteractiveMs": 162,
+ "durationMs": 378.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/backups",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 535,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 524,
+ "cls": 0.000029851948302469136,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 91.30000000074506,
+ "domInteractiveMs": 219.10000000149012,
+ "durationMs": 511.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/backups",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1024,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 552,
+ "cls": 0.1396459982421875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 51.900000002235174,
+ "domInteractiveMs": 199.30000000074506,
+ "durationMs": 523.2000000029802
+ },
+ "violations": [
+ {
+ "id": "color-contrast",
+ "impact": "serious",
+ "targets": [
+ [
+ ".rounded.py-1.transition-colors:nth-child(2) > .opacity-60.tabular-nums.text-xs"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/certificates",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 586,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 856,
+ "cls": 0.032897526041666666,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.79999999701977,
+ "domInteractiveMs": 222.30000000074506,
+ "durationMs": 492.30000000074506
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/certificates",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 499,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 508,
+ "cls": 0.013925775000000001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 48.100000001490116,
+ "domInteractiveMs": 151,
+ "durationMs": 421.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/containers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 547,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 548,
+ "cls": 0.011791851851851852,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 100.89999999850988,
+ "domInteractiveMs": 233.19999999925494,
+ "durationMs": 435.8999999985099
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/containers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 468,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 404,
+ "cls": 0.025738421874999997,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.899999998509884,
+ "domInteractiveMs": 160.39999999850988,
+ "durationMs": 347.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/alerts",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 528,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1000,
+ "cls": 0.2825991898148148,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 92.5,
+ "domInteractiveMs": 220.89999999850988,
+ "durationMs": 395.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/alerts",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 521,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 912,
+ "cls": 0.67899836875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.5,
+ "domInteractiveMs": 197.59999999776483,
+ "durationMs": 378.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/settings",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 549,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 544,
+ "cls": 0.0034562909613715283,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 75.89999999850988,
+ "domInteractiveMs": 204.80000000074506,
+ "durationMs": 447.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/settings",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 545,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 472,
+ "cls": 0.0003595375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.599999997764826,
+ "domInteractiveMs": 216.20000000298023,
+ "durationMs": 388.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/releases",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 847,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 808,
+ "cls": 0.000013114872685185185,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 391.79999999701977,
+ "domInteractiveMs": 533.5,
+ "durationMs": 708.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/releases",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 511,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 452,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 65.20000000298023,
+ "domInteractiveMs": 197.80000000074506,
+ "durationMs": 367.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/dashboard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 680,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 16
+ },
+ "performance": {
+ "lcp": 676,
+ "cls": 0.10677783227237654,
+ "longTasks": 1,
+ "longTaskMs": 53,
+ "ttfbMs": 95.30000000074506,
+ "domInteractiveMs": 279.6000000014901,
+ "durationMs": 667.1999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/dashboard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 672,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 16
+ },
+ "performance": {
+ "lcp": 640,
+ "cls": 0.111360046875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 94.09999999776483,
+ "domInteractiveMs": 333.5,
+ "durationMs": 557.1999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/servers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 550,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 636,
+ "cls": 0.000044211847752700614,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 99.70000000298023,
+ "domInteractiveMs": 236.19999999925494,
+ "durationMs": 465.1000000014901
+ },
+ "violations": [
+ {
+ "id": "color-contrast",
+ "impact": "serious",
+ "targets": [
+ [
+ "tr[data-row-id=\"1\"] > td:nth-child(2) > .bg-emerald-500\\/10.text-emerald-400.border-emerald-500\\/20"
+ ],
+ [
+ "tr[data-row-id=\"2\"] > td:nth-child(2) > .bg-emerald-500\\/10.text-emerald-400.border-emerald-500\\/20"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/servers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 502,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 492,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.69999999925494,
+ "domInteractiveMs": 195.70000000298023,
+ "durationMs": 449.30000000074506
+ },
+ "violations": [
+ {
+ "id": "color-contrast",
+ "impact": "serious",
+ "targets": [
+ [
+ "tr[data-row-id=\"1\"] > td:nth-child(2) > .bg-emerald-500\\/10.text-emerald-400.border-emerald-500\\/20"
+ ],
+ [
+ "tr[data-row-id=\"2\"] > td:nth-child(2) > .bg-emerald-500\\/10.text-emerald-400.border-emerald-500\\/20"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/uptime",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 814,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 756,
+ "cls": 0.01717699266975309,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.69999999925494,
+ "domInteractiveMs": 187.10000000149012,
+ "durationMs": 484.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/uptime",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 793,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 780,
+ "cls": 0.08757044687499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 48.599999997764826,
+ "domInteractiveMs": 175.39999999850988,
+ "durationMs": 422.0999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/traffic",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 538,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 988,
+ "cls": 0.02607868776674624,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 86.19999999925494,
+ "domInteractiveMs": 218.90000000223517,
+ "durationMs": 461
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/traffic",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1018,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 588,
+ "cls": 0.11048095278320312,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.399999998509884,
+ "domInteractiveMs": 203,
+ "durationMs": 573.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/statistics",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 689,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 808,
+ "cls": 0.05435100839120371,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 101.5,
+ "domInteractiveMs": 308,
+ "durationMs": 641.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/statistics",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 461,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 576,
+ "cls": 0.07823437187499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.899999998509884,
+ "domInteractiveMs": 142.70000000298023,
+ "durationMs": 417.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/wireguard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1039,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1024,
+ "cls": 0.00016905755690586422,
+ "longTasks": 1,
+ "longTaskMs": 57,
+ "ttfbMs": 93.69999999925494,
+ "domInteractiveMs": 230.69999999925494,
+ "durationMs": 619.1000000014901
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/wireguard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 520,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 504,
+ "cls": 0.00063807578125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56,
+ "domInteractiveMs": 210.39999999850988,
+ "durationMs": 417.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ipsec",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 574,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 576,
+ "cls": 0.00004806086033950618,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 100.39999999850988,
+ "domInteractiveMs": 243.59999999776483,
+ "durationMs": 536.6999999992549
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ipsec",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 477,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 428,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 57.69999999925494,
+ "domInteractiveMs": 161.60000000149012,
+ "durationMs": 363.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/gre",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 554,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 532,
+ "cls": 0.06258026005497686,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 97.79999999701977,
+ "domInteractiveMs": 243.30000000074506,
+ "durationMs": 448.4000000022352
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/gre",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 528,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 488,
+ "cls": 0.10702192187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.400000002235174,
+ "domInteractiveMs": 207,
+ "durationMs": 411.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/vxlan",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 538,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 516,
+ "cls": 0.12087190923996914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 95.19999999925494,
+ "domInteractiveMs": 226.89999999850988,
+ "durationMs": 424.69999999925494
+ },
+ "violations": [
+ {
+ "id": "color-contrast",
+ "impact": "serious",
+ "targets": [
+ [
+ ".text-sky-600"
+ ]
+ ]
+ },
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/vxlan",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1070,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 608,
+ "cls": 0.24465938125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55,
+ "domInteractiveMs": 225.39999999850988,
+ "durationMs": 554.0999999977648
+ },
+ "violations": [
+ {
+ "id": "color-contrast",
+ "impact": "serious",
+ "targets": [
+ [
+ ".text-sky-600"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ospf",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 543,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 532,
+ "cls": 0.0054106802782600315,
+ "longTasks": 1,
+ "longTaskMs": 51,
+ "ttfbMs": 97.30000000074506,
+ "domInteractiveMs": 231.19999999925494,
+ "durationMs": 447
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ospf",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 530,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 480,
+ "cls": 0.019630139453125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.600000001490116,
+ "domInteractiveMs": 216.60000000149012,
+ "durationMs": 428
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/bgp",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 553,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 540,
+ "cls": 0.008227206790123459,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 95,
+ "domInteractiveMs": 231.90000000223517,
+ "durationMs": 478.6000000014901
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/bgp",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 533,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": true,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 688,
+ "cls": 0.043276721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.899999998509884,
+ "domInteractiveMs": 209.10000000149012,
+ "durationMs": 424.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/filters",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 594,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 604,
+ "cls": 0.036411010079089506,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 96.80000000074506,
+ "domInteractiveMs": 226.80000000074506,
+ "durationMs": 445.30000000074506
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/filters",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 600,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 576,
+ "cls": 0.073080721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.100000001490116,
+ "domInteractiveMs": 199.89999999850988,
+ "durationMs": 421.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/backups",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 542,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 544,
+ "cls": 0.000029851948302469136,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 90.79999999701977,
+ "domInteractiveMs": 224,
+ "durationMs": 528.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/backups",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 492,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 492,
+ "cls": 0.0657015625,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 58.5,
+ "domInteractiveMs": 167.40000000223517,
+ "durationMs": 482.30000000074506
+ },
+ "violations": [
+ {
+ "id": "color-contrast",
+ "impact": "serious",
+ "targets": [
+ [
+ ".rounded.py-1.transition-colors:nth-child(2) > .opacity-60.tabular-nums.text-xs"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/certificates",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 497,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 752,
+ "cls": 0.032897526041666666,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50,
+ "domInteractiveMs": 181.20000000298023,
+ "durationMs": 385
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/certificates",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 507,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 496,
+ "cls": 0.013925775000000001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.100000001490116,
+ "domInteractiveMs": 172,
+ "durationMs": 397
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/containers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 561,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 560,
+ "cls": 0.011791851851851852,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 100.89999999850988,
+ "domInteractiveMs": 240.5,
+ "durationMs": 448.1000000014901
+ },
+ "violations": [
+ {
+ "id": "target-size",
+ "impact": "serious",
+ "targets": [
+ [
+ ".p-1\\.5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/containers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 478,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 432,
+ "cls": 0.025738421874999997,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.900000002235174,
+ "domInteractiveMs": 162.5,
+ "durationMs": 362.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/alerts",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 531,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 972,
+ "cls": 0.2825991898148148,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 89.19999999925494,
+ "domInteractiveMs": 218.89999999850988,
+ "durationMs": 395.5999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/alerts",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 527,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 940,
+ "cls": 0.67622226875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 48.5,
+ "domInteractiveMs": 200.20000000298023,
+ "durationMs": 385.20000000298023
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/settings",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 504,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 468,
+ "cls": 0.0035598427553530094,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.19999999925494,
+ "domInteractiveMs": 185.29999999701977,
+ "durationMs": 361.29999999701977
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/settings",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 468,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 460,
+ "cls": 0.0003595375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50,
+ "domInteractiveMs": 152.29999999701977,
+ "durationMs": 368.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/releases",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 555,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 508,
+ "cls": 0.000013114872685185185,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 99.10000000149012,
+ "domInteractiveMs": 241.59999999776483,
+ "durationMs": 415.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/releases",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 566,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 488,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 82.10000000149012,
+ "domInteractiveMs": 242,
+ "durationMs": 402.5999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ }
+ ],
+ "blockedWrites": [],
+ "bounds": [
+ "Default rendered state only; data can still be refreshing",
+ "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals",
+ "Automated WCAG-tagged checks, not manual screen-reader certification"
+ ],
+ "completed": true,
+ "passed": false
+}
diff --git a/docs/ux-ui/acceptance/ui260-deployment.json b/docs/ux-ui/acceptance/ui260-deployment.json
new file mode 100644
index 00000000..64573650
--- /dev/null
+++ b/docs/ux-ui/acceptance/ui260-deployment.json
@@ -0,0 +1,20 @@
+{
+ "date": "2026-10-15",
+ "block": "12.5.260",
+ "productionBuild": "passed",
+ "sourceMatched": 528,
+ "services": {
+ "backend": {
+ "image": "mmapp-test/backend:ux-certificates258",
+ "imageId": "sha256:984684709e619972bae8657208e3150d12f1c4f2b5642fecf5286a5d87e9fe71",
+ "status": "running",
+ "health": "healthy"
+ },
+ "frontend": {
+ "image": "mmapp-test/frontend:ux-accessibility260-final",
+ "imageId": "sha256:0b7b12cfa0e64a021c8c77a3362cc54488c6aea75269f0c2ae2a0cb0b4b5192a",
+ "status": "running",
+ "health": null
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/ux-ui/acceptance/ui260-final.json b/docs/ux-ui/acceptance/ui260-final.json
new file mode 100644
index 00000000..e4aed324
--- /dev/null
+++ b/docs/ux-ui/acceptance/ui260-final.json
@@ -0,0 +1,2122 @@
+{
+ "date": "2026-10-15",
+ "block": "12.5.260",
+ "origin": "http://localhost:3000",
+ "realPortalUiLogin": true,
+ "responseMocks": false,
+ "deviceWritesPermitted": false,
+ "cases": [
+ {
+ "theme": "dark",
+ "path": "/dashboard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1243,
+ "requiredReads": [
+ "/api/servers",
+ "/api/uptime/overview",
+ "/api/filters/rules",
+ "/api/bgp/catalog",
+ "/api/internet-path/latest",
+ "/api/filters/gre-tunnels",
+ "/api/wireguard",
+ "/api/traffic/servers",
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 3114,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 876,
+ "cls": 0.18891521697621286,
+ "longTasks": 1,
+ "longTaskMs": 106,
+ "ttfbMs": 101.19999999925494,
+ "domInteractiveMs": 390.6000000014901,
+ "durationMs": 745.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/dashboard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 469,
+ "requiredReads": [
+ "/api/servers",
+ "/api/uptime/overview",
+ "/api/filters/rules",
+ "/api/bgp/catalog",
+ "/api/internet-path/latest",
+ "/api/filters/gre-tunnels",
+ "/api/wireguard",
+ "/api/traffic/servers",
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 2342,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 432,
+ "cls": 0.071583796875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 57.600000001490116,
+ "domInteractiveMs": 148.5,
+ "durationMs": 404.4000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/servers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 760,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 1639,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1232,
+ "cls": 0.000044211847752700614,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 144.09999999776483,
+ "domInteractiveMs": 413.5,
+ "durationMs": 740.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/servers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 480,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 585,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 452,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.30000000074506,
+ "domInteractiveMs": 157.19999999925494,
+ "durationMs": 389.29999999701977
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/uptime",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 816,
+ "requiredReads": [
+ "/api/uptime/overview"
+ ],
+ "dataResponsesMs": 1180,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 796,
+ "cls": 0.01717699266975309,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 53.899999998509884,
+ "domInteractiveMs": 192.5,
+ "durationMs": 412.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/uptime",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 890,
+ "requiredReads": [
+ "/api/uptime/overview"
+ ],
+ "dataResponsesMs": 1246,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 880,
+ "cls": 0.08757044687499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 97.29999999701977,
+ "domInteractiveMs": 247.39999999850988,
+ "durationMs": 517.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/traffic",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 568,
+ "requiredReads": [
+ "/api/traffic/servers"
+ ],
+ "dataResponsesMs": 921,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1056,
+ "cls": 0.007212276415412808,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 92,
+ "domInteractiveMs": 226.5,
+ "durationMs": 540.6999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/traffic",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 543,
+ "requiredReads": [
+ "/api/traffic/servers"
+ ],
+ "dataResponsesMs": 896,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 556,
+ "cls": 0.11047610429687499,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.20000000298023,
+ "domInteractiveMs": 199.60000000149012,
+ "durationMs": 526.9000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/statistics",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 551,
+ "requiredReads": [
+ "/api/statistics"
+ ],
+ "dataResponsesMs": 911,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 720,
+ "cls": 0.05435100839120371,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.5,
+ "domInteractiveMs": 189.20000000298023,
+ "durationMs": 533.2000000029802
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/statistics",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 531,
+ "requiredReads": [
+ "/api/statistics"
+ ],
+ "dataResponsesMs": 641,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 620,
+ "cls": 0.07823437187499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 51.100000001490116,
+ "domInteractiveMs": 178.80000000074506,
+ "durationMs": 462.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/wireguard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 562,
+ "requiredReads": [
+ "/api/wireguard"
+ ],
+ "dataResponsesMs": 918,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 836,
+ "cls": 0.00016905755690586422,
+ "longTasks": 1,
+ "longTaskMs": 58,
+ "ttfbMs": 87,
+ "domInteractiveMs": 235,
+ "durationMs": 452.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/wireguard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 473,
+ "requiredReads": [
+ "/api/wireguard"
+ ],
+ "dataResponsesMs": 830,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 460,
+ "cls": 0.00063807578125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 65.69999999925494,
+ "domInteractiveMs": 156.10000000149012,
+ "durationMs": 396.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ipsec",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 587,
+ "requiredReads": [
+ "/api/ipsec"
+ ],
+ "dataResponsesMs": 1449,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 980,
+ "cls": 0.00006102382330246914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 102.80000000074506,
+ "domInteractiveMs": 233.69999999925494,
+ "durationMs": 519.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ipsec",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 958,
+ "requiredReads": [
+ "/api/ipsec"
+ ],
+ "dataResponsesMs": 958,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 540,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.69999999925494,
+ "domInteractiveMs": 141.79999999701977,
+ "durationMs": 446.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/gre",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 574,
+ "requiredReads": [
+ "/api/gre/tunnels"
+ ],
+ "dataResponsesMs": 950,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 552,
+ "cls": 0.06258026005497686,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 124.40000000223517,
+ "domInteractiveMs": 253.20000000298023,
+ "durationMs": 474.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/gre",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 478,
+ "requiredReads": [
+ "/api/gre/tunnels"
+ ],
+ "dataResponsesMs": 2353,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 476,
+ "cls": 0.10702192187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.20000000298023,
+ "domInteractiveMs": 158,
+ "durationMs": 407.4000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/vxlan",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 581,
+ "requiredReads": [
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 948,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 556,
+ "cls": 0.12087190923996914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 110.89999999850988,
+ "domInteractiveMs": 249.19999999925494,
+ "durationMs": 457.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/vxlan",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 452,
+ "requiredReads": [
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 1315,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 444,
+ "cls": 0.24465938125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 58.69999999925494,
+ "domInteractiveMs": 150.30000000074506,
+ "durationMs": 383.5999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ospf",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 571,
+ "requiredReads": [
+ "/api/ospf/catalog"
+ ],
+ "dataResponsesMs": 1434,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 540,
+ "cls": 0.0054106802782600315,
+ "longTasks": 1,
+ "longTaskMs": 50,
+ "ttfbMs": 105.90000000223517,
+ "domInteractiveMs": 244.89999999850988,
+ "durationMs": 470.29999999701977
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ospf",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 485,
+ "requiredReads": [
+ "/api/ospf/catalog"
+ ],
+ "dataResponsesMs": 2343,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 444,
+ "cls": 0.019630139453125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.80000000074506,
+ "domInteractiveMs": 147.5,
+ "durationMs": 403.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/bgp",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 626,
+ "requiredReads": [
+ "/api/bgp/catalog"
+ ],
+ "dataResponsesMs": 2508,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 628,
+ "cls": 0.008227206790123459,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 109.10000000149012,
+ "domInteractiveMs": 242.89999999850988,
+ "durationMs": 571.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/bgp",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1032,
+ "requiredReads": [
+ "/api/bgp/catalog"
+ ],
+ "dataResponsesMs": 1033,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 764,
+ "cls": 0.043276721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.400000002235174,
+ "domInteractiveMs": 178.30000000074506,
+ "durationMs": 471.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/filters",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 875,
+ "requiredReads": [
+ "/api/filters/rules"
+ ],
+ "dataResponsesMs": 875,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 816,
+ "cls": 0.036411010079089506,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 166.10000000149012,
+ "domInteractiveMs": 310.20000000298023,
+ "durationMs": 603.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/filters",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 516,
+ "requiredReads": [
+ "/api/filters/rules"
+ ],
+ "dataResponsesMs": 516,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 524,
+ "cls": 0.073080721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.100000001490116,
+ "domInteractiveMs": 163.89999999850988,
+ "durationMs": 382
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/backups",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 520,
+ "requiredReads": [
+ "/api/backups"
+ ],
+ "dataResponsesMs": 877,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 500,
+ "cls": 0.000029851948302469136,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 96.5,
+ "domInteractiveMs": 191.80000000074506,
+ "durationMs": 421.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/backups",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 501,
+ "requiredReads": [
+ "/api/backups"
+ ],
+ "dataResponsesMs": 601,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 468,
+ "cls": 0.1396483580078125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 54.899999998509884,
+ "domInteractiveMs": 181.09999999776483,
+ "durationMs": 457.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/certificates",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 546,
+ "requiredReads": [
+ "/api/certificates/state"
+ ],
+ "dataResponsesMs": 2408,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 856,
+ "cls": 0.062860234375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 53.099999997764826,
+ "domInteractiveMs": 203.5,
+ "durationMs": 428.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/certificates",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 520,
+ "requiredReads": [
+ "/api/certificates/state"
+ ],
+ "dataResponsesMs": 2416,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 520,
+ "cls": 0.061482699999999994,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.19999999925494,
+ "domInteractiveMs": 176.89999999850988,
+ "durationMs": 450.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/containers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 548,
+ "requiredReads": [
+ "/api/containers/state"
+ ],
+ "dataResponsesMs": 2414,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 544,
+ "cls": 0.11558015046296297,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 109.20000000298023,
+ "domInteractiveMs": 239.30000000074506,
+ "durationMs": 441.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/containers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 502,
+ "requiredReads": [
+ "/api/containers/state"
+ ],
+ "dataResponsesMs": 2361,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 456,
+ "cls": 0.11563842187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50,
+ "domInteractiveMs": 173.80000000074506,
+ "durationMs": 391.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/alerts",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 581,
+ "requiredReads": [
+ "/api/alerts"
+ ],
+ "dataResponsesMs": 942,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 960,
+ "cls": 0.2825991898148148,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 94.40000000223517,
+ "domInteractiveMs": 226.10000000149012,
+ "durationMs": 504.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/alerts",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 510,
+ "requiredReads": [
+ "/api/alerts"
+ ],
+ "dataResponsesMs": 1381,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 948,
+ "cls": 0.67899836875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.19999999925494,
+ "domInteractiveMs": 180.09999999776483,
+ "durationMs": 347.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/settings",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 620,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 620,
+ "cls": 0.0035598427553530094,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 117.5,
+ "domInteractiveMs": 269.5,
+ "durationMs": 509.0999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/settings",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 531,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 480,
+ "cls": 0.0003595375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 65.09999999776483,
+ "domInteractiveMs": 216.60000000149012,
+ "durationMs": 391
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/releases",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 743,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 696,
+ "cls": 0.000013114872685185185,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 278.5,
+ "domInteractiveMs": 428.29999999701977,
+ "durationMs": 603.2999999970198
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/releases",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 530,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 464,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 62.5,
+ "domInteractiveMs": 217.09999999776483,
+ "durationMs": 386.5999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/dashboard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 588,
+ "requiredReads": [
+ "/api/servers",
+ "/api/uptime/overview",
+ "/api/filters/rules",
+ "/api/bgp/catalog",
+ "/api/internet-path/latest",
+ "/api/filters/gre-tunnels",
+ "/api/wireguard",
+ "/api/traffic/servers",
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 3482,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 596,
+ "cls": 0.11174993919843508,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 97.29999999701977,
+ "domInteractiveMs": 239.5,
+ "durationMs": 553
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/dashboard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 607,
+ "requiredReads": [
+ "/api/servers",
+ "/api/uptime/overview",
+ "/api/filters/rules",
+ "/api/bgp/catalog",
+ "/api/internet-path/latest",
+ "/api/filters/gre-tunnels",
+ "/api/wireguard",
+ "/api/traffic/servers",
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 2484,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 556,
+ "cls": 0.071302821875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 66.80000000074506,
+ "domInteractiveMs": 167.69999999925494,
+ "durationMs": 490.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/servers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1150,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 1150,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 988,
+ "cls": 0.000044211847752700614,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 115.19999999925494,
+ "domInteractiveMs": 323.6000000014901,
+ "durationMs": 730.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/servers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1052,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 1052,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 632,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 63,
+ "domInteractiveMs": 236,
+ "durationMs": 567
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/uptime",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 804,
+ "requiredReads": [
+ "/api/uptime/overview"
+ ],
+ "dataResponsesMs": 910,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 736,
+ "cls": 0.01717699266975309,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.5,
+ "domInteractiveMs": 182.90000000223517,
+ "durationMs": 465.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/uptime",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 808,
+ "requiredReads": [
+ "/api/uptime/overview"
+ ],
+ "dataResponsesMs": 916,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 748,
+ "cls": 0.08757044687499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.600000001490116,
+ "domInteractiveMs": 181.09999999776483,
+ "durationMs": 454.79999999701977
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/traffic",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 541,
+ "requiredReads": [
+ "/api/traffic/servers"
+ ],
+ "dataResponsesMs": 895,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 908,
+ "cls": 0.025901459757486978,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 90.80000000074506,
+ "domInteractiveMs": 218.69999999925494,
+ "durationMs": 509.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/traffic",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 572,
+ "requiredReads": [
+ "/api/traffic/servers"
+ ],
+ "dataResponsesMs": 930,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 588,
+ "cls": 0.11093063403320312,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.399999998509884,
+ "domInteractiveMs": 218.5,
+ "durationMs": 566.6999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/statistics",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 536,
+ "requiredReads": [
+ "/api/statistics"
+ ],
+ "dataResponsesMs": 900,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 676,
+ "cls": 0.05435100839120371,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.5,
+ "domInteractiveMs": 201.29999999701977,
+ "durationMs": 514.0999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/statistics",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 516,
+ "requiredReads": [
+ "/api/statistics"
+ ],
+ "dataResponsesMs": 616,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 620,
+ "cls": 0.07823437187499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 48.5,
+ "domInteractiveMs": 177.20000000298023,
+ "durationMs": 449.9000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/wireguard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 548,
+ "requiredReads": [
+ "/api/wireguard"
+ ],
+ "dataResponsesMs": 900,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 820,
+ "cls": 0.00016905755690586422,
+ "longTasks": 1,
+ "longTaskMs": 54,
+ "ttfbMs": 88.60000000149012,
+ "domInteractiveMs": 221.59999999776483,
+ "durationMs": 433.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/wireguard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 448,
+ "requiredReads": [
+ "/api/wireguard"
+ ],
+ "dataResponsesMs": 807,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 400,
+ "cls": 0.00063807578125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.399999998509884,
+ "domInteractiveMs": 136.80000000074506,
+ "durationMs": 341.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ipsec",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 621,
+ "requiredReads": [
+ "/api/ipsec"
+ ],
+ "dataResponsesMs": 2492,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1804,
+ "cls": 0.00006102382330246914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 95.5,
+ "domInteractiveMs": 226.90000000223517,
+ "durationMs": 654.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ipsec",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 516,
+ "requiredReads": [
+ "/api/ipsec"
+ ],
+ "dataResponsesMs": 1378,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 504,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 65.39999999850988,
+ "domInteractiveMs": 203.30000000074506,
+ "durationMs": 432.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/gre",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 553,
+ "requiredReads": [
+ "/api/gre/tunnels"
+ ],
+ "dataResponsesMs": 1409,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 524,
+ "cls": 0.06258026005497686,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 108.19999999925494,
+ "domInteractiveMs": 243.70000000298023,
+ "durationMs": 460.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/gre",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 484,
+ "requiredReads": [
+ "/api/gre/tunnels"
+ ],
+ "dataResponsesMs": 2342,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 488,
+ "cls": 0.10702192187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 48.899999998509884,
+ "domInteractiveMs": 133.89999999850988,
+ "durationMs": 448.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/vxlan",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 564,
+ "requiredReads": [
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 929,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 528,
+ "cls": 0.12087190923996914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 106.80000000074506,
+ "domInteractiveMs": 244.5,
+ "durationMs": 446.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/vxlan",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 498,
+ "requiredReads": [
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 854,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 444,
+ "cls": 0.24465938125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.5,
+ "domInteractiveMs": 184.19999999925494,
+ "durationMs": 368.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ospf",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 544,
+ "requiredReads": [
+ "/api/ospf/catalog"
+ ],
+ "dataResponsesMs": 2408,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 544,
+ "cls": 0.0054106802782600315,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 95.80000000074506,
+ "domInteractiveMs": 225.70000000298023,
+ "durationMs": 637.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ospf",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 503,
+ "requiredReads": [
+ "/api/ospf/catalog"
+ ],
+ "dataResponsesMs": 2382,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 444,
+ "cls": 0.019630139453125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.900000002235174,
+ "domInteractiveMs": 178.80000000074506,
+ "durationMs": 421.0999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/bgp",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 616,
+ "requiredReads": [
+ "/api/bgp/catalog"
+ ],
+ "dataResponsesMs": 968,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 612,
+ "cls": 0.008227206790123459,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 106.70000000298023,
+ "domInteractiveMs": 275.9000000022352,
+ "durationMs": 549.1999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/bgp",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 478,
+ "requiredReads": [
+ "/api/bgp/catalog"
+ ],
+ "dataResponsesMs": 1363,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1228,
+ "cls": 0.043276721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.399999998509884,
+ "domInteractiveMs": 156,
+ "durationMs": 394.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/filters",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 770,
+ "requiredReads": [
+ "/api/filters/rules"
+ ],
+ "dataResponsesMs": 770,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 700,
+ "cls": 0.036411010079089506,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 109.39999999850988,
+ "domInteractiveMs": 247.5,
+ "durationMs": 474.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/filters",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 609,
+ "requiredReads": [
+ "/api/filters/rules"
+ ],
+ "dataResponsesMs": 609,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 580,
+ "cls": 0.073081659375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.30000000074506,
+ "domInteractiveMs": 184.19999999925494,
+ "durationMs": 407.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/backups",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1066,
+ "requiredReads": [
+ "/api/backups"
+ ],
+ "dataResponsesMs": 1066,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 652,
+ "cls": 0.000029851948302469136,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 94.30000000074506,
+ "domInteractiveMs": 237.69999999925494,
+ "durationMs": 623.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/backups",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 462,
+ "requiredReads": [
+ "/api/backups"
+ ],
+ "dataResponsesMs": 815,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 420,
+ "cls": 0.1396459982421875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.30000000074506,
+ "domInteractiveMs": 142.5,
+ "durationMs": 394.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/certificates",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 536,
+ "requiredReads": [
+ "/api/certificates/state"
+ ],
+ "dataResponsesMs": 2389,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 804,
+ "cls": 0.06154219907407407,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.100000001490116,
+ "domInteractiveMs": 200.30000000074506,
+ "durationMs": 442.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/certificates",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 508,
+ "requiredReads": [
+ "/api/certificates/state"
+ ],
+ "dataResponsesMs": 2377,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 432,
+ "cls": 0.060179696874999994,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.5,
+ "domInteractiveMs": 181.39999999850988,
+ "durationMs": 371.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/containers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 615,
+ "requiredReads": [
+ "/api/containers/state"
+ ],
+ "dataResponsesMs": 2494,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 612,
+ "cls": 0.11558015046296297,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 119.5,
+ "domInteractiveMs": 282.30000000074506,
+ "durationMs": 490.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/containers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 494,
+ "requiredReads": [
+ "/api/containers/state"
+ ],
+ "dataResponsesMs": 2352,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 464,
+ "cls": 0.11563842187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.29999999701977,
+ "domInteractiveMs": 179.69999999925494,
+ "durationMs": 386.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/alerts",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 617,
+ "requiredReads": [
+ "/api/alerts"
+ ],
+ "dataResponsesMs": 970,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 972,
+ "cls": 0.2825991898148148,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 88,
+ "domInteractiveMs": 277.69999999925494,
+ "durationMs": 480.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/alerts",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 472,
+ "requiredReads": [
+ "/api/alerts"
+ ],
+ "dataResponsesMs": 842,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 808,
+ "cls": 0.67622226875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.69999999925494,
+ "domInteractiveMs": 148.19999999925494,
+ "durationMs": 367.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/settings",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 539,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 496,
+ "cls": 0.0034562909613715283,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.400000002235174,
+ "domInteractiveMs": 221.60000000149012,
+ "durationMs": 397.4000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/settings",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 488,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 424,
+ "cls": 0.0003595375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 59.69999999925494,
+ "domInteractiveMs": 168.19999999925494,
+ "durationMs": 342.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/releases",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 557,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 504,
+ "cls": 0.000013114872685185185,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 105.30000000074506,
+ "domInteractiveMs": 234.19999999925494,
+ "durationMs": 408.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/releases",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 576,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 496,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 66.09999999776483,
+ "domInteractiveMs": 238.80000000074506,
+ "durationMs": 412
+ },
+ "violations": [],
+ "auditCompleted": true
+ }
+ ],
+ "blockedWrites": [],
+ "bounds": [
+ "Default rendered state only; data can still be refreshing",
+ "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals",
+ "Automated WCAG-tagged checks, not manual screen-reader certification"
+ ],
+ "completed": true,
+ "passed": true,
+ "phases": [
+ {
+ "report": "ui260-first-recheck.json",
+ "cases": 72,
+ "violating": 2,
+ "frontend": "ux-accessibility260"
+ },
+ {
+ "report": "ui260-servers-final.json",
+ "cases": 4,
+ "violating": 0,
+ "frontend": "ux-accessibility260-final",
+ "onlyProductChange": "TypeBadge text-foreground"
+ }
+ ],
+ "coverageNote": "72 unique states accepted across two rechecks; four server cases replace prior results, not added as distinct states"
+}
diff --git a/docs/ux-ui/acceptance/ui260-first-recheck.json b/docs/ux-ui/acceptance/ui260-first-recheck.json
new file mode 100644
index 00000000..b4dc8de4
--- /dev/null
+++ b/docs/ux-ui/acceptance/ui260-first-recheck.json
@@ -0,0 +1,2132 @@
+{
+ "date": "2026-10-15",
+ "block": "12.5.260",
+ "origin": "http://localhost:3000",
+ "realPortalUiLogin": true,
+ "responseMocks": false,
+ "deviceWritesPermitted": false,
+ "cases": [
+ {
+ "theme": "dark",
+ "path": "/dashboard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1243,
+ "requiredReads": [
+ "/api/servers",
+ "/api/uptime/overview",
+ "/api/filters/rules",
+ "/api/bgp/catalog",
+ "/api/internet-path/latest",
+ "/api/filters/gre-tunnels",
+ "/api/wireguard",
+ "/api/traffic/servers",
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 3114,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 876,
+ "cls": 0.18891521697621286,
+ "longTasks": 1,
+ "longTaskMs": 106,
+ "ttfbMs": 101.19999999925494,
+ "domInteractiveMs": 390.6000000014901,
+ "durationMs": 745.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/dashboard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 469,
+ "requiredReads": [
+ "/api/servers",
+ "/api/uptime/overview",
+ "/api/filters/rules",
+ "/api/bgp/catalog",
+ "/api/internet-path/latest",
+ "/api/filters/gre-tunnels",
+ "/api/wireguard",
+ "/api/traffic/servers",
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 2342,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 432,
+ "cls": 0.071583796875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 57.600000001490116,
+ "domInteractiveMs": 148.5,
+ "durationMs": 404.4000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/servers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 502,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 865,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 628,
+ "cls": 0.000044211847752700614,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 53,
+ "domInteractiveMs": 187.80000000074506,
+ "durationMs": 415.30000000074506
+ },
+ "violations": [
+ {
+ "id": "color-contrast",
+ "impact": "serious",
+ "targets": [
+ [
+ "tr[data-row-id=\"1\"] > td:nth-child(2) > .text-success-foreground.px-1\\.25.h-5"
+ ],
+ [
+ "tr[data-row-id=\"2\"] > td:nth-child(2) > .text-success-foreground.px-1\\.25.h-5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/servers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 468,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 836,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 464,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 57.5,
+ "domInteractiveMs": 151.80000000074506,
+ "durationMs": 416.70000000298023
+ },
+ "violations": [
+ {
+ "id": "color-contrast",
+ "impact": "serious",
+ "targets": [
+ [
+ "tr[data-row-id=\"1\"] > td:nth-child(2) > .text-success-foreground.px-1\\.25.h-5"
+ ],
+ [
+ "tr[data-row-id=\"2\"] > td:nth-child(2) > .text-success-foreground.px-1\\.25.h-5"
+ ]
+ ]
+ }
+ ],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/uptime",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 816,
+ "requiredReads": [
+ "/api/uptime/overview"
+ ],
+ "dataResponsesMs": 1180,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 796,
+ "cls": 0.01717699266975309,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 53.899999998509884,
+ "domInteractiveMs": 192.5,
+ "durationMs": 412.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/uptime",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 890,
+ "requiredReads": [
+ "/api/uptime/overview"
+ ],
+ "dataResponsesMs": 1246,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 880,
+ "cls": 0.08757044687499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 97.29999999701977,
+ "domInteractiveMs": 247.39999999850988,
+ "durationMs": 517.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/traffic",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 568,
+ "requiredReads": [
+ "/api/traffic/servers"
+ ],
+ "dataResponsesMs": 921,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1056,
+ "cls": 0.007212276415412808,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 92,
+ "domInteractiveMs": 226.5,
+ "durationMs": 540.6999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/traffic",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 543,
+ "requiredReads": [
+ "/api/traffic/servers"
+ ],
+ "dataResponsesMs": 896,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 556,
+ "cls": 0.11047610429687499,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.20000000298023,
+ "domInteractiveMs": 199.60000000149012,
+ "durationMs": 526.9000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/statistics",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 551,
+ "requiredReads": [
+ "/api/statistics"
+ ],
+ "dataResponsesMs": 911,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 720,
+ "cls": 0.05435100839120371,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.5,
+ "domInteractiveMs": 189.20000000298023,
+ "durationMs": 533.2000000029802
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/statistics",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 531,
+ "requiredReads": [
+ "/api/statistics"
+ ],
+ "dataResponsesMs": 641,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 620,
+ "cls": 0.07823437187499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 51.100000001490116,
+ "domInteractiveMs": 178.80000000074506,
+ "durationMs": 462.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/wireguard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 562,
+ "requiredReads": [
+ "/api/wireguard"
+ ],
+ "dataResponsesMs": 918,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 836,
+ "cls": 0.00016905755690586422,
+ "longTasks": 1,
+ "longTaskMs": 58,
+ "ttfbMs": 87,
+ "domInteractiveMs": 235,
+ "durationMs": 452.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/wireguard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 473,
+ "requiredReads": [
+ "/api/wireguard"
+ ],
+ "dataResponsesMs": 830,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 460,
+ "cls": 0.00063807578125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 65.69999999925494,
+ "domInteractiveMs": 156.10000000149012,
+ "durationMs": 396.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ipsec",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 587,
+ "requiredReads": [
+ "/api/ipsec"
+ ],
+ "dataResponsesMs": 1449,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 980,
+ "cls": 0.00006102382330246914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 102.80000000074506,
+ "domInteractiveMs": 233.69999999925494,
+ "durationMs": 519.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ipsec",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 958,
+ "requiredReads": [
+ "/api/ipsec"
+ ],
+ "dataResponsesMs": 958,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 540,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.69999999925494,
+ "domInteractiveMs": 141.79999999701977,
+ "durationMs": 446.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/gre",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 574,
+ "requiredReads": [
+ "/api/gre/tunnels"
+ ],
+ "dataResponsesMs": 950,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 552,
+ "cls": 0.06258026005497686,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 124.40000000223517,
+ "domInteractiveMs": 253.20000000298023,
+ "durationMs": 474.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/gre",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 478,
+ "requiredReads": [
+ "/api/gre/tunnels"
+ ],
+ "dataResponsesMs": 2353,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 476,
+ "cls": 0.10702192187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.20000000298023,
+ "domInteractiveMs": 158,
+ "durationMs": 407.4000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/vxlan",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 581,
+ "requiredReads": [
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 948,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 556,
+ "cls": 0.12087190923996914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 110.89999999850988,
+ "domInteractiveMs": 249.19999999925494,
+ "durationMs": 457.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/vxlan",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 452,
+ "requiredReads": [
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 1315,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 444,
+ "cls": 0.24465938125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 58.69999999925494,
+ "domInteractiveMs": 150.30000000074506,
+ "durationMs": 383.5999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ospf",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 571,
+ "requiredReads": [
+ "/api/ospf/catalog"
+ ],
+ "dataResponsesMs": 1434,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 540,
+ "cls": 0.0054106802782600315,
+ "longTasks": 1,
+ "longTaskMs": 50,
+ "ttfbMs": 105.90000000223517,
+ "domInteractiveMs": 244.89999999850988,
+ "durationMs": 470.29999999701977
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/ospf",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 485,
+ "requiredReads": [
+ "/api/ospf/catalog"
+ ],
+ "dataResponsesMs": 2343,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 444,
+ "cls": 0.019630139453125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.80000000074506,
+ "domInteractiveMs": 147.5,
+ "durationMs": 403.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/bgp",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 626,
+ "requiredReads": [
+ "/api/bgp/catalog"
+ ],
+ "dataResponsesMs": 2508,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 628,
+ "cls": 0.008227206790123459,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 109.10000000149012,
+ "domInteractiveMs": 242.89999999850988,
+ "durationMs": 571.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/bgp",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1032,
+ "requiredReads": [
+ "/api/bgp/catalog"
+ ],
+ "dataResponsesMs": 1033,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 764,
+ "cls": 0.043276721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.400000002235174,
+ "domInteractiveMs": 178.30000000074506,
+ "durationMs": 471.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/filters",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 875,
+ "requiredReads": [
+ "/api/filters/rules"
+ ],
+ "dataResponsesMs": 875,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 816,
+ "cls": 0.036411010079089506,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 166.10000000149012,
+ "domInteractiveMs": 310.20000000298023,
+ "durationMs": 603.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/filters",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 516,
+ "requiredReads": [
+ "/api/filters/rules"
+ ],
+ "dataResponsesMs": 516,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 524,
+ "cls": 0.073080721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.100000001490116,
+ "domInteractiveMs": 163.89999999850988,
+ "durationMs": 382
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/backups",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 520,
+ "requiredReads": [
+ "/api/backups"
+ ],
+ "dataResponsesMs": 877,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 500,
+ "cls": 0.000029851948302469136,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 96.5,
+ "domInteractiveMs": 191.80000000074506,
+ "durationMs": 421.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/backups",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 501,
+ "requiredReads": [
+ "/api/backups"
+ ],
+ "dataResponsesMs": 601,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 468,
+ "cls": 0.1396483580078125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 54.899999998509884,
+ "domInteractiveMs": 181.09999999776483,
+ "durationMs": 457.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/certificates",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 546,
+ "requiredReads": [
+ "/api/certificates/state"
+ ],
+ "dataResponsesMs": 2408,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 856,
+ "cls": 0.062860234375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 53.099999997764826,
+ "domInteractiveMs": 203.5,
+ "durationMs": 428.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/certificates",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 520,
+ "requiredReads": [
+ "/api/certificates/state"
+ ],
+ "dataResponsesMs": 2416,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 520,
+ "cls": 0.061482699999999994,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.19999999925494,
+ "domInteractiveMs": 176.89999999850988,
+ "durationMs": 450.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/containers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 548,
+ "requiredReads": [
+ "/api/containers/state"
+ ],
+ "dataResponsesMs": 2414,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 544,
+ "cls": 0.11558015046296297,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 109.20000000298023,
+ "domInteractiveMs": 239.30000000074506,
+ "durationMs": 441.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/containers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 502,
+ "requiredReads": [
+ "/api/containers/state"
+ ],
+ "dataResponsesMs": 2361,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 456,
+ "cls": 0.11563842187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50,
+ "domInteractiveMs": 173.80000000074506,
+ "durationMs": 391.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/alerts",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 581,
+ "requiredReads": [
+ "/api/alerts"
+ ],
+ "dataResponsesMs": 942,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 960,
+ "cls": 0.2825991898148148,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 94.40000000223517,
+ "domInteractiveMs": 226.10000000149012,
+ "durationMs": 504.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/alerts",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 510,
+ "requiredReads": [
+ "/api/alerts"
+ ],
+ "dataResponsesMs": 1381,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 948,
+ "cls": 0.67899836875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.19999999925494,
+ "domInteractiveMs": 180.09999999776483,
+ "durationMs": 347.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/settings",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 620,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 620,
+ "cls": 0.0035598427553530094,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 117.5,
+ "domInteractiveMs": 269.5,
+ "durationMs": 509.0999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/settings",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 531,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 480,
+ "cls": 0.0003595375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 65.09999999776483,
+ "domInteractiveMs": 216.60000000149012,
+ "durationMs": 391
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/releases",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 743,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 696,
+ "cls": 0.000013114872685185185,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 278.5,
+ "domInteractiveMs": 428.29999999701977,
+ "durationMs": 603.2999999970198
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/releases",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 530,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 464,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 62.5,
+ "domInteractiveMs": 217.09999999776483,
+ "durationMs": 386.5999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/dashboard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 588,
+ "requiredReads": [
+ "/api/servers",
+ "/api/uptime/overview",
+ "/api/filters/rules",
+ "/api/bgp/catalog",
+ "/api/internet-path/latest",
+ "/api/filters/gre-tunnels",
+ "/api/wireguard",
+ "/api/traffic/servers",
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 3482,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 596,
+ "cls": 0.11174993919843508,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 97.29999999701977,
+ "domInteractiveMs": 239.5,
+ "durationMs": 553
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/dashboard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 607,
+ "requiredReads": [
+ "/api/servers",
+ "/api/uptime/overview",
+ "/api/filters/rules",
+ "/api/bgp/catalog",
+ "/api/internet-path/latest",
+ "/api/filters/gre-tunnels",
+ "/api/wireguard",
+ "/api/traffic/servers",
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 2484,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 556,
+ "cls": 0.071302821875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 66.80000000074506,
+ "domInteractiveMs": 167.69999999925494,
+ "durationMs": 490.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/servers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 534,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 635,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 608,
+ "cls": 0.000044211847752700614,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 58,
+ "domInteractiveMs": 218.59999999776483,
+ "durationMs": 449.79999999701977
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/servers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 453,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 559,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 396,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 48.899999998509884,
+ "domInteractiveMs": 136.30000000074506,
+ "durationMs": 375.70000000298023
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/uptime",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 804,
+ "requiredReads": [
+ "/api/uptime/overview"
+ ],
+ "dataResponsesMs": 910,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 736,
+ "cls": 0.01717699266975309,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.5,
+ "domInteractiveMs": 182.90000000223517,
+ "durationMs": 465.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/uptime",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 808,
+ "requiredReads": [
+ "/api/uptime/overview"
+ ],
+ "dataResponsesMs": 916,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 748,
+ "cls": 0.08757044687499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.600000001490116,
+ "domInteractiveMs": 181.09999999776483,
+ "durationMs": 454.79999999701977
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/traffic",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 541,
+ "requiredReads": [
+ "/api/traffic/servers"
+ ],
+ "dataResponsesMs": 895,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 908,
+ "cls": 0.025901459757486978,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 90.80000000074506,
+ "domInteractiveMs": 218.69999999925494,
+ "durationMs": 509.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/traffic",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 572,
+ "requiredReads": [
+ "/api/traffic/servers"
+ ],
+ "dataResponsesMs": 930,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 588,
+ "cls": 0.11093063403320312,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.399999998509884,
+ "domInteractiveMs": 218.5,
+ "durationMs": 566.6999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/statistics",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 536,
+ "requiredReads": [
+ "/api/statistics"
+ ],
+ "dataResponsesMs": 900,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 676,
+ "cls": 0.05435100839120371,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.5,
+ "domInteractiveMs": 201.29999999701977,
+ "durationMs": 514.0999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/statistics",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 516,
+ "requiredReads": [
+ "/api/statistics"
+ ],
+ "dataResponsesMs": 616,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 620,
+ "cls": 0.07823437187499999,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 48.5,
+ "domInteractiveMs": 177.20000000298023,
+ "durationMs": 449.9000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/wireguard",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 548,
+ "requiredReads": [
+ "/api/wireguard"
+ ],
+ "dataResponsesMs": 900,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 820,
+ "cls": 0.00016905755690586422,
+ "longTasks": 1,
+ "longTaskMs": 54,
+ "ttfbMs": 88.60000000149012,
+ "domInteractiveMs": 221.59999999776483,
+ "durationMs": 433.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/wireguard",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 448,
+ "requiredReads": [
+ "/api/wireguard"
+ ],
+ "dataResponsesMs": 807,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 400,
+ "cls": 0.00063807578125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.399999998509884,
+ "domInteractiveMs": 136.80000000074506,
+ "durationMs": 341.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ipsec",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 621,
+ "requiredReads": [
+ "/api/ipsec"
+ ],
+ "dataResponsesMs": 2492,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1804,
+ "cls": 0.00006102382330246914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 95.5,
+ "domInteractiveMs": 226.90000000223517,
+ "durationMs": 654.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ipsec",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 516,
+ "requiredReads": [
+ "/api/ipsec"
+ ],
+ "dataResponsesMs": 1378,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 504,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 65.39999999850988,
+ "domInteractiveMs": 203.30000000074506,
+ "durationMs": 432.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/gre",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 553,
+ "requiredReads": [
+ "/api/gre/tunnels"
+ ],
+ "dataResponsesMs": 1409,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 524,
+ "cls": 0.06258026005497686,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 108.19999999925494,
+ "domInteractiveMs": 243.70000000298023,
+ "durationMs": 460.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/gre",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 484,
+ "requiredReads": [
+ "/api/gre/tunnels"
+ ],
+ "dataResponsesMs": 2342,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 488,
+ "cls": 0.10702192187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 48.899999998509884,
+ "domInteractiveMs": 133.89999999850988,
+ "durationMs": 448.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/vxlan",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 564,
+ "requiredReads": [
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 929,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 528,
+ "cls": 0.12087190923996914,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 106.80000000074506,
+ "domInteractiveMs": 244.5,
+ "durationMs": 446.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/vxlan",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 498,
+ "requiredReads": [
+ "/api/vxlan"
+ ],
+ "dataResponsesMs": 854,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 444,
+ "cls": 0.24465938125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.5,
+ "domInteractiveMs": 184.19999999925494,
+ "durationMs": 368.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ospf",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 544,
+ "requiredReads": [
+ "/api/ospf/catalog"
+ ],
+ "dataResponsesMs": 2408,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 544,
+ "cls": 0.0054106802782600315,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 95.80000000074506,
+ "domInteractiveMs": 225.70000000298023,
+ "durationMs": 637.6000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/ospf",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 503,
+ "requiredReads": [
+ "/api/ospf/catalog"
+ ],
+ "dataResponsesMs": 2382,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 444,
+ "cls": 0.019630139453125,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.900000002235174,
+ "domInteractiveMs": 178.80000000074506,
+ "durationMs": 421.0999999977648
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/bgp",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 616,
+ "requiredReads": [
+ "/api/bgp/catalog"
+ ],
+ "dataResponsesMs": 968,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 612,
+ "cls": 0.008227206790123459,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 106.70000000298023,
+ "domInteractiveMs": 275.9000000022352,
+ "durationMs": 549.1999999992549
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/bgp",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 478,
+ "requiredReads": [
+ "/api/bgp/catalog"
+ ],
+ "dataResponsesMs": 1363,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1228,
+ "cls": 0.043276721875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.399999998509884,
+ "domInteractiveMs": 156,
+ "durationMs": 394.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/filters",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 770,
+ "requiredReads": [
+ "/api/filters/rules"
+ ],
+ "dataResponsesMs": 770,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 700,
+ "cls": 0.036411010079089506,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 109.39999999850988,
+ "domInteractiveMs": 247.5,
+ "durationMs": 474.30000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/filters",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 609,
+ "requiredReads": [
+ "/api/filters/rules"
+ ],
+ "dataResponsesMs": 609,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 580,
+ "cls": 0.073081659375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.30000000074506,
+ "domInteractiveMs": 184.19999999925494,
+ "durationMs": 407.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/backups",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1066,
+ "requiredReads": [
+ "/api/backups"
+ ],
+ "dataResponsesMs": 1066,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 652,
+ "cls": 0.000029851948302469136,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 94.30000000074506,
+ "domInteractiveMs": 237.69999999925494,
+ "durationMs": 623.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/backups",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 462,
+ "requiredReads": [
+ "/api/backups"
+ ],
+ "dataResponsesMs": 815,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 420,
+ "cls": 0.1396459982421875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.30000000074506,
+ "domInteractiveMs": 142.5,
+ "durationMs": 394.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/certificates",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 536,
+ "requiredReads": [
+ "/api/certificates/state"
+ ],
+ "dataResponsesMs": 2389,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 804,
+ "cls": 0.06154219907407407,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 56.100000001490116,
+ "domInteractiveMs": 200.30000000074506,
+ "durationMs": 442.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/certificates",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 508,
+ "requiredReads": [
+ "/api/certificates/state"
+ ],
+ "dataResponsesMs": 2377,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 432,
+ "cls": 0.060179696874999994,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 52.5,
+ "domInteractiveMs": 181.39999999850988,
+ "durationMs": 371.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/containers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 615,
+ "requiredReads": [
+ "/api/containers/state"
+ ],
+ "dataResponsesMs": 2494,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 612,
+ "cls": 0.11558015046296297,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 119.5,
+ "domInteractiveMs": 282.30000000074506,
+ "durationMs": 490.69999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/containers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 494,
+ "requiredReads": [
+ "/api/containers/state"
+ ],
+ "dataResponsesMs": 2352,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 464,
+ "cls": 0.11563842187500001,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.29999999701977,
+ "domInteractiveMs": 179.69999999925494,
+ "durationMs": 386.3999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/alerts",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 617,
+ "requiredReads": [
+ "/api/alerts"
+ ],
+ "dataResponsesMs": 970,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 972,
+ "cls": 0.2825991898148148,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 88,
+ "domInteractiveMs": 277.69999999925494,
+ "durationMs": 480.80000000074506
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/alerts",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 472,
+ "requiredReads": [
+ "/api/alerts"
+ ],
+ "dataResponsesMs": 842,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 808,
+ "cls": 0.67622226875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 49.69999999925494,
+ "domInteractiveMs": 148.19999999925494,
+ "durationMs": 367.19999999925494
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/settings",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 539,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 496,
+ "cls": 0.0034562909613715283,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 50.400000002235174,
+ "domInteractiveMs": 221.60000000149012,
+ "durationMs": 397.4000000022352
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/settings",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 488,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 424,
+ "cls": 0.0003595375,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 59.69999999925494,
+ "domInteractiveMs": 168.19999999925494,
+ "durationMs": 342.8999999985099
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/releases",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 557,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 504,
+ "cls": 0.000013114872685185185,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 105.30000000074506,
+ "domInteractiveMs": 234.19999999925494,
+ "durationMs": 408.1000000014901
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/releases",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 576,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 496,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 66.09999999776483,
+ "domInteractiveMs": 238.80000000074506,
+ "durationMs": 412
+ },
+ "violations": [],
+ "auditCompleted": true
+ }
+ ],
+ "blockedWrites": [],
+ "bounds": [
+ "Default rendered state only; data can still be refreshing",
+ "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals",
+ "Automated WCAG-tagged checks, not manual screen-reader certification"
+ ],
+ "completed": true,
+ "passed": false
+}
diff --git a/docs/ux-ui/acceptance/ui260-servers-final.json b/docs/ux-ui/acceptance/ui260-servers-final.json
new file mode 100644
index 00000000..60f79d45
--- /dev/null
+++ b/docs/ux-ui/acceptance/ui260-servers-final.json
@@ -0,0 +1,135 @@
+{
+ "date": "2026-10-15",
+ "block": "12.5.260",
+ "origin": "http://localhost:3000",
+ "realPortalUiLogin": true,
+ "responseMocks": false,
+ "deviceWritesPermitted": false,
+ "cases": [
+ {
+ "theme": "dark",
+ "path": "/servers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 760,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 1639,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 1232,
+ "cls": 0.000044211847752700614,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 144.09999999776483,
+ "domInteractiveMs": 413.5,
+ "durationMs": 740.3000000007451
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "dark",
+ "path": "/servers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 480,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 585,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "dark",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 452,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 55.30000000074506,
+ "domInteractiveMs": 157.19999999925494,
+ "durationMs": 389.29999999701977
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/servers",
+ "width": 1440,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1150,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 1150,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 988,
+ "cls": 0.000044211847752700614,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 115.19999999925494,
+ "domInteractiveMs": 323.6000000014901,
+ "durationMs": 730.5
+ },
+ "violations": [],
+ "auditCompleted": true
+ },
+ {
+ "theme": "light",
+ "path": "/servers",
+ "width": 320,
+ "pageErrors": [],
+ "apiErrors": [],
+ "mainVisibleMs": 1052,
+ "requiredReads": [
+ "/api/servers"
+ ],
+ "dataResponsesMs": 1052,
+ "layout": {
+ "overflow": false,
+ "mainOverflow": false,
+ "theme": "light",
+ "pendingSkeletons": 0
+ },
+ "performance": {
+ "lcp": 632,
+ "cls": 0.000062321875,
+ "longTasks": 0,
+ "longTaskMs": 0,
+ "ttfbMs": 63,
+ "domInteractiveMs": 236,
+ "durationMs": 567
+ },
+ "violations": [],
+ "auditCompleted": true
+ }
+ ],
+ "blockedWrites": [],
+ "readOnlyPosts": [],
+ "bounds": [
+ "Default rendered state only; data can still be refreshing",
+ "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals",
+ "Automated WCAG-tagged checks, not manual screen-reader certification"
+ ],
+ "completed": true,
+ "passed": true
+}
diff --git a/docs/ux-ui/acceptance/ui260-summary.json b/docs/ux-ui/acceptance/ui260-summary.json
new file mode 100644
index 00000000..f226af55
--- /dev/null
+++ b/docs/ux-ui/acceptance/ui260-summary.json
@@ -0,0 +1,62 @@
+{
+ "date": "2026-10-15",
+ "block": "12.5.260",
+ "runtime": {
+ "backend": "ux-certificates258",
+ "frontend": "ux-accessibility260-final"
+ },
+ "baseline": {
+ "cases": 72,
+ "violatingCases": 23,
+ "documentOverflow": 0,
+ "internalBgpOverflowCases": 2
+ },
+ "final": {
+ "cases": 72,
+ "axeViolations": 0,
+ "documentOverflow": 0,
+ "mainOverflow": 0,
+ "pageErrors": 0,
+ "apiErrors": 0,
+ "blockedWrites": 0,
+ "pendingSkeletonCases": 0
+ },
+ "timings": {
+ "mainVisible": {
+ "count": 72,
+ "p50Ms": 548,
+ "p95Ms": 1052,
+ "maxMs": 1243
+ },
+ "requiredApiResponses": {
+ "count": 64,
+ "p50Ms": 1033,
+ "p95Ms": 2494,
+ "maxMs": 3482
+ }
+ },
+ "local": {
+ "initialE2E": {
+ "passed": 55,
+ "failed": 2
+ },
+ "afterFocusRemountFix": {
+ "passed": 6,
+ "failed": 0
+ },
+ "finalSharedCases": {
+ "passed": 4,
+ "failed": 0
+ },
+ "uniqueCasesThatPassed": 59,
+ "typecheck": "passed",
+ "scopedLint": "passed",
+ "productionBuild": "passed",
+ "sourceManifestMatched": 528
+ },
+ "bounds": [
+ "Default rendered state only; data can still be refreshing",
+ "Synthetic lab timing over SSH, no CPU/network throttling; not field Core Web Vitals",
+ "Automated WCAG-tagged checks, not manual screen-reader certification"
+ ]
+}
diff --git a/docs/ux-ui/screens/ui260-bgp.png b/docs/ux-ui/screens/ui260-bgp.png
new file mode 100644
index 00000000..cfcd121f
Binary files /dev/null and b/docs/ux-ui/screens/ui260-bgp.png differ
diff --git a/docs/ux-ui/screens/ui260-servers.png b/docs/ux-ui/screens/ui260-servers.png
new file mode 100644
index 00000000..4112f41e
Binary files /dev/null and b/docs/ux-ui/screens/ui260-servers.png differ
diff --git a/tests/e2e/server-rail-accessibility.spec.ts b/tests/e2e/server-rail-accessibility.spec.ts
new file mode 100644
index 00000000..13e391eb
--- /dev/null
+++ b/tests/e2e/server-rail-accessibility.spec.ts
@@ -0,0 +1,68 @@
+import { test, expect } from "@playwright/test"
+import axe from "axe-core"
+import { serverReadSchema } from "../../packages/contracts/src/servers"
+
+for (const theme of ["light", "dark"]) test(`Server type badges: all types have readable contrast/${theme}`, async ({ page }) => {
+ await page.addInitScript(value => localStorage.setItem("rl-theme", value), theme)
+ await page.route("**/health", route => route.fulfill({ json: { ok: true } }))
+ const servers = ["home-router", "exit-node", "jump-host"].map((type, i) => serverReadSchema.parse({
+ id: i + 1, name: `Контраст ${i + 1}`, host: `192.0.2.${i + 1}`, port: 443,
+ username: "fixture", password: "fixture", useSsl: true, verifySsl: true, type, site: "TEST", country: "RU", asn: "", comment: "",
+ enabled: true, lanSubnet: "", wanUplinks: [], createdAt: "2026-10-15T00:00:00Z", updatedAt: "2026-10-15T00:00:00Z",
+ status: "online", latency: 1, os: "7.24.5", model: "CHR", uptime: null, cpuLoad: null,
+ freeMemory: null, totalMemory: null, identityName: null, sessions: 0, polledAt: null,
+ }))
+ await page.route("**/api/**", route => route.fulfill({ json: new URL(route.request().url()).pathname === "/api/servers" ? servers
+ : new URL(route.request().url()).pathname === "/api/auth/config" ? { required: false } : {} }))
+ await page.goto("/servers")
+ for (const name of ["Home Router", "Exit Node", "JumpHost"]) await expect(page.getByRole("cell", { name, exact: true })).toBeVisible()
+ await page.addScriptTag({ content: axe.source })
+ const violations = await page.evaluate(async () => (await (window as unknown as { axe: typeof axe }).axe.run("#main-content", {
+ runOnly: { type: "rule", values: ["color-contrast"] },
+ })).violations.map(v => ({ id: v.id, targets: v.nodes.map(n => n.target) })))
+ expect(violations).toEqual([])
+})
+
+for (const theme of ["light", "dark"]) test(`Server rail: keyboard selection across pages and target sizes/${theme}`, async ({ page }) => {
+ await page.addInitScript(value => localStorage.setItem("rl-theme", value), theme)
+ await page.route("**/health", route => route.fulfill({ json: { ok: true } }))
+ await page.route("**/api/**", route => {
+ const path = new URL(route.request().url()).pathname
+ return route.fulfill({ json: path === "/api/auth/config" ? { required: false }
+ : path === "/api/servers" ? Array.from({ length: 61 }, (_, i) => ({
+ id: i + 1, name: `Роутер ${String(i + 1).padStart(3, "0")}`, host: `192.0.2.${i + 1}`,
+ enabled: true, status: "online", type: "home-router", country: "RU",
+ })) : path === "/api/gre/tunnels" ? { tunnels: [], failures: [] } : {} })
+ })
+ await page.goto("/gre")
+ const rail = page.getByRole("listbox", { name: "Серверы", exact: true })
+ const all = rail.getByRole("option", { name: /Все серверы/ })
+ await expect(all).toHaveAttribute("aria-selected", "true")
+ await all.focus()
+ await page.keyboard.press("ArrowDown")
+ const first = rail.getByRole("option", { name: /Роутер 001/ })
+ await expect(first).toBeFocused()
+ await expect(first).toHaveAttribute("aria-selected", "true")
+ await expect(page).toHaveURL(/server=1(?:&|$)/)
+ await page.keyboard.press("End")
+ const last = rail.getByRole("option", { name: /Роутер 061/ })
+ await expect(last).toBeFocused()
+ await expect(last).toHaveAttribute("aria-selected", "true")
+ await expect(last).toBeInViewport()
+ await expect(page).toHaveURL(/server=61(?:&|$)/)
+ await page.keyboard.press("ArrowUp")
+ await expect(rail.getByRole("option", { name: /Роутер 060/ })).toBeFocused()
+ await page.keyboard.press("Home")
+ await expect(all).toBeFocused()
+ await expect(all).toHaveAttribute("aria-selected", "true")
+ await page.keyboard.press("Tab")
+ await expect.poll(() => rail.evaluate(el => el.contains(document.activeElement))).toBe(false)
+ await page.addScriptTag({ content: axe.source })
+ const violations = await page.evaluate(async () => {
+ const engine = (window as unknown as { axe: typeof axe }).axe
+ return (await engine.run(document.querySelector('[role="listbox"][aria-label="Серверы"]')!, {
+ runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"] },
+ })).violations.map(v => ({ id: v.id, targets: v.nodes.map(n => n.target) }))
+ })
+ expect(violations).toEqual([])
+})