diff --git a/app/(main)/gre/page.tsx b/app/(main)/gre/page.tsx
index cb8b846f..d818ff45 100644
--- a/app/(main)/gre/page.tsx
+++ b/app/(main)/gre/page.tsx
@@ -15,7 +15,7 @@ import { FormField, FormToggle, SectionTitle } from "@/components/form-kit"
import { greTunnels as mockGreTunnels, grePools as mockGrePools, servers as mockServers } from "@/lib/data"
import type { GreTunnel, GreStatus, Server } from "@/lib/data"
import { useDataSource } from "@/lib/data-source"
-import { generateGreRsc } from "@/lib/gre-export"
+import { generateGreRsc, greExportWarnings } from "@/lib/gre-export"
import { greResponseSchema } from "@/lib/gre-response"
import { vxlanServersSchema } from "@/lib/vxlan-response"
import type { z } from "zod"
@@ -333,7 +333,7 @@ function GrePageContent({ selectedServerId, selectedTunnelId, allTunnelsHref, on
keepaliveRetries: form.keepaliveRetries,
dscp: form.dscp,
clampTcpMss: form.clampTcpMss,
- allowFastPath: form.allowFastPath,
+ allowFastPath: form.ipsecEnabled ? false : form.allowFastPath,
ipsecSecret: form.ipsecEnabled ? form.ipsecSecret : "",
}
}
@@ -757,7 +757,7 @@ function GrePageContent({ selectedServerId, selectedTunnelId, allTunnelsHref, on
open={!!codePreviewTunnel}
onClose={() => setCodePreviewTunnel(null)}
title={codePreviewTunnel?.name ?? "GRE"}
- description={(codePreviewTunnel?.ipsec && !codePreviewTunnel.ipsec.secret ? "Шаблон: ключ IPsec скрыт. Укажите ключ перед применением. " : "") + (exportContext?.mock ? "Моковый снимок · данные не получены с RouterOS" : `Снимок на момент открытия · ${exportContext?.at ? formatReceipt(exportContext.at) : "время получения неизвестно"} · ${exportContext?.stale ? "актуальность не подтверждена" : "последний успешный опрос"}`)}
+ description={(codePreviewTunnel ? greExportWarnings(codePreviewTunnel).join(" ") + " " : "") + (exportContext?.mock ? "Моковый снимок · данные не получены с RouterOS" : `Снимок на момент открытия · ${exportContext?.at ? formatReceipt(exportContext.at) : "время получения неизвестно"} · ${exportContext?.stale ? "актуальность не подтверждена" : "последний успешный опрос"}`)}
formats={[
{
id: "rsc",
@@ -905,14 +905,14 @@ function GrePageContent({ selectedServerId, selectedTunnelId, allTunnelsHref, on
{[
{ key: "clampTcpMss" as const, label: "Clamp TCP MSS", desc: "Ограничить MSS до MTU туннеля" },
- { key: "allowFastPath" as const, label: "Allow Fast Path", desc: "Ускоренный путь обработки пакетов; отключите для IPsec" },
+ { key: "allowFastPath" as const, label: "Allow Fast Path", desc: "Ускоренный путь обработки пакетов; при IPsec выключен" },
].map(({ key, label, desc }) => (
-
setT(key, v)} />
+ setT(key, v)} />
))}
diff --git a/backend/scripts/acceptance/gre-compatibility-sql.mjs b/backend/scripts/acceptance/gre-compatibility-sql.mjs
new file mode 100644
index 00000000..451909d9
--- /dev/null
+++ b/backend/scripts/acceptance/gre-compatibility-sql.mjs
@@ -0,0 +1,18 @@
+import assert from 'node:assert/strict'
+import fs from 'node:fs'
+import {createServer} from 'node:http'
+import {randomBytes} from 'node:crypto'
+const database=process.env.MMAPP_OWN_SQL_DB,url=new URL(process.env.DATABASE_URL)
+assert(/^mm_accept_166_\d+$/.test(database??''));assert.equal(url.pathname,'/'+database);assert.equal(url.username,database)
+process.env.AUTH_REQUIRED='true';process.env.AUTH_JWT_SECRET=randomBytes(32).toString('hex');process.env.AUTH_ISSUER='https://own212.invalid';process.env.AUTH_PORTAL_URL='https://own212.invalid'
+const {pool,closePool}=await import('/app/backend/dist/db/index.js'),{initDatabase}=await import('/app/backend/dist/db/bootstrap.js'),{buildApp}=await import('/app/backend/dist/index.js')
+assert.equal((await pool.query('SELECT current_database() AS name')).rows[0].name,database);assert(Object.values((await pool.query('SELECT rolsuper,rolcreatedb,rolcreaterole,rolbypassrls FROM pg_roles WHERE rolname=current_user')).rows[0]).every(v=>v===false))
+
+const rows=[],writes=[],cases=[];let app,serverId,nativeCalls=0;
+const native=createServer(async(req,res)=>{nativeCalls++;let text='';for await(const chunk of req)text+=chunk;const body=text?JSON.parse(text):{};res.setHeader('content-type','application/json');if(req.method==='GET'){res.end(JSON.stringify(req.url.endsWith('/interface/gre')?rows:[]));return}writes.push({method:req.method,path:req.url,body});if(req.method==='PUT'){const row={...body,'.id':'*'+(rows.length+1),running:'false'};rows.push(row);res.end(JSON.stringify(row));return}if(req.method==='PATCH'){const id=decodeURIComponent(req.url.split('/').pop()),row=rows.find(r=>r['.id']===id);assert(row);Object.assign(row,body);res.end(JSON.stringify(row));return}res.statusCode=500;res.end('{}')});await new Promise(r=>native.listen(0,'127.0.0.1',r));
+const report={date:'2026-10-13',block:'12.5.212',database,cases,scope:'Production compiled GRE create/patch using isolated PostgreSQL and native HTTP fixture',passed:false};
+try{await initDatabase();serverId=Number((await pool.query("INSERT INTO servers(name,host,port,use_ssl,enabled,username,password) VALUES('own212-native','127.0.0.1',$1,false,true,'fixture','fixture') RETURNING id",[native.address().port])).rows[0].id);app=await buildApp({logger:false,startScheduler:false});await app.ready();const token=app.jwt.sign({sub:'own212-writer',apps:['mm'],permissions:['mm:network:write'],iss:process.env.AUTH_ISSUER},{expiresIn:'5m'}),headers={authorization:'Bearer '+token};
+for(const field of ['keepaliveInterval','keepaliveRetries'])for(const value of [0,-1,1.5,4294967296]){const before=nativeCalls;const r=await app.inject({method:'POST',url:'/api/gre/tunnels',headers,payload:{serverId:String(serverId),name:'invalid',remoteAddress:'192.0.2.2',[field]:value}});assert.equal(r.statusCode,400);assert.equal(nativeCalls,before);cases.push({field,value,status:400,nativeCalls:0})}
+for(const params of [{},{keepaliveInterval:60},{keepaliveRetries:3},{keepaliveInterval:90,keepaliveRetries:7}]){const name='own212-'+rows.length;const r=await app.inject({method:'POST',url:'/api/gre/tunnels',headers,payload:{serverId:String(serverId),name,remoteAddress:'192.0.2.2',enabled:false,...params}});assert.equal(r.statusCode,201);const expected=(params.keepaliveInterval??10)+'s,'+(params.keepaliveRetries??10);assert.equal(rows.at(-1).keepalive,expected);cases.push({operation:'create',params,keepalive:expected,status:201})}
+rows[0].keepalive='1m,3';const patch=await app.inject({method:'PATCH',url:'/api/gre/tunnels',headers,payload:{serverId:String(serverId),rosId:rows[0]['.id'],comment:'own212 updated'}});assert.equal(patch.statusCode,200);assert.equal(rows[0].keepalive,'1m,3');assert.equal(rows[0].comment,'own212 updated');cases.push({operation:'patch-other-field',nativeKeepalivePreserved:'1m,3',status:200});const read=await app.inject({method:'GET',url:'/api/gre/tunnels?serverId='+serverId,headers});assert.equal(read.statusCode,200);assert.equal(read.json().tunnels.find(t=>t.name===rows[0].name).keepaliveInterval,60);cases.push({operation:'read-minutes',seconds:60,status:200});const encrypted=await app.inject({method:'POST',url:'/api/gre/tunnels',headers,payload:{serverId:String(serverId),name:'own212-psk',remoteAddress:'192.0.2.2',enabled:false,ipsecSecret:'own212-synthetic-psk'}});assert.equal(encrypted.statusCode,201);const secured=rows.find(r=>r.name==='own212-psk');assert.equal(secured['allow-fast-path'],'no');cases.push({operation:'psk-create-default-fastpath',status:201,fastPath:'no'});let before=nativeCalls;const conflict=await app.inject({method:'POST',url:'/api/gre/tunnels',headers,payload:{serverId:String(serverId),name:'conflict',remoteAddress:'192.0.2.2',ipsecSecret:'own212-synthetic-psk',allowFastPath:true}});assert.equal(conflict.statusCode,400);assert.equal(nativeCalls,before);cases.push({operation:'explicit-psk-fastpath-conflict',status:400,nativeCalls:0});before=writes.length;const invalidPatch=await app.inject({method:'PATCH',url:'/api/gre/tunnels',headers,payload:{serverId:String(serverId),rosId:secured['.id'],allowFastPath:true}});assert.equal(invalidPatch.statusCode,400);assert.equal(writes.length,before);assert.equal(secured['allow-fast-path'],'no');cases.push({operation:'existing-psk-fastpath-conflict',status:400,nativeWrites:0});const upgrade=await app.inject({method:'PATCH',url:'/api/gre/tunnels',headers,payload:{serverId:String(serverId),rosId:rows[1]['.id'],ipsecSecret:'own212-synthetic-psk'}});assert.equal(upgrade.statusCode,200);assert.equal(rows[1]['allow-fast-path'],'no');cases.push({operation:'enable-psk-with-omitted-fastpath',status:200,fastPath:'no'});report.passed=true;report.nativeWrites=writes.length;
+}catch(e){report.errorName=e.name;fs.writeFileSync('/app/backend/acceptance/gre212-private.log',String(e.stack??e),{mode:0o600})}finally{if(app)await app.close();native.closeAllConnections();await new Promise(r=>native.close(r));await closePool();fs.writeFileSync('/app/backend/acceptance/gre212-keepalive-result.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report))}if(!report.passed)process.exitCode=1;
diff --git a/backend/src/routes/gre.ts b/backend/src/routes/gre.ts
index 6a53a5b6..7b655ab7 100644
--- a/backend/src/routes/gre.ts
+++ b/backend/src/routes/gre.ts
@@ -98,7 +98,7 @@ function tunnelFromBody(body: z.infer & { keepaliveInt
keepalive,
dscp,
clampTcpMss: body.clampTcpMss,
- allowFastPath: body.allowFastPath,
+ allowFastPath: body.allowFastPath ?? !body.ipsecSecret?.trim(),
ipsecSecret: body.ipsecSecret ?? "",
}],
}).tunnels[0]!
@@ -122,6 +122,9 @@ const greRoutes: FastifyPluginAsyncZod = async (app) => {
return reply.status(400).send({ error: "Некорректное тело запроса", details: parsed.error.flatten() })
}
const body = parsed.data
+ if (body.ipsecSecret?.trim() && body.allowFastPath === true) {
+ return reply.status(400).send({ error: "IPsec несовместим с FastPath. Отключите FastPath для GRE с PSK." })
+ }
const server = await getEnabledServerById(body.serverId)
if (!server) return reply.status(404).send({ error: "Сервер не найден" })
const client = MikrotikClient.fromServer(server)
@@ -153,6 +156,9 @@ const greRoutes: FastifyPluginAsyncZod = async (app) => {
if (!live) return reply.status(404).send({ error: "Туннель не найден" })
if (body.rosId && body.rosId !== live.rosId) return reply.status(409).send({ error: "ID и имя GRE указывают на разные объекты" })
if (body.name && body.name !== live.name) return reply.status(409).send({ error: "Переименование GRE требует отдельной операции" })
+ if ((body.ipsecSecret ?? live.ipsecSecret).trim() && body.allowFastPath === true) {
+ return reply.status(400).send({ error: "IPsec несовместим с FastPath. Отключите FastPath для GRE с PSK." })
+ }
const original = state.snapshot.tunnels.find(t => t.name === live.name)!
const merged = tunnelFromBody({
serverId: body.serverId,
@@ -168,7 +174,7 @@ const greRoutes: FastifyPluginAsyncZod = async (app) => {
keepaliveRetries: body.keepaliveRetries ?? parseKeepalive(live.keepalive).retries,
dscp: body.dscp ?? live.dscp,
clampTcpMss: body.clampTcpMss ?? live.clampTcpMss,
- allowFastPath: body.allowFastPath ?? live.allowFastPath,
+ allowFastPath: body.allowFastPath ?? (body.ipsecSecret?.trim() ? false : live.allowFastPath),
ipsecSecret: body.ipsecSecret ?? live.ipsecSecret,
})
// A patch to another field must not reformat an observed native timer.
diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md
index 2dcc4bac..f8a20e2e 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-13: блок 211 завершён. Закрыт анонимный обход JWT и section RBAC через encoded URL: проверяется маршрут Fastify, HEAD наследует GET policy, OSPF parameter routes сохраняют права. Пройдены auth42, production SQL/API29 и live26; тестовые DB/role/users удалены. Backend ux-auth211/source477/compiled1048, frontend ux-gre210. Следующий212 — совместимость GRE FastPath/PSK и шаблон экспорта недопустимого keepalive. Этап12.5 открыт.
+Актуальное продолжение 2026-10-13: блок 212 завершён. GRE с PSK по умолчанию отключает FastPath; явный конфликт получает400. Форма и экспорт учитывают совместимость и недопустимый keepalive. SQL/API18, E2E10 и live GRE/Filters UI4 прошли; два собственных интерфейса удалены. Backend/frontend ux-gre212; source478/527 и compiled1048 совпали. Auth-исправление211 сохранено. Следующий213 — выбор IPv4/IPv6-команды в GRE экспорте. Этап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).
@@ -2571,3 +2571,7 @@ Production compiled SQL/API29 на собственной ограниченно
Следующий212: GRE create с PSK и опущенным FastPath, explicit incompatible combination, экспорт недопустимого/неизвестного keepalive. 12.5 остаётся открытой по внешней матрице.
+
+## 12.5.212 — GRE PSK/FastPath (2026-10-13)
+
+Результат, проверки и ограничения: [полная запись212](ux-ui-progress.md#125212--совместимость-gre-pskfastpath-и-шаблоны-экспорта-2026-10-13). Runtime ux-gre212, SQL/API18, E2E10, native/UI и cleanup подтверждены; auth211 сохранён.
diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md
index 2d497157..04ec6e8b 100644
--- a/docs/ux-ui-plan.md
+++ b/docs/ux-ui-plan.md
@@ -1,6 +1,6 @@
# План реализации UX/UI по пятичасовым окнам
-Актуальное продолжение 2026-10-13: блок 211 завершён. Закрыт анонимный обход JWT и section RBAC через encoded URL: проверяется маршрут Fastify, HEAD наследует GET policy, OSPF parameter routes сохраняют права. Пройдены auth42, production SQL/API29 и live26; тестовые DB/role/users удалены. Backend ux-auth211/source477/compiled1048, frontend ux-gre210. Следующий212 — совместимость GRE FastPath/PSK и шаблон экспорта недопустимого keepalive. Этап12.5 открыт.
+Актуальное продолжение 2026-10-13: блок 212 завершён. GRE с PSK по умолчанию отключает FastPath; явный конфликт получает400. Форма и экспорт учитывают совместимость и недопустимый keepalive. SQL/API18, E2E10 и live GRE/Filters UI4 прошли; два собственных интерфейса удалены. Backend/frontend ux-gre212; source478/527 и compiled1048 совпали. Auth-исправление211 сохранено. Следующий213 — выбор IPv4/IPv6-команды в GRE экспорте. Этап12.5 открыт.
Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-13. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат.
diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md
index 5778ece1..5306ad59 100644
--- a/docs/ux-ui-progress.md
+++ b/docs/ux-ui-progress.md
@@ -1,6 +1,6 @@
# UX/UI: журнал реализации
-Актуальное продолжение 2026-10-13: блок 211 завершён. Закрыт анонимный обход JWT и section RBAC через encoded URL: проверяется маршрут Fastify, HEAD наследует GET policy, OSPF parameter routes сохраняют права. Пройдены auth42, production SQL/API29 и live26; тестовые DB/role/users удалены. Backend ux-auth211/source477/compiled1048, frontend ux-gre210. Следующий212 — совместимость GRE FastPath/PSK и шаблон экспорта недопустимого keepalive. Этап12.5 открыт.
+Актуальное продолжение 2026-10-13: блок 212 завершён. GRE с PSK по умолчанию отключает FastPath; явный конфликт получает400. Форма и экспорт учитывают совместимость и недопустимый keepalive. SQL/API18, E2E10 и live GRE/Filters UI4 прошли; два собственных интерфейса удалены. Backend/frontend ux-gre212; source478/527 и compiled1048 совпали. Auth-исправление211 сохранено. Следующий213 — выбор IPv4/IPv6-команды в GRE экспорте. Этап12.5 открыт.
Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md).
@@ -38,6 +38,22 @@
| 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 |
| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная приёмка завершена в границах12.3.83; внешняя12.5 в работе, выполненные блоки1–196 и ограничения ниже. Runtime backend/frontend ux-peak196. |
+## 12.5.212 — совместимость GRE PSK/FastPath и шаблоны экспорта (2026-10-13)
+
+POST с PSK и опущенным allowFastPath теперь создаёт GRE с FastPath=false. При явном PSK+FastPath=true возвращается400 до native requests; PATCH учитывает уже настроенный PSK и не пишет несовместимое состояние. Включение PSK через PATCH без FastPath отключает его; изменения другого поля сохраняют прочие настройки. Форма показывает FastPath выключенным и недоступным при IPsec, отправляет false. Global IPsec peer/policy/proposal не меняются.
+
+Экспорт недопустимого keepalive или несовместимого IPsec/FastPath явно обозначен как шаблон: вместо готовой неверной команды выводятся , , и пояснения. Скрытый PSK сохраняет прежний . Валидные наблюдаемые значения и escaping не изменены. ReUI primitives и тема не затронуты.
+
+Проверки: production SQL/API18 на собственной ограниченной DB/role, native HTTP fixture; DB/role/container удалены. Профиль live-maps, GRE export/schema/network-map units, TypeScript/build/ESLint прошли. E2E10: FastPath при включении IPsec, прежние PSK/keepalive/reader export и несовместимый снимок на1440/320. Source478/527 и compiled1048 совпали; backend/frontend ux-gre212 healthy/running, auth211 сохранён.
+
+Live: по одному собственному отключённому GRE с PSK на каждом CHR без явных keepalive/FastPath; native10s,10 и FastPath=false подтверждены, конфликт PATCH400 сохраняет состояние. Четыре UI GRE/Filters на1440/320, без UI writes/errors. Оба интерфейса удалены; пароли не менялись. Это проверка конфигурации/API/интерфейса, не data plane.
+
+Первый deploy preflight обнаружил старое имя отчёта gre212-keepalive-result.json. Публичная копия названа compatibility с сохранением originalReportedFilename; содержимое18 cases и cleanup проверено. До успешного deploy preflight стенд оставался на211, поэтому преждевременный live create получил старый502 и был очищен; финальная проверка началась после health и прошла. На стенде очищен только неиспользуемый Docker build cache старше часа; runtime и rollback images сохранены.
+
+Протоколы: [SQL/API18](ux-ui/acceptance/gre212-compatibility-result.json), [профили](ux-ui/acceptance/gre212-profile-2026-10-13.json), [E2E10](ux-ui/acceptance/gre212-e2e-2026-10-13.json), [native/UI/cleanup](ux-ui/acceptance/gre212-native-live-2026-10-13.json), [source](ux-ui/acceptance/source212-result.json), [compiled](ux-ui/acceptance/compiled212-result.json), [deployment](ux-ui/acceptance/gre212-deployment-2026-10-13.json).
+
+Следующий213: GRE exporter всегда использует /ip/address/add, даже для уже поддерживаемого приложением внутреннего IPv6. Исправить family и проверить собственные ULA без обещания public IPv6/terminal import.
+
## 12.5.211 — JWT/RBAC для закодированных URL (2026-10-13)
Actual baseline: обычный /api/servers без JWT возвращал401, но /%61pi/servers возвращал200 и server rows с непустыми паролями. Fastify распознаёт encoded static segments; middleware сравнивал raw URL и пропускал auth для encoded /api prefix. Для encoded имени раздела можно было также получить fallback dashboard permission. В evidence сохранены только статусы, число строк и boolean наличия пароля; raw body, credentials и JWT не сохранялись и не выводились.
diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md
index c1c8a1e2..f8813fd5 100644
--- a/docs/ux-ui-remaining-acceptance.md
+++ b/docs/ux-ui-remaining-acceptance.md
@@ -1,10 +1,10 @@
# Оставшаяся внешняя приёмка — 2026-10-13
-Актуальное продолжение 2026-10-13: блок 211 завершён. Закрыт анонимный обход JWT и section RBAC через encoded URL: проверяется маршрут Fastify, HEAD наследует GET policy, OSPF parameter routes сохраняют права. Пройдены auth42, production SQL/API29 и live26; тестовые DB/role/users удалены. Backend ux-auth211/source477/compiled1048, frontend ux-gre210. Следующий212 — совместимость GRE FastPath/PSK и шаблон экспорта недопустимого keepalive. Этап12.5 открыт.
+Актуальное продолжение 2026-10-13: блок 212 завершён. GRE с PSK по умолчанию отключает FastPath; явный конфликт получает400. Форма и экспорт учитывают совместимость и недопустимый keepalive. SQL/API18, E2E10 и live GRE/Filters UI4 прошли; два собственных интерфейса удалены. Backend/frontend ux-gre212; source478/527 и compiled1048 совпали. Auth-исправление211 сохранено. Следующий213 — выбор IPv4/IPv6-команды в GRE экспорте. Этап12.5 открыт.
-Это актуальная матрица после 12.5.211. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
+Это актуальная матрица после 12.5.212. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии.
-Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-auth211`, frontend `ux-gre210`; 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/frontend `ux-gre212`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись.
| Раздел | Подтверждено | Осталось / условие завершения |
| --- | --- | --- |
@@ -26,7 +26,7 @@
## Следующая последовательность — 2026-10-13
-1. **212 — GRE compatibility:** PSK/FastPath default и explicit conflict; экспорт недопустимого/неизвестного keepalive. Canonical auth/RBAC211 закрыт и развёрнут.
+1. **213 — GRE IPv6 export:** команда локального IPv6 вместо /ip/address/add; собственные ULA и профильные UI/экспорт проверки.
2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания.
3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций.
@@ -1489,3 +1489,7 @@ Production compiled SQL/API29 на собственной ограниченно
Следующий212: GRE create с PSK и опущенным FastPath, explicit incompatible combination, экспорт недопустимого/неизвестного keepalive. 12.5 остаётся открытой по внешней матрице.
+
+## 12.5.212 — GRE PSK/FastPath (2026-10-13)
+
+Результат, проверки и ограничения: [полная запись212](ux-ui-progress.md#125212--совместимость-gre-pskfastpath-и-шаблоны-экспорта-2026-10-13). Runtime ux-gre212, SQL/API18, E2E10, native/UI и cleanup подтверждены; auth211 сохранён.
diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md
index 928cba8d..556a50ec 100644
--- a/docs/ux-ui-stage-12.md
+++ b/docs/ux-ui-stage-12.md
@@ -1,6 +1,6 @@
# Этап 12: итоговая приёмка UX/UI
-Актуальное продолжение 2026-10-13: блок 211 завершён. Закрыт анонимный обход JWT и section RBAC через encoded URL: проверяется маршрут Fastify, HEAD наследует GET policy, OSPF parameter routes сохраняют права. Пройдены auth42, production SQL/API29 и live26; тестовые DB/role/users удалены. Backend ux-auth211/source477/compiled1048, frontend ux-gre210. Следующий212 — совместимость GRE FastPath/PSK и шаблон экспорта недопустимого keepalive. Этап12.5 открыт.
+Актуальное продолжение 2026-10-13: блок 212 завершён. GRE с PSK по умолчанию отключает FastPath; явный конфликт получает400. Форма и экспорт учитывают совместимость и недопустимый keepalive. SQL/API18, E2E10 и live GRE/Filters UI4 прошли; два собственных интерфейса удалены. Backend/frontend ux-gre212; source478/527 и compiled1048 совпали. Auth-исправление211 сохранено. Следующий213 — выбор IPv4/IPv6-команды в GRE экспорте. Этап12.5 открыт.
План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push.
@@ -3878,3 +3878,20 @@ Production compiled SQL/API29 на собственной ограниченно
Следующий212: GRE create с PSK и опущенным FastPath, explicit incompatible combination, экспорт недопустимого/неизвестного keepalive. 12.5 остаётся открытой по внешней матрице.
+
+## 12.5.212 — совместимость GRE PSK/FastPath и шаблоны экспорта (2026-10-13)
+
+POST с PSK и опущенным allowFastPath теперь создаёт GRE с FastPath=false. При явном PSK+FastPath=true возвращается400 до native requests; PATCH учитывает уже настроенный PSK и не пишет несовместимое состояние. Включение PSK через PATCH без FastPath отключает его; изменения другого поля сохраняют прочие настройки. Форма показывает FastPath выключенным и недоступным при IPsec, отправляет false. Global IPsec peer/policy/proposal не меняются.
+
+Экспорт недопустимого keepalive или несовместимого IPsec/FastPath явно обозначен как шаблон: вместо готовой неверной команды выводятся , , и пояснения. Скрытый PSK сохраняет прежний . Валидные наблюдаемые значения и escaping не изменены. ReUI primitives и тема не затронуты.
+
+Проверки: production SQL/API18 на собственной ограниченной DB/role, native HTTP fixture; DB/role/container удалены. Профиль live-maps, GRE export/schema/network-map units, TypeScript/build/ESLint прошли. E2E10: FastPath при включении IPsec, прежние PSK/keepalive/reader export и несовместимый снимок на1440/320. Source478/527 и compiled1048 совпали; backend/frontend ux-gre212 healthy/running, auth211 сохранён.
+
+Live: по одному собственному отключённому GRE с PSK на каждом CHR без явных keepalive/FastPath; native10s,10 и FastPath=false подтверждены, конфликт PATCH400 сохраняет состояние. Четыре UI GRE/Filters на1440/320, без UI writes/errors. Оба интерфейса удалены; пароли не менялись. Это проверка конфигурации/API/интерфейса, не data plane.
+
+Первый deploy preflight обнаружил старое имя отчёта gre212-keepalive-result.json. Публичная копия названа compatibility с сохранением originalReportedFilename; содержимое18 cases и cleanup проверено. До успешного deploy preflight стенд оставался на211, поэтому преждевременный live create получил старый502 и был очищен; финальная проверка началась после health и прошла. На стенде очищен только неиспользуемый Docker build cache старше часа; runtime и rollback images сохранены.
+
+Протоколы: [SQL/API18](ux-ui/acceptance/gre212-compatibility-result.json), [профили](ux-ui/acceptance/gre212-profile-2026-10-13.json), [E2E10](ux-ui/acceptance/gre212-e2e-2026-10-13.json), [native/UI/cleanup](ux-ui/acceptance/gre212-native-live-2026-10-13.json), [source](ux-ui/acceptance/source212-result.json), [compiled](ux-ui/acceptance/compiled212-result.json), [deployment](ux-ui/acceptance/gre212-deployment-2026-10-13.json).
+
+Следующий213: GRE exporter всегда использует /ip/address/add, даже для уже поддерживаемого приложением внутреннего IPv6. Исправить family и проверить собственные ULA без обещания public IPv6/terminal import.
+
diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md
index 49a2dec5..0bc82a64 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-13: блок 211 завершён. Закрыт анонимный обход JWT и section RBAC через encoded URL: проверяется маршрут Fastify, HEAD наследует GET policy, OSPF parameter routes сохраняют права. Пройдены auth42, production SQL/API29 и live26; тестовые DB/role/users удалены. Backend ux-auth211/source477/compiled1048, frontend ux-gre210. Следующий212 — совместимость GRE FastPath/PSK и шаблон экспорта недопустимого keepalive. Этап12.5 открыт.
+Актуальное продолжение 2026-10-13: блок 212 завершён. GRE с PSK по умолчанию отключает FastPath; явный конфликт получает400. Форма и экспорт учитывают совместимость и недопустимый keepalive. SQL/API18, E2E10 и live GRE/Filters UI4 прошли; два собственных интерфейса удалены. Backend/frontend ux-gre212; source478/527 и compiled1048 совпали. Auth-исправление211 сохранено. Следующий213 — выбор IPv4/IPv6-команды в GRE экспорте. Этап12.5 открыт.
| Параметр | Проверенное состояние |
| --- | --- |
@@ -2302,3 +2302,7 @@ Production compiled SQL/API29 на собственной ограниченно
Следующий212: GRE create с PSK и опущенным FastPath, explicit incompatible combination, экспорт недопустимого/неизвестного keepalive. 12.5 остаётся открытой по внешней матрице.
+
+## 12.5.212 — GRE PSK/FastPath (2026-10-13)
+
+Результат, проверки и ограничения: [полная запись212](ux-ui-progress.md#125212--совместимость-gre-pskfastpath-и-шаблоны-экспорта-2026-10-13). Runtime ux-gre212, SQL/API18, E2E10, native/UI и cleanup подтверждены; auth211 сохранён.
diff --git a/docs/ux-ui/acceptance/compiled212-result.json b/docs/ux-ui/acceptance/compiled212-result.json
new file mode 100644
index 00000000..9e95d60f
--- /dev/null
+++ b/docs/ux-ui/acceptance/compiled212-result.json
@@ -0,0 +1,1058 @@
+{
+ "date": "2026-10-13",
+ "block": "12.5.212",
+ "backendVersion": "1.0.0-test.gre212",
+ "normalization": "CRLF to LF",
+ "matched": [
+ "backend/dist/config.js",
+ "backend/dist/config.js.map",
+ "backend/dist/config.d.ts",
+ "backend/dist/config.d.ts.map",
+ "backend/dist/constants/ping-probe.js",
+ "backend/dist/constants/ping-probe.js.map",
+ "backend/dist/constants/ping-probe.d.ts",
+ "backend/dist/constants/ping-probe.d.ts.map",
+ "backend/dist/db/bootstrap.js",
+ "backend/dist/db/bootstrap.js.map",
+ "backend/dist/db/bootstrap.d.ts",
+ "backend/dist/db/bootstrap.d.ts.map",
+ "backend/dist/db/index.js",
+ "backend/dist/db/index.js.map",
+ "backend/dist/db/index.d.ts",
+ "backend/dist/db/index.d.ts.map",
+ "backend/dist/db/json.js",
+ "backend/dist/db/json.js.map",
+ "backend/dist/db/json.d.ts",
+ "backend/dist/db/json.d.ts.map",
+ "backend/dist/db/migrate.js",
+ "backend/dist/db/migrate.js.map",
+ "backend/dist/db/migrate.d.ts",
+ "backend/dist/db/migrate.d.ts.map",
+ "backend/dist/db/operation-lock.js",
+ "backend/dist/db/operation-lock.js.map",
+ "backend/dist/db/operation-lock.d.ts",
+ "backend/dist/db/operation-lock.d.ts.map",
+ "backend/dist/db/partitions.js",
+ "backend/dist/db/partitions.js.map",
+ "backend/dist/db/partitions.d.ts",
+ "backend/dist/db/partitions.d.ts.map",
+ "backend/dist/db/schema.js",
+ "backend/dist/db/schema.js.map",
+ "backend/dist/db/schema.d.ts",
+ "backend/dist/db/schema.d.ts.map",
+ "backend/dist/db/sql-bind.js",
+ "backend/dist/db/sql-bind.js.map",
+ "backend/dist/db/sql-bind.d.ts",
+ "backend/dist/db/sql-bind.d.ts.map",
+ "backend/dist/db/sqlite-import.js",
+ "backend/dist/db/sqlite-import.js.map",
+ "backend/dist/db/sqlite-import.d.ts",
+ "backend/dist/db/sqlite-import.d.ts.map",
+ "backend/dist/db/traffic-flags.js",
+ "backend/dist/db/traffic-flags.js.map",
+ "backend/dist/db/traffic-flags.d.ts",
+ "backend/dist/db/traffic-flags.d.ts.map",
+ "backend/dist/index.js",
+ "backend/dist/index.js.map",
+ "backend/dist/index.d.ts",
+ "backend/dist/index.d.ts.map",
+ "backend/dist/lib/permissions.js",
+ "backend/dist/lib/permissions.js.map",
+ "backend/dist/lib/permissions.d.ts",
+ "backend/dist/lib/permissions.d.ts.map",
+ "backend/dist/managed-markers.js",
+ "backend/dist/managed-markers.js.map",
+ "backend/dist/managed-markers.d.ts",
+ "backend/dist/managed-markers.d.ts.map",
+ "backend/dist/modules/events/repository/events-repository.js",
+ "backend/dist/modules/events/repository/events-repository.js.map",
+ "backend/dist/modules/events/repository/events-repository.d.ts",
+ "backend/dist/modules/events/repository/events-repository.d.ts.map",
+ "backend/dist/modules/events/service/events-service.js",
+ "backend/dist/modules/events/service/events-service.js.map",
+ "backend/dist/modules/events/service/events-service.d.ts",
+ "backend/dist/modules/events/service/events-service.d.ts.map",
+ "backend/dist/modules/servers/mapper/servers-mapper.js",
+ "backend/dist/modules/servers/mapper/servers-mapper.js.map",
+ "backend/dist/modules/servers/mapper/servers-mapper.d.ts",
+ "backend/dist/modules/servers/mapper/servers-mapper.d.ts.map",
+ "backend/dist/modules/servers/repository/servers-repository.js",
+ "backend/dist/modules/servers/repository/servers-repository.js.map",
+ "backend/dist/modules/servers/repository/servers-repository.d.ts",
+ "backend/dist/modules/servers/repository/servers-repository.d.ts.map",
+ "backend/dist/modules/servers/service/servers-service.js",
+ "backend/dist/modules/servers/service/servers-service.js.map",
+ "backend/dist/modules/servers/service/servers-service.d.ts",
+ "backend/dist/modules/servers/service/servers-service.d.ts.map",
+ "backend/dist/modules/users/iface-type.js",
+ "backend/dist/modules/users/iface-type.js.map",
+ "backend/dist/modules/users/iface-type.d.ts",
+ "backend/dist/modules/users/iface-type.d.ts.map",
+ "backend/dist/modules/users/interface-inventory.js",
+ "backend/dist/modules/users/interface-inventory.js.map",
+ "backend/dist/modules/users/interface-inventory.d.ts",
+ "backend/dist/modules/users/interface-inventory.d.ts.map",
+ "backend/dist/modules/users/peer-bind.js",
+ "backend/dist/modules/users/peer-bind.js.map",
+ "backend/dist/modules/users/peer-bind.d.ts",
+ "backend/dist/modules/users/peer-bind.d.ts.map",
+ "backend/dist/modules/users/repository/users-repository.js",
+ "backend/dist/modules/users/repository/users-repository.js.map",
+ "backend/dist/modules/users/repository/users-repository.d.ts",
+ "backend/dist/modules/users/repository/users-repository.d.ts.map",
+ "backend/dist/modules/users/service/users-service.js",
+ "backend/dist/modules/users/service/users-service.js.map",
+ "backend/dist/modules/users/service/users-service.d.ts",
+ "backend/dist/modules/users/service/users-service.d.ts.map",
+ "backend/dist/plugins/auth.js",
+ "backend/dist/plugins/auth.js.map",
+ "backend/dist/plugins/auth.d.ts",
+ "backend/dist/plugins/auth.d.ts.map",
+ "backend/dist/plugins/database-request-gate.js",
+ "backend/dist/plugins/database-request-gate.js.map",
+ "backend/dist/plugins/database-request-gate.d.ts",
+ "backend/dist/plugins/database-request-gate.d.ts.map",
+ "backend/dist/routes/alerts.js",
+ "backend/dist/routes/alerts.js.map",
+ "backend/dist/routes/alerts.d.ts",
+ "backend/dist/routes/alerts.d.ts.map",
+ "backend/dist/routes/backups.js",
+ "backend/dist/routes/backups.js.map",
+ "backend/dist/routes/backups.d.ts",
+ "backend/dist/routes/backups.d.ts.map",
+ "backend/dist/routes/bgp.js",
+ "backend/dist/routes/bgp.js.map",
+ "backend/dist/routes/bgp.d.ts",
+ "backend/dist/routes/bgp.d.ts.map",
+ "backend/dist/routes/certificates.js",
+ "backend/dist/routes/certificates.js.map",
+ "backend/dist/routes/certificates.d.ts",
+ "backend/dist/routes/certificates.d.ts.map",
+ "backend/dist/routes/containers.js",
+ "backend/dist/routes/containers.js.map",
+ "backend/dist/routes/containers.d.ts",
+ "backend/dist/routes/containers.d.ts.map",
+ "backend/dist/routes/events.js",
+ "backend/dist/routes/events.js.map",
+ "backend/dist/routes/events.d.ts",
+ "backend/dist/routes/events.d.ts.map",
+ "backend/dist/routes/evobgp-catalog.js",
+ "backend/dist/routes/evobgp-catalog.js.map",
+ "backend/dist/routes/evobgp-catalog.d.ts",
+ "backend/dist/routes/evobgp-catalog.d.ts.map",
+ "backend/dist/routes/evobgp-settings.js",
+ "backend/dist/routes/evobgp-settings.js.map",
+ "backend/dist/routes/evobgp-settings.d.ts",
+ "backend/dist/routes/evobgp-settings.d.ts.map",
+ "backend/dist/routes/evobgp.js",
+ "backend/dist/routes/evobgp.js.map",
+ "backend/dist/routes/evobgp.d.ts",
+ "backend/dist/routes/evobgp.d.ts.map",
+ "backend/dist/routes/exec.js",
+ "backend/dist/routes/exec.js.map",
+ "backend/dist/routes/exec.d.ts",
+ "backend/dist/routes/exec.d.ts.map",
+ "backend/dist/routes/filters.js",
+ "backend/dist/routes/filters.js.map",
+ "backend/dist/routes/filters.d.ts",
+ "backend/dist/routes/filters.d.ts.map",
+ "backend/dist/routes/firewall.js",
+ "backend/dist/routes/firewall.js.map",
+ "backend/dist/routes/firewall.d.ts",
+ "backend/dist/routes/firewall.d.ts.map",
+ "backend/dist/routes/geoip.js",
+ "backend/dist/routes/geoip.js.map",
+ "backend/dist/routes/geoip.d.ts",
+ "backend/dist/routes/geoip.d.ts.map",
+ "backend/dist/routes/gre.js",
+ "backend/dist/routes/gre.js.map",
+ "backend/dist/routes/gre.d.ts",
+ "backend/dist/routes/gre.d.ts.map",
+ "backend/dist/routes/internet-path.js",
+ "backend/dist/routes/internet-path.js.map",
+ "backend/dist/routes/internet-path.d.ts",
+ "backend/dist/routes/internet-path.d.ts.map",
+ "backend/dist/routes/ipsec.js",
+ "backend/dist/routes/ipsec.js.map",
+ "backend/dist/routes/ipsec.d.ts",
+ "backend/dist/routes/ipsec.d.ts.map",
+ "backend/dist/routes/network.js",
+ "backend/dist/routes/network.js.map",
+ "backend/dist/routes/network.d.ts",
+ "backend/dist/routes/network.d.ts.map",
+ "backend/dist/routes/ospf.js",
+ "backend/dist/routes/ospf.js.map",
+ "backend/dist/routes/ospf.d.ts",
+ "backend/dist/routes/ospf.d.ts.map",
+ "backend/dist/routes/ping-probes-write.js",
+ "backend/dist/routes/ping-probes-write.js.map",
+ "backend/dist/routes/ping-probes-write.d.ts",
+ "backend/dist/routes/ping-probes-write.d.ts.map",
+ "backend/dist/routes/probes.js",
+ "backend/dist/routes/probes.js.map",
+ "backend/dist/routes/probes.d.ts",
+ "backend/dist/routes/probes.d.ts.map",
+ "backend/dist/routes/recursive-routes.js",
+ "backend/dist/routes/recursive-routes.js.map",
+ "backend/dist/routes/recursive-routes.d.ts",
+ "backend/dist/routes/recursive-routes.d.ts.map",
+ "backend/dist/routes/scheduler.js",
+ "backend/dist/routes/scheduler.js.map",
+ "backend/dist/routes/scheduler.d.ts",
+ "backend/dist/routes/scheduler.d.ts.map",
+ "backend/dist/routes/servers-api-ping.js",
+ "backend/dist/routes/servers-api-ping.js.map",
+ "backend/dist/routes/servers-api-ping.d.ts",
+ "backend/dist/routes/servers-api-ping.d.ts.map",
+ "backend/dist/routes/servers.js",
+ "backend/dist/routes/servers.js.map",
+ "backend/dist/routes/servers.d.ts",
+ "backend/dist/routes/servers.d.ts.map",
+ "backend/dist/routes/sidebar-counts.js",
+ "backend/dist/routes/sidebar-counts.js.map",
+ "backend/dist/routes/sidebar-counts.d.ts",
+ "backend/dist/routes/sidebar-counts.d.ts.map",
+ "backend/dist/routes/speed-probes-write.js",
+ "backend/dist/routes/speed-probes-write.js.map",
+ "backend/dist/routes/speed-probes-write.d.ts",
+ "backend/dist/routes/speed-probes-write.d.ts.map",
+ "backend/dist/routes/speed-test.js",
+ "backend/dist/routes/speed-test.js.map",
+ "backend/dist/routes/speed-test.d.ts",
+ "backend/dist/routes/speed-test.d.ts.map",
+ "backend/dist/routes/statistics.js",
+ "backend/dist/routes/statistics.js.map",
+ "backend/dist/routes/statistics.d.ts",
+ "backend/dist/routes/statistics.d.ts.map",
+ "backend/dist/routes/system-database.js",
+ "backend/dist/routes/system-database.js.map",
+ "backend/dist/routes/system-database.d.ts",
+ "backend/dist/routes/system-database.d.ts.map",
+ "backend/dist/routes/traffic-flow-purge.js",
+ "backend/dist/routes/traffic-flow-purge.js.map",
+ "backend/dist/routes/traffic-flow-purge.d.ts",
+ "backend/dist/routes/traffic-flow-purge.d.ts.map",
+ "backend/dist/routes/traffic-flow-settings.js",
+ "backend/dist/routes/traffic-flow-settings.js.map",
+ "backend/dist/routes/traffic-flow-settings.d.ts",
+ "backend/dist/routes/traffic-flow-settings.d.ts.map",
+ "backend/dist/routes/traffic-flow.js",
+ "backend/dist/routes/traffic-flow.js.map",
+ "backend/dist/routes/traffic-flow.d.ts",
+ "backend/dist/routes/traffic-flow.d.ts.map",
+ "backend/dist/routes/traffic.js",
+ "backend/dist/routes/traffic.js.map",
+ "backend/dist/routes/traffic.d.ts",
+ "backend/dist/routes/traffic.d.ts.map",
+ "backend/dist/routes/uptime.js",
+ "backend/dist/routes/uptime.js.map",
+ "backend/dist/routes/uptime.d.ts",
+ "backend/dist/routes/uptime.d.ts.map",
+ "backend/dist/routes/users.js",
+ "backend/dist/routes/users.js.map",
+ "backend/dist/routes/users.d.ts",
+ "backend/dist/routes/users.d.ts.map",
+ "backend/dist/routes/vxlan.js",
+ "backend/dist/routes/vxlan.js.map",
+ "backend/dist/routes/vxlan.d.ts",
+ "backend/dist/routes/vxlan.d.ts.map",
+ "backend/dist/routes/wireguard.js",
+ "backend/dist/routes/wireguard.js.map",
+ "backend/dist/routes/wireguard.d.ts",
+ "backend/dist/routes/wireguard.d.ts.map",
+ "backend/dist/scripts/migrate-sqlite-to-pg.js",
+ "backend/dist/scripts/migrate-sqlite-to-pg.js.map",
+ "backend/dist/scripts/migrate-sqlite-to-pg.d.ts",
+ "backend/dist/scripts/migrate-sqlite-to-pg.d.ts.map",
+ "backend/dist/scripts/rebuild-flow-facts.js",
+ "backend/dist/scripts/rebuild-flow-facts.js.map",
+ "backend/dist/scripts/rebuild-flow-facts.d.ts",
+ "backend/dist/scripts/rebuild-flow-facts.d.ts.map",
+ "backend/dist/services/acme-cloudflare.js",
+ "backend/dist/services/acme-cloudflare.js.map",
+ "backend/dist/services/acme-cloudflare.d.ts",
+ "backend/dist/services/acme-cloudflare.d.ts.map",
+ "backend/dist/services/alert-collector-hooks.js",
+ "backend/dist/services/alert-collector-hooks.js.map",
+ "backend/dist/services/alert-collector-hooks.d.ts",
+ "backend/dist/services/alert-collector-hooks.d.ts.map",
+ "backend/dist/services/alert-engine/bgp-signals.js",
+ "backend/dist/services/alert-engine/bgp-signals.js.map",
+ "backend/dist/services/alert-engine/bgp-signals.d.ts",
+ "backend/dist/services/alert-engine/bgp-signals.d.ts.map",
+ "backend/dist/services/alert-engine/collector-context-reader.js",
+ "backend/dist/services/alert-engine/collector-context-reader.js.map",
+ "backend/dist/services/alert-engine/collector-context-reader.d.ts",
+ "backend/dist/services/alert-engine/collector-context-reader.d.ts.map",
+ "backend/dist/services/alert-engine/collector-source-signals.js",
+ "backend/dist/services/alert-engine/collector-source-signals.js.map",
+ "backend/dist/services/alert-engine/collector-source-signals.d.ts",
+ "backend/dist/services/alert-engine/collector-source-signals.d.ts.map",
+ "backend/dist/services/alert-engine/confirm-stability.js",
+ "backend/dist/services/alert-engine/confirm-stability.js.map",
+ "backend/dist/services/alert-engine/confirm-stability.d.ts",
+ "backend/dist/services/alert-engine/confirm-stability.d.ts.map",
+ "backend/dist/services/alert-engine/cooldown.js",
+ "backend/dist/services/alert-engine/cooldown.js.map",
+ "backend/dist/services/alert-engine/cooldown.d.ts",
+ "backend/dist/services/alert-engine/cooldown.d.ts.map",
+ "backend/dist/services/alert-engine/decision-engine.js",
+ "backend/dist/services/alert-engine/decision-engine.js.map",
+ "backend/dist/services/alert-engine/decision-engine.d.ts",
+ "backend/dist/services/alert-engine/decision-engine.d.ts.map",
+ "backend/dist/services/alert-engine/evaluate-rule.js",
+ "backend/dist/services/alert-engine/evaluate-rule.js.map",
+ "backend/dist/services/alert-engine/evaluate-rule.d.ts",
+ "backend/dist/services/alert-engine/evaluate-rule.d.ts.map",
+ "backend/dist/services/alert-engine/gre-signals.js",
+ "backend/dist/services/alert-engine/gre-signals.js.map",
+ "backend/dist/services/alert-engine/gre-signals.d.ts",
+ "backend/dist/services/alert-engine/gre-signals.d.ts.map",
+ "backend/dist/services/alert-engine/groups.js",
+ "backend/dist/services/alert-engine/groups.js.map",
+ "backend/dist/services/alert-engine/groups.d.ts",
+ "backend/dist/services/alert-engine/groups.d.ts.map",
+ "backend/dist/services/alert-engine/monitoring-history.js",
+ "backend/dist/services/alert-engine/monitoring-history.js.map",
+ "backend/dist/services/alert-engine/monitoring-history.d.ts",
+ "backend/dist/services/alert-engine/monitoring-history.d.ts.map",
+ "backend/dist/services/alert-engine/outbox-execution.js",
+ "backend/dist/services/alert-engine/outbox-execution.js.map",
+ "backend/dist/services/alert-engine/outbox-execution.d.ts",
+ "backend/dist/services/alert-engine/outbox-execution.d.ts.map",
+ "backend/dist/services/alert-engine/outbox.js",
+ "backend/dist/services/alert-engine/outbox.js.map",
+ "backend/dist/services/alert-engine/outbox.d.ts",
+ "backend/dist/services/alert-engine/outbox.d.ts.map",
+ "backend/dist/services/alert-engine/prev-live-store.js",
+ "backend/dist/services/alert-engine/prev-live-store.js.map",
+ "backend/dist/services/alert-engine/prev-live-store.d.ts",
+ "backend/dist/services/alert-engine/prev-live-store.d.ts.map",
+ "backend/dist/services/alert-engine/rule-matcher.js",
+ "backend/dist/services/alert-engine/rule-matcher.js.map",
+ "backend/dist/services/alert-engine/rule-matcher.d.ts",
+ "backend/dist/services/alert-engine/rule-matcher.d.ts.map",
+ "backend/dist/services/alert-engine/run-once.js",
+ "backend/dist/services/alert-engine/run-once.js.map",
+ "backend/dist/services/alert-engine/run-once.d.ts",
+ "backend/dist/services/alert-engine/run-once.d.ts.map",
+ "backend/dist/services/alert-engine/server-signal-identity.js",
+ "backend/dist/services/alert-engine/server-signal-identity.js.map",
+ "backend/dist/services/alert-engine/server-signal-identity.d.ts",
+ "backend/dist/services/alert-engine/server-signal-identity.d.ts.map",
+ "backend/dist/services/alert-engine/signal-ingestor.js",
+ "backend/dist/services/alert-engine/signal-ingestor.js.map",
+ "backend/dist/services/alert-engine/signal-ingestor.d.ts",
+ "backend/dist/services/alert-engine/signal-ingestor.d.ts.map",
+ "backend/dist/services/alert-engine/signals-from-collectors.js",
+ "backend/dist/services/alert-engine/signals-from-collectors.js.map",
+ "backend/dist/services/alert-engine/signals-from-collectors.d.ts",
+ "backend/dist/services/alert-engine/signals-from-collectors.d.ts.map",
+ "backend/dist/services/alert-engine/snapshot.js",
+ "backend/dist/services/alert-engine/snapshot.js.map",
+ "backend/dist/services/alert-engine/snapshot.d.ts",
+ "backend/dist/services/alert-engine/snapshot.d.ts.map",
+ "backend/dist/services/alert-engine/source-watermark.js",
+ "backend/dist/services/alert-engine/source-watermark.js.map",
+ "backend/dist/services/alert-engine/source-watermark.d.ts",
+ "backend/dist/services/alert-engine/source-watermark.d.ts.map",
+ "backend/dist/services/alert-engine/state-machine.js",
+ "backend/dist/services/alert-engine/state-machine.js.map",
+ "backend/dist/services/alert-engine/state-machine.d.ts",
+ "backend/dist/services/alert-engine/state-machine.d.ts.map",
+ "backend/dist/services/alert-engine/target-issues.js",
+ "backend/dist/services/alert-engine/target-issues.js.map",
+ "backend/dist/services/alert-engine/target-issues.d.ts",
+ "backend/dist/services/alert-engine/target-issues.d.ts.map",
+ "backend/dist/services/alert-engine/telegram-emoji.js",
+ "backend/dist/services/alert-engine/telegram-emoji.js.map",
+ "backend/dist/services/alert-engine/telegram-emoji.d.ts",
+ "backend/dist/services/alert-engine/telegram-emoji.d.ts.map",
+ "backend/dist/services/alert-engine/types.js",
+ "backend/dist/services/alert-engine/types.js.map",
+ "backend/dist/services/alert-engine/types.d.ts",
+ "backend/dist/services/alert-engine/types.d.ts.map",
+ "backend/dist/services/alerts-config-execution.js",
+ "backend/dist/services/alerts-config-execution.js.map",
+ "backend/dist/services/alerts-config-execution.d.ts",
+ "backend/dist/services/alerts-config-execution.d.ts.map",
+ "backend/dist/services/alerts-outbox-overview.js",
+ "backend/dist/services/alerts-outbox-overview.js.map",
+ "backend/dist/services/alerts-outbox-overview.d.ts",
+ "backend/dist/services/alerts-outbox-overview.d.ts.map",
+ "backend/dist/services/alerts-outbox-retry.js",
+ "backend/dist/services/alerts-outbox-retry.js.map",
+ "backend/dist/services/alerts-outbox-retry.d.ts",
+ "backend/dist/services/alerts-outbox-retry.d.ts.map",
+ "backend/dist/services/alerts-overview.js",
+ "backend/dist/services/alerts-overview.js.map",
+ "backend/dist/services/alerts-overview.d.ts",
+ "backend/dist/services/alerts-overview.d.ts.map",
+ "backend/dist/services/alerts-revision.js",
+ "backend/dist/services/alerts-revision.js.map",
+ "backend/dist/services/alerts-revision.d.ts",
+ "backend/dist/services/alerts-revision.d.ts.map",
+ "backend/dist/services/alerts-service.js",
+ "backend/dist/services/alerts-service.js.map",
+ "backend/dist/services/alerts-service.d.ts",
+ "backend/dist/services/alerts-service.d.ts.map",
+ "backend/dist/services/alerts-telegram-delivery.js",
+ "backend/dist/services/alerts-telegram-delivery.js.map",
+ "backend/dist/services/alerts-telegram-delivery.d.ts",
+ "backend/dist/services/alerts-telegram-delivery.d.ts.map",
+ "backend/dist/services/alerts-telegram-send.js",
+ "backend/dist/services/alerts-telegram-send.js.map",
+ "backend/dist/services/alerts-telegram-send.d.ts",
+ "backend/dist/services/alerts-telegram-send.d.ts.map",
+ "backend/dist/services/alerts-telegram-settings.js",
+ "backend/dist/services/alerts-telegram-settings.js.map",
+ "backend/dist/services/alerts-telegram-settings.d.ts",
+ "backend/dist/services/alerts-telegram-settings.d.ts.map",
+ "backend/dist/services/backup-deletion.js",
+ "backend/dist/services/backup-deletion.js.map",
+ "backend/dist/services/backup-deletion.d.ts",
+ "backend/dist/services/backup-deletion.d.ts.map",
+ "backend/dist/services/backup-dto.js",
+ "backend/dist/services/backup-dto.js.map",
+ "backend/dist/services/backup-dto.d.ts",
+ "backend/dist/services/backup-dto.d.ts.map",
+ "backend/dist/services/backup-job-execution.js",
+ "backend/dist/services/backup-job-execution.js.map",
+ "backend/dist/services/backup-job-execution.d.ts",
+ "backend/dist/services/backup-job-execution.d.ts.map",
+ "backend/dist/services/backup-prune.js",
+ "backend/dist/services/backup-prune.js.map",
+ "backend/dist/services/backup-prune.d.ts",
+ "backend/dist/services/backup-prune.d.ts.map",
+ "backend/dist/services/backup-restore.js",
+ "backend/dist/services/backup-restore.js.map",
+ "backend/dist/services/backup-restore.d.ts",
+ "backend/dist/services/backup-restore.d.ts.map",
+ "backend/dist/services/backup-scheduler-collector.js",
+ "backend/dist/services/backup-scheduler-collector.js.map",
+ "backend/dist/services/backup-scheduler-collector.d.ts",
+ "backend/dist/services/backup-scheduler-collector.d.ts.map",
+ "backend/dist/services/backup-scheduler-run.js",
+ "backend/dist/services/backup-scheduler-run.js.map",
+ "backend/dist/services/backup-scheduler-run.d.ts",
+ "backend/dist/services/backup-scheduler-run.d.ts.map",
+ "backend/dist/services/backup-service.js",
+ "backend/dist/services/backup-service.js.map",
+ "backend/dist/services/backup-service.d.ts",
+ "backend/dist/services/backup-service.d.ts.map",
+ "backend/dist/services/backup-storage-update.js",
+ "backend/dist/services/backup-storage-update.js.map",
+ "backend/dist/services/backup-storage-update.d.ts",
+ "backend/dist/services/backup-storage-update.d.ts.map",
+ "backend/dist/services/bandwidth-test-rates.js",
+ "backend/dist/services/bandwidth-test-rates.js.map",
+ "backend/dist/services/bandwidth-test-rates.d.ts",
+ "backend/dist/services/bandwidth-test-rates.d.ts.map",
+ "backend/dist/services/bandwidth-test-status.js",
+ "backend/dist/services/bandwidth-test-status.js.map",
+ "backend/dist/services/bandwidth-test-status.d.ts",
+ "backend/dist/services/bandwidth-test-status.d.ts.map",
+ "backend/dist/services/bgp-catalog-poll.js",
+ "backend/dist/services/bgp-catalog-poll.js.map",
+ "backend/dist/services/bgp-catalog-poll.d.ts",
+ "backend/dist/services/bgp-catalog-poll.d.ts.map",
+ "backend/dist/services/bgp-parse-sessions.js",
+ "backend/dist/services/bgp-parse-sessions.js.map",
+ "backend/dist/services/bgp-parse-sessions.d.ts",
+ "backend/dist/services/bgp-parse-sessions.d.ts.map",
+ "backend/dist/services/bgp-peers-live.js",
+ "backend/dist/services/bgp-peers-live.js.map",
+ "backend/dist/services/bgp-peers-live.d.ts",
+ "backend/dist/services/bgp-peers-live.d.ts.map",
+ "backend/dist/services/btest-node-locks.js",
+ "backend/dist/services/btest-node-locks.js.map",
+ "backend/dist/services/btest-node-locks.d.ts",
+ "backend/dist/services/btest-node-locks.d.ts.map",
+ "backend/dist/services/certificate-issue-dto.js",
+ "backend/dist/services/certificate-issue-dto.js.map",
+ "backend/dist/services/certificate-issue-dto.d.ts",
+ "backend/dist/services/certificate-issue-dto.d.ts.map",
+ "backend/dist/services/certificate-issue-execution.js",
+ "backend/dist/services/certificate-issue-execution.js.map",
+ "backend/dist/services/certificate-issue-execution.d.ts",
+ "backend/dist/services/certificate-issue-execution.d.ts.map",
+ "backend/dist/services/certificate-issue-request.js",
+ "backend/dist/services/certificate-issue-request.js.map",
+ "backend/dist/services/certificate-issue-request.d.ts",
+ "backend/dist/services/certificate-issue-request.d.ts.map",
+ "backend/dist/services/certificate-issue-runner.js",
+ "backend/dist/services/certificate-issue-runner.js.map",
+ "backend/dist/services/certificate-issue-runner.d.ts",
+ "backend/dist/services/certificate-issue-runner.d.ts.map",
+ "backend/dist/services/certificate-parse.js",
+ "backend/dist/services/certificate-parse.js.map",
+ "backend/dist/services/certificate-parse.d.ts",
+ "backend/dist/services/certificate-parse.d.ts.map",
+ "backend/dist/services/certificate-renew-collector.js",
+ "backend/dist/services/certificate-renew-collector.js.map",
+ "backend/dist/services/certificate-renew-collector.d.ts",
+ "backend/dist/services/certificate-renew-collector.d.ts.map",
+ "backend/dist/services/certificates-service.js",
+ "backend/dist/services/certificates-service.js.map",
+ "backend/dist/services/certificates-service.d.ts",
+ "backend/dist/services/certificates-service.d.ts.map",
+ "backend/dist/services/certificates-state.js",
+ "backend/dist/services/certificates-state.js.map",
+ "backend/dist/services/certificates-state.d.ts",
+ "backend/dist/services/certificates-state.d.ts.map",
+ "backend/dist/services/complete-count.js",
+ "backend/dist/services/complete-count.js.map",
+ "backend/dist/services/complete-count.d.ts",
+ "backend/dist/services/complete-count.d.ts.map",
+ "backend/dist/services/config-apply-plan.js",
+ "backend/dist/services/config-apply-plan.js.map",
+ "backend/dist/services/config-apply-plan.d.ts",
+ "backend/dist/services/config-apply-plan.d.ts.map",
+ "backend/dist/services/config-revisions.js",
+ "backend/dist/services/config-revisions.js.map",
+ "backend/dist/services/config-revisions.d.ts",
+ "backend/dist/services/config-revisions.d.ts.map",
+ "backend/dist/services/containers-live.js",
+ "backend/dist/services/containers-live.js.map",
+ "backend/dist/services/containers-live.d.ts",
+ "backend/dist/services/containers-live.d.ts.map",
+ "backend/dist/services/containers-state.js",
+ "backend/dist/services/containers-state.js.map",
+ "backend/dist/services/containers-state.d.ts",
+ "backend/dist/services/containers-state.d.ts.map",
+ "backend/dist/services/database-background-work.js",
+ "backend/dist/services/database-background-work.js.map",
+ "backend/dist/services/database-background-work.d.ts",
+ "backend/dist/services/database-background-work.d.ts.map",
+ "backend/dist/services/database-request-gate.js",
+ "backend/dist/services/database-request-gate.js.map",
+ "backend/dist/services/database-request-gate.d.ts",
+ "backend/dist/services/database-request-gate.d.ts.map",
+ "backend/dist/services/entity-snapshots.js",
+ "backend/dist/services/entity-snapshots.js.map",
+ "backend/dist/services/entity-snapshots.d.ts",
+ "backend/dist/services/entity-snapshots.d.ts.map",
+ "backend/dist/services/evobgp-catalog.js",
+ "backend/dist/services/evobgp-catalog.js.map",
+ "backend/dist/services/evobgp-catalog.d.ts",
+ "backend/dist/services/evobgp-catalog.d.ts.map",
+ "backend/dist/services/firewall-live.js",
+ "backend/dist/services/firewall-live.js.map",
+ "backend/dist/services/firewall-live.d.ts",
+ "backend/dist/services/firewall-live.d.ts.map",
+ "backend/dist/services/firewall-poll.js",
+ "backend/dist/services/firewall-poll.js.map",
+ "backend/dist/services/firewall-poll.d.ts",
+ "backend/dist/services/firewall-poll.d.ts.map",
+ "backend/dist/services/geoip-settings.js",
+ "backend/dist/services/geoip-settings.js.map",
+ "backend/dist/services/geoip-settings.d.ts",
+ "backend/dist/services/geoip-settings.d.ts.map",
+ "backend/dist/services/geoip-update-collector.js",
+ "backend/dist/services/geoip-update-collector.js.map",
+ "backend/dist/services/geoip-update-collector.d.ts",
+ "backend/dist/services/geoip-update-collector.d.ts.map",
+ "backend/dist/services/gre-bgp-snapshot-collector.js",
+ "backend/dist/services/gre-bgp-snapshot-collector.js.map",
+ "backend/dist/services/gre-bgp-snapshot-collector.d.ts",
+ "backend/dist/services/gre-bgp-snapshot-collector.d.ts.map",
+ "backend/dist/services/gre-bgp-snapshot-history.js",
+ "backend/dist/services/gre-bgp-snapshot-history.js.map",
+ "backend/dist/services/gre-bgp-snapshot-history.d.ts",
+ "backend/dist/services/gre-bgp-snapshot-history.d.ts.map",
+ "backend/dist/services/gre-bgp-timing.js",
+ "backend/dist/services/gre-bgp-timing.js.map",
+ "backend/dist/services/gre-bgp-timing.d.ts",
+ "backend/dist/services/gre-bgp-timing.d.ts.map",
+ "backend/dist/services/gre-catalog-poll.js",
+ "backend/dist/services/gre-catalog-poll.js.map",
+ "backend/dist/services/gre-catalog-poll.d.ts",
+ "backend/dist/services/gre-catalog-poll.d.ts.map",
+ "backend/dist/services/gre-comment.js",
+ "backend/dist/services/gre-comment.js.map",
+ "backend/dist/services/gre-comment.d.ts",
+ "backend/dist/services/gre-comment.d.ts.map",
+ "backend/dist/services/gre-live.js",
+ "backend/dist/services/gre-live.js.map",
+ "backend/dist/services/gre-live.d.ts",
+ "backend/dist/services/gre-live.d.ts.map",
+ "backend/dist/services/gre-read.js",
+ "backend/dist/services/gre-read.js.map",
+ "backend/dist/services/gre-read.d.ts",
+ "backend/dist/services/gre-read.d.ts.map",
+ "backend/dist/services/gre-tunnels-live.js",
+ "backend/dist/services/gre-tunnels-live.js.map",
+ "backend/dist/services/gre-tunnels-live.d.ts",
+ "backend/dist/services/gre-tunnels-live.d.ts.map",
+ "backend/dist/services/internet-path-collector.js",
+ "backend/dist/services/internet-path-collector.js.map",
+ "backend/dist/services/internet-path-collector.d.ts",
+ "backend/dist/services/internet-path-collector.d.ts.map",
+ "backend/dist/services/internet-path-snapshot.js",
+ "backend/dist/services/internet-path-snapshot.js.map",
+ "backend/dist/services/internet-path-snapshot.d.ts",
+ "backend/dist/services/internet-path-snapshot.d.ts.map",
+ "backend/dist/services/ipsec-ca.js",
+ "backend/dist/services/ipsec-ca.js.map",
+ "backend/dist/services/ipsec-ca.d.ts",
+ "backend/dist/services/ipsec-ca.d.ts.map",
+ "backend/dist/services/ipsec-certificate-dn.js",
+ "backend/dist/services/ipsec-certificate-dn.js.map",
+ "backend/dist/services/ipsec-certificate-dn.d.ts",
+ "backend/dist/services/ipsec-certificate-dn.d.ts.map",
+ "backend/dist/services/ipsec-cleanup.js",
+ "backend/dist/services/ipsec-cleanup.js.map",
+ "backend/dist/services/ipsec-cleanup.d.ts",
+ "backend/dist/services/ipsec-cleanup.d.ts.map",
+ "backend/dist/services/ipsec-config.js",
+ "backend/dist/services/ipsec-config.js.map",
+ "backend/dist/services/ipsec-config.d.ts",
+ "backend/dist/services/ipsec-config.d.ts.map",
+ "backend/dist/services/ipsec-live.js",
+ "backend/dist/services/ipsec-live.js.map",
+ "backend/dist/services/ipsec-live.d.ts",
+ "backend/dist/services/ipsec-live.d.ts.map",
+ "backend/dist/services/ipsec-operation.js",
+ "backend/dist/services/ipsec-operation.js.map",
+ "backend/dist/services/ipsec-operation.d.ts",
+ "backend/dist/services/ipsec-operation.d.ts.map",
+ "backend/dist/services/ipsec-ros.js",
+ "backend/dist/services/ipsec-ros.js.map",
+ "backend/dist/services/ipsec-ros.d.ts",
+ "backend/dist/services/ipsec-ros.d.ts.map",
+ "backend/dist/services/mikrotik.js",
+ "backend/dist/services/mikrotik.js.map",
+ "backend/dist/services/mikrotik.d.ts",
+ "backend/dist/services/mikrotik.d.ts.map",
+ "backend/dist/services/monitoring-server-catalog.js",
+ "backend/dist/services/monitoring-server-catalog.js.map",
+ "backend/dist/services/monitoring-server-catalog.d.ts",
+ "backend/dist/services/monitoring-server-catalog.d.ts.map",
+ "backend/dist/services/ospf-catalog-poll.js",
+ "backend/dist/services/ospf-catalog-poll.js.map",
+ "backend/dist/services/ospf-catalog-poll.d.ts",
+ "backend/dist/services/ospf-catalog-poll.d.ts.map",
+ "backend/dist/services/ospf-effective-disabled.js",
+ "backend/dist/services/ospf-effective-disabled.js.map",
+ "backend/dist/services/ospf-effective-disabled.d.ts",
+ "backend/dist/services/ospf-effective-disabled.d.ts.map",
+ "backend/dist/services/ospf-observed-values.js",
+ "backend/dist/services/ospf-observed-values.js.map",
+ "backend/dist/services/ospf-observed-values.d.ts",
+ "backend/dist/services/ospf-observed-values.d.ts.map",
+ "backend/dist/services/ospf-optimizer-identity.js",
+ "backend/dist/services/ospf-optimizer-identity.js.map",
+ "backend/dist/services/ospf-optimizer-identity.d.ts",
+ "backend/dist/services/ospf-optimizer-identity.d.ts.map",
+ "backend/dist/services/ospf-optimizer-metrics.js",
+ "backend/dist/services/ospf-optimizer-metrics.js.map",
+ "backend/dist/services/ospf-optimizer-metrics.d.ts",
+ "backend/dist/services/ospf-optimizer-metrics.d.ts.map",
+ "backend/dist/services/ospf-optimizer-plan.js",
+ "backend/dist/services/ospf-optimizer-plan.js.map",
+ "backend/dist/services/ospf-optimizer-plan.d.ts",
+ "backend/dist/services/ospf-optimizer-plan.d.ts.map",
+ "backend/dist/services/ospf-route-parse.js",
+ "backend/dist/services/ospf-route-parse.js.map",
+ "backend/dist/services/ospf-route-parse.d.ts",
+ "backend/dist/services/ospf-route-parse.d.ts.map",
+ "backend/dist/services/ping-probe-replacement.js",
+ "backend/dist/services/ping-probe-replacement.js.map",
+ "backend/dist/services/ping-probe-replacement.d.ts",
+ "backend/dist/services/ping-probe-replacement.d.ts.map",
+ "backend/dist/services/poller.js",
+ "backend/dist/services/poller.js.map",
+ "backend/dist/services/poller.d.ts",
+ "backend/dist/services/poller.d.ts.map",
+ "backend/dist/services/recursive-apply.js",
+ "backend/dist/services/recursive-apply.js.map",
+ "backend/dist/services/recursive-apply.d.ts",
+ "backend/dist/services/recursive-apply.d.ts.map",
+ "backend/dist/services/recursive-route-catalog.js",
+ "backend/dist/services/recursive-route-catalog.js.map",
+ "backend/dist/services/recursive-route-catalog.d.ts",
+ "backend/dist/services/recursive-route-catalog.d.ts.map",
+ "backend/dist/services/recursive-route-validation.js",
+ "backend/dist/services/recursive-route-validation.js.map",
+ "backend/dist/services/recursive-route-validation.d.ts",
+ "backend/dist/services/recursive-route-validation.d.ts.map",
+ "backend/dist/services/ros-metric-parse.js",
+ "backend/dist/services/ros-metric-parse.js.map",
+ "backend/dist/services/ros-metric-parse.d.ts",
+ "backend/dist/services/ros-metric-parse.d.ts.map",
+ "backend/dist/services/ros-ops.js",
+ "backend/dist/services/ros-ops.js.map",
+ "backend/dist/services/ros-ops.d.ts",
+ "backend/dist/services/ros-ops.d.ts.map",
+ "backend/dist/services/s3-backup-client.js",
+ "backend/dist/services/s3-backup-client.js.map",
+ "backend/dist/services/s3-backup-client.d.ts",
+ "backend/dist/services/s3-backup-client.d.ts.map",
+ "backend/dist/services/scheduler-recorded-run.js",
+ "backend/dist/services/scheduler-recorded-run.js.map",
+ "backend/dist/services/scheduler-recorded-run.d.ts",
+ "backend/dist/services/scheduler-recorded-run.d.ts.map",
+ "backend/dist/services/scheduler-running.js",
+ "backend/dist/services/scheduler-running.js.map",
+ "backend/dist/services/scheduler-running.d.ts",
+ "backend/dist/services/scheduler-running.d.ts.map",
+ "backend/dist/services/scheduler.js",
+ "backend/dist/services/scheduler.js.map",
+ "backend/dist/services/scheduler.d.ts",
+ "backend/dist/services/scheduler.d.ts.map",
+ "backend/dist/services/serial-operation.js",
+ "backend/dist/services/serial-operation.js.map",
+ "backend/dist/services/serial-operation.d.ts",
+ "backend/dist/services/serial-operation.d.ts.map",
+ "backend/dist/services/servers-rest-ping-collector.js",
+ "backend/dist/services/servers-rest-ping-collector.js.map",
+ "backend/dist/services/servers-rest-ping-collector.d.ts",
+ "backend/dist/services/servers-rest-ping-collector.d.ts.map",
+ "backend/dist/services/speed-history-dto.js",
+ "backend/dist/services/speed-history-dto.js.map",
+ "backend/dist/services/speed-history-dto.d.ts",
+ "backend/dist/services/speed-history-dto.d.ts.map",
+ "backend/dist/services/speed-probe-dto.js",
+ "backend/dist/services/speed-probe-dto.js.map",
+ "backend/dist/services/speed-probe-dto.d.ts",
+ "backend/dist/services/speed-probe-dto.d.ts.map",
+ "backend/dist/services/speed-probe-replacement.js",
+ "backend/dist/services/speed-probe-replacement.js.map",
+ "backend/dist/services/speed-probe-replacement.d.ts",
+ "backend/dist/services/speed-probe-replacement.d.ts.map",
+ "backend/dist/services/speed-run-error-message.js",
+ "backend/dist/services/speed-run-error-message.js.map",
+ "backend/dist/services/speed-run-error-message.d.ts",
+ "backend/dist/services/speed-run-error-message.d.ts.map",
+ "backend/dist/services/speed-run-persistence.js",
+ "backend/dist/services/speed-run-persistence.js.map",
+ "backend/dist/services/speed-run-persistence.d.ts",
+ "backend/dist/services/speed-run-persistence.d.ts.map",
+ "backend/dist/services/statistics-aggregate.js",
+ "backend/dist/services/statistics-aggregate.js.map",
+ "backend/dist/services/statistics-aggregate.d.ts",
+ "backend/dist/services/statistics-aggregate.d.ts.map",
+ "backend/dist/services/system-database-backup.js",
+ "backend/dist/services/system-database-backup.js.map",
+ "backend/dist/services/system-database-backup.d.ts",
+ "backend/dist/services/system-database-backup.d.ts.map",
+ "backend/dist/services/system-database-cache.js",
+ "backend/dist/services/system-database-cache.js.map",
+ "backend/dist/services/system-database-cache.d.ts",
+ "backend/dist/services/system-database-cache.d.ts.map",
+ "backend/dist/services/system-database-operation.js",
+ "backend/dist/services/system-database-operation.js.map",
+ "backend/dist/services/system-database-operation.d.ts",
+ "backend/dist/services/system-database-operation.d.ts.map",
+ "backend/dist/services/system-database-partition-restore.js",
+ "backend/dist/services/system-database-partition-restore.js.map",
+ "backend/dist/services/system-database-partition-restore.d.ts",
+ "backend/dist/services/system-database-partition-restore.d.ts.map",
+ "backend/dist/services/system-database-restore-execution.js",
+ "backend/dist/services/system-database-restore-execution.js.map",
+ "backend/dist/services/system-database-restore-execution.d.ts",
+ "backend/dist/services/system-database-restore-execution.d.ts.map",
+ "backend/dist/services/traffic-collection-lock.js",
+ "backend/dist/services/traffic-collection-lock.js.map",
+ "backend/dist/services/traffic-collection-lock.d.ts",
+ "backend/dist/services/traffic-collection-lock.d.ts.map",
+ "backend/dist/services/traffic-collection-state.js",
+ "backend/dist/services/traffic-collection-state.js.map",
+ "backend/dist/services/traffic-collection-state.d.ts",
+ "backend/dist/services/traffic-collection-state.d.ts.map",
+ "backend/dist/services/traffic-collector.js",
+ "backend/dist/services/traffic-collector.js.map",
+ "backend/dist/services/traffic-collector.d.ts",
+ "backend/dist/services/traffic-collector.d.ts.map",
+ "backend/dist/services/traffic-flow-analytics.js",
+ "backend/dist/services/traffic-flow-analytics.js.map",
+ "backend/dist/services/traffic-flow-analytics.d.ts",
+ "backend/dist/services/traffic-flow-analytics.d.ts.map",
+ "backend/dist/services/traffic-flow-apps.js",
+ "backend/dist/services/traffic-flow-apps.js.map",
+ "backend/dist/services/traffic-flow-apps.d.ts",
+ "backend/dist/services/traffic-flow-apps.d.ts.map",
+ "backend/dist/services/traffic-flow-brands.js",
+ "backend/dist/services/traffic-flow-brands.js.map",
+ "backend/dist/services/traffic-flow-brands.d.ts",
+ "backend/dist/services/traffic-flow-brands.d.ts.map",
+ "backend/dist/services/traffic-flow-classify.js",
+ "backend/dist/services/traffic-flow-classify.js.map",
+ "backend/dist/services/traffic-flow-classify.d.ts",
+ "backend/dist/services/traffic-flow-classify.d.ts.map",
+ "backend/dist/services/traffic-flow-collector-ipc.js",
+ "backend/dist/services/traffic-flow-collector-ipc.js.map",
+ "backend/dist/services/traffic-flow-collector-ipc.d.ts",
+ "backend/dist/services/traffic-flow-collector-ipc.d.ts.map",
+ "backend/dist/services/traffic-flow-collector-worker.js",
+ "backend/dist/services/traffic-flow-collector-worker.js.map",
+ "backend/dist/services/traffic-flow-collector-worker.d.ts",
+ "backend/dist/services/traffic-flow-collector-worker.d.ts.map",
+ "backend/dist/services/traffic-flow-dedup.js",
+ "backend/dist/services/traffic-flow-dedup.js.map",
+ "backend/dist/services/traffic-flow-dedup.d.ts",
+ "backend/dist/services/traffic-flow-dedup.d.ts.map",
+ "backend/dist/services/traffic-flow-dest.js",
+ "backend/dist/services/traffic-flow-dest.js.map",
+ "backend/dist/services/traffic-flow-dest.d.ts",
+ "backend/dist/services/traffic-flow-dest.d.ts.map",
+ "backend/dist/services/traffic-flow-engine.js",
+ "backend/dist/services/traffic-flow-engine.js.map",
+ "backend/dist/services/traffic-flow-engine.d.ts",
+ "backend/dist/services/traffic-flow-engine.d.ts.map",
+ "backend/dist/services/traffic-flow-facts-filter.js",
+ "backend/dist/services/traffic-flow-facts-filter.js.map",
+ "backend/dist/services/traffic-flow-facts-filter.d.ts",
+ "backend/dist/services/traffic-flow-facts-filter.d.ts.map",
+ "backend/dist/services/traffic-flow-facts-rebuild.js",
+ "backend/dist/services/traffic-flow-facts-rebuild.js.map",
+ "backend/dist/services/traffic-flow-facts-rebuild.d.ts",
+ "backend/dist/services/traffic-flow-facts-rebuild.d.ts.map",
+ "backend/dist/services/traffic-flow-facts.js",
+ "backend/dist/services/traffic-flow-facts.js.map",
+ "backend/dist/services/traffic-flow-facts.d.ts",
+ "backend/dist/services/traffic-flow-facts.d.ts.map",
+ "backend/dist/services/traffic-flow-geoip.js",
+ "backend/dist/services/traffic-flow-geoip.js.map",
+ "backend/dist/services/traffic-flow-geoip.d.ts",
+ "backend/dist/services/traffic-flow-geoip.d.ts.map",
+ "backend/dist/services/traffic-flow-host-file-builders.js",
+ "backend/dist/services/traffic-flow-host-file-builders.js.map",
+ "backend/dist/services/traffic-flow-host-file-builders.d.ts",
+ "backend/dist/services/traffic-flow-host-file-builders.d.ts.map",
+ "backend/dist/services/traffic-flow-host-files.js",
+ "backend/dist/services/traffic-flow-host-files.js.map",
+ "backend/dist/services/traffic-flow-host-files.d.ts",
+ "backend/dist/services/traffic-flow-host-files.d.ts.map",
+ "backend/dist/services/traffic-flow-ifaces.js",
+ "backend/dist/services/traffic-flow-ifaces.js.map",
+ "backend/dist/services/traffic-flow-ifaces.d.ts",
+ "backend/dist/services/traffic-flow-ifaces.d.ts.map",
+ "backend/dist/services/traffic-flow-ifindex.js",
+ "backend/dist/services/traffic-flow-ifindex.js.map",
+ "backend/dist/services/traffic-flow-ifindex.d.ts",
+ "backend/dist/services/traffic-flow-ifindex.d.ts.map",
+ "backend/dist/services/traffic-flow-ingest.js",
+ "backend/dist/services/traffic-flow-ingest.js.map",
+ "backend/dist/services/traffic-flow-ingest.d.ts",
+ "backend/dist/services/traffic-flow-ingest.d.ts.map",
+ "backend/dist/services/traffic-flow-ip.js",
+ "backend/dist/services/traffic-flow-ip.js.map",
+ "backend/dist/services/traffic-flow-ip.d.ts",
+ "backend/dist/services/traffic-flow-ip.d.ts.map",
+ "backend/dist/services/traffic-flow-key-execution.js",
+ "backend/dist/services/traffic-flow-key-execution.js.map",
+ "backend/dist/services/traffic-flow-key-execution.d.ts",
+ "backend/dist/services/traffic-flow-key-execution.d.ts.map",
+ "backend/dist/services/traffic-flow-map-exporter.js",
+ "backend/dist/services/traffic-flow-map-exporter.js.map",
+ "backend/dist/services/traffic-flow-map-exporter.d.ts",
+ "backend/dist/services/traffic-flow-map-exporter.d.ts.map",
+ "backend/dist/services/traffic-flow-map-hops.js",
+ "backend/dist/services/traffic-flow-map-hops.js.map",
+ "backend/dist/services/traffic-flow-map-hops.d.ts",
+ "backend/dist/services/traffic-flow-map-hops.d.ts.map",
+ "backend/dist/services/traffic-flow-overlay.js",
+ "backend/dist/services/traffic-flow-overlay.js.map",
+ "backend/dist/services/traffic-flow-overlay.d.ts",
+ "backend/dist/services/traffic-flow-overlay.d.ts.map",
+ "backend/dist/services/traffic-flow-parse.js",
+ "backend/dist/services/traffic-flow-parse.js.map",
+ "backend/dist/services/traffic-flow-parse.d.ts",
+ "backend/dist/services/traffic-flow-parse.d.ts.map",
+ "backend/dist/services/traffic-flow-planes.js",
+ "backend/dist/services/traffic-flow-planes.js.map",
+ "backend/dist/services/traffic-flow-planes.d.ts",
+ "backend/dist/services/traffic-flow-planes.d.ts.map",
+ "backend/dist/services/traffic-flow-purge-execution.js",
+ "backend/dist/services/traffic-flow-purge-execution.js.map",
+ "backend/dist/services/traffic-flow-purge-execution.d.ts",
+ "backend/dist/services/traffic-flow-purge-execution.d.ts.map",
+ "backend/dist/services/traffic-flow-ripe.js",
+ "backend/dist/services/traffic-flow-ripe.js.map",
+ "backend/dist/services/traffic-flow-ripe.d.ts",
+ "backend/dist/services/traffic-flow-ripe.d.ts.map",
+ "backend/dist/services/traffic-flow-settings.js",
+ "backend/dist/services/traffic-flow-settings.js.map",
+ "backend/dist/services/traffic-flow-settings.d.ts",
+ "backend/dist/services/traffic-flow-settings.d.ts.map",
+ "backend/dist/services/traffic-flow-topology.js",
+ "backend/dist/services/traffic-flow-topology.js.map",
+ "backend/dist/services/traffic-flow-topology.d.ts",
+ "backend/dist/services/traffic-flow-topology.d.ts.map",
+ "backend/dist/services/traffic-flow-worker-shutdown.js",
+ "backend/dist/services/traffic-flow-worker-shutdown.js.map",
+ "backend/dist/services/traffic-flow-worker-shutdown.d.ts",
+ "backend/dist/services/traffic-flow-worker-shutdown.d.ts.map",
+ "backend/dist/services/traffic-freshness.js",
+ "backend/dist/services/traffic-freshness.js.map",
+ "backend/dist/services/traffic-freshness.d.ts",
+ "backend/dist/services/traffic-freshness.d.ts.map",
+ "backend/dist/services/traffic-live.js",
+ "backend/dist/services/traffic-live.js.map",
+ "backend/dist/services/traffic-live.d.ts",
+ "backend/dist/services/traffic-live.d.ts.map",
+ "backend/dist/services/traffic-observation.js",
+ "backend/dist/services/traffic-observation.js.map",
+ "backend/dist/services/traffic-observation.d.ts",
+ "backend/dist/services/traffic-observation.d.ts.map",
+ "backend/dist/services/traffic-peer-observation.js",
+ "backend/dist/services/traffic-peer-observation.js.map",
+ "backend/dist/services/traffic-peer-observation.d.ts",
+ "backend/dist/services/traffic-peer-observation.d.ts.map",
+ "backend/dist/services/traffic-rate.js",
+ "backend/dist/services/traffic-rate.js.map",
+ "backend/dist/services/traffic-rate.d.ts",
+ "backend/dist/services/traffic-rate.d.ts.map",
+ "backend/dist/services/traffic-users.js",
+ "backend/dist/services/traffic-users.js.map",
+ "backend/dist/services/traffic-users.d.ts",
+ "backend/dist/services/traffic-users.d.ts.map",
+ "backend/dist/services/uptime-collector.js",
+ "backend/dist/services/uptime-collector.js.map",
+ "backend/dist/services/uptime-collector.d.ts",
+ "backend/dist/services/uptime-collector.d.ts.map",
+ "backend/dist/services/uptime-probe-observations.js",
+ "backend/dist/services/uptime-probe-observations.js.map",
+ "backend/dist/services/uptime-probe-observations.d.ts",
+ "backend/dist/services/uptime-probe-observations.d.ts.map",
+ "backend/dist/services/uptime-speed-service.js",
+ "backend/dist/services/uptime-speed-service.js.map",
+ "backend/dist/services/uptime-speed-service.d.ts",
+ "backend/dist/services/uptime-speed-service.d.ts.map",
+ "backend/dist/services/vxlan-live.js",
+ "backend/dist/services/vxlan-live.js.map",
+ "backend/dist/services/vxlan-live.d.ts",
+ "backend/dist/services/vxlan-live.d.ts.map",
+ "backend/dist/services/wg-keys.js",
+ "backend/dist/services/wg-keys.js.map",
+ "backend/dist/services/wg-keys.d.ts",
+ "backend/dist/services/wg-keys.d.ts.map",
+ "backend/dist/services/wireguard-config.js",
+ "backend/dist/services/wireguard-config.js.map",
+ "backend/dist/services/wireguard-config.d.ts",
+ "backend/dist/services/wireguard-config.d.ts.map",
+ "backend/dist/services/wireguard-delete.js",
+ "backend/dist/services/wireguard-delete.js.map",
+ "backend/dist/services/wireguard-delete.d.ts",
+ "backend/dist/services/wireguard-delete.d.ts.map",
+ "backend/dist/services/wireguard-import-apply.js",
+ "backend/dist/services/wireguard-import-apply.js.map",
+ "backend/dist/services/wireguard-import-apply.d.ts",
+ "backend/dist/services/wireguard-import-apply.d.ts.map",
+ "backend/dist/services/wireguard-import-revision.js",
+ "backend/dist/services/wireguard-import-revision.js.map",
+ "backend/dist/services/wireguard-import-revision.d.ts",
+ "backend/dist/services/wireguard-import-revision.d.ts.map",
+ "backend/dist/services/wireguard-import.js",
+ "backend/dist/services/wireguard-import.js.map",
+ "backend/dist/services/wireguard-import.d.ts",
+ "backend/dist/services/wireguard-import.d.ts.map",
+ "backend/dist/services/wireguard-live.js",
+ "backend/dist/services/wireguard-live.js.map",
+ "backend/dist/services/wireguard-live.d.ts",
+ "backend/dist/services/wireguard-live.d.ts.map",
+ "backend/dist/services/wireguard-observed-values.js",
+ "backend/dist/services/wireguard-observed-values.js.map",
+ "backend/dist/services/wireguard-observed-values.d.ts",
+ "backend/dist/services/wireguard-observed-values.d.ts.map",
+ "backend/dist/services/wireguard-ros.js",
+ "backend/dist/services/wireguard-ros.js.map",
+ "backend/dist/services/wireguard-ros.d.ts",
+ "backend/dist/services/wireguard-ros.d.ts.map",
+ "backend/dist/test/pg.js",
+ "backend/dist/test/pg.js.map",
+ "backend/dist/test/pg.d.ts",
+ "backend/dist/test/pg.d.ts.map",
+ "backend/dist/types/scheduler-run-snapshot.js",
+ "backend/dist/types/scheduler-run-snapshot.js.map",
+ "backend/dist/types/scheduler-run-snapshot.d.ts",
+ "backend/dist/types/scheduler-run-snapshot.d.ts.map",
+ "backend/dist/types/server.js",
+ "backend/dist/types/server.js.map",
+ "backend/dist/types/server.d.ts",
+ "backend/dist/types/server.d.ts.map",
+ "backend/dist/utils/abort-signals.js",
+ "backend/dist/utils/abort-signals.js.map",
+ "backend/dist/utils/abort-signals.d.ts",
+ "backend/dist/utils/abort-signals.d.ts.map",
+ "backend/dist/utils/ros-src-address.js",
+ "backend/dist/utils/ros-src-address.js.map",
+ "backend/dist/utils/ros-src-address.d.ts",
+ "backend/dist/utils/ros-src-address.d.ts.map",
+ "backend/dist/utils/server-id.js",
+ "backend/dist/utils/server-id.js.map",
+ "backend/dist/utils/server-id.d.ts",
+ "backend/dist/utils/server-id.d.ts.map",
+ "packages/contracts/dist/alerts.js",
+ "packages/contracts/dist/alerts.js.map",
+ "packages/contracts/dist/alerts.d.ts",
+ "packages/contracts/dist/alerts.d.ts.map",
+ "packages/contracts/dist/backups.js",
+ "packages/contracts/dist/backups.js.map",
+ "packages/contracts/dist/backups.d.ts",
+ "packages/contracts/dist/backups.d.ts.map",
+ "packages/contracts/dist/certificates.js",
+ "packages/contracts/dist/certificates.js.map",
+ "packages/contracts/dist/certificates.d.ts",
+ "packages/contracts/dist/certificates.d.ts.map",
+ "packages/contracts/dist/containers.js",
+ "packages/contracts/dist/containers.js.map",
+ "packages/contracts/dist/containers.d.ts",
+ "packages/contracts/dist/containers.d.ts.map",
+ "packages/contracts/dist/events.js",
+ "packages/contracts/dist/events.js.map",
+ "packages/contracts/dist/events.d.ts",
+ "packages/contracts/dist/events.d.ts.map",
+ "packages/contracts/dist/evobgp.js",
+ "packages/contracts/dist/evobgp.js.map",
+ "packages/contracts/dist/evobgp.d.ts",
+ "packages/contracts/dist/evobgp.d.ts.map",
+ "packages/contracts/dist/geoip.js",
+ "packages/contracts/dist/geoip.js.map",
+ "packages/contracts/dist/geoip.d.ts",
+ "packages/contracts/dist/geoip.d.ts.map",
+ "packages/contracts/dist/index.js",
+ "packages/contracts/dist/index.js.map",
+ "packages/contracts/dist/index.d.ts",
+ "packages/contracts/dist/index.d.ts.map",
+ "packages/contracts/dist/ipsec.js",
+ "packages/contracts/dist/ipsec.js.map",
+ "packages/contracts/dist/ipsec.d.ts",
+ "packages/contracts/dist/ipsec.d.ts.map",
+ "packages/contracts/dist/ospf-optimizer.js",
+ "packages/contracts/dist/ospf-optimizer.js.map",
+ "packages/contracts/dist/ospf-optimizer.d.ts",
+ "packages/contracts/dist/ospf-optimizer.d.ts.map",
+ "packages/contracts/dist/servers.js",
+ "packages/contracts/dist/servers.js.map",
+ "packages/contracts/dist/servers.d.ts",
+ "packages/contracts/dist/servers.d.ts.map",
+ "packages/contracts/dist/statistics.js",
+ "packages/contracts/dist/statistics.js.map",
+ "packages/contracts/dist/statistics.d.ts",
+ "packages/contracts/dist/statistics.d.ts.map",
+ "packages/contracts/dist/traffic-flow.js",
+ "packages/contracts/dist/traffic-flow.js.map",
+ "packages/contracts/dist/traffic-flow.d.ts",
+ "packages/contracts/dist/traffic-flow.d.ts.map",
+ "packages/contracts/dist/uptime-probes.js",
+ "packages/contracts/dist/uptime-probes.js.map",
+ "packages/contracts/dist/uptime-probes.d.ts",
+ "packages/contracts/dist/uptime-probes.d.ts.map",
+ "packages/contracts/dist/uptime-speed.js",
+ "packages/contracts/dist/uptime-speed.js.map",
+ "packages/contracts/dist/uptime-speed.d.ts",
+ "packages/contracts/dist/uptime-speed.d.ts.map",
+ "packages/contracts/dist/users.js",
+ "packages/contracts/dist/users.js.map",
+ "packages/contracts/dist/users.d.ts",
+ "packages/contracts/dist/users.d.ts.map",
+ "packages/contracts/dist/wireguard.js",
+ "packages/contracts/dist/wireguard.js.map",
+ "packages/contracts/dist/wireguard.d.ts",
+ "packages/contracts/dist/wireguard.d.ts.map"
+ ],
+ "missing": [],
+ "different": []
+}
\ No newline at end of file
diff --git a/docs/ux-ui/acceptance/gre212-compatibility-result.json b/docs/ux-ui/acceptance/gre212-compatibility-result.json
new file mode 100644
index 00000000..aefa58fc
--- /dev/null
+++ b/docs/ux-ui/acceptance/gre212-compatibility-result.json
@@ -0,0 +1,121 @@
+{
+ "date": "2026-10-13",
+ "block": "12.5.212",
+ "database": "mm_accept_166_1791901979711",
+ "cases": [
+ {
+ "field": "keepaliveInterval",
+ "value": 0,
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "field": "keepaliveInterval",
+ "value": -1,
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "field": "keepaliveInterval",
+ "value": 1.5,
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "field": "keepaliveInterval",
+ "value": 4294967296,
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "field": "keepaliveRetries",
+ "value": 0,
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "field": "keepaliveRetries",
+ "value": -1,
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "field": "keepaliveRetries",
+ "value": 1.5,
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "field": "keepaliveRetries",
+ "value": 4294967296,
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "operation": "create",
+ "params": {},
+ "keepalive": "10s,10",
+ "status": 201
+ },
+ {
+ "operation": "create",
+ "params": {
+ "keepaliveInterval": 60
+ },
+ "keepalive": "60s,10",
+ "status": 201
+ },
+ {
+ "operation": "create",
+ "params": {
+ "keepaliveRetries": 3
+ },
+ "keepalive": "10s,3",
+ "status": 201
+ },
+ {
+ "operation": "create",
+ "params": {
+ "keepaliveInterval": 90,
+ "keepaliveRetries": 7
+ },
+ "keepalive": "90s,7",
+ "status": 201
+ },
+ {
+ "operation": "patch-other-field",
+ "nativeKeepalivePreserved": "1m,3",
+ "status": 200
+ },
+ {
+ "operation": "read-minutes",
+ "seconds": 60,
+ "status": 200
+ },
+ {
+ "operation": "psk-create-default-fastpath",
+ "status": 201,
+ "fastPath": "no"
+ },
+ {
+ "operation": "explicit-psk-fastpath-conflict",
+ "status": 400,
+ "nativeCalls": 0
+ },
+ {
+ "operation": "existing-psk-fastpath-conflict",
+ "status": 400,
+ "nativeWrites": 0
+ },
+ {
+ "operation": "enable-psk-with-omitted-fastpath",
+ "status": 200,
+ "fastPath": "no"
+ }
+ ],
+ "scope": "Production compiled GRE create/patch using isolated PostgreSQL and native HTTP fixture",
+ "passed": true,
+ "nativeWrites": 7,
+ "originalReportedFilename": "gre212-keepalive-result.json",
+ "databaseAndRoleRemoved": true
+}
\ No newline at end of file
diff --git a/docs/ux-ui/acceptance/gre212-deployment-2026-10-13.json b/docs/ux-ui/acceptance/gre212-deployment-2026-10-13.json
new file mode 100644
index 00000000..61b5a70f
--- /dev/null
+++ b/docs/ux-ui/acceptance/gre212-deployment-2026-10-13.json
@@ -0,0 +1,35 @@
+{
+ "date": "2026-10-13",
+ "block": "12.5.212",
+ "runtime": "ux-gre212",
+ "services": {
+ "backend": {
+ "image": "mmapp-test/backend:ux-gre212",
+ "imageId": "sha256:15b4c11614737ff12d4a4d40ed585324dabcace1f96342f0e966266125b78a1b",
+ "running": true,
+ "health": "healthy"
+ },
+ "frontend": {
+ "image": "mmapp-test/frontend:ux-gre212",
+ "imageId": "sha256:0cb19a8d93d5056870372c48bd189246da1e198bf32fc471861a6e70b0111ccf",
+ "running": true,
+ "health": null
+ }
+ },
+ "rollbackTags": {
+ "MMAPP_IMAGE_TAG": "ux-gre210",
+ "BACKEND_IMAGE_TAG": "ux-auth211"
+ },
+ "chrPasswordsChanged": false,
+ "source": [
+ {
+ "kind": "backend",
+ "matched": 478
+ },
+ {
+ "kind": "frontend",
+ "matched": 527
+ }
+ ],
+ "compiledMatched": 1048
+}
\ No newline at end of file
diff --git a/docs/ux-ui/acceptance/gre212-e2e-2026-10-13.json b/docs/ux-ui/acceptance/gre212-e2e-2026-10-13.json
new file mode 100644
index 00000000..1419248a
--- /dev/null
+++ b/docs/ux-ui/acceptance/gre212-e2e-2026-10-13.json
@@ -0,0 +1,24 @@
+{
+ "date": "2026-10-13",
+ "block": "12.5.212",
+ "uniqueCases": 10,
+ "groups": [
+ {
+ "startTime": "2026-10-13T14:28:25.941Z",
+ "duration": 20683.281000000003,
+ "expected": 8,
+ "skipped": 0,
+ "unexpected": 0,
+ "flaky": 0
+ },
+ {
+ "startTime": "2026-10-13T14:29:34.020Z",
+ "duration": 9814.235,
+ "expected": 2,
+ "skipped": 0,
+ "unexpected": 0,
+ "flaky": 0
+ }
+ ],
+ "apiMocked": true
+}
diff --git a/docs/ux-ui/acceptance/gre212-native-live-2026-10-13.json b/docs/ux-ui/acceptance/gre212-native-live-2026-10-13.json
new file mode 100644
index 00000000..2420af16
--- /dev/null
+++ b/docs/ux-ui/acceptance/gre212-native-live-2026-10-13.json
@@ -0,0 +1,69 @@
+{
+ "date": "2026-10-13",
+ "block": "12.5.212",
+ "runtime": "ux-gre212",
+ "cases": [
+ {
+ "host": "194.226.121.150",
+ "defaultCreateStatus": 201,
+ "defaultNative": "10s,10",
+ "pskDefaultFastPathDisabled": true,
+ "explicitFastPathConflictStatus": 400,
+ "minutesReadAsSeconds": 60,
+ "patchOtherFieldPreservesNative": "1m,3",
+ "invalidIntervalStatus": 400,
+ "invalidPatchLeavesNativeUnchanged": true,
+ "catalogSeconds": 60
+ },
+ {
+ "host": "104.165.244.7",
+ "defaultCreateStatus": 201,
+ "defaultNative": "10s,10",
+ "pskDefaultFastPathDisabled": true,
+ "explicitFastPathConflictStatus": 400,
+ "minutesReadAsSeconds": 60,
+ "patchOtherFieldPreservesNative": "1m,3",
+ "invalidIntervalStatus": 400,
+ "invalidPatchLeavesNativeUnchanged": true,
+ "catalogSeconds": 60
+ }
+ ],
+ "uiCases": [
+ {
+ "section": "gre",
+ "width": 1440,
+ "actualNativeRead": true,
+ "minutesFormSeconds": 60,
+ "invalidInputPreservesForm": true,
+ "mutationRequests": 0,
+ "pageErrors": 0
+ },
+ {
+ "section": "filters",
+ "width": 1440,
+ "actualNativeRead": true,
+ "configuredGreCatalogAccepted": true,
+ "mutationRequests": 0,
+ "pageErrors": 0
+ },
+ {
+ "section": "gre",
+ "width": 320,
+ "actualNativeRead": true,
+ "minutesFormSeconds": 60,
+ "invalidInputPreservesForm": true,
+ "mutationRequests": 0,
+ "pageErrors": 0
+ },
+ {
+ "section": "filters",
+ "width": 320,
+ "actualNativeRead": true,
+ "configuredGreCatalogAccepted": true,
+ "mutationRequests": 0,
+ "pageErrors": 0
+ }
+ ],
+ "scope": "Disabled own native GRE; defaults, unit parsing, patch preservation and UI validation; not data-plane",
+ "cleanupCompleted": true
+}
diff --git a/docs/ux-ui/acceptance/gre212-profile-2026-10-13.json b/docs/ux-ui/acceptance/gre212-profile-2026-10-13.json
new file mode 100644
index 00000000..18e8008c
--- /dev/null
+++ b/docs/ux-ui/acceptance/gre212-profile-2026-10-13.json
@@ -0,0 +1,15 @@
+{
+ "date": "2026-10-13",
+ "block": "12.5.212",
+ "commands": [
+ "npm --prefix backend run test:live-maps",
+ "npx tsx lib/gre-export.test.ts",
+ "npx tsx lib/filters-response.test.ts",
+ "npx tsx lib/network-map-response.test.ts"
+ ],
+ "passed": true,
+ "frontendTypecheckPassed": true,
+ "backendBuildPassed": true,
+ "eslintErrors": 0,
+ "scope": "GRE compatibility and export; existing catalog consumers retained"
+}
diff --git a/docs/ux-ui/acceptance/source212-result.json b/docs/ux-ui/acceptance/source212-result.json
new file mode 100644
index 00000000..7d63982a
--- /dev/null
+++ b/docs/ux-ui/acceptance/source212-result.json
@@ -0,0 +1,14 @@
+{
+ "date": "2026-10-13",
+ "block": "12.5.212",
+ "kinds": [
+ {
+ "kind": "backend",
+ "matched": 478
+ },
+ {
+ "kind": "frontend",
+ "matched": 527
+ }
+ ]
+}
\ No newline at end of file
diff --git a/lib/gre-export.test.ts b/lib/gre-export.test.ts
index 0d88e89f..15f316aa 100644
--- a/lib/gre-export.test.ts
+++ b/lib/gre-export.test.ts
@@ -16,7 +16,7 @@ for (const unsupported of ["/ip ipsec peer add", "/ip/ipsec/peer/add", "proposal
const noInner = generateGreRsc({ ...tunnel, localInnerIp: "", ipsec: null, localAddress: "192.0.2.10", keepaliveInterval: 0, dscp: 12, enabled: true }, {})
assert.equal(noInner.split("\n").filter(line => line.startsWith("/")).length, 1)
assert.ok(noInner.includes('local-address="192.0.2.10"'))
-assert.ok(noInner.includes("keepalive=0 dscp=12"))
+assert.ok(noInner.includes("keepalive=s, dscp=12"))
assert.ok(!noInner.includes("ipsec-secret="))
console.log("gre-export: escaped strings, observed GRE/PSK only, optional IP and no duplicate IPsec passed")
const masked = generateGreRsc({ ...tunnel, ipsec: { secret: '' } }, {})
@@ -24,3 +24,7 @@ assert.ok(masked.includes('ipsec-secret=""'))
assert.ok(masked.includes('# Шаблон: ключ IPsec не раскрыт.'))
assert.ok(!masked.includes('ipsec-secret=""'))
console.log('gre-export: hidden configured PSK produces an explicitly labelled template, never an empty live key')
+
+const incompatible = generateGreRsc({ ...tunnel, allowFastPath: true }, {})
+assert.ok(incompatible.includes("allow-fast-path="))
+assert.ok(incompatible.includes("IPsec несовместим с FastPath"))
diff --git a/lib/gre-export.ts b/lib/gre-export.ts
index c2068fb2..93320b41 100644
--- a/lib/gre-export.ts
+++ b/lib/gre-export.ts
@@ -2,6 +2,15 @@ import type { GreTunnel, Server } from "./data"
import { quoteRosString } from "./vxlan-export"
const commentLine = (value: string) => value.replace(/[\r\n\x00-\x1f\x7f]/g, " ")
+const validKeepalive = (t: GreTunnel) => [t.keepaliveInterval, t.keepaliveRetries].every(value => Number.isInteger(value) && value >= 1 && value <= 4294967295)
+
+export function greExportWarnings(t: GreTunnel): string[] {
+ return [
+ t.ipsec && !t.ipsec.secret ? "Шаблон: ключ IPsec скрыт. Укажите ключ перед применением." : "",
+ !validKeepalive(t) ? "Шаблон: задайте допустимый интервал keepalive и число попыток перед применением." : "",
+ t.ipsec && t.allowFastPath ? "Шаблон: IPsec несовместим с FastPath. Проверьте этот параметр перед применением." : "",
+ ].filter(Boolean)
+}
/** GRE/PSK fragment: https://help.mikrotik.com/docs/spaces/ROS/pages/24805531/GRE */
export function generateGreRsc(t: GreTunnel, servers: Record): string {
@@ -9,11 +18,13 @@ export function generateGreRsc(t: GreTunnel, servers: Record): s
const lines = ["# GRE — " + commentLine(t.name)]
if (server) lines.push("# Сервер: " + commentLine(server.name) + " (" + commentLine(server.host) + ")")
lines.push("# Фрагмент интерфейса и локального IP; удалённая сторона и маршруты не включены", "")
+ if (!validKeepalive(t)) lines.push("# Шаблон: задайте KEEPALIVE_SECONDS и KEEPALIVE_RETRIES — целые значения от 1 до 4294967295.")
+ if (t.ipsec && t.allowFastPath) lines.push("# Шаблон: IPsec несовместим с FastPath. Проверьте ALLOW_FAST_PATH перед применением.")
const args = [
"name=" + quoteRosString(t.name), "remote-address=" + quoteRosString(t.remoteAddress),
- "mtu=" + t.mtu, "keepalive=" + (t.keepaliveInterval > 0 ? t.keepaliveInterval + "s," + t.keepaliveRetries : "0"),
+ "mtu=" + t.mtu, "keepalive=" + (validKeepalive(t) ? t.keepaliveInterval + "s," + t.keepaliveRetries : "s,"),
"dscp=" + t.dscp, "clamp-tcp-mss=" + (t.clampTcpMss ? "yes" : "no"),
- "allow-fast-path=" + (t.allowFastPath ? "yes" : "no"), "disabled=" + (t.enabled ? "no" : "yes"),
+ "allow-fast-path=" + (t.ipsec && t.allowFastPath ? "" : t.allowFastPath ? "yes" : "no"), "disabled=" + (t.enabled ? "no" : "yes"),
"comment=" + quoteRosString(t.comment),
]
if (t.localAddress && t.localAddress !== "0.0.0.0") args.push("local-address=" + quoteRosString(t.localAddress))
diff --git a/tests/e2e/gre-export-compatibility.spec.ts b/tests/e2e/gre-export-compatibility.spec.ts
new file mode 100644
index 00000000..55fdc033
--- /dev/null
+++ b/tests/e2e/gre-export-compatibility.spec.ts
@@ -0,0 +1,15 @@
+import {test,expect} from '@playwright/test'
+for(const width of [1440,320])test(`GRE: несовместимый снимок помечает параметры экспорта ${width}`,async({page})=>{
+ let writes=0
+ const token=[Buffer.from('{}').toString('base64url'),Buffer.from(JSON.stringify({sub:'own209',apps:['mm'],permissions:['mm:dashboard:read','mm:servers:read','mm:network:read'],exp:Math.floor(Date.now()/1000)+600})).toString('base64url'),'fixture'].join('.')
+ await page.addInitScript(t=>localStorage.setItem('mmapp_token',t),token)
+ const tunnel={id:'*1',serverId:'1',name:'own209-gre',localAddress:'192.0.2.1',remoteAddress:'192.0.2.2',localInnerIp:'10.209.0.1/30',remoteInnerIp:'10.209.0.2/30',poolId:'live',ipsec:{secret:''},mtu:1476,keepaliveInterval:0,keepaliveRetries:0,dscp:'inherit',clampTcpMss:true,allowFastPath:true,comment:'',enabled:true,status:'up'}
+ await page.route('**/health',r=>r.fulfill({json:{ok:true}}))
+ await page.route('**/api/**',r=>{if(r.request().method()!=='GET')writes++;const p=new URL(r.request().url()).pathname;return r.fulfill({json:p==='/api/auth/config'?{required:true}:p==='/api/servers'?[{id:1,name:'Own209 router',host:'192.0.2.1',enabled:true,country:'RU'}]:p==='/api/gre/tunnels'?{tunnels:[tunnel]}:{}})})
+ await page.setViewportSize({width,height:1000});await page.goto('/gre?server=1')
+ const row=page.getByRole('row').filter({hasText:'own209-gre'});await expect(row).toContainText('IPsec');await expect(row).not.toContainText('Открытый')
+ await expect(page.getByRole('button',{name:'Добавить туннель',exact:true})).toBeDisabled()
+ await page.getByRole('button',{name:'Просмотр кода: own209-gre',exact:true}).click()
+ const sheet=page.getByRole('dialog');await expect(sheet).toContainText('Шаблон: ключ IPsec скрыт');await expect(sheet.locator('pre')).toContainText('');await expect(sheet.locator('pre')).not.toContainText('ipsec-secret=""')
+ await expect(sheet).toContainText('задайте допустимый интервал keepalive');await expect(sheet.locator('pre')).toContainText('');await expect(sheet.locator('pre')).toContainText('');expect(writes).toBe(0);expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth+1)).toBe(true)
+})
diff --git a/tests/e2e/gre-fastpath.spec.ts b/tests/e2e/gre-fastpath.spec.ts
new file mode 100644
index 00000000..0178c91d
--- /dev/null
+++ b/tests/e2e/gre-fastpath.spec.ts
@@ -0,0 +1,6 @@
+import {test,expect} from '@playwright/test'
+for(const width of [1440,320])test(`GRE: IPsec выключает FastPath перед PATCH ${width}`,async({page})=>{
+ let writes=0;const tunnel={id:'*1',serverId:'1',name:'own212-gre',localAddress:'192.0.2.1',remoteAddress:'192.0.2.2',localInnerIp:'',remoteInnerIp:'',poolId:'live',ipsec:null,mtu:1476,keepaliveInterval:10,keepaliveRetries:3,dscp:'inherit',clampTcpMss:true,allowFastPath:true,comment:'',enabled:false,status:'down'}
+ await page.route('**/health',r=>r.fulfill({json:{ok:true}}));await page.route('**/api/**',r=>{if(r.request().method()==='PATCH'){writes++;expect(r.request().postDataJSON()).toMatchObject({ipsecSecret:'own212-synthetic-psk',allowFastPath:false});return r.fulfill({json:{ok:true}})}const p=new URL(r.request().url()).pathname;return r.fulfill({json:p==='/api/auth/config'?{required:false}:p==='/api/servers'?[{id:1,name:'Own212 router',host:'192.0.2.1',enabled:true,country:'RU'}]:p==='/api/gre/tunnels'?{tunnels:[tunnel]}:{}})})
+ await page.setViewportSize({width,height:1000});await page.goto('/gre?server=1');await page.getByRole('button',{name:'Действия: own212-gre',exact:true}).click();await page.getByRole('menuitem',{name:'Редактировать',exact:true}).click();const form=page.getByRole('dialog');await form.getByRole('button',{name:'Дополнительно',exact:true}).click();await expect(form.getByRole('switch',{name:'Allow Fast Path',exact:true})).toBeChecked();await form.getByRole('switch',{name:'Включить IPsec',exact:true}).click();await expect(form.getByRole('switch',{name:'Allow Fast Path',exact:true})).not.toBeChecked();await expect(form.getByRole('switch',{name:'Allow Fast Path',exact:true})).toBeDisabled();await form.getByLabel('Пароль (PSK)').fill('own212-synthetic-psk');await form.getByRole('button',{name:'Сохранить',exact:true}).click();await expect(form).toHaveCount(0);expect(writes).toBe(1)
+})