Архитектура

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.dbSQLite: envs, members, domains, audit — единственный источник истины
/var/lib/env-traefik/env<id>.ymlмаршрут Traefik: домен → активатор пробуждения
/var/lib/env-wake/registry.jsonhost → { name, upstream, idle } — читает активатор
/var/lib/env-routes/route.jsondomain → { 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.

Благодаря масштабированию до нуля число заведённых окружений не связано с числом одновременно работающих — большинство спит.

Бэкенды изоляции

Подробнее о компонентах плоскости управления — в разделе Control plane. Подробнее о безопасности — в модели безопасности.