8dcb50a07c
Предыдущий проход сделал правильным порядок «сначала долговременная запись, потом разрыв сессии» и правильно запретил откат при неудаче разрыва. Способа прийти к согласованному состоянию ПОТОМ он не дал: у двух операций повтор не работал вовсе. Импорт, заменивший auth_id: после неудавшегося /kick старое значение не хранится нигде, повтор того же файла читает из базы уже новое и рвёт его, а cron пропускал незнакомый authID молча — dao.ListPeer просто не возвращала строку. Живая сессия оставалась навсегда. Снижение maxDevices: повтор формы даёт 1 < 1 -> false, разрыва больше нет. Лимит устройств в политику доступа не входит и входить не должен — это свойство сессий, — поэтому механизма схождения у него не было. enforcePeerAccess стал сверкой живых сессий: обход идёт по каждому authID из /online. Нет строки в базе -> kick; peerAccessDenied -> kick; непригодный maxDevices -> kick; устройств больше разрешённого -> kick. Отказ базы при этом не рвёт ничего. Ни таблицы отложенных операций, ни очереди retry: список живых сессий уже есть, и это /online. Отдельно закрыт второй TOCTOU лимита устройств. Учёт выданных разрешений закрыл сравнение двух одинаковых снимков, но сетевой запрос выполнялся вне блокировки, поэтому снимки приходили в резервацию в произвольном порядке и устаревший откатывал lastOnline назад, возвращая уже занятое место. Это не data race — память защищена мьютексом, и -race здесь молчит принципиально. Последовательность «прочитать /online -> занять место» выполняется под замком по authId; глобальный замок не годится, внутри идёт сетевой запрос. Учёт разрешений больше не растёт бесконечно: запись снималась только на ветке отказа, поэтому в карте копились удалённые пиры и переписанные импортом идентификаторы. Уборка идёт по фактической картине подключений. Гейты приёмки доращены под все три инварианта и проверены в обе стороны. Go 1.26.7 -> 1.26.8. Документация приведена в соответствие в двух местах, где описывала снятую архитектуру. Разбор: docs/acceptance/2026-09-02-v1.0.0-rc3-preflight-findings.md
108 lines
7.1 KiB
Bash
108 lines
7.1 KiB
Bash
# HY2XS: единственный источник истины для контракта
|
||
# «продукт / платформа / toolchain».
|
||
#
|
||
# Что здесь ЕСТЬ:
|
||
# версия продукта, линия релиза, схема конфигурации, целевая платформа,
|
||
# версии и контрольные суммы build-toolchain, политика выбора Hysteria.
|
||
#
|
||
# Чего здесь НЕТ и быть не должно:
|
||
# 1. Прикладные зависимости (Vue, Gin, GORM, npm/Go модули). У них уже есть
|
||
# канонические lock-механизмы: apps/frontend/pnpm-lock.yaml,
|
||
# orchestrator/bun.lock, apps/go.sum. Второй слой неизбежно разъедется
|
||
# с настоящим графом зависимостей.
|
||
# 2. Конкретная версия Hysteria. Здесь живёт только ПОЛИТИКА выбора
|
||
# (HYSTERIA_CHANNEL). Результат резолва конкретной сборки замораживается
|
||
# в tools/build/hysteria-lock.env. Пин версии здесь вернул бы ручное
|
||
# обновление, от которого мы ушли.
|
||
#
|
||
# Файл читается сборкой (tools/build/lib/versions.sh) и проверяется шагом
|
||
# verify_versions_contract: рассинхрон с package.json / profile.ts /
|
||
# hy2xs.env / go.mod роняет сборку до создания tarball.
|
||
#
|
||
# Синтаксис — POSIX shell: файл должен безопасно подключаться через `.`
|
||
# без jq, Node и Python.
|
||
|
||
# ------------------------------------------------------------- продукт ---
|
||
|
||
HY2XS_VERSION=1.0.0
|
||
HY2XS_RELEASE_LINE=1
|
||
HY2XS_CONFIG_SCHEMA_VERSION=2
|
||
|
||
# ------------------------------------------------------------ платформа ---
|
||
|
||
HY2XS_BUILD_OS=debian
|
||
HY2XS_BUILD_OS_VERSION=13
|
||
HY2XS_BUILD_ARCH=amd64
|
||
|
||
HY2XS_TARGET_OS=debian
|
||
HY2XS_TARGET_OS_VERSION=13
|
||
HY2XS_TARGET_ARCH=amd64
|
||
|
||
# ------------------------------------------------------------ toolchain ---
|
||
|
||
# Go входит в production-артефакт, а не только в сборку: им компилируется
|
||
# hy2xs-admin, и его stdlib целиком попадает в бинарь. Поэтому версия здесь
|
||
# выбирается по политике поддержки Go (major поддерживается, пока не вышли две
|
||
# более новые), а не по удобству.
|
||
#
|
||
# Было 1.21.13 — линия вне поддержки. На ней `govulncheck ./...` находил 21
|
||
# ВЫЗЫВАЕМУЮ уязвимость, из них 17 в stdlib. На 1.26.x — ноль.
|
||
#
|
||
# Patch-релиз держится актуальным по той же причине: stdlib целиком попадает в
|
||
# бинарь, поэтому «на один патч позади» — это свойство выпускаемого артефакта,
|
||
# а не среды сборки. 1.26.8 вышел 2026-09-01 (fixes в cgo, компиляторе,
|
||
# runtime, `debug/elf` и `os`); major при этом не меняется — линия 1.26
|
||
# поддерживается, и переход на 1.27 ради номера не нужен.
|
||
GO_VERSION=1.26.8
|
||
GO_LINUX_AMD64_SHA256=d0f743b33e8d8945e6b1f432edd15785c70507121d6e2a723b21285eddf8b57b
|
||
|
||
# Bun выбирает артефакт по наличию AVX2, поэтому одной контрольной суммы
|
||
# архитектурно недостаточно: обе должны быть зафиксированы заранее.
|
||
#
|
||
# 1.3.13 оставлена сознательно, хотя 1.4 уже вышел. Оркестратор собирается
|
||
# через `bun build --compile`, то есть Bun runtime физически входит в
|
||
# исполняемый файл, а 1.4 — крупное внутреннее обновление рантайма. Такой
|
||
# переход стоит отдельного контролируемого прохода по всей матрице
|
||
# `bun test → tsc → compile → Debian acceptance`, а не строки в общем патче.
|
||
BUN_VERSION=1.3.13
|
||
BUN_LINUX_X64_SHA256=79c0771fa8b92c33aae41e15a0e0d307ea99d0e2f00317c71c6c53237a78e25a
|
||
BUN_LINUX_X64_BASELINE_SHA256=9d8a24292a7068090205daac0a5a223f5f69736f5287e37bf88d3b4031edc750
|
||
|
||
# Node живёт только на build-хосте (сборка frontend-ассетов) и в runtime-артефакт
|
||
# не попадает. Но 20.x достигла EOL, то есть перестала получать в том числе
|
||
# security-обновления, а собирает она код, который уезжает в production.
|
||
# 24.20.0 — текущая LTS-линия.
|
||
NODE_VERSION=24.20.0
|
||
NODE_LINUX_X64_SHA256=2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2
|
||
|
||
# pnpm ставится через corepack, отдельного архива с контрольной суммой нет.
|
||
#
|
||
# Линия 9 оставлена вместе с apps/frontend/pnpm-lock.yaml: переход на 10 меняет
|
||
# формат lock-файла, то есть затрагивает воспроизводимость сборки frontend, и
|
||
# относится к обновлению его зависимостей, а не toolchain.
|
||
PNPM_VERSION=9.15.9
|
||
|
||
# ------------------------------------------------ проверка зависимостей ---
|
||
|
||
# Версия govulncheck пиньтся здесь, а не берётся @latest: инструмент, которым
|
||
# проверяется релиз, сам обязан быть частью контракта сборки. Иначе один и тот
|
||
# же коммит проверяется разными анализаторами в разные дни.
|
||
#
|
||
# База уязвимостей при этом ЖИВАЯ и подтягивается на каждом запуске — это и
|
||
# нужно: пин версии инструмента не должен превращаться в пин знаний о мире.
|
||
GOVULNCHECK_VERSION=v1.7.0
|
||
|
||
# Порог pnpm audit по ВСЕМУ lock-графу frontend, а не только по production-
|
||
# зависимостям. `--prod` здесь стоял с обоснованием «devDependencies в артефакт
|
||
# не попадают», и для frontend build tooling это обоснование неверно: vite и
|
||
# rollup формируют production-бандл, который уезжает в артефакт, поэтому
|
||
# уязвимость в них — уязвимость в том, что мы выпускаем.
|
||
# Значения: low | moderate | high | critical.
|
||
PNPM_AUDIT_LEVEL=high
|
||
|
||
# ------------------------------------------------ политика выбора Hysteria ---
|
||
|
||
# stable — резолвить последний стабильный upstream-релиз на build-машине;
|
||
# pinned — использовать закоммиченный tools/build/hysteria-lock.env офлайн.
|
||
HYSTERIA_CHANNEL=stable
|