Files
nixos/docs/arch/invariants.md
2026-10-07 11:29:21 +03:00

15 KiB
Raw Permalink Blame History

Инварианты: вопросы владельцу

Проход по репозиторию сверху вниз, 2026-10-05. 120 .nix, ~8.5k строк.

Структура:

  • Сводный ответ, ядро и ловушки → AGENTS.md (корень репозитория).
  • Подробная карта архитектуры с per-host деталями и инвентарём сервисов → docs/arch/map.md.
  • Этот файл → открытые вопросы (слои 0–8) + ещё непрочитанные слои 9–11 (home-manager, deploy, формат).

Пометки: [!] — найденный дефект, не вопрос. [?] — не смог определить по коду. [✓] — отвечено владельцем 2026-10-05.

Статус ответов (2026-10-05)

Отвечено: 2.1, 5.2, 6.1, 6.3, 6.5, 7.1, 7.2 (7 пунктов). Остальные ждут ответа (таблица ниже).

Ключевое из ответов, что меняет картину:

  • 6.5 — моя ошибка. 100.64.0.0 не «сетевой адрес вместо интерфейса»: это Tailscale-адрес sapphira, назначенный вручную.
  • 6.3 — это не дыра, а осознанное решение. Граница держится на роутере: на сервер пробрасываются ровно 5 портов — 443, 80, 22000 (syncthing), 8443 (xray), 22 (ssh). firewall.enable = false на sapphira — следствие, а не недосмотр. Проблема в другом: список пробросов нигде не записан в репозитории, и именно его агент обязан уважать (D1 в todo.md).
  • 7.2 — 3x-ui рабочий. Откат сделан осознанно: панель последняя, ядро Xray осталось на 26.7.28, миграция на 26.9 провалена, лишний код закомментирован. Состояние — «заморожено», а не «сломано».
  • 5.2 — подтверждённая дыра в защите данных. Guard для несмонтированного носителя не был продуман → задача B1.

Сводка подтверждённых инвариантов

# Пункт Краткая формулировка См.
1 Все outputs флейка вычисляются A1: правка lib/xlib.nix → lib/xlib; закрепить через nix flake check todo A1
2 External-диск монтируется до сервисов mkServiceStorage + bind без guard'а → сервис стартует на пустой БД todo B1
3 Сетевая граница sapphira = роутер 5 портов: 22, 80, 443, 8443, 22000; firewall.enable = false намеренно todo D1
4 100.64.0.0 = Tailscale sapphira Назначен вручную; в 4 файлах AGENTS.md §5
5 3x-ui заморожен Панель на latest; ядро Xray на 26.7.x; миграция 26.9 провалена todo C1–C5
6 nftables на VDS — явная финальная политика Сейчас ruleset без финального правила + конфликт с firewall.* todo A3

Сводка по ловушкам

Полная таблица (10 пунктов) в AGENTS.md → раздел «Ловушки». Кратко: firewall.enable=false намеренно · uid=1001 на sapphira · image = …:latest намеренно для 3x-ui · reality443Forwarding=true — следствие отката · 15 закомментированных модулей в server/default.nix · serviceConfig vs Service в home-manager · nftables без финального правила · 100.64.0.0 не сеть · /mnt/services mode 0777 · stateVersion разный между хостами.

Неотвеченные вопросы (слои 0–8)

Самые важные — выделены.

ID Вопрос Что блокирует
0.1 Восстанавливать ли migration notes, удалённые в 22a19be? C1: реконструкция заметки 3x-ui
0.2 Комментарий-density 38/120 файлов без комментариев — нормально? Стиль модулей
0.3 15 закомментированных модулей: удалить или хранить как референс? E3: чистота кода
0.4 README пустой, todo.md нет — норма? E1: AGENTS.md/README
1.3 Лишние inputs в flake (justray, nix-minecraft, proxy-suite)? Чистота flake
1.5 nix-systems через follows — оптимизация размера lock Документация
2.2 vetymae / lamet / therima / soptur — те же машины или хосты вне реестра? DNS/nginx/identity
2.3 sapphira uid=1001: блокер ли использование /mnt/archive//mnt/mobile? Миграция ФС
2.5 stateVersion 24.05 / 24.11 / 25.05 / 26.05 — намеренный дрейф? Миграции
2.6 Есть ли escape hatch для per-host отличий в xlib? Архитектура
2.7 devices.termux без NixOS-хоста — закрытый список Документация
3.2 any.nix (minimal) нужны home-manager + sops + disko? Минималка
4.1 Как root получает доступ по SSH — authorizedKeys в коде нет deploy, безопасность
4.2 Как разрешается цикл «ключ в секрете, а нужен для расшифровки»? bootstrap, recovery
4.3 Все файлы в secrets/ покрыты path_regex? sops
4.4 Как подключается вторая машина / второй человек при одном age-ключе? sops, scale
4.5 users.nix:87 — личный ключ или общий «ключ от деплоя»? Безопасность
5.1 /mnt/services mode 0777 — осознанно? Безопасность
5.3 NFS выключен, Samba работает — миграция? Сетевые сервисы
5.4 NTFS-том lamet-drive mask=0000 — что на нём? Семантика
5.5 therima / vetymae / soptur в dirs.nix — реально смонтированы? Семантика
5.6 Где бэкапы БД и 3x-ui? B2
6.6 192.168.1.20 зашит в 30 мест — константа? Рефакторинг
6.7 DNS ↔ сервисы — как ловим рассинхрон? Документация, CI
6.8 Публичные IP + SSH-алиасы в home/termux.nix — карта «хост → адреса» нужна? Архитектура
6.9 Какой путь REALITY правильный сейчас? C5
7.4 Почему не публиковать весь диапазон 14380-15379? 3x-ui
8.2 lamet.opencodes → :6061 (порт miniflux) — ошибка? nginx
8.4 onlyoffice после трёх регрессов — работает? Статус сервиса
8.5 Что слушает :3002 (/whiteboard nextcloud)? Карта сервисов
8.6 Бэкапы вне Nix — записать Документация

Где это раньше лежало

До переноса в AGENTS.md / map.md здесь был подробный Q&A по слоям 0–8 с разделами «Вопрос», «Факт», «Риск», «Кандидат». Этот текст сохранён в git-истории файла (последний коммит, где Q&A был полным). Восстановить: git log -p docs/arch/invariants.md | less.


Слой 9. home-manager

9.1 home/home.nix:52-57 — для пользователя импортируется home/${xlib.device.type}.nix; для root — без профиля (строка 51). Вопрос: почему у root нет home-профиля — сознательно? Кандидат: home/<type>.nix = единственный источник «что есть на этом хосте» для пользователя; добавление пакета в новый тип = правильный файл, а не home/default.nix.

9.2 [!] home/home.nix:28-43 для headless-хостов: xdg.userDirs.* = null и createDirectories = false, при этом lib/xlib/dirs.nix:26 обещает music-library = "${user-home}/Music". Вопрос: кто создаёт ~/Music и ~/Storage? createDirectories = false означает, что home-manager их не создаст, а dirs.nix на них ссылается. Риск: на headless-хосте путь в конфиге есть, а каталога нет → тихий сбой сервиса, который туда пишет.

9.3 [!] home/modules/opencode.nix:339-350 (c73a698): в home-manager нельзя писать serviceConfig = { ... } — рендерится литеральная секция [serviceConfig], которую systemd молча игнорирует («Unknown section 'serviceConfig'. Ignoring.»). Правильно: systemd.user.services.opencode-web.Service = { ... }. Кандидат (готовый инвариант, стоит закрепить буквально в AGENTS.md): в home-manager cgroup-опции (MemoryHigh, OOMScoreAdjust, …) пишутся в systemd.user.services.<name>.Service, не в serviceConfig. Ошибка не диагностируется — она просто не применяется.

9.4 linger = true добавлен ради opencode-web (users.nix:71-75) и включён для всех хостов. Кандидат: «user-сервисы переживают logout на всех хостах» — закрепить, потому что это неочевидное поведение, влияющее на ресурсы и на безопасность.

9.5 home/modules/opencode.nix:286-303 — opencode.web слушает 0.0.0.0:4096 (комментарий: nginx проксирует 127.0.0.1:4096), и nginx на sapphira ходит туда же по Tailscale у двух других хостов (см. 8.3). Кандидат: 0.0.0.0 в opencode.web — обязательное условие для внешнего доступа через opencodes.*; пароль приходит из sops-секрета opencode_server.

9.6 Секреты opencode приходят в ~/.config/opencode/server.env (dotenv), ~/.local/share/opencode/auth.json и account.json (json, key = ""). Кандидат: эти три файла перезаписываются sops при каждой активации — ручные правки в них теряются. Уже отражено в комментарии users.nix:120-131, стоит закрепить как инвариант.


Слой 10. deploy и проверка

10.1 deploy/default.nix:20-24 — цели: sapphira (server), otrecа (vds), rydiwo (ноутбук). Нет atoridu (основной десктоп), wsl, epral. Вопрос: почему не деплоится десктоп? И безопасно ли пересобирать ноутбук rydiwo по SSH (он может быть выключен/на другом Wi-Fi)? Кандидат: deploy-rs = только серверы + ноутбук; десктоп и WSL обновляются вручную. Инвариант: не добавлять в deploy.nodes хост, который нельзя пересобрать в любой момент без риска потерять доступ.

10.2 deploy/default.nix:18-19 — sshUser = "oqyude", user = "root". См. 4.1: root-доход по SSH не описан в конфигурации. Кандидат: деплой требует ручной настройки root-доступа на каждом из 3 хостов — это скрытая зависимость, которую агент не выведет.

10.3 deploy/default.nix:27-29 — checks = builtins.mapAttrs (... deployChecks). Вопрос: nix flake check реально проходит сейчас? Учитывая 2.1 (lib/xlib.nix) он должен падать на nixOnDroidConfigurations. Падает или checks покрывают не всё дерево outputs? Кандидат (первое, что стоит сделать): добиться, чтобы nix flake check был зелёным — это единственная автоматическая защита от подобных breakage'ов.

10.4 CI нет, flake check не запускается автоматически. Кандидат: минимальный локальный набор перед коммитом: nix flake check && nix build .#nixosConfigurations.<хост>.config.system.build.toplevel --dry-run.


Слой 11. Формат (то, что я предлагаю зафиксировать как процесс)

11.1 Где будет жить итог: AGENTS.md в корне (читается агентом всегда), docs/arch/map.md (карта хостов/сервисов), docs/arch/invariants.md (этот файл). Кандидат: этот файл после ответов превращается в docs/arch/invariants.md с колонкой «ответ» и становится источником для AGENTS.md; AGENTS.md — краткая выжимка, без подробностей.

11.2 Какие инварианты можно превратить в автоматическую проверку (тогда они перестанут «забываться»):

  1. ни одного :latest в образах (grep по image =);
  2. nix flake check зелёный;
  3. каждый домен из coredns.nix имеет vhost в nginx.nix и наоборот;
  4. каждый сервис в mkServiceStorage имеет каталог в /mnt/services на External-диске;
  5. в самописном nftables-ruleset последнее правило цепочки явное;
  6. каждый listen.addr — реально назначенный адрес, а не сеть;
  7. все файлы в secrets/ матчат path_regex из .sops.yaml.

Вопрос: какие из этих проверок ты хочешь, а какие — лишний CI?