Control plane
Плоскость управления — типизированный воркспейс на Rust (pkgs/mev-rs). Она вычисляет flake, проверяет схему, авторизует запросы, ведёт хранилище SQLite и управляет жизненным циклом окружений. Модель привилегий и сборщики Nix при этом не меняются: узкие root-помощники остаются отдельными бинарниками.
Структура воркспейса
pkgs/mev-rs/
crates/mev-core/ библиотека: spec, validate, authz, store, eval, exec, reconcile, publish
crates/mevd/ mevd + mevd-apply + HTTP API (axum) + дашборд статуса
crates/mev/ клиент CLI
crates/mev-mcp/ MCP-сервер (rmcp) поверх HTTP API
crates/mev-activator/ активатор пробуждения: HTTP, reaper, управляющий сокет, метрикиmev-core
Общая библиотека, которой пользуются все остальные крейты.
spec— типыEnvSpec(изnix eval --json ...#mev) иDescriptor(вход дляmev-container); поля повторяютlib/env-spec.nix.validate— проверка имени, slug, имён секретов, квоты; арифметика /30-адресов (те же константы, что и вchecks.ip-math).authz— политики create/update/destroy/fork/view как чистые функции от (оператор, владелец, сохранённые участники, admin). Авторизация опирается на сохранённую базу членства, а не на полеusers[]из входящего flake.store— SQLite (rusqlite, bundled) по пути/var/lib/mev/mev.db. Таблицы:envs,members,domains,audit,schema_versions. Выделение id, проверка конфликтов доменов и согласование членства идут внутри одной транзакцииBEGIN IMMEDIATE.eval— песочница для недоверенного flake: те же флагиnix eval(--pure-eval --restrict-eval, без IFD и нативного кода) под тремя ограничениями — таймаут по часам (SIGKILL),RLIMIT_CPU,RLIMIT_AS. Нарушение завершается кодом 65, до привилегированного согласования дело не доходит.build— реализация замыкания поpackageсервиса (level-2 binary supply): отдельный ДОВЕРЕННЫЙ шаг ВНЕ песочницы eval. Названный выход flake собирается на стороне оператора неприв. воркеромmev(он не trusted-user nix, поэтому песочницу сборки демона отключить не может — она и есть граница доверия), с лимитом по времени; ошибка или таймаут дают код 65. Реализованное замыкание удерживается per-env indirect GC-рутом (/var/lib/mev/env-roots/<leaf>/<сервис>), снимаемым при удалении окружения, чтобы сборщик мусора store не удалил бинарь работающего приложения. Только backendnspawn.exec— граница привилегий в виде трейта: боевая реализация вызывает root-помощники (mev-container,mev-data,mev-logs,mev-stat) напрямую черезsudo; тестовая реализация записывает вызовы для проверкиsudo-scope.reconcile— оркестратор: учёт и авторизация выполняются в процессе, эскалация — только черезexec.publish— перегенерация реестра пробуждения, таблицы маршрутов и фрагментов Traefik из хранилища под файловой блокировкой после каждого согласования.
mevd
Воркер forced-command по SSH и HTTP API.
mevd <operator>— forced-command при пуше по SSH. Поток: принять tar-архив → распаковать → песочница вычисления → валидация → (опционально) проверка подписи коммита → (если сервис объявилpackage) сборка замыкания → согласование. Управляющие команды (list,status,logs,secrets,destroy,fork,audit,stats) передаются вSSH_ORIGINAL_COMMAND. Транспорт, коды завершения и вывод — те же.mevd-apply <request.json>— точка входа согласования: заранее проверенный JSON-запрос{operator, spec}или{op:"destroy"|"fork", ...}, выполняется без привилегий от пользователяmev.mevd serve [--listen ADDR]— HTTP API (axum), по умолчанию127.0.0.1:8088. Идентификатор оператора передаётся в заголовкеX-Mev-Operator(loopback). Эндпоинты:
| Маршрут | Описание |
GET /healthz | проверка работоспособности |
GET /metrics | метрики активатора для Prometheus |
GET /v1/envs | список окружений |
POST /v1/envs | создать / обновить окружение |
GET /v1/envs/{owner}/{slug} | состояние окружения |
DELETE /v1/envs/{owner}/{slug} | удалить окружение |
GET /v1/envs/{owner}/{slug}/logs | журнал юнита |
GET /v1/envs/{owner}/{slug}/events | события окружения |
GET /v1/envs/{owner}/{slug}/stats | статистика ресурсов окружения |
POST /v1/fork | форк окружения |
POST /v1/wake | принудительно разбудить |
GET /v1/audit | журнал аудита |
GET /v1/stats | статистика хоста |
GET /metrics/stats | статистика хоста для Prometheus (admin) |
mevd stats-prometheus --output <path>— пишет метрики хоста и каждого окружения в textfile дляnode_exporter. Запускается по таймеруmev-stats-metrics.mevd status-site [--listen ADDR]— HTML-дашборд состояния хоста, по умолчанию127.0.0.1:8089. Интерфейс только для чтения. Traefik направляет на него домен статуса и закрывает его basic-аутентификацией. Отдаёт/(HTML) и/stats.json.
mev (клиент)
Клиент для рабочей машины разработчика. Справочник команд — в CLI mev. Три пути получить бинарник: nix run из этого репозитория, dev shell (
.#mev -- ...direnv allow / nix, где
developmev уже на PATH), либо он уже установлен в environment.systemPackages на самом хосте платформы (modules/platform/mev.nix).
mev-mcp
MCP-сервер (rmcp) поверх HTTP API. Транспорт: stdio. Оператор задаётся переменной MEV_OPERATOR. Логи пишутся только в stderr.
| Инструмент | Описание |
create_env | Create or update an environment from typed fields. Arg: { name: "owner/slug", users: [..], domains?: {host: port}, packages?: [..], backend?: "nspawn"|"microvm" }. The owner must be in users; server-side authz still applies. |
destroy_env | Destroy an environment (owner/admin only). Arg: { name, purge? }. purge=true moves the data to trash (recoverable for 7 days); default keeps the data for a re-push. |
env_events | Read an environment's audit/event trail (recent first). Arg: { name: "owner/slug" }. |
env_logs | Tail an environment's unit journal. Arg: { name, n? } (n defaults to 50). |
env_status | Get one environment's status: id, backend, wake mode, idle, members, domains, live state and last-seen time. Arg: { name: "owner/slug" }. |
fork_env | Fork an existing environment into a new one. Arg: { src: "owner/slug", name: "owner/slug", users: [..] }. You must be a member of src; the new owner must be in users. |
list_envs | List environments you may see (owner/member; admins see all). Returns name, id, owner, backend and members for each. |
wake_env | Resolve which environment/upstream a wake for a domain would target. Arg: { domain }. |
mev-activator
Активатор пробуждения на Rust (замена версии на Go). Те же флаги CLI, имена метрик Prometheus и логика контроля допуска. Принимает HTTP-запросы от Traefik на 127.0.0.1:8080; управляющий unix-сокет (/run/wake/ctl.sock, 0660, группа wake-ctl) использует SSH-диспетчер, чтобы будить окружения и обновлять lastSeen. Работает без привилегий от пользователя wake.
Контроль допуска: объединение одинаковых запросов, общий лимит холодных стартов, бюджет свободной RAM и ограничение частоты по источнику сдерживают последствия наплыва запросов.
Корневые помощники
Узкие привилегированные бинарники (в pkgs/mev, не в pkgs/mev-rs):
| Помощник | Назначение |
mev-container | extra-container create/destroy; пиннит <nixpkgs>, сам строит выражение — root никогда не вычисляет выбранный вызывающим nixpkgs |
mev-data | ZFS-датасет / каталог данных; leaf повторно проверяется до env<id>, путь не может выйти за rpool/environments |
mev-logs | фиксированный journalctl -u (container\|microvm)@env<id> |
mev-stat | чтение использования ZFS; только чтение, отдельно от mev-data |
Правила sudo покрывают только эти четыре помощника. Правило polkit разрешает пользователю wake лишь start/stop юнитов container@env<N> / microvm@env<N>.
Связывание модулей
modules/platform/mev.nix— устанавливает бинарникиmev-rsи четыре root-помощника, регистрирует ключи forced-command дляmevd, задаёт правила sudo.modules/platform/wake.nix— запускаетwake-activator.serviceот пользователяwakeс тем же правом polkit.
Подробнее об архитектуре в целом — в разделе Архитектура.