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>
4.8 KiB
title, type, tags, sources, updated
| title | type | tags | sources | updated | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Verdaccio token lifecycle — restart trap + JWT fix | concept |
|
2026-06-11 |
Verdaccio token lifecycle — restart trap + JWT fix
Root cause: auto-generated secret
Verdaccio без явного secret: в конфиге генерирует случайный секрет при каждом запуске. Все токены (legacy opaque и JWT) подписаны этим секретом — перезапуск контейнера = мгновенная инвалидация всех выданных токенов у всех клиентов.
Симптом: 401 Unauthorized после рестарта VDS/контейнера, несмотря на то что токен в .npmrc визуально «есть».
Фикс (применён 2026-06-11): явный secret: в config.yaml → токены переживают рестарты.
max_users: -1 + pnpm login → 409
Стандартный workaround «перелогинься» (pnpm login --registry ...) не работает при max_users: -1.
Verdaccio htpasswd plugin v6 проверяет max_users первым в adduser(), до проверки существования пользователя:
if (max_users === -1) → return 409 "user registration disabled"
pnpm login отправляет PUT /-/user/org.couchdb.user:<name> — это registration-path. Verdaccio блокирует даже для существующих юзеров, даже если пароль верный.
Изменение max_users на положительное число решает проблему, но открывает регистрацию всем — нежелательно.
Правильный способ получить токен при max_users: -1
Использовать web-UI login endpoint, который не проходит через adduser():
$body = '{"username":"vitya","password":"..."}'
$r = Invoke-RestMethod -Uri "https://verdaccio.kzntsv.site/-/verdaccio/sec/login" `
-Method POST -Body $body -ContentType "application/json"
$r.token # JWT, 7d expiry
Полученный токен — в ~/.npmrc:
//verdaccio.kzntsv.site/:_authToken=eyJ...
Конфиг-фикс (2026-06-11)
Добавлено в /opt/stacks/verdaccio/config/config.yaml на vds-kzntsv:
secret: <32-byte-hex-permanent>
security:
api:
jwt:
sign:
expiresIn: 30d
notBefore: 0
web:
sign:
expiresIn: 7d
verify: {}
До фикса: нет secret и нет security → legacy tokens, привязанные к ephemeral secret.
После: web-UI и npm-API используют один JWT формат, подписанный постоянным секретом.
Примечание:
security.api.jwtменяет формат API-токенов с legacy opaque (5a3X...==) на JWT (eyJ...). Старые legacy-токены в.npmrcу всех клиентов перестают работать после смены конфига — нужен refresh.
Срок жизни токенов
| Endpoint | Expiry | Используется для |
|---|---|---|
/-/verdaccio/sec/login |
7d (web.sign) | Ручной refresh, агенты |
| npm API (pnpm/yarn/npm login) | 30d (api.jwt.sign) | После решения max_users проблемы |
Клиенты: где хранится токен
npm / pnpm / yarn classic (1.x)
Читают ~/.npmrc (user-level) или .npmrc в корне проекта:
//verdaccio.kzntsv.site/:_authToken=eyJ...
@snollajs:registry=https://verdaccio.kzntsv.site/
Yarn classic читает тот же ~/.npmrc — update токена в одном файле покрывает всех.
yarn berry (2.x+)
Не читает ~/.npmrc. Конфиг в .yarnrc.yml:
npmRegistries:
"https://verdaccio.kzntsv.site":
npmAuthToken: "eyJ..."
npmAlwaysAuth: true
npmScopes:
snollajs:
npmRegistryServer: "https://verdaccio.kzntsv.site"
Refresh токена = обновить npmAuthToken в .yarnrc.yml каждого проекта, либо использовать yarn npm login --scope snollajs (тоже обходит max_users: -1 через web UI path).
Связанные страницы
- verdaccio-prune-semantics — storage layout и GC
- yarn-npm-minimal-age-gate — гейт #2: YN0016 на свежих версиях (Yarn ≥4.16), независим от auth
- vds-kzntsv — хост где крутится инстанс