DNS zone is on Yandex DNS (dns1/dns2.yandex.net), not reg.ru. Flip propagated
unevenly (dns2 first, dns1 lagged); cutover gate = BOTH authoritative Yandex NS
agree apex+www -> 89.253.255.94 (monitor waited for it) so LE HTTP-01 can't hit
RUVDS via a stale auth server and burn the rate-limit.
Stack 17 traefik-rule -> Host(labtools.ru)||Host(www.labtools.ru) via Portainer
PUT (env 8/8 preserved, pullImage=false). LE cert issued (~t+20s; brief self-signed
window during issuance). Live-smoke GREEN: both hosts 200 (nav+catalog+products),
redirects parity (trailing/lowercase/.php), sitemap index host=labtools.ru (this
tenant's siteUrl is non-www), theme assets byte-identical (RUVDS 301 apex->www made
the direct md5 an artifact; -L => MD5-OK). Staging host removed (->404). External:
apex->VDS 200; www still split-brain on RUVDS cache (200, identical content, TTL 6h).
RUVDS left intact as rollback.
Task closed. Whole batch done: labtools.ru + labtools.pro LIVE on VDS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DNS flip confirmed at authoritative ns1.reg.ru (Resolve-DnsName) + 8.8.8.8 ->
89.253.255.94 (labtools.pro/www; NOT labtools.ru). Earlier checks hit stale TTL
cache; authoritative NS was the truth.
Stack 19 traefik-rule -> Host(labtools.pro)||Host(www.labtools.pro) via Portainer
PUT (env 8/8 preserved, pullImage=false). LE cert issued instantly (ssl_verify=0).
Live-smoke GREEN: both hosts 200 (nav+catalog+products), redirects parity, sitemap
index host=www, real content (lang=en). Staging host removed (->404). External
check from RUVDS box: labtools.pro/www -> 89.253.255.94 HTTP 200 trusted cert.
RUVDS labtools.pro left intact as rollback.
Task closed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Migration to vds-kzntsv confirmed by operator. Off-LAN check from the
IIS host itself: emspb.ru + www.emspb.ru -> 89.253.255.94 HTTP 200.
Removed only the two migrated SNI bindings (26->24 total); emspb.snolla.com
retained (not migrated, DNS still -> IIS). Config backup taken:
pre-emspb-decommission-2026-07-02. Site snolla + apppool Started, other 23
hosts untouched. win-acme is binding-driven (--source iis) so next renewal
auto-drops the emspb.ru SANs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ADR-0010: forms moved to @snollajs/forms-api, @snollajs/snolla dropped
entirely, core@0.1.1 (Buffer over btoa). Stack 16 → pilonuxt:cf2bba2.
Pre-deploy tree-check of .output/server/node_modules 4/4: 0x snolla,
0x btoa, exactly 1x data@0.9.1, core 0.1.1 + content-api 0.14 + forms-api
0.1.0. Smoke green: SSR /+/catalog 200, robots/yandex from DB (ADR-0009
no regress), form ?path=/checkout empty -> 422 JSON, /nope -> 404 (no
valid submitted). Smoke gotcha: 422 only with Accept: application/json.
Closes [deploy-pilonuxt-forms-api-drop-snolla].
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stack 16 redeployed to registry.kzntsv.site/pilonuxt:40bb383 (ADR-0009:
DB-backed staticPages + robots.txt). Build from monorepo with forced client
regen (rm src/generated + schema-gen 0.6 → getStaticPage/getRobotsTxt);
home push hit traefik-499 on .output layer (425MB) → save|ssh load + push
from VDS. Smoke acceptance 6/6 verified by body (robots full from DB w/
Yandex Clean-param + Sitemap, yandex/google verifications, /nope→404).
Closes [deploy-pilonuxt-static-pages-robots].
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bookva MariaDB is published at 89.253.255.133:33306 (0.0.0.0:33306->3306,
port 33306 since 3306 is slovo books-db). Added public DB-endpoints block
to § Доступ (slovo + bookva), noted mongo/ES stay internal, and a
.Ports-vs-.Networks reminder. Closes the port wiki-drift in follow-up #2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New entity + inventory source for previously-undocumented family VPN.
Secrets stored separately in pass vdsina-outline/full-env.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
books закрыли named-tag предохранителем (62d812e: protectRe +master|latest).
keep/drop digest-коллизия — снято: keep=1 у books-api это buildcache
(group-by-digest через Map делает keep/drop взаимоисключающими).
Восстановление снесённого named-тега = re-push локального образа с VDS
(docker tag inspect-Image + push под books-ci), не rebuild → плоский
single-platform манифест с .config (pullable, датируется). Применено к
books-api + books-ops-mcp -> :master снова 200. Гоча: books-api вне
CI-матрицы (embed-api-into-web) -> re-push единственный путь.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Первый боевой registryGc dryRun:false (books master-289c660, под добро юзера):
63 DELETE, 0 ошибок, 0x405 -> REGISTRY_STORAGE_DELETE_ENABLED=true подтверждён
живьём; 61 dangling + 2 datable снесены, реестр почищен.
Урок: master у books-api/books-ops-mcp был dangling (не пересобирались ~3нед)
-> логика drop-dangling снесла named-тег -> :master 404. Outage нет (контейнеры
на локальных образах), но redeploy упрётся. Политика: НЕ удалять named-теги
master/latest даже dangling (protectRe -> +master/latest); проверить keep/drop
на digest-коллизию (books-api keep=1 но 0 не-buildcache осталось).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Перепрогон registryGc dryRun на descent-фиксе (master-dcd7c91) вскрыл 2-й
root-cause: реестр засорён dangling OCI image-индексами — тег жив, но его
платформенный sub-manifest отдаёт MANIFEST_UNKNOWN (вычищен прежним host-side
registry garbage-collect, не следящим index->child для multi-arch).
books-web: 19 тегов -> 3 датируемых, 16 dangling.
date-based GC защищает dangling как null-dated -> никогда не удаляет, хотя они
и есть мусор (логика задом наперёд). Рекомендация books: различать
transient-error (protect) vs MANIFEST_UNKNOWN (eligible for delete).
freedBytes от dangling ~0 (слои уже вычищены).
+registry-oci-image-index-gc.md (раздел) +index.md +log.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Следствие закрытия [books-task-runner-registry-auth-cred].
concepts/registry-oci-image-index-gc.md (new): books-* образы в registry =
OCI image-index (buildx), top-level .config=null, дата .created в платформенном
sub-manifest. Наивный GC по top-level дате → null у всех → null-dated группы
защищаются → drop=0 всегда (keepLastN не применяется). Правила: Accept со
всеми media-types, дата из sub, DELETE по index-digest не sub. Зафиксировано
на books registryGc dryRun (deleted=0 при 14 tags) + манифест-dump books-web.
concepts/bindmount-config-edit-preserve-mode.md: дополнен гочей про
bind-mount shadow (config образа затенён целиком → полная секция, не дельта)
+ worked example task-runner (mode не слетел, постмортем сработал).
+index.md (2 строки) +log.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
registry.kzntsv.site is registry:2 + htpasswd Basic (binary access, no
per-repo ACL), NOT Gitea-packages. Added books-ci user (pass
vds-kzntsv/registry-books-ci) for books job-scheduler docker-runner pull.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ingest .wiki/concepts/stostayer-web-deploy-runbook.md — канал деплоя легаси
web (build offline → push docker.stostayer.ru → Portainer-стек 16 через
container-IP API с хоста, мимо Angie BA) + rollback + гоча VPN-IP бан
хостером при retry-push-шторме.
БЛОКЕР задокументирован: легаси packages/web (node16/CJS/Nuxt2) не
пересобрать ни с какого свежего дерева — 5 ESM-ставших депов (@snollajs/snolla,
@snollajs/content-api, @stostayer/api, @stostayer/data ×2). 0.3.18 заморожен.
Фикс формы (120bc07) едет с web4-cutover.
Таска stostayer-web-complaint-form-deploy → 🔵 park (blocker=web4-cutover);
попытка деплоя 0.3.19 откатана на 0.3.18, сайт восстановлен. Дохлый 0.3.19
оставлен в registry по решению vitya.
+ index.md, log.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Запрос snolla (raw-stream content-api) потребовал MinIO endpoint+креды;
ответ был выводим из вики, но не собран одним куском. Добавлен раздел
«S3 access для клиентских приложений» в minio-imgproxy-on-vds.md:
endpoint (https://minio.kzntsv.site / сырой :9000 / inter minio:9000),
root accessKey + pass-ссылка на secret, форма ключа, ssl/region/pathStyle.
Следующий такой вопрос — grep по вики, без SSH на сервер.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
snolla galleries 404 (imgproxy "Source unreachable"): legacy storageClient
"galleries" = MoreThenCms Local storage class (App_Data\galleries\<siteId>),
never in S3 — products were migrated, galleries weren't. Path A (user pick):
new bucket `galleries`, 301 pilorama98 originals (77 MiB) rclone'd from RUVDS
IIS → s3://galleries/37e6…/<guid>.jpg verbatim. snolla code unchanged
(storageClient=bucket is the working convention). imgproxy smoke from
books-vds: real obj 200 image/webp, fake guid 404. Other sites unmigrated
(scope). Inbox sent to victor/snolla.
- close [migrate-gallery-originals-to-s3] (scope pilorama98)
- NEW .wiki concept galleries-storage-class-local-not-s3
- fix stale minio-imgproxy-on-vds (pipeline on books-vds since 2026-06-08)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bookva-es (отдельный ES 7.10, Portainer stack 37) не снапшотился — path.repo
не сконфигурирован. Пересоздан через Portainer API (PUT /api/stacks/37) с
path.repo=/snapshots + bind /usr/docker/bookva-es/snapshots (uid 1000:0);
внешний том bookva-es-data сохранён (epz/products целы). Repo kreknin
зарегистрирован.
Добавлен Step 2b (bookva-es snapshot + prune) в run.sh.
Gotcha пойман на проверке приёмника ДО коммита: оба ES-каталога называются
`snapshots` → как отдельные rsync-источники сливаются в один dest/snapshots/
и портят оба репо (видно по двойному index-N). Fix: источник bookva-es =
родительский /usr/docker/bookva-es (basename bookva-es → dest/bookva-es/snapshots/).
Verified green: BOOKS-VDS backup OK 10m28s, на kreknin раздельно
snapshots/ (slovo, index-41, 795M) + bookva-es/snapshots/ (index-4, 801M),
по одному index-N в каждом → оба независимо рестораблельны.
Закрывает .tasks/bookva-es-snapshot-repo.md (🟢). bookva tenant полностью
покрыт (db/mongo/minio/es).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bookva tenant (bookva-db/mongo/es/minio) поднят 26.05 при cutover-prep, а
books-vds backup написан 25.05 — до bookva. Покрытие не расширили; gap висел
wiki-follow-up #2 ~2.5 недели. Тот же класс, что MSSQL: новый stateful, бэкап
отстал, повешен как заметка.
Добавлено в scripts/books-vds-backup-daily-kreknin/run.sh (deploy == repo,
бэкап .bak-pre-bookva):
- bookva-db: mariadb-dump (тот же BOOKS_DB_ROOT_PASSWORD, стек клонирован)
- bookva-mongo: mongodump --archive (no-auth)
- bookva-minio: raw rsync named volume bookva-minio-data (immutable objects)
Каждая команда протестирована изолированно ДО внесения в скрипт. Verified
зелёным прогоном: BOOKS-VDS backup OK 10m42s, артефакты на kreknin
(bookva-mariadb 257M, bookva-mongo 3.2M, bookva-minio 1.4G).
bookva-es отложен (path.repo не сконфигурирован, нужен ES restart через
Portainer) -> .tasks/bookva-es-snapshot-repo.md. Покрыт по факту: индексы
epz/products идентичны slovo ES, который снапшотится.
- .wiki/entities/books-vds.md: Backup-секция + follow-up #2 частично закрыт
- .wiki/concepts/backup-inventory-2026-06.md: bookva row -> done
- .tasks/bookva-es-snapshot-repo.md: backlog для ES-снапшота
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
VDS daily backup пал с 12.06 (line 108, exit 1): mssql-блок (added 11.06,
commit f8ca0794) использовал `WITH ... INIT`, упирался в компрессованный
media-header майских .bak (созданы WITH COMPRESSION на Developer-источнике).
Express не пишет в compression-форматированный media set -> Msg 1844, молчаливый
провал первым же cron-запуском. 5 боевых CMS-баз 3 недели без offsite-копии.
Fix: INIT -> FORMAT (всегда новый media set, иммунно к остаткам). Прогон
verified зелёным: VDS backup OK 95m41s, все 5 .bak на kreknin
(MoreThenCms 910M, StayerCalculator 528M, StayerPrice 39M, TireService 4.5M,
stostayer 990M), speedup 22.62 (--link-dest хардлинкует).
- scripts/vds-backup-rsync-kreknin/run.sh: синхронизирован с задеплоенным
(mssql-блока в репо не было); FORMAT
- .wiki/concepts/mssql-on-vds.md: gotcha #2 (INIT vs FORMAT) + backup-gap
- .wiki/concepts/backup-inventory-2026-06.md: новая — карта estate × что реально
бэкапится (с доказательством); триаж дыр
- openwrt UCI backup настроен (cron 03:30 -> kreknin, restricted forced-command
key) — документация в entity + inventory
- .tasks/kreknin-self-backup.md: backlog #1 SPOF (приёмник сам не бэкапится)
- STATUS.md: incident + audit summary
Урок: бэкап-шаг не готов, пока не предъявлен лог одного реального успеха;
прод-крон не должен быть первым тестом бэкап-пути.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cross-ref the practical auth runbook (projects-wiki concepts/verdaccio-token-usage)
from the local postmortem. lifecycle = why, token-usage = how.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Yarn >=4.16 npmMinimalAgeGate (default 1d) карантинит версии <24ч на
КЛИЕНТЕ — независимый от auth gate #2 (после 401). Серверного карантина
в verdaccio нет; первичная гипотеза опровергнута пруфом из yarn.js.
Глобальный config (per-scope не работает), fix npmMinimalAgeGate:0.
Backlink из verdaccio-token-lifecycle + index/log.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
disaster-restore вернул тарболлы но древний/пустой packument →
publish свежей версии EEXISTS 409. Fix: снять только коллизирующий
целевой .tgz (backup first), republish; НЕ rm -rf каталог.
Применено к @snollajs/{data,mailer,numbering,content-api}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause (NOT a migration defect): MoreThenCms tenant `maljarka`
had `dbo.Sites.SettingsData = NULL` (no `httpSecure` block) -> on a
real HTTPS request SnollaMiddleware throws KeyNotFoundException ->
502. HTTP served 200 fine. DNS/TLS/IIS binding all correct.
Fix applied to shared MSSQL (mssql.kzntsv.site): UPDATE Sites set
SettingsData with httpSecure{enableHttps:true,...} + recycle snolla
pool. Verified 443->200 server-local and external via 80.64.31.36;
kupimknigi untouched. Audit: maljarka was the only NULL-settings
site with a :443 binding (of 25); rimiz degraded for another reason.
- new concept: morethencms-null-settingsdata-https-502
- entities/ruvds-iis-host: maljarka moved from Degraded -> fixed
- index.md + log.md + STATUS.md + NEXT_SESSION.md updated
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3 classic chacha20-ietf-poly1305 SS inbounds (ports 32031/32/33),
one ss:// key each, per-key revocable. Protocol e2e-tested OK;
RF reachability unverified (SS DPI-blocked in RF on this node).
Keys/passwords in pass nl-vds-3xui/full-env.
Also flagged undocumented VLESS:13027 inbound found in DB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stood up a permanent self-renewing Let's Encrypt pipeline on the RUVDS
IIS host, replacing the manual traefik acme.json -> PFX import and
closing the 2026-07-22 cert-expiry deadline (new 25-SAN cert valid to
2026-09-03, SYSTEM scheduled task renews 55 days before expiry).
Key obstacle: the MoreThenCms OWIN catch-all (owin:HandleAllRequests)
swallowed /.well-known/acme-challenge/. Solved by carving the challenge
path into a separate IIS application in a No-Managed-Code app pool, plus
patching win-acme's Web_Config.xml template to remove the inherited Owin
handler. Staging + prod validation green for all 25 hostnames; live TLS
smoke confirms the new cert is served (incl degraded maljarka/rimiz).
- scripts/iis-migration-to-ruvds/03-ruvds-winacme.ps1 (idempotent setup)
- scripts/iis-migration-to-ruvds/winacme-Web_Config.xml (patched template)
- .wiki/concepts/winacme-iis-owin-catchall-http01.md (recipe + gotchas)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- new sources/nl-vds-3xui-setup-2026-06-05.md (full chronicle; HONEST outcome:
у реальных клиентов из РФ работает только plain VLESS 32030; Reality/MTProto/
SOCKS не поднялись)
- new concepts/proxy-debugging-test-the-real-client.md (anti-pattern: own curl/
standalone tests passed while user's real clients failed; overclaim + bad
MSS-clamp fix that broke things)
- rewrote entities/nl-vds-3xui.md — removed false "Reality verified/fixed" &
"mtg works" claims; honest status table; MSS-clamp removed
- caveat added to reality-pq concept (disabling PQ != working Reality for GUI
clients); index.md + log.md updated
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Confirmed by experiment: Reality on :443 worked ~12min then died (all
timeouts via live v2rayN); moved inbound to :2053 and verified from same
RU PC/core -> exit NL, google 200, 10MB @5.3MB/s. RKN port-blocks :443
dynamically. New X25519 share link (port 2053) in pass. Caveat: 2053 may
also get flagged under heavy use; rotate port if it dies.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause of "Reality fails on client": v2rayN 7.19.5 + phone apps do NOT emit
mldsa65Verify to the xray core, so they can't auth to a PQ-mandated REALITY
inbound (only plain VLESS 32030 worked). Replica matrix: noPQ+no-verify=204,
PQ+no-verify=000, noPQ+with-verify=000. Removed mldsa65Seed from inbound id=1 ->
standard Reality X25519. Verified verify-less client through real :443 -> 204.
Fresh X25519 share-link generated for re-import. Wiki concept updated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SOCKS5 works server-side but DPI on user's path blocks it -> stood up mtg
v2.2.8 as systemd service (DynamicUser, Restart=always), FakeTLS domain
www.cloudflare.com. Verified: openssl probe -> valid TLS1.3 CN=www.cloudflare.com
(indistinguishable from real HTTPS). Secret + tg link in pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Рецидив вчерашнего ES-инцидента вскрыл истинную причину. Вчерашняя
гипотеза «оператор в cutover попал на canonical» опровергнута.
Root cause: ES (stack 33) публиковал 0.0.0.0:9200 мимо traefik. Free-ES
7.10 без auth → порт открыт всему интернету. Ransom-бот сносил индексы
by-name (мимо Control #1 destructive_requires_name), оставлял read_me с
BTC-выкупом. accessLog (Control #2) пуст — бот шёл прямо в порт, не через
traefik. firewalld бесполезен (docker-publish обходит INPUT-зоны).
Fix (Control #3): убрана публикация host-порта из stack 33 (Portainer
PUT), дыра закрыта; re-restore epz/products/artmone из daily-2026-05-25.
Отдельный баг: epz-поиск падал у ОБОИХ тенантов — getTenantIdSeller(
config.get("tenant")) через node-config, а tenant не задан ни в
default.json, ни в env-маппинге (TENANT env = мёртвый груз). Добавлен
tenant в overlay default.json (slovo/bookva). products работал —
отдельный код-путь.
accessLog откатан (сторожил не ту дверь).
- wiki concept: correction-блок + секция «Рецидив 2026-05-29» + exposure-audit
- tasks: restore-es reopened+reclosed; new ⚪ harden-books-vds-exposed-ports
- NEXT_SESSION handoff
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
snapshot restore из kreknin:daily-2026-05-25 за 46 сек (epz=820604,
products=105922, artmone=2621, counts == source).
RCA: 5 индексов (включая system .tasks) удалены через ES API DELETE _all
за 1 сек на 2026-05-26 10:21 UTC — 1ч 11мин после создания bookva-es в
cutover-prep. Каноничный endpoint elasticsearch.kzntsv.site попал под
команду которая предназначалась bookva-es:9200 (internal-only, без
traefik route). Caller identity unrecoverable: ES audit = X-Pack платный,
traefik accessLog был выключен, Portainer CE без audit.
Preventive controls applied + verified:
1. ES env action.destructive_requires_name=true (stack 33) — DELETE _all
и wildcard теперь 400 BadRequest; by-name DELETE работает (нужно для
reindex). Pattern удаления что случился физически невозможен.
2. Traefik JSON accessLog в /letsencrypt/access.log — будущие DELETE
оставят forensic след с IP/user/method/path.
Wiki concept: .wiki/concepts/es-destructive-delete-incident-2026-05-26.md
с recovery runbook + preventive controls + cross-refs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>