pimenov.ai

База знаний

mise + sops + age: варианты применения в проектах команды

Связка mise + sops + age для команд: воспроизводимый запуск окружения, безопасное хранение project-level секретов и контроль доступа по контурам.

Опубликовано

Связка mise + sops + age решает две практические задачи в проектах команды: воспроизводимый запуск окружения и безопасное хранение project-level секретов. В репозиторий коммитится зашифрованный конфиг, приватный ключ для расшифровки живёт отдельно — у разработчика, в Keychain, 1Password или CI secrets.

Что это такое

mise — менеджер версий и команд проекта. В одном файле .mise.toml вы фиксируете, какие Node, Python, pnpm нужны проекту, и описываете команды вроде mise run dev или mise run smoke. Заменяет nvm, pyenv, asdf и зоопарк самописных скриптов. Новый человек клонирует репозиторий, делает mise install — и у него ровно те же версии и те же команды, что у вас.

sops — утилита от Mozilla для шифрования конфигов. Берёт ваш YAML или JSON и шифрует только значения, оставляя ключи открытыми. Diff в Git остаётся читаемым: видно, какое поле поменялось, а само значение зашифровано. Файл secrets/dev.sops.yaml спокойно лежит в репозитории — без приватного ключа из него ничего не достать.

age — современная замена GPG для команд. Простой формат ключей, понятный CLI, никакого keyring и веб-доверия. Публичный ключ — одна короткая строка, её можно коммитить рядом с проектом. Приватный ключ живёт у каждого человека отдельно: в Keychain, 1Password или в файле с правильными правами. Именно age решает, кто имеет право расшифровать sops-файл.

Как они работают вместе. mise запускает проект, в нужный момент дёргает sops, тот через age расшифровывает секреты и подсовывает их в окружение. Снаружи это одна команда mise run dev. Внутри — три инструмента, каждый отвечает за свой слой: окружение, формат конфига, криптография.

Целевая архитектура

В этой связке три инструмента закрывают три разных слоя проекта.

  • mise отвечает за окружение и команды: версии Node, Python, pnpm, ENV-переменные, задачи dev, test, build, smoke.
  • sops отвечает за зашифрованные файлы конфигурации: YAML, JSON, .env-подобные структуры, которые безопасно лежат в репозитории.
  • age отвечает за саму криптографию: публичный ключ коммитится в проект, приватный ключ хранится вне репозитория.

Идея простая: репозиторий перестаёт быть местом, куда «случайно попал .env». Там лежит инструкция запуска и зашифрованный сейф, а право открыть этот сейф у конкретных людей и сервисов.

Чеклист быстрой проверки

Пройдите по списку перед тем, как внедрять связку в команде.

В проекте есть .mise.toml с фиксированными версиями инструментов.
mise tasks показывает понятный набор команд (dev, test, sync:dry, build, smoke).
В репозитории нет ни одного незашифрованного файла с реальными токенами.
Секреты лежат в secrets/<contour>.sops.yaml с разделением по контурам.
У каждого контура свой список recipients в .sops.yaml.
Публичные age-ключи команды собраны в одном файле и закоммичены.
Приватные age-ключи разработчиков хранятся в Keychain или 1Password, а не в репозитории.
CI получает отдельный age-ключ с правами только на свой контур.
Описан процесс добавления нового человека: какой ключ выдать, какие файлы перешифровать.
Описан процесс отзыва доступа: ротация ключа и перешифровка секретов.
Production-секреты не лежат в репозитории — даже зашифрованными.
В README зафиксировано, какие команды безопасные, а какие требуют отдельного approval.

Роли инструментов

mise — воспроизводимое окружение

mise фиксирует версии runtime и описывает команды проекта в одном файле. Новый человек открывает репозиторий и запускает mise install плюс одну понятную команду вместо ручной установки Node, pnpm и Python нужных версий.

sops — зашифрованные конфиги

sops шифрует значения в YAML или JSON, оставляя ключи в открытом виде. Diff в Git остаётся читаемым: видно, какое поле изменилось, а само значение зашифровано. Файлы вида secrets/dev.sops.yaml спокойно коммитятся в репозиторий.

age — криптография под капотом

age — современная замена GPG для команд. Простой формат ключей, понятный CLI, никакого keyring и веб-доверия. Публичный ключ лежит в проекте рядом с .sops.yaml, приватный — у владельца.

Варианты применения в команде

1. Быстрый запуск проекта новым человеком

В проект кладётся .mise.toml с версиями инструментов и понятными задачами. Новый разработчик клонирует репозиторий, получает age-ключ, и через десять минут уже запускает mise run dev или mise run sync:dry.

2. Зашифрованные dev и staging секреты

В репозитории живут secrets/dev.sops.yaml и secrets/staging.sops.yaml. Расшифровать их может только человек или CI с подходящим приватным ключом. Если ключ скомпрометирован, он отзывается, файлы перешифровываются, история в Git остаётся чистой.

3. Безопасные dry-run и sandbox сценарии

Для интеграций с Notion, Telegram, Directus, OpenAI и CRM удобно держать отдельные sandbox-секреты. Это даёт возможность проверять скрипты без доступа к production-данным и без риска случайных live writes.

4. Управляемый CI/CD

CI получает свой отдельный ключ расшифровки и запускает только разрешённые задачи: тесты, сборку, sync:dry, smoke. Секреты не лежат в репозитории как простой текст и не передаются вручную между людьми.

5. Разделение доступов по контурам

Один файл — один контур: dev, staging, production, client-demo, sandbox. У каждого контура свой набор recipients. Разработчик читает dev-секреты, но не production. CI читает только то, что ему нужно для своей задачи.

6. Операционный стандарт команды

Связка помогает зафиксировать рабочую дисциплину: где лежат секреты, кто имеет доступ, как добавить нового человека, как отозвать доступ, как ротировать ключи и какие команды считаются безопасными для запуска без approval.

Что НЕ класть в project secrets

В project-level сейфе не должно быть глобальных админских токенов, root SSH, личных ключей разработчиков, account-wide доступов Cloudflare, приватных age identities и production-секретов без отдельного процесса ротации. Для таких вещей подходят:

  • Keychain или 1Password — для личных и админских ключей.
  • CI secrets — для токенов, которыми пользуется только pipeline.
  • Server secret store — для production runtime.
  • Отдельный технический пользователь с минимальными правами — вместо того чтобы выдавать сервису ключи владельца аккаунта.

Эталонный шаблон проекта

Минимальная рабочая структура для команды.

project/
├── .mise.toml
├── .sops.yaml
├── secrets/
│   ├── dev.sops.yaml
│   └── staging.sops.yaml
└── age-keys/
    ├── team.pub          # публичные ключи команды
    └── ci.pub            # публичный ключ CI

Пример .mise.toml с базовыми задачами:

[tools]
node = "20"
pnpm = "9"
python = "3.12"

[env]
PROJECT_ENV = "dev"

[tasks.dev]
description = "Локальный запуск с расшифровкой dev-секретов"
run = "sops exec-env secrets/dev.sops.yaml 'pnpm dev'"

[tasks."sync:dry"]
description = "Dry-run синхронизации без записи"
run = "sops exec-env secrets/dev.sops.yaml 'pnpm sync --dry-run'"

[tasks.smoke]
description = "Smoke-тесты после деплоя"
run = "pnpm smoke"

Пример .sops.yaml, разделяющий доступ по контурам:

creation_rules:
  - path_regex: secrets/dev\.sops\.yaml$
    age: >-
      age1devkey...,
      age1cikey...
  - path_regex: secrets/staging\.sops\.yaml$
    age: >-
      age1leadkey...,
      age1cikey...

С чего начать пилот

Начинайте не с production-критичных систем. Хорошие первые проекты для пилота:

  • Контентные пайплайны и Notion-to-site синхронизация.
  • Sandbox-боты и staging-интеграции.
  • Внутренние devtools и скрипты команды.
  • Тестовые контуры новых интеграций до выхода в production.

Production runtime и клиентские live-секреты стоит переводить только после отдельного preflight и плана ротации.

📌
Практическое правило. В репозитории живёт инструкция и зашифрованный сейф. Ключ от сейфа живёт вне репозитория. mise запускает проект в правильном порядке, sops открывает зашифрованный конфиг там, где есть право доступа, age определяет, у кого это право есть.

По теме

Если в команде уже накопились проекты с .env-файлами по разным машинам или ручной передачей токенов в личке, имеет смысл проверить на одном пилоте, насколько mise + sops + age упрощает жизнь.

Захотите обсудить, как это применить у себя или в команде — пишите в Telegram @pimenov