Nix Store: Почему пути выглядят как абракадабра
Загадочные хеши в путях Nix: почему они такие и зачем нужны
Каждый разработчик, работавший с Nix, хоть раз замирал перед путём вроде этого:
/nix/store/s0psayl7zvkvwdcqc8fy1sbv8rlf1yq8-bash-5.3p9
Справа всё понятно — bash-5.3p9 это название и версия. А вот 32 символа слева от дефиса вызывают вопросы. И вот что важно: большинство объяснений в интернете дают лишь часть картины.
Стандартное объяснение — и почему оно неполное
«Хеш считается от входных данных» — именно это вы услышите чаще всего. Это не ошибка, но есть нюанс. Какие именно входные данные? Исходный код? Инструкции сборки? Результат компиляции? Зависимости?
Честный ответ: зависит от ситуации.
Две системы именования в одном хранилище
Nix использует два разных механизма для генерации имён в store, и оба спокойно сосуществуют в /nix/store. Именно смешивание этих механизмов создаёт путаницу.
Input-addressed: имя до сборки
Для большинства пакетов Nix работает как архитектор, изучающий чертежи. Когда вы пишете .nix выражение, Nix вычисляет derivation — детальную спецификацию будущей сборки:
- Скрипт сборки
- Аргументы
- Переменные окружения
- Выходные артефакты
- Зависимости
Получив этот рецепт, Nix может сгенерировать уникальный идентификатор ещё до начала сборки. Бинарников не существует, но имя уже известно.
Вот почему изменение Nix-выражения не всегда влияет на путь в store. Если правки не затрагивают рецепт сборки, идентификатор остаётся прежним. Два разных выражения могут описывать идентичную сборку — и получат одинаковый путь. Nix называет то, что будет построено, а не форму записи кода.
Content-addressed: имя из байтов
Nix также умеет именовать объекты по их содержимому. Команда nix store add принимает готовый файл и вычисляет хеш от его байтов. Никакой оценки, никакого рецепта — только содержимое.
Здесь путь определяется тем, что реально находится в файле. Изменили один байт — хеш другой.
Почему это разделение важно
Осознание двух систем именования делает поведение Nix прозрачным:
Предсказание изменений путей. Задаётесь вопросом «изменится ли путь, если я отредактирую X?» Спросите себя: это влияет на рецепт сборки или на содержимое? Комментарий в .nix файле? Скорее всего, путь прежний. Изменили версию зависимости? Новый путь гарантирован.
Сосуществование сборок. Почему старая и новая версии пакета лежат рядом? Потому что у них разные идентификаторы. Nix не перезаписывает — он просто не нуждается в этом.
Переиспользование кеша логично. Binary cache уверенно отдаёт готовую сборку, ведь имя однозначно идентифицирует входные данные. Одно имя — одинаковое содержимое, без исключений.
Больше, чем просто имя
Путь в store никогда не существует изолированно. Собранные пакеты ссылаются на другие пути — динамические библиотеки, интерпретаторы, общие ресурсы. Nix отслеживает эти связи как граф.
Это не просто метаданные — это полная карта зависимостей. От любого пути можно проследить его замыкание (closure): всё, что должно переехать вместе с пакетом на другую машину. Так работает nix-copy-closure, и почему деплой в Nix надёжен.
Та же база данных позволяет проверять целостность. Nix сверяет байты на диске с записью при создании пути. Пути в store — не непрозрачные имена папок, а запросимые идентификаторы с полной историей.
Суть
Криптографические хеши — не произвол. Это результат осознанного решения: Nix называет сборки по их входам, а контент — по байтам. Такая двойственная система делает Nix воспроизводимым, проверяемым и удивительно предсказуемым — если понять принцип.
В следующий раз, увидев путь в store, не паникуйте. Читайте справа налево — там человеческая часть. Хеш слева это обещание Nix: это имя означает именно эту сборку, и никакую другую.
Хотите копнуть глубже? Неважно, деплоите ли вы контейнеры, управляете инфраструктурой или просто интересуетесь воспроизводимыми сборками — понимание внутренностей системы всегда окупается. В NameOcean мы верим: лучшие разработчики это те, кто понимает, что происходит под капотом, а не просто запоминает команды.
Удачной сборки.