diff --git a/backend/scripts/acceptance/acme-renew-tls.mjs b/backend/scripts/acceptance/acme-renew-tls.mjs new file mode 100644 index 00000000..b9faaa2a --- /dev/null +++ b/backend/scripts/acceptance/acme-renew-tls.mjs @@ -0,0 +1,71 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import tls from 'node:tls'; +import { X509Certificate } from 'node:crypto'; +import { pathToFileURL } from 'node:url'; + +const fingerprint = value => { + assert.equal(typeof value, 'string', 'Fingerprint is required'); + const normalized = value.replaceAll(':', '').toLowerCase(); + assert(/^[a-f0-9]{64}$/.test(normalized), 'Expected SHA-256 fingerprint'); + return normalized; +}; + +export function compareRenewal(before, after) { + for (const sample of [before, after]) { + assert(/^mmt253-[a-f0-9]{8}\.pngs\.top$/.test(sample.fqdn), 'Only the prepared exact test domain is permitted'); + assert.equal(sample.remoteAddress, '104.165.244.7'); + assert.equal(sample.authorized, true); + assert.equal(sample.hostnameMatched, true); + assert.equal(sample.nativePrivateKey, true); + assert.equal(sample.nativeName, sample.certName); + assert.equal(sample.certName, sample.fqdn.replace('.pngs.top', '')); + assert.equal(fingerprint(sample.nativeFingerprint256), fingerprint(sample.fingerprint256), 'Native leaf must match served TLS'); + assert(Number.isFinite(Date.parse(sample.validUntil)), 'Expiry is required'); + assert(Number.isFinite(Date.parse(sample.sampledAt)), 'Sample time is required'); + assert(Date.parse(sample.validUntil) > Date.parse(sample.sampledAt), 'Certificate is expired'); + } + assert.equal(after.fqdn, before.fqdn); + assert.equal(after.certName, before.certName); + assert.notEqual(fingerprint(after.fingerprint256), fingerprint(before.fingerprint256), 'Old certificate is still being served'); + assert(Date.parse(after.sampledAt) >= Date.parse(before.sampledAt)); + assert(Date.parse(after.validUntil) > Date.parse(after.sampledAt), 'New certificate is expired'); + // A second issuance on the same day need not extend expiry by a full day. + return { fingerprintChanged: true, nativeAndTlsMatch: true, trustedHostnameMatch: true }; +} + +async function sample(fixture) { + assert(/^mmt253-[a-f0-9]{8}\.pngs\.top$/.test(fixture.fqdn)); + assert.equal(fixture.certName, fixture.fqdn.replace('.pngs.top', '')); + assert.equal(fixture.nativePrivateKey, true); + fingerprint(fixture.nativeFingerprint256); + return await new Promise((resolve, reject) => { + const socket = tls.connect({ host: '104.165.244.7', port: 443, servername: fixture.fqdn, rejectUnauthorized: true }); + socket.setTimeout(15000, () => socket.destroy(new Error('TLS acceptance timeout'))); + socket.once('error', reject); + socket.once('secureConnect', () => { + try { + assert.equal(socket.authorized, true); + const cert = new X509Certificate(socket.getPeerCertificate().raw); + assert(cert.checkHost(fixture.fqdn), 'Hostname mismatch'); + assert(Date.parse(cert.validTo) > Date.now(), 'Certificate is expired'); + assert.equal(fingerprint(cert.fingerprint256), fingerprint(fixture.nativeFingerprint256), 'Native leaf does not match TLS'); + resolve({ sampledAt: new Date().toISOString(), fqdn: fixture.fqdn, certName: fixture.certName, + remoteAddress: socket.remoteAddress, authorized: socket.authorized, hostnameMatched: true, + protocol: socket.getProtocol(), fingerprint256: cert.fingerprint256, + validFrom: cert.validFrom, validUntil: cert.validTo, + nativeName: fixture.certName, nativePrivateKey: true, nativeFingerprint256: fixture.nativeFingerprint256 }); + } catch (error) { reject(error); } finally { socket.end(); } + }); + }); +} + +if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { + const [mode, input, output] = process.argv.slice(2); + assert(input && output && ['sample', 'compare'].includes(mode), 'Usage: sample native-summary.json output.json | compare samples.json output.json'); + assert(!fs.existsSync(output), 'Never overwrite acceptance evidence'); + const data = JSON.parse(fs.readFileSync(input, 'utf8')); + const result = mode === 'sample' ? await sample(data) : compareRenewal(data.before, data.after); + fs.writeFileSync(output, JSON.stringify(result, null, 2) + '\n', { flag: 'wx' }); + console.log(mode === 'sample' ? 'Trusted TLS/native fingerprint sample saved' : 'Renewal fingerprint comparison passed'); +} diff --git a/backend/scripts/acceptance/acme-renew-tls.test.mjs b/backend/scripts/acceptance/acme-renew-tls.test.mjs new file mode 100644 index 00000000..c7b7c675 --- /dev/null +++ b/backend/scripts/acceptance/acme-renew-tls.test.mjs @@ -0,0 +1,13 @@ +import assert from 'node:assert/strict'; +import { compareRenewal } from './acme-renew-tls.mjs'; +const before = { fqdn: 'mmt253-a1b2c3d4.pngs.top', certName: 'mmt253-a1b2c3d4', nativeName: 'mmt253-a1b2c3d4', + remoteAddress: '104.165.244.7', authorized: true, hostnameMatched: true, nativePrivateKey: true, + fingerprint256: 'a'.repeat(64), nativeFingerprint256: 'a'.repeat(64), sampledAt: '2026-10-14T12:00:00Z', validUntil: '2027-01-12T12:00:00Z' }; +const after = { ...before, fingerprint256: 'b'.repeat(64), nativeFingerprint256: 'b'.repeat(64), sampledAt: '2026-10-14T12:01:00Z' }; +assert.equal(compareRenewal(before, after).fingerprintChanged, true); +for (const patch of [{ fingerprint256: before.fingerprint256, nativeFingerprint256: before.nativeFingerprint256 }, + { nativeFingerprint256: before.nativeFingerprint256 }, { authorized: false }, { hostnameMatched: false }, + { nativePrivateKey: false }, { nativeName: 'another' }, { fqdn: 'other.pngs.top' }, + { remoteAddress: '194.226.121.150' }, { validUntil: 'bad' }, { validUntil: '2026-10-13T12:00:00Z' }, + { sampledAt: '2026-10-14T11:00:00Z' }]) assert.throws(() => compareRenewal(before, { ...after, ...patch })); +console.log('Renewal acceptance comparison: changed leaf passes; 11 false-success cases rejected. No network or CA calls.'); diff --git a/deploy/TEST-ACME.md b/deploy/TEST-ACME.md index 37a6c75a..b54eac59 100644 --- a/deploy/TEST-ACME.md +++ b/deploy/TEST-ACME.md @@ -13,3 +13,19 @@ Выданный публичный сертификат CA не объявляется отозванным: экземпляры и ключевые файлы на CHR удалены, DNS очищен; запись о выпуске сохраняется у CA. Это не тест автоматического renewal, CA revocation или нескольких backend. Протоколы: [UI](../docs/ux-ui/acceptance/acme250-live-ui.json), [TLS](../docs/ux-ui/acceptance/acme250-tls.json), [DNS](../docs/ux-ui/acceptance/acme250-cloudflare.json), [native/cleanup](../docs/ux-ui/acceptance/acme250-native.json). + +## Подготовка повторной выдачи / renewal — блок253 (2026-10-14) + +Подготовлен конкретный домен `mmt253-8e62a941.pngs.top`, certName `mmt253-8e62a941`, CHR `104.165.244.7` (server2). Это обычное имя без wildcard. Этот раздел описывает следующий тест; выдача и продление253 ещё не запускались. Первичная выдача250 не доказывает замену сертификата при повторном импорте. HTTP ACK и terminal job сами по себе также не доказывают, какой leaf обслуживает TLS. + +1. Перед изменениями снять свежий приватный baseline: native export, certificates/files/services; SQL ACME и renewal settings, список managed targets/jobs; scheduler state. Подтвердить отсутствие любых других managed targets/active jobs и DNS записей на обоих точных именах. Если они появились — остановиться до мутации. Не использовать удалённый baseline250. +2. Временно остановить автоматический certificates_renew через его настройки и дождаться отсутствия текущего запуска. Сохранить исходные настройки целиком. Не запускать несколько экземпляров collector. Настроить тестовый Cloudflare token и выполнить первый UI выпуск с новым requestId. Публично сохранить только terminal job/protocol и native metadata без секретов. +3. По аутентифицированному RouterOS чтению выбрать ровно один собственный leaf с exact name/CN, private-key=true и отпечатком; снять served TLS с SNI/обычной проверкой доверия. Если native fingerprint не SHA-256, сначала экспортировать только публичный сертификат и вычислить SHA-256 через X509Certificate; не интерпретировать другой алгоритм как SHA-256. Изолированная сводка для helper содержит только fqdn/certName/nativePrivateKey/nativeFingerprint256. +4. Read-only helper `node backend/scripts/acceptance/acme-renew-tls.mjs sample native-summary.json before.json` подключается только к104.165.244.7:443 с точным SNI, rejectUnauthorized=true и15s timeout. Требует совпадения native и TLS SHA-256, private key и hostname. Существующий output не перезаписывается. Этот helper ничего не выпускает и не меняет RouterOS/DNS. +5. Для реального collector renewal подтвердить канонический срок/статус leaf по state. Не менять даты на CHR и не обходить unknown guard252. Если RouterOS дата не распознаётся, записать препятствие и не запускать повторную выдачу. Для известного срока при выключенном renewal подготовить порог90 дней и интервал не менее3600s, подтвердить единственный собственный target. Затем один PUT enabled=true запускает certificates_renew через refreshScheduler немедленно: отдельный run-now НЕ отправлять. После terminal сразу выключить renewal до следующего интервала. Сохранить ID созданной scheduler-задачи; при неизвестном ACK проверять тот же ID, новый run не отправлять. Проверить source=scheduler, matching context, terminal done и snapshot renewed1/errors0. +6. Снять after.json тем же helper и сравнить `node backend/scripts/acceptance/acme-renew-tls.mjs compare samples.json comparison.json`, где samples.json содержит before/after. Новый SHA-256 должен отличаться от первого, native должен совпасть с served TLS. Не требовать увеличения expiry на целый день: обе выдачи могут быть в один день. Проверить оба назначения www-ssl/api-ssl и HTTPS REST с обычным доверием. Если job done, а TLS leaf прежний, это ошибка приёмки; не удалять первый leaf ради получения ложного успеха. +7. Независимо от результата остановить own renewal, вернуть исходные SSL сервисы, удалить только собственные exact cert IDs/fingerprints/PEM файлы/job IDs; вернуть ACME/renew settings целиком. Удалять только DNS записи с проверенными exact name/type/content/ID, не чужие записи. Подтвердить исходные коллекции, normalized export, settings и0 own DNS; удалить private baseline/token. Контрольный194.226.121.150 не менять, пароли не менять, перезагрузки не нужны. + +Для этих двух выдач и повторного назначения SSL необходимо согласие на конкретный сценарий. Разрешение250 относилось к уже завершённому тесту. До согласия выполняются только подготовка/чтение. Полная регрессия и Docker build для изменения лишь ручного acceptance helper не нужны; comparator проверяется отдельно. CA revocation и multi-instance здесь не принимаются. + +Нормативная справка для native полей/import: [MikroTik Certificates](https://help.mikrotik.com/docs/spaces/ROS/pages/2555969/Certificates). Документация не гарантирует успех повторного импорта в текущем приложении; именно это предстоит измерить. diff --git a/docs/ux-ui-external-acceptance.md b/docs/ux-ui-external-acceptance.md index 00422d51..7bd91940 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-14: 252 — сертификаты используют strict-ID/enabled guards; renewal больше не объявляет failed/pending успешным и не подменяет неизвестный срок нулём. Восемь профильных scripts и 39 API/SQL checks прошли, own DB/role удалены. Backend ux-certificates252/frontend ux-certificates251 healthy, source526/dist1080 matched. Реальный Portal/read1440/390: оба CHR без ошибок, writes0. Следующий253 — подготовка реальной повторной выдачи/renewal и безопасного сравнения сертификата/TLS; эта приёмка ещё открыта. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 253 — подготовлена реальная приёмка renewal для exact mmt253-8e62a941.pngs.top на CHR104, read-only DNS empty/оба REST200. TLS/native SHA-256 comparator: positive и11 false-success отказов passed, syntax/lint passed. Никаких выдач/изменений253 не запускали. Runtime backend ux-certificates252/frontend ux-certificates251. Далее согласовать два выпуска/renewal с назначением SSL и полным откатом по deploy/TEST-ACME.md; этап12.5 открыт. IPsec78: реальный PSK responder established, mode-config198.18.78.10 и online=true подтверждены API; точные собственные объекты удалены. Сертификатный/DN online, late failure и history restore остаются открытыми. [Отчёт](ux-ui/acceptance/ipsec-mode-config-2026-10-09.json), [журнал](ux-ui-progress.md). @@ -18,6 +18,18 @@ OSPF84: real GRE Full adjacency/private routes/Ping3/3 и4 live PC/320px URL/bro BFD85: Up/Full/own route/Ping,4 live UI и loss/withdrawal/recovery приняты; exact own cleanup8 коллекций×2 passed. Withdrawal5,245ms не подтверждает sub-second BFD timing; этот вопрос открыт. Runtime routing83 без новой сборки. [Отчёт](ux-ui/acceptance/ospf-bfd-loss-2026-10-09.json). +## 12.5.253 — подготовка проверки реальной замены сертификата при renewal (2026-10-14) + +Подготовлен exact mmt253-8e62a941.pngs.top/certName mmt253-8e62a941 на CHR104/server2. Read-only preflight с Debian: точный FQDN и challenge name имеют0 Cloudflare records, оба CHR REST200 с прежними паролями. Ни DNS/settings/native writes, ни ACME orders не выполнялись. Это не свежий приватный baseline для будущей мутации: его нужно снять непосредственно перед запуском. + +Новый ручной helper backend/scripts/acceptance/acme-renew-tls.mjs делает bounded15s TLS к фиксированному104:443 с exact SNI и rejectUnauthorized=true. Сверяет hostname, native SHA-256/private-key/name и served leaf. Compare требует изменения SHA-256 после повторного выпуска, совпадения native/TLS, корректных дат; output не перезаписывается. Ввод содержит только публичную native сводку, не keys/passwords. Один positive и11 negative comparator случаев passed без сети/CA; syntax и scoped ESLint passed. Это проверка проверяющего инструмента, не реального renewal. Native fingerprint другого алгоритма сначала пересчитывается из публичного PEM, не угадывается. + +Процедура в [TEST-ACME](../deploy/TEST-ACME.md) включает свежий private baseline/settings/managed targets, единственный собственный target, первый UI выпуск, initial TLS, canonical date guard252 и один scheduler renewal. Code review пути settings выявил важное условие: PUT enabled=true вызывает refreshScheduler с немедленным запуском, поэтому дополнительный run-now запрещён. После terminal renewal выключается до следующего интервала. Если дата unknown, повторный выпуск не запускается и ограничение фиксируется. Job done без нового served fingerprint считается провалом, старый leaf не удаляется ради прохождения теста. + +Откат описан по exact IDs/fingerprints/files/record content и исходным settings/services/normalized export, контрольный CHR194 не меняется, перезагрузки/смена паролей не нужны. Публичная CA выдача не объявляется revoked. Справка native полей/import: [MikroTik Certificates](https://help.mikrotik.com/docs/spaces/ROS/pages/2555969/Certificates); факт замены проверяется измерением, документация его не подтверждает для текущего приложения. + +Production код не менялся, Docker build/deploy не нужен, backend ux-certificates252/frontend ux-certificates251 сохранены. Следующий шаг — согласование конкретных двух выдач/renewal с включением www-ssl/api-ssl и полным откатом; разрешение250 относилось к уже завершённому выпуску. До согласия только подготовка/чтение. Реальное renewal, CA revoke и multi-instance остаются открыты. + ## 12.5.252 — выключенные серверы и достоверность результата renewal (2026-10-14) Общий getter сертификатов теперь использует parseDbServerId и SQL enabled=true: numeric prefix/небезопасные ID не доходят до БД, выключенные серверы не возвращаются. Executor повторно отклоняет disabled перед DNS/native вызовами. Renewal использует каноническое наблюдаемое состояние вместо legacy expiry=0: неизвестный срок/статус пропускается, ошибка чтения/неоднозначное имя не запускает выпуск. После runner читается свежая запись задачи и проверяются id/server/name/source/domains/keyType/trustStore; renewed увеличивается только при status=done и step=done. Failed даёт error, queued/running и неподтверждённый результат не дают успеха. Local singleflight сохраняется; атомарная защита нескольких backend не заявляется. diff --git a/docs/ux-ui-plan.md b/docs/ux-ui-plan.md index 8c479cac..d803eb49 100644 --- a/docs/ux-ui-plan.md +++ b/docs/ux-ui-plan.md @@ -1,9 +1,21 @@ # План реализации UX/UI по пятичасовым окнам -Актуальное продолжение 2026-10-14: 252 — сертификаты используют strict-ID/enabled guards; renewal больше не объявляет failed/pending успешным и не подменяет неизвестный срок нулём. Восемь профильных scripts и 39 API/SQL checks прошли, own DB/role удалены. Backend ux-certificates252/frontend ux-certificates251 healthy, source526/dist1080 matched. Реальный Portal/read1440/390: оба CHR без ошибок, writes0. Следующий253 — подготовка реальной повторной выдачи/renewal и безопасного сравнения сертификата/TLS; эта приёмка ещё открыта. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 253 — подготовлена реальная приёмка renewal для exact mmt253-8e62a941.pngs.top на CHR104, read-only DNS empty/оба REST200. TLS/native SHA-256 comparator: positive и11 false-success отказов passed, syntax/lint passed. Никаких выдач/изменений253 не запускали. Runtime backend ux-certificates252/frontend ux-certificates251. Далее согласовать два выпуска/renewal с назначением SSL и полным откатом по deploy/TEST-ACME.md; этап12.5 открыт. Общий план проекта MikrotikManager. Основан на согласованном плане из задачи; точка продолжения обновлена 2026-10-14. Источник фактического прогресса, проверок и ограничений — [журнал реализации](ux-ui-progress.md). Этот документ задаёт объём и критерии завершения, журнал фиксирует результат. +## 12.5.253 — подготовка проверки реальной замены сертификата при renewal (2026-10-14) + +Подготовлен exact mmt253-8e62a941.pngs.top/certName mmt253-8e62a941 на CHR104/server2. Read-only preflight с Debian: точный FQDN и challenge name имеют0 Cloudflare records, оба CHR REST200 с прежними паролями. Ни DNS/settings/native writes, ни ACME orders не выполнялись. Это не свежий приватный baseline для будущей мутации: его нужно снять непосредственно перед запуском. + +Новый ручной helper backend/scripts/acceptance/acme-renew-tls.mjs делает bounded15s TLS к фиксированному104:443 с exact SNI и rejectUnauthorized=true. Сверяет hostname, native SHA-256/private-key/name и served leaf. Compare требует изменения SHA-256 после повторного выпуска, совпадения native/TLS, корректных дат; output не перезаписывается. Ввод содержит только публичную native сводку, не keys/passwords. Один positive и11 negative comparator случаев passed без сети/CA; syntax и scoped ESLint passed. Это проверка проверяющего инструмента, не реального renewal. Native fingerprint другого алгоритма сначала пересчитывается из публичного PEM, не угадывается. + +Процедура в [TEST-ACME](../deploy/TEST-ACME.md) включает свежий private baseline/settings/managed targets, единственный собственный target, первый UI выпуск, initial TLS, canonical date guard252 и один scheduler renewal. Code review пути settings выявил важное условие: PUT enabled=true вызывает refreshScheduler с немедленным запуском, поэтому дополнительный run-now запрещён. После terminal renewal выключается до следующего интервала. Если дата unknown, повторный выпуск не запускается и ограничение фиксируется. Job done без нового served fingerprint считается провалом, старый leaf не удаляется ради прохождения теста. + +Откат описан по exact IDs/fingerprints/files/record content и исходным settings/services/normalized export, контрольный CHR194 не меняется, перезагрузки/смена паролей не нужны. Публичная CA выдача не объявляется revoked. Справка native полей/import: [MikroTik Certificates](https://help.mikrotik.com/docs/spaces/ROS/pages/2555969/Certificates); факт замены проверяется измерением, документация его не подтверждает для текущего приложения. + +Production код не менялся, Docker build/deploy не нужен, backend ux-certificates252/frontend ux-certificates251 сохранены. Следующий шаг — согласование конкретных двух выдач/renewal с включением www-ssl/api-ssl и полным откатом; разрешение250 относилось к уже завершённому выпуску. До согласия только подготовка/чтение. Реальное renewal, CA revoke и multi-instance остаются открыты. + ## 12.5.252 — выключенные серверы и достоверность результата renewal (2026-10-14) Общий getter сертификатов теперь использует parseDbServerId и SQL enabled=true: numeric prefix/небезопасные ID не доходят до БД, выключенные серверы не возвращаются. Executor повторно отклоняет disabled перед DNS/native вызовами. Renewal использует каноническое наблюдаемое состояние вместо legacy expiry=0: неизвестный срок/статус пропускается, ошибка чтения/неоднозначное имя не запускает выпуск. После runner читается свежая запись задачи и проверяются id/server/name/source/domains/keyType/trustStore; renewed увеличивается только при status=done и step=done. Failed даёт error, queued/running и неподтверждённый результат не дают успеха. Local singleflight сохраняется; атомарная защита нескольких backend не заявляется. diff --git a/docs/ux-ui-progress.md b/docs/ux-ui-progress.md index 42e36ef0..965d4a08 100644 --- a/docs/ux-ui-progress.md +++ b/docs/ux-ui-progress.md @@ -1,6 +1,6 @@ # UX/UI: журнал реализации -Актуальное продолжение 2026-10-14: 252 — сертификаты используют strict-ID/enabled guards; renewal больше не объявляет failed/pending успешным и не подменяет неизвестный срок нулём. Восемь профильных scripts и 39 API/SQL checks прошли, own DB/role удалены. Backend ux-certificates252/frontend ux-certificates251 healthy, source526/dist1080 matched. Реальный Portal/read1440/390: оба CHR без ошибок, writes0. Следующий253 — подготовка реальной повторной выдачи/renewal и безопасного сравнения сертификата/TLS; эта приёмка ещё открыта. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 253 — подготовлена реальная приёмка renewal для exact mmt253-8e62a941.pngs.top на CHR104, read-only DNS empty/оба REST200. TLS/native SHA-256 comparator: positive и11 false-success отказов passed, syntax/lint passed. Никаких выдач/изменений253 не запускали. Runtime backend ux-certificates252/frontend ux-certificates251. Далее согласовать два выпуска/renewal с назначением SSL и полным откатом по deploy/TEST-ACME.md; этап12.5 открыт. Общий план: [UX/UI по этапам и пятичасовым окнам](ux-ui-plan.md). План задаёт объём, этот журнал фиксирует выполненные блоки и ограничения. Записи расположены от новых к старым; подробная история этапа12 также сохранена в [ux-ui-stage-12.md](ux-ui-stage-12.md). @@ -36,7 +36,19 @@ | 10 | Маршрутизация, диагностика, карта и терминал | Завершён в границах локальной приёмки 2026-10-01; блоки 10.1–10.71, внешние ограничения в 10.71 | | 11А | Пользователи, контейнеры, сертификаты и резервные копии | Завершён в границах локальной приёмки 2026-10-01; 11А.1–11А.33, внешние ограничения в 11А.33 | | 11Б | Оповещения, сбор данных, интеграции, релизы и настройки | Завершён в границах локальной приёмки 2026-10-03; ограничения и evidence в11Б.7 | -| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная12.3.83; внешняя12.5 открыта, завершённые блоки1–252 в записанных scopes. Runtime backend ux-certificates252/frontend ux-certificates251. Общая регрессия249 и профиль сертификатов252 прошли; реальный renewal остаётся открытым. | +| 12 | Итоговая доступность, адаптивность, производительность и регрессия | Локальная12.3.83; внешняя12.5 открыта, завершённые блоки1–253 (253 — подготовка) в записанных scopes. Runtime backend ux-certificates252/frontend ux-certificates251. Общая регрессия249 и профиль сертификатов252 прошли; реальный renewal остаётся открытым. | + +## 12.5.253 — подготовка проверки реальной замены сертификата при renewal (2026-10-14) + +Подготовлен exact mmt253-8e62a941.pngs.top/certName mmt253-8e62a941 на CHR104/server2. Read-only preflight с Debian: точный FQDN и challenge name имеют0 Cloudflare records, оба CHR REST200 с прежними паролями. Ни DNS/settings/native writes, ни ACME orders не выполнялись. Это не свежий приватный baseline для будущей мутации: его нужно снять непосредственно перед запуском. + +Новый ручной helper backend/scripts/acceptance/acme-renew-tls.mjs делает bounded15s TLS к фиксированному104:443 с exact SNI и rejectUnauthorized=true. Сверяет hostname, native SHA-256/private-key/name и served leaf. Compare требует изменения SHA-256 после повторного выпуска, совпадения native/TLS, корректных дат; output не перезаписывается. Ввод содержит только публичную native сводку, не keys/passwords. Один positive и11 negative comparator случаев passed без сети/CA; syntax и scoped ESLint passed. Это проверка проверяющего инструмента, не реального renewal. Native fingerprint другого алгоритма сначала пересчитывается из публичного PEM, не угадывается. + +Процедура в [TEST-ACME](../deploy/TEST-ACME.md) включает свежий private baseline/settings/managed targets, единственный собственный target, первый UI выпуск, initial TLS, canonical date guard252 и один scheduler renewal. Code review пути settings выявил важное условие: PUT enabled=true вызывает refreshScheduler с немедленным запуском, поэтому дополнительный run-now запрещён. После terminal renewal выключается до следующего интервала. Если дата unknown, повторный выпуск не запускается и ограничение фиксируется. Job done без нового served fingerprint считается провалом, старый leaf не удаляется ради прохождения теста. + +Откат описан по exact IDs/fingerprints/files/record content и исходным settings/services/normalized export, контрольный CHR194 не меняется, перезагрузки/смена паролей не нужны. Публичная CA выдача не объявляется revoked. Справка native полей/import: [MikroTik Certificates](https://help.mikrotik.com/docs/spaces/ROS/pages/2555969/Certificates); факт замены проверяется измерением, документация его не подтверждает для текущего приложения. + +Production код не менялся, Docker build/deploy не нужен, backend ux-certificates252/frontend ux-certificates251 сохранены. Следующий шаг — согласование конкретных двух выдач/renewal с включением www-ssl/api-ssl и полным откатом; разрешение250 относилось к уже завершённому выпуску. До согласия только подготовка/чтение. Реальное renewal, CA revoke и multi-instance остаются открыты. ## 12.5.252 — выключенные серверы и достоверность результата renewal (2026-10-14) diff --git a/docs/ux-ui-remaining-acceptance.md b/docs/ux-ui-remaining-acceptance.md index 77a507ed..45028d73 100644 --- a/docs/ux-ui-remaining-acceptance.md +++ b/docs/ux-ui-remaining-acceptance.md @@ -1,11 +1,23 @@ # Оставшаяся внешняя приёмка — 2026-10-14 -Актуальное продолжение 2026-10-14: 252 — сертификаты используют strict-ID/enabled guards; renewal больше не объявляет failed/pending успешным и не подменяет неизвестный срок нулём. Восемь профильных scripts и 39 API/SQL checks прошли, own DB/role удалены. Backend ux-certificates252/frontend ux-certificates251 healthy, source526/dist1080 matched. Реальный Portal/read1440/390: оба CHR без ошибок, writes0. Следующий253 — подготовка реальной повторной выдачи/renewal и безопасного сравнения сертификата/TLS; эта приёмка ещё открыта. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 253 — подготовлена реальная приёмка renewal для exact mmt253-8e62a941.pngs.top на CHR104, read-only DNS empty/оба REST200. TLS/native SHA-256 comparator: positive и11 false-success отказов passed, syntax/lint passed. Никаких выдач/изменений253 не запускали. Runtime backend ux-certificates252/frontend ux-certificates251. Далее согласовать два выпуска/renewal с назначением SSL и полным откатом по deploy/TEST-ACME.md; этап12.5 открыт. -Это актуальная матрица после 12.5.252. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. +Это актуальная матрица после 12.5.253. Исторические записи сохраняют версии и ограничения на момент выполнения; поздние проверки ниже уточняют их. Этап 12.5 открыт. Успех в двух CHR и одном backend не означает приёмку всех конфигураций, интеграций или полной регрессии. Стенд: Debian13/Compose, PostgreSQL18.6, два CHR RouterOS7.24.5. Backend `ux-certificates252`, frontend `ux-certificates251`; production lab с195 использует AUTH_REQUIRED=true и Portal1.3.1. Подписанные JWT/RBAC проверялись в изолированных процессах. Все собственные объекты112 удалены, исходные13 коллекций×2 (включая logging/certificates/files/routes) проверены; пароли не менялись. +## 12.5.253 — подготовка проверки реальной замены сертификата при renewal (2026-10-14) + +Подготовлен exact mmt253-8e62a941.pngs.top/certName mmt253-8e62a941 на CHR104/server2. Read-only preflight с Debian: точный FQDN и challenge name имеют0 Cloudflare records, оба CHR REST200 с прежними паролями. Ни DNS/settings/native writes, ни ACME orders не выполнялись. Это не свежий приватный baseline для будущей мутации: его нужно снять непосредственно перед запуском. + +Новый ручной helper backend/scripts/acceptance/acme-renew-tls.mjs делает bounded15s TLS к фиксированному104:443 с exact SNI и rejectUnauthorized=true. Сверяет hostname, native SHA-256/private-key/name и served leaf. Compare требует изменения SHA-256 после повторного выпуска, совпадения native/TLS, корректных дат; output не перезаписывается. Ввод содержит только публичную native сводку, не keys/passwords. Один positive и11 negative comparator случаев passed без сети/CA; syntax и scoped ESLint passed. Это проверка проверяющего инструмента, не реального renewal. Native fingerprint другого алгоритма сначала пересчитывается из публичного PEM, не угадывается. + +Процедура в [TEST-ACME](../deploy/TEST-ACME.md) включает свежий private baseline/settings/managed targets, единственный собственный target, первый UI выпуск, initial TLS, canonical date guard252 и один scheduler renewal. Code review пути settings выявил важное условие: PUT enabled=true вызывает refreshScheduler с немедленным запуском, поэтому дополнительный run-now запрещён. После terminal renewal выключается до следующего интервала. Если дата unknown, повторный выпуск не запускается и ограничение фиксируется. Job done без нового served fingerprint считается провалом, старый leaf не удаляется ради прохождения теста. + +Откат описан по exact IDs/fingerprints/files/record content и исходным settings/services/normalized export, контрольный CHR194 не меняется, перезагрузки/смена паролей не нужны. Публичная CA выдача не объявляется revoked. Справка native полей/import: [MikroTik Certificates](https://help.mikrotik.com/docs/spaces/ROS/pages/2555969/Certificates); факт замены проверяется измерением, документация его не подтверждает для текущего приложения. + +Production код не менялся, Docker build/deploy не нужен, backend ux-certificates252/frontend ux-certificates251 сохранены. Следующий шаг — согласование конкретных двух выдач/renewal с включением www-ssl/api-ssl и полным откатом; разрешение250 относилось к уже завершённому выпуску. До согласия только подготовка/чтение. Реальное renewal, CA revoke и multi-instance остаются открыты. + ## 12.5.252 — выключенные серверы и достоверность результата renewal (2026-10-14) Общий getter сертификатов теперь использует parseDbServerId и SQL enabled=true: numeric prefix/небезопасные ID не доходят до БД, выключенные серверы не возвращаются. Executor повторно отклоняет disabled перед DNS/native вызовами. Renewal использует каноническое наблюдаемое состояние вместо legacy expiry=0: неизвестный срок/статус пропускается, ошибка чтения/неоднозначное имя не запускает выпуск. После runner читается свежая запись задачи и проверяются id/server/name/source/domains/keyType/trustStore; renewed увеличивается только при status=done и step=done. Failed даёт error, queued/running и неподтверждённый результат не дают успеха. Local singleflight сохраняется; атомарная защита нескольких backend не заявляется. @@ -254,7 +266,7 @@ Portal offline JWT действует доexp после logout/role change; э ## Следующая последовательность — 2026-10-14 -1. **Сертификаты253:** первичный issuance250, UX/Stepper251 и guards/result252 приняты. Подготовить реальную повторную выдачу/renewal с fingerprint/TLS и полным откатом. CA revoke/remote publishing/multi-instance остаются отдельными scope. +1. **Сертификаты254:** подготовка253 завершена; согласовать и выполнить first issuance + scheduler renewal exact mmt253-8e62a941.pngs.top с native/TLS fingerprint и откатом. Реальный renewal пока не принят. CA revoke/remote publishing/multi-instance — отдельные scopes. 2. **Профильный контроль:** для новых изменений выполнять затронутые SQL/API/UI проверки. Общая регрессия192 выполнена; не повторять её без изменений общей основы или нового основания. 3. **Внешние зависимости:** Portal issuer/login/revoke, Telegram bot/chat/разрешённое сообщение, клиентские ОС, независимые WAN/public IPv6 и scale/multi-instance — отдельные группы по таблице выше. Ограничения текущего стенда не превращать в обещание приёмки всех конфигураций. diff --git a/docs/ux-ui-stage-12.md b/docs/ux-ui-stage-12.md index 4058a3e9..5b3b5fb1 100644 --- a/docs/ux-ui-stage-12.md +++ b/docs/ux-ui-stage-12.md @@ -1,6 +1,6 @@ # Этап 12: итоговая приёмка UX/UI -Актуальное продолжение 2026-10-14: 252 — сертификаты используют strict-ID/enabled guards; renewal больше не объявляет failed/pending успешным и не подменяет неизвестный срок нулём. Восемь профильных scripts и 39 API/SQL checks прошли, own DB/role удалены. Backend ux-certificates252/frontend ux-certificates251 healthy, source526/dist1080 matched. Реальный Portal/read1440/390: оба CHR без ошибок, writes0. Следующий253 — подготовка реальной повторной выдачи/renewal и безопасного сравнения сертификата/TLS; эта приёмка ещё открыта. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 253 — подготовлена реальная приёмка renewal для exact mmt253-8e62a941.pngs.top на CHR104, read-only DNS empty/оба REST200. TLS/native SHA-256 comparator: positive и11 false-success отказов passed, syntax/lint passed. Никаких выдач/изменений253 не запускали. Runtime backend ux-certificates252/frontend ux-certificates251. Далее согласовать два выпуска/renewal с назначением SSL и полным откатом по deploy/TEST-ACME.md; этап12.5 открыт. План: [ux-ui-plan.md](ux-ui-plan.md). Фактические результаты: [ux-ui-progress.md](ux-ui-progress.md). Подготовлен после сводных проверок 11Б; завершение каждого блока требует локального коммита без push. @@ -4152,6 +4152,18 @@ Real Portal/browser API, provider local verified: создан один job ро Runtime backend ux-backup234/frontend ux-routing218 healthy; новое application image для volume-only fix не требовалось. Пароли CHR не менялись, S3 mutations0 в235, configs body/JWT не опубликованы, private local manifest удалён. Исходные10 missing local files не восстановлены и records не удалены. Force-recreate — не host reboot/power loss/fsync/DR приёмка. Далее236 — UX недоступных local files, затем version retention границы и оставшиеся7 группы матрицы; multi-instance guard не принимается. Этап12.5 открыт. +## 12.5.253 — подготовка проверки реальной замены сертификата при renewal (2026-10-14) + +Подготовлен exact mmt253-8e62a941.pngs.top/certName mmt253-8e62a941 на CHR104/server2. Read-only preflight с Debian: точный FQDN и challenge name имеют0 Cloudflare records, оба CHR REST200 с прежними паролями. Ни DNS/settings/native writes, ни ACME orders не выполнялись. Это не свежий приватный baseline для будущей мутации: его нужно снять непосредственно перед запуском. + +Новый ручной helper backend/scripts/acceptance/acme-renew-tls.mjs делает bounded15s TLS к фиксированному104:443 с exact SNI и rejectUnauthorized=true. Сверяет hostname, native SHA-256/private-key/name и served leaf. Compare требует изменения SHA-256 после повторного выпуска, совпадения native/TLS, корректных дат; output не перезаписывается. Ввод содержит только публичную native сводку, не keys/passwords. Один positive и11 negative comparator случаев passed без сети/CA; syntax и scoped ESLint passed. Это проверка проверяющего инструмента, не реального renewal. Native fingerprint другого алгоритма сначала пересчитывается из публичного PEM, не угадывается. + +Процедура в [TEST-ACME](../deploy/TEST-ACME.md) включает свежий private baseline/settings/managed targets, единственный собственный target, первый UI выпуск, initial TLS, canonical date guard252 и один scheduler renewal. Code review пути settings выявил важное условие: PUT enabled=true вызывает refreshScheduler с немедленным запуском, поэтому дополнительный run-now запрещён. После terminal renewal выключается до следующего интервала. Если дата unknown, повторный выпуск не запускается и ограничение фиксируется. Job done без нового served fingerprint считается провалом, старый leaf не удаляется ради прохождения теста. + +Откат описан по exact IDs/fingerprints/files/record content и исходным settings/services/normalized export, контрольный CHR194 не меняется, перезагрузки/смена паролей не нужны. Публичная CA выдача не объявляется revoked. Справка native полей/import: [MikroTik Certificates](https://help.mikrotik.com/docs/spaces/ROS/pages/2555969/Certificates); факт замены проверяется измерением, документация его не подтверждает для текущего приложения. + +Production код не менялся, Docker build/deploy не нужен, backend ux-certificates252/frontend ux-certificates251 сохранены. Следующий шаг — согласование конкретных двух выдач/renewal с включением www-ssl/api-ssl и полным откатом; разрешение250 относилось к уже завершённому выпуску. До согласия только подготовка/чтение. Реальное renewal, CA revoke и multi-instance остаются открыты. + ## 12.5.252 — выключенные серверы и достоверность результата renewal (2026-10-14) Общий getter сертификатов теперь использует parseDbServerId и SQL enabled=true: numeric prefix/небезопасные ID не доходят до БД, выключенные серверы не возвращаются. Executor повторно отклоняет disabled перед DNS/native вызовами. Renewal использует каноническое наблюдаемое состояние вместо legacy expiry=0: неизвестный срок/статус пропускается, ошибка чтения/неоднозначное имя не запускает выпуск. После runner читается свежая запись задачи и проверяются id/server/name/source/domains/keyType/trustStore; renewed увеличивается только при status=done и step=done. Failed даёт error, queued/running и неподтверждённый результат не дают успеха. Local singleflight сохраняется; атомарная защита нескольких backend не заявляется. diff --git a/docs/ux-ui-test-server.md b/docs/ux-ui-test-server.md index 74b7cd46..68171ac1 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-14: 252 — сертификаты используют strict-ID/enabled guards; renewal больше не объявляет failed/pending успешным и не подменяет неизвестный срок нулём. Восемь профильных scripts и 39 API/SQL checks прошли, own DB/role удалены. Backend ux-certificates252/frontend ux-certificates251 healthy, source526/dist1080 matched. Реальный Portal/read1440/390: оба CHR без ошибок, writes0. Следующий253 — подготовка реальной повторной выдачи/renewal и безопасного сравнения сертификата/TLS; эта приёмка ещё открыта. Этап12.5 открыт. +Актуальное продолжение 2026-10-14: 253 — подготовлена реальная приёмка renewal для exact mmt253-8e62a941.pngs.top на CHR104, read-only DNS empty/оба REST200. TLS/native SHA-256 comparator: positive и11 false-success отказов passed, syntax/lint passed. Никаких выдач/изменений253 не запускали. Runtime backend ux-certificates252/frontend ux-certificates251. Далее согласовать два выпуска/renewal с назначением SSL и полным откатом по deploy/TEST-ACME.md; этап12.5 открыт. | Параметр | Проверенное состояние | | --- | --- | @@ -34,6 +34,18 @@ OSPF84: real GRE Full adjacency/private routes/Ping3/3 и4 live PC/320px URL/bro BFD85: Up/Full/own route/Ping,4 live UI и loss/withdrawal/recovery приняты; exact own cleanup8 коллекций×2 passed. Withdrawal5,245ms не подтверждает sub-second BFD timing; этот вопрос открыт. Runtime routing83 без новой сборки. [Отчёт](ux-ui/acceptance/ospf-bfd-loss-2026-10-09.json). +## 12.5.253 — подготовка проверки реальной замены сертификата при renewal (2026-10-14) + +Подготовлен exact mmt253-8e62a941.pngs.top/certName mmt253-8e62a941 на CHR104/server2. Read-only preflight с Debian: точный FQDN и challenge name имеют0 Cloudflare records, оба CHR REST200 с прежними паролями. Ни DNS/settings/native writes, ни ACME orders не выполнялись. Это не свежий приватный baseline для будущей мутации: его нужно снять непосредственно перед запуском. + +Новый ручной helper backend/scripts/acceptance/acme-renew-tls.mjs делает bounded15s TLS к фиксированному104:443 с exact SNI и rejectUnauthorized=true. Сверяет hostname, native SHA-256/private-key/name и served leaf. Compare требует изменения SHA-256 после повторного выпуска, совпадения native/TLS, корректных дат; output не перезаписывается. Ввод содержит только публичную native сводку, не keys/passwords. Один positive и11 negative comparator случаев passed без сети/CA; syntax и scoped ESLint passed. Это проверка проверяющего инструмента, не реального renewal. Native fingerprint другого алгоритма сначала пересчитывается из публичного PEM, не угадывается. + +Процедура в [TEST-ACME](../deploy/TEST-ACME.md) включает свежий private baseline/settings/managed targets, единственный собственный target, первый UI выпуск, initial TLS, canonical date guard252 и один scheduler renewal. Code review пути settings выявил важное условие: PUT enabled=true вызывает refreshScheduler с немедленным запуском, поэтому дополнительный run-now запрещён. После terminal renewal выключается до следующего интервала. Если дата unknown, повторный выпуск не запускается и ограничение фиксируется. Job done без нового served fingerprint считается провалом, старый leaf не удаляется ради прохождения теста. + +Откат описан по exact IDs/fingerprints/files/record content и исходным settings/services/normalized export, контрольный CHR194 не меняется, перезагрузки/смена паролей не нужны. Публичная CA выдача не объявляется revoked. Справка native полей/import: [MikroTik Certificates](https://help.mikrotik.com/docs/spaces/ROS/pages/2555969/Certificates); факт замены проверяется измерением, документация его не подтверждает для текущего приложения. + +Production код не менялся, Docker build/deploy не нужен, backend ux-certificates252/frontend ux-certificates251 сохранены. Следующий шаг — согласование конкретных двух выдач/renewal с включением www-ssl/api-ssl и полным откатом; разрешение250 относилось к уже завершённому выпуску. До согласия только подготовка/чтение. Реальное renewal, CA revoke и multi-instance остаются открыты. + ## 12.5.252 — выключенные серверы и достоверность результата renewal (2026-10-14) Общий getter сертификатов теперь использует parseDbServerId и SQL enabled=true: numeric prefix/небезопасные ID не доходят до БД, выключенные серверы не возвращаются. Executor повторно отклоняет disabled перед DNS/native вызовами. Renewal использует каноническое наблюдаемое состояние вместо legacy expiry=0: неизвестный срок/статус пропускается, ошибка чтения/неоднозначное имя не запускает выпуск. После runner читается свежая запись задачи и проверяются id/server/name/source/domains/keyType/trustStore; renewed увеличивается только при status=done и step=done. Failed даёт error, queued/running и неподтверждённый результат не дают успеха. Local singleflight сохраняется; атомарная защита нескольких backend не заявляется. diff --git a/docs/ux-ui/acceptance/acme253-preparation.json b/docs/ux-ui/acceptance/acme253-preparation.json new file mode 100644 index 00000000..3970f471 --- /dev/null +++ b/docs/ux-ui/acceptance/acme253-preparation.json @@ -0,0 +1,26 @@ +{ + "date": "2026-10-14", + "block": "12.5.253", + "fqdn": "mmt253-8e62a941.pngs.top", + "serverId": "2", + "nativeTarget": "104.165.244.7", + "readOnlyPreflight": { + "exactDomainRecords": 0, + "challengeRecords": 0, + "chrRestStatus": [ + 200, + 200 + ], + "writes": 0 + }, + "comparator": { + "positive": 1, + "negative": 11, + "passed": true, + "networkCalls": 0 + }, + "syntax": "passed", + "scopedLint": "passed", + "realRenewalExecuted": false, + "productionRuntimeChanged": false +}