Архитектура
maximaster.dev — это один NixOS-хост, на котором живут изолированные окружения разработчиков. Каждое окружение описывается flake, запускается в контейнере или виртуальной машине, получает маршрут по HTTPS и засыпает при простое. Запрос будит его незаметно для пользователя.
Компоненты
| Компонент | Назначение |
mev (клиент) | CLI на машине разработчика: упаковывает flake и отправляет по SSH |
mevd (воркер) | forced-command по SSH на хосте: вычисление → валидация → авторизация → согласование |
mev-core | библиотека: схема, авторизация, хранилище SQLite, песочница вычисления, согласование |
| Root-помощники | mev-container (extra-container/microvm), mev-data (ZFS), mev-logs, mev-stat — узкие привилегированные помощники |
| Активатор пробуждения | сервис на Rust: принимает HTTP-запросы, будит окружение, проксирует |
| Traefik | обратный прокси: TLS на :443, маршрутизация по Host, передаёт трафик активатору |
| SSH-диспетчер | аккаунт proxy: ключ → имя пользователя → маршрут → пробуждение → канал к sshd окружения |
| ZFS | постоянное хранилище: датасет окружения rpool/environments/env<id> с квотой и клонированием |
Жизненный цикл 1: пуш и согласование
Рабочая машина Хост (dev.maximaster.ru)
-------------- ------------------------
mev ./myenv
tar flake-dir ──SSH──▶ пользователь mev, forced command: mevd <operator>
nix eval --json .#mev
(--pure-eval --restrict-eval, без IFD/native-code,
под таймаутом + RLIMIT_CPU + RLIMIT_AS)
проверка схемы и авторизации
mevd-apply (БЕЗ ПРИВИЛЕГИЙ, пользователь mev):
блокировка → выделить id для "<owner>/<slug>"
разрешить users → ключи (/etc/mev/sshkeys.json)
проверить уникальность доменов
sudo mev-data create env<id> (ZFS refquota)
sudo mev-container create descriptor.json
(extra-container build + create)
записать /var/lib/env-traefik/env<id>.yml
записать /var/lib/env-wake/registry.jsonОкружение создаётся остановленным (autoStart = false). Повторный пуш — это обновление: id не меняется, данные сохраняются.
Жизненный цикл 2: HTTP-запрос будит спящее окружение
Клиент ──HTTPS──▶ Traefik (:443, TLS, маршрутизация по Host)
маршрут для <domain> ──▶ активатор (127.0.0.1:8080)
активатор:
Host → реестр
обновить lastSeen
если цель недоступна:
systemctl start container@env<id>
дождаться готовности (опрос)
проксировать → IP контейнера (100.64.x):<port>
Reaper (~каждые 5 с): простой > порога → systemctl stop container@env<id>Первый запрос после засыпания незаметно ждёт холодного старта. Активатор работает по схеме «придержать и проксировать» (как Knative или Fly).
Жизненный цикл 3: SSH-подключение к окружению
Клиент ──SSH──▶ sshd хоста, пользователь proxy
AuthorizedKeysCommand: ключ → имя пользователя (sshkeys.json)
forced command: env-ssh <username>
domain = $SSH_ORIGINAL_COMMAND (= %h из ProxyCommand)
domain → route.json → { name, ip, users }
авторизация: username в users (или admin)
проба ip:22; если отказ → управляющий сокет активатора: будит + опрос
exec nc <ip> 22
── реальная SSH-сессия клиента идёт по этому каналу ──Конфигурация клиента:
Host *.dev.maximaster.ru
ProxyCommand ssh proxy@dev.maximaster.ru %hВнешняя сессия ограничена (restrict); проброс портов (-L/-R) и PTY согласуются уже во внутренней сессии по каналу — туннели работают без отдельного порта на хосте.
Контракты данных
| Файл / хранилище | Содержимое |
/var/lib/mev/mev.db | SQLite: envs, members, domains, audit — единственный источник истины |
/var/lib/env-traefik/env<id>.yml | маршрут Traefik: домен → активатор пробуждения |
/var/lib/env-wake/registry.json | host → { name, upstream, idle } — читает активатор |
/var/lib/env-routes/route.json | domain → { name, ip, users } — читает SSH-диспетчер |
/etc/mev/sshkeys.json | реестр SSH-ключей платформы |
/etc/mev/authz.json | флаги admin и signingKeys операторов |
Адресация
Каждому окружению выдаётся /30 из диапазона 100.64.0.0/10 (RFC 6598), вычисляемый из его id. Адрес хоста — 100.<64+t/65536>.<t/256%256>.<t%256+1>, адрес контейнера — …+2, где t = id * 4. Это около миллиона окружений. Отдельные порты на хосте под каждое окружение не нужны: HTTP идёт через :443 (Traefik), SSH — через единственный аккаунт proxy.
Благодаря масштабированию до нуля число заведённых окружений не связано с числом одновременно работающих — большинство спит.
Бэкенды изоляции
nspawn(по умолчанию) — контейнерsystemd-nspawnна общем с хостом ядре. Управляется черезextra-container. Быстрый старт.microvm— виртуальная машинаmicrovm.nixс собственным ядром. Хранилище блочное (erofs для/nix/store, ext4 для данных). Поддерживает режимы пробужденияcold/warm/suspend.
Подробнее о компонентах плоскости управления — в разделе Control plane. Подробнее о безопасности — в модели безопасности.