diff --git a/.wiki/concepts/verdaccio-token-lifecycle.md b/.wiki/concepts/verdaccio-token-lifecycle.md index 3e7122f..83c3122 100644 --- a/.wiki/concepts/verdaccio-token-lifecycle.md +++ b/.wiki/concepts/verdaccio-token-lifecycle.md @@ -111,4 +111,5 @@ Refresh токена = обновить `npmAuthToken` в `.yarnrc.yml` кажд ## Связанные страницы - [verdaccio-prune-semantics](verdaccio-prune-semantics.md) — storage layout и GC +- [yarn-npm-minimal-age-gate](yarn-npm-minimal-age-gate.md) — гейт #2: YN0016 на свежих версиях (Yarn ≥4.16), независим от auth - [vds-kzntsv](../entities/vds-kzntsv.md) — хост где крутится инстанс diff --git a/.wiki/concepts/yarn-npm-minimal-age-gate.md b/.wiki/concepts/yarn-npm-minimal-age-gate.md new file mode 100644 index 0000000..1a8a44a --- /dev/null +++ b/.wiki/concepts/yarn-npm-minimal-age-gate.md @@ -0,0 +1,84 @@ +--- +title: Yarn 4.16 npmMinimalAgeGate — YN0016 "quarantined" на свежих пакетах +type: concept +tags: [yarn, yarn-berry, verdaccio, npm, supply-chain, gotcha, YN0016, age-gate] +sources: [] +updated: 2026-06-11 +--- + +# Yarn 4.16 `npmMinimalAgeGate` — клиентский «карантин» свежих версий + +## Симптом + +`yarn install` (Yarn Berry **≥4.16**) падает на резолве: + +``` +@snolla/site-schema-gen@npm:^0.5.0: All versions satisfying "^0.5.0" are quarantined +``` + +Код ошибки — **YN0016**. Версия физически есть в реестре, `dist-tags.latest` указывает на неё, токен валиден (`whoami` отдаёт юзера) — но yarn её не берёт. + +## Root cause — клиентский age-gate, НЕ сервер + +Yarn 4.16 ввёл настройку **`npmMinimalAgeGate`** (supply-chain мера против свежезалитых вредоносных версий, по следам атак на npm 2025). Из бинаря `~/AppData/Local/node/corepack/v1/yarn/4.16.0/yarn.js`: + +``` +npmMinimalAgeGate: { type: "DURATION", unit: "m", default: "1d" } # = 1440 минут +``` + +Фильтр перед резолвом отсеивает версию, если: +- у неё нет даты публикации в packument-поле `time`, **или** +- `(now − publishDate) < npmMinimalAgeGate`. + +Если после фильтра кандидатов не осталось → `throw YN0016 "...are quarantined"`. + +**Карантин целиком на стороне yarn-клиента.** На сервере verdaccio никакого карантина нет (нет в `config.yaml`, plugins-папка пустая) — серверная проверка пакумента его не видит. Реестр отдаёт версию чисто любому валидному read-токену. + +## Версионная разница (почему «у одних работает») + +| yarn | `npmMinimalAgeGate` default | +|---|---| +| ≤ 4.14 (напр. snolla 4.14.1) | `0` — гейта нет | +| ≥ 4.16 | `1d` (1440 мин) | + +Проект на 4.14 «просто работает», на 4.16 тот же канон ловит YN0016 первые 24 ч после публикации пакета. + +## Два независимых последовательных гейта + +Важно не путать с auth — это разные стены, в таком порядке: + +1. **auth** — кривой/протухший токен → `YN0041 Invalid authentication` (это 401, см. [[verdaccio-token-lifecycle]]). +2. **age-gate** — валидный токен, но версия моложе порога → `YN0016 …quarantined`. + +Решающий признак: если на **валидном** токене (auth прошёл, 401 нет) всё равно падает — это гейт #2, не auth. Token-fix его НЕ снимает. + +## Fix + +```yaml +# .yarnrc.yml — глобально: +npmMinimalAgeGate: 0 +``` +```bash +# либо разово, per-invocation (сохраняет защитный дефолт для обычных install'ов): +YARN_NPM_MINIMAL_AGE_GATE=0 yarn install +``` + +### Нюанс: гейт читается ГЛОБАЛЬНО, не per-scope + +В фильтре — `configuration.get("npmMinimalAgeGate")` (глобальный config), не scope-aware lookup. Поэтому `npmMinimalAgeGate` под `npmScopes.@snolla.*` **игнорируется** — нельзя ослабить гейт только для доверенного приватного скоупа. Глобальный `0` снимает age-защиту и с **публичных** npmjs-пакетов, которые verdaccio проксирует через uplink. Это реальный security trade-off — в этом и смысл фичи. + +**Рекомендация:** не коммитить `0` в общий канон бездумно. Для CI / чужих проектов — разовый `YARN_NPM_MINIMAL_AGE_GATE=0` под осознанный install сразу после своей публикации. Для тесного publish→consume dev-loop своих пакетов (напр. pilonuxt) глобальный `0` оправдан — реестр один доверенный, DX важнее. Финальный risk-call — за владельцем экосистемы. + +## Ecosystem heads-up + +Любой потребитель на **yarn ≥4.16**, тянущий свежеопубликованные `@snolla`/`@snollajs` пакеты, будет ловить YN0016 первые 24 ч. Когда snolla-монореп/проекты переедут на 4.16+ — решить, добавлять ли `npmMinimalAgeGate: 0` в snolla-канон `.yarnrc.yml` или держать override on-demand. + +## Применено / источник + +Диагностировано 2026-06-11 совместно с сессией pilonuxt (миграция pnpm→yarn 4.16). Первичная ошибочная гипотеза «карантин = серверный дефект verdaccio» опровергнута: пруф — строка `are quarantined` + `npmMinimalAgeGate` в `yarn.js` 4.16.0, и YN0016 воспроизводится на валидном vitya-токене, снимается только `npmMinimalAgeGate: 0`. + +## Связанные + +- [[verdaccio-token-lifecycle]] — гейт #1 (auth / JWT / 401), соседняя стена +- [[verdaccio-restore-packument-desync]] — почему версии вообще пришлось republish (disaster-restore) +- [[../entities/vds-kzntsv]] — хост verdaccio diff --git a/.wiki/index.md b/.wiki/index.md index 0b29efb..a17eeed 100644 --- a/.wiki/index.md +++ b/.wiki/index.md @@ -52,6 +52,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever - [verdaccio-prune-semantics](concepts/verdaccio-prune-semantics.md) — Verdaccio prune semantics — proxied vs locally-published - [verdaccio-restore-packument-desync](concepts/verdaccio-restore-packument-desync.md) — disaster-restore: тарболл на диске + древний packument → EEXISTS 409; fix = снять коллизирующий .tgz, не rm -rf каталог - [verdaccio-token-lifecycle](concepts/verdaccio-token-lifecycle.md) — restart trap (ephemeral secret) + max_users:-1 pnpm 409 + JWT fix + yarn clients +- [yarn-npm-minimal-age-gate](concepts/yarn-npm-minimal-age-gate.md) — Yarn ≥4.16 client-side age-gate (YN0016 «quarantined») на версиях <24ч; глобальный, не per-scope; fix npmMinimalAgeGate:0 - [winacme-iis-owin-catchall-http01](concepts/winacme-iis-owin-catchall-http01.md) — win-acme HTTP-01 авто-renewal LE на IIS под OWIN-catch-all CMS — challenge в отдельном No-Managed-Code приложении + патч шаблона; заменяет ручной PFX-импорт - [wd40efax-smr-cascade](concepts/wd40efax-smr-cascade.md) — WD40EFAX SMR Cascade — root cause NAS failure 2026-05-18 - [windows-server-2025-core-bootstrap](concepts/windows-server-2025-core-bootstrap.md) — Win Server 2025 Core (RUVDS) — bootstrap для IIS-хоста: default-blockers + transfer-методов матрица diff --git a/.wiki/log.md b/.wiki/log.md index 109d5b6..b6d43b4 100644 --- a/.wiki/log.md +++ b/.wiki/log.md @@ -4,6 +4,7 @@ Append-only log of wiki operations (ingests, promotions, lints, migrations). ## [2026-06-11] ingest | verdaccio-restore-packument-desync — postmortem: disaster-restore вернул тарболлы но древний/пустой packument → publish свежей версии EEXISTS 409. Fix: снять только коллизирующий целевой .tgz (бэкап first), republish; НЕ rm -rf каталог (убьёт locally-published историю). Применено к @snollajs/{data,mailer,numbering,content-api}. ## [2026-06-11] ingest | verdaccio-token-lifecycle — restart trap (ephemeral secret → 401), max_users:-1 + pnpm 409 постмортем, JWT config fix, yarn classic vs berry token refresh recipe. +## [2026-06-11] ingest | yarn-npm-minimal-age-gate — Yarn ≥4.16 client-side supply-chain gate: версии <24ч → YN0016 «quarantined». Независим от auth (gate #2 после 401). default 1d, читается глобально (per-scope не работает), fix npmMinimalAgeGate:0 / YARN_NPM_MINIMAL_AGE_GATE=0. Диагностировано с pilonuxt; опровергнута первичная гипотеза «серверный карантин verdaccio». ## [2026-06-11] update | MSSQL backup gap closed — `/opt/stacks/backup/scripts/run.sh` расширен: 5 prod DBs через `docker exec mssql sqlcmd BACKUP DATABASE ... WITH COPY_ONLY, INIT` (Express, без COMPRESSION); .bak в bind-mount → mv в DUMP_DIR → rsync kreknin. Обновлён [[concepts/mssql-on-vds]] (TODO→реализовано). ## [2026-06-11] lint | 10 issues found, 8 fixed inline