39 KiB
Спецификация MontanaOS — версия 0.2
Дата: 2026-05-06 Статус: Pre-mainnet, исследовательская стадия Базовая операционная система: GrapheneOS на ядре Linux
История версий:
- 0.1 (2026-05-06) — первый черновик
- 0.2 (2026-05-06) — закрытие девяти блокеров критика, тринадцати дефектов второго порядка, пять открытых вопросов оформлены явно
0. Прелюдия
В сети Монтана телефон по своей природе — клиент, не валидатор. Это не предубеждение и не упущение, это архитектурная реальность мобильных операционных систем: ни одна из них не предназначена для демонов, работающих круглосуточно, с холодным запуском после перезагрузки и без вмешательства владельца устройства.
MontanaOS отвечает на конкретный вопрос: что нужно изменить в смартфоне Google Pixel, чтобы он стал полноценным валидаторским узлом сети Монтана, не уступающим по корректности и аудитируемости узлу на настольном сервере или одноплатном компьютере.
Ответ — три преобразования базовой операционной системы:
- Снять программный запрет на блокировку оперативной памяти в пользовательском пространстве.
- Заменить мобильную модель приложений с фоновыми ограничениями на нативный супервизор демонов уровня системы.
- Прошить сетевую и батарейную подсистемы под режим круглосуточной работы.
Эти три преобразования снимают около восьмидесяти процентов отличий между Android-телефоном и обычным Linux-сервером. Оставшиеся двадцать процентов — физика устройства: тепловыделение, износ аккумулятора, отсутствие прямого внешнего адреса у мобильного оператора. Эти ограничения не предмет спецификации, они закрываются эксплуатационными решениями.
1. Определение
MontanaOS — производный дистрибутив операционной системы для смартфонов Google Pixel, основанный на GrapheneOS, с минимально необходимыми и достаточными отклонениями от исходного дерева для запуска валидаторского узла сети Монтана как нативного системного сервиса.
MontanaOS не является самостоятельной операционной системой. MontanaOS — это GrapheneOS плюс конечный набор именованных патчей. Каждый патч имеет имя, описание, обоснование, тест-критерий и сопровождающего.
2. Цели
2.1. Узел сети Монтана запускается автоматически после первой загрузки устройства и переживает все последующие перезагрузки без участия пользователя.
2.2. Узел работает непрерывно при штатных условиях питания: непрерывная зарядка от внешнего источника, активное охлаждение в стационарной док-станции.
2.3. Приватные ключи узла находятся в оперативной памяти, недоступной для подкачки на диск, аварийных снимков ядра и отладочных дампов. Это требование наследуется из Карт Безопасности основной спецификации Монтаны и является обязательным, не опциональным. Реализация описана в разделе 6.2 как набор контрактов между двоичным файлом узла и системой инициализации.
2.4. Сборка MontanaOS воспроизводима. Один и тот же файл фиксации версий, поданный двум разным сборщикам в чистое окружение Debian 12, обязан давать побайтово идентичный итоговый образ, включая имя файла. Источник детерминизма — переменная SOURCE_DATE_EPOCH, выводимая из метки времени коммита фиксации версий, не из локальной даты сборщика.
2.5. Релизы подписываются ключом MontanaOS и проверяются цепочкой проверенной загрузки конкретного устройства Pixel. Процедура подписи описана в разделе 7.3.
3. Не-цели
3.1. MontanaOS не пытается заменить настольный Linux на телефоне. Если нужен полноценный пользовательский Linux на мобильном железе — это отдельная инициатива, реализуемая через форк postmarketOS или Mobile NixOS.
3.2. MontanaOS включает сетевой слой (раздел 5.5), но конкретные адреса генезис-серверов сети Монтана не являются частью этой спецификации и хранятся в основной спецификации Монтаны как единственный источник истины. MontanaOS лишь читает их и использует как точки выхода туннеля.
3.3. MontanaOS не продлевает физический срок службы аккумулятора. При работе круглосуточно на постоянной зарядке аккумулятор Pixel 9 Pro XL сохраняет более восьмидесяти процентов ёмкости в течение двенадцати-восемнадцати месяцев. Замена аккумулятора через сервис — нормальная часть эксплуатации, не сбой системы.
3.4. MontanaOS не предотвращает термический троттлинг процессора Tensor G4 при длительной нагрузке от верифицируемой задержки времени. Решение — стационарная док-станция с активным радиатором. Спецификация требует только диагностику перегрева в приложении статуса (раздел 5.6), не аппаратное решение.
4. Целевое железо
Этот раздел — единственный источник истины по списку поддерживаемых устройств. Дорожная карта и инструкции сборки ссылаются на него.
4.1. Основное устройство: Google Pixel 9 Pro XL, кодовое имя «комодо». Конфигурация: шестнадцать гигабайт оперативной памяти, один терабайт основного хранилища стандарта UFS 4.0, аккумулятор пять тысяч шестьдесят миллиампер-часов.
4.2. Поддерживаемые устройства первого ряда: — Pixel 9 Pro (кодовое имя «кайман») — Pixel 9 (кодовое имя «токай»), урезанная версия с двенадцатью гигабайтами оперативной памяти — Pixel 8 Pro (кодовое имя «хаски») — Pixel 8 (кодовое имя «шиба»)
4.3. Не поддерживаются: — Любые устройства не от Google. Причина: отсутствие проверенной загрузки с пользовательскими ключами. — Pixel 7 и более ранние модели. Причина: приближающийся конец срока поддержки от производителя. — Устройства с разблокированным загрузчиком, но без активной цепочки доверия MontanaOS. Причина: невозможность аттестовать узел перед сетью.
5. Архитектура
5.1. Структурный обзор
Стек MontanaOS снизу вверх:
Слой 0. Загрузчик и проверенная загрузка. Наследуется от GrapheneOS, заменяется только публичный ключ. Слой 1. Ядро Linux плюс патчи MontanaOS. Слой 2. Система инициализации Android плюс новый сервисный класс montana_main. Слой 3. Двоичный файл узла сети Монтана, упакованный в системный образ. Слой 4. Клиент туннелирования плюс конфигурация первичной загрузки. Слой 5. Системное приложение статуса узла. Слой 6. Канал обновлений по воздуху через хаб Монтаны.
Каждый слой имеет независимую вложенную спецификацию. Зависимости направлены строго снизу вверх. Слой не имеет права требовать поведения от слоёв выше своего уровня.
5.2. Слой ядра
Ядро Linux в составе GrapheneOS — это ванильное ядро Pixel со стандартными патчами безопасности GrapheneOS. MontanaOS добавляет один кумулятивный патч, опционально, и одну гарантированную настройку через систему инициализации.
Гарантированная настройка: жёсткий лимит блокировки памяти для класса сервисов montana_main устанавливается в значение «без ограничений» через систему инициализации. Это первый выбор реализации.
Опциональный патч ядра: новая системная переменная ядра по пути
/proc/sys/kernel/user_mlock_unrestricted
Применяется только если эмпирическая проверка в Фазе 2 покажет, что подъём жёсткого лимита через систему инициализации недостаточен для конкретных архитектурных требований узла. До завершения Фазы 2 этот патч находится в состоянии «эскиз», его реальная применимость — открытый вопрос (раздел 8.1).
Содержимое эскиза патча — в файле kernel/patches/0001-user-mlock-unrestricted.patch, не в этом документе. Спецификация фиксирует только обоснование и тест-критерий.
Тест-критерий слоя ядра: процесс класса montana_main, не имеющий системной привилегии блокировки памяти, успешно блокирует восемь мегабайт оперативной памяти и удерживает их без подкачки в течение часа под нагрузкой VDF.
5.3. Слой инициализации
Система инициализации Android — не systemd, но обладает достаточной выразительностью для класса сервисов «запустить на загрузке, перезапустить при падении, сторожевой таймер, ограничения ресурсов».
Содержимое сервисной записи — в файле system/init.montana-node.rc, не в этом документе. Спецификация фиксирует обязательные свойства этой записи:
— Класс сервиса montana_main стартует на этапе post-fs, после класса late_start. — Жёсткий лимит блокировки памяти: без ограничений. — Жёсткий лимит размера ядра при аварии: ноль, ноль (запрет аварийных дампов на уровне системы инициализации). — Ограничение влияния на убийцу при нехватке памяти: oom_score_adj равен минус восемьсот (защита от вытеснения системными приложениями). — Системные привилегии: только привязка к привилегированным портам и блокировка памяти. — Метка безопасности: u:r:montana_node:s0 (ограничение через политику обязательного контроля доступа, см. раздел 6.2). — Подкачка отключена глобально через swapoff -a в блоке on early-boot.
Сторожевой таймер реализован отдельным сервисом класса montana_main, скрипт system/montana-watchdog.sh. Скрипт периодически опрашивает локальный канал управления узла и при отсутствии ответа в течение шестидесяти секунд вызывает перезапуск сервиса узла через стандартный механизм управления Android.
Дополнительно, сторожевой таймер проверяет состояние процесса узла на предмет: — Установлен ли флаг «процесс не дампится» в состоянии процесса (см. раздел 6.2). — Активна ли блокировка памяти на рабочих сегментах процесса. При нарушении этих условий сторожевой таймер не пытается «починить» процесс, а останавливает узел и записывает блокирующий дефект в системный журнал. Узел не должен продолжать работу с нарушенной защитой ключей.
5.4. Слой узла Монтаны
Двоичный файл узла собирается отдельно, в составе основной системы сборки сети Монтана. Целевая платформа сборки: aarch64-linux-android.
Двоичный файл встраивается в системный образ MontanaOS на этапе сборки через директиву PRODUCT_COPY_FILES в файле сборки целевого устройства.
Контракты между двоичным файлом узла и MontanaOS (обязательные):
— Узел при старте устанавливает флаг «процесс не дампится» через системный вызов prctl(PR_SET_DUMPABLE, 0) до загрузки приватных ключей в память.
— Узел загружает приватные ключи из зашифрованного хранилища в блокированную память. Незашифрованная копия не покидает блокированный регион ни на одной фазе жизни ключа.
— Узел регистрирует обработчик сигнала аварии, который очищает все рабочие копии секретов перед завершением.
— Узел публикует локальный канал управления на адресе обратной связи на порту, согласованном с приложением статуса (текущий выбор: восемь тысяч четыреста сорок четыре).
Версия узла, входящая в данный релиз MontanaOS, фиксируется в файле фиксации версий (см. раздел 7.2).
5.5. Сетевой слой
Внешние входящие соединения на устройство, находящееся за совместным скрытым адресом мобильного оператора, невозможны без туннеля. MontanaOS устанавливает постоянный туннель на один из генезис-серверов сети Монтана.
При первой загрузке мастер первичной настройки предлагает пользователю выбор точки выхода туннеля. Список доступных генезис-серверов читается из основной спецификации Монтаны (см. Не-цель 3.2). MontanaOS не дублирует эти адреса в собственных артефактах.
Семантика смены сети, на которую туннель должен реагировать переподключением:
— Переход с одной беспроводной сети на другую беспроводную сеть. — Переход с беспроводной сети на мобильный интернет или обратно. — Полная потеря связи на длительный период (более тридцати секунд) с последующим восстановлением.
В каждом из этих случаев туннель переустанавливается с нуля, без сохранения предыдущей сессии. Конфигурация туннеля генерируется в момент первой загрузки локально на устройстве и подписывается ключом устройства. Конфигурация не передаётся в открытом виде по сети и не покидает устройство в процессе генерации.
Согласие пользователя на установку туннеля и публикацию устройства в сети — обязательный шаг мастера первичной настройки. Без явного согласия узел работает только локально, не присоединяясь к публичной сети.
5.6. Приложение статуса
Системное приложение носит имя «Статус Монтаны» и пакет com.montana.status. Оно отображает:
— Высоту цепочки и отставание от сети — Количество активных соседних узлов — Баланс кошелька устройства — Состояние ключей: блокировка памяти активна, флаг «процесс не дампится» установлен, секрет в памяти, не в подкачке — Состояние туннеля — Температуру процессора с пороговыми предупреждениями: жёлтый при семидесяти градусах Цельсия, красный при восьмидесяти — Состояние аккумулятора и статус зарядки
Приложение не требует разрешений сверх чтения системных журналов и доступа в интернет. Все данные оно получает через локальный канал управления узлом.
Сбор телеметрии в адрес разработчиков MontanaOS не осуществляется. Все индикаторы остаются на устройстве. Любая будущая телеметрия, если решение о её введении будет принято, должна быть оформлена через явный opt-in с отдельным экраном согласия.
6. Безопасность
6.1. Проверенная загрузка
MontanaOS использует тот же механизм проверенной загрузки, что и GrapheneOS, с заменой публичного ключа подписания на ключ релизов MontanaOS. Прошивка устройства без разблокировки загрузчика невозможна. Разблокировка без полной очистки пользовательских данных невозможна. Обратная блокировка с пользовательским ключом — стандартная процедура устройств Pixel.
Канонический публичный ключ релизов MontanaOS публикуется в одном месте — файл MONTANAOS_RELEASE_KEY.pub в корне репозитория. Любые упоминания в других документах — ссылки на этот файл, не копии.
6.2. Карты Безопасности
Каждая криптографическая примитива в коде узла имеет Карту Безопасности по требованиям основной спецификации Монтаны. MontanaOS не вводит новых криптографических примитив. MontanaOS обеспечивает корректное окружение исполнения для существующих.
Контракт окружения исполнения, обязательный к выполнению, документирован отдельно: SECURITY_CARD-montana-node-process.md. Спецификация ссылается на него, не дублирует.
Сводка обязательных условий: — Подкачка отключена глобально через swapoff -a в момент ранней инициализации. — Аварийные дампы ядра запрещены жёстким лимитом размера ядра ноль ноль на уровне системы инициализации. — Флаг «процесс не дампится» установлен самим узлом до загрузки ключей (контракт двоичного файла узла, см. раздел 5.4). — Политика обязательного контроля доступа запрещает присоединение отладчика к домену montana_node со всех остальных доменов, включая системные. — Ключи в памяти под блокировкой от подкачки в течение всей жизни процесса узла.
6.3. Аттестация устройства
При подключении к сети Монтана телефон может опубликовать аттестацию железа, подписанную защищённым элементом Titan M2. Это позволяет другим узлам в сети убедиться, что конкретный соседний узел действительно работает на MontanaOS на подлинном устройстве Google Pixel.
Публикация аттестации — opt-in, не по умолчанию. Решение принимает пользователь в мастере первичной настройки на отдельном экране. Без аттестации узел работает в анонимном режиме и принимается сетью с пониженным уровнем доверия (вес голоса при достижении согласия снижается, конкретный множитель определяется основной спецификацией Монтаны).
6.4. Защита ключей
Приватные ключи узла хранятся в двух местах:
— В оперативной памяти процесса узла, под блокировкой от подкачки. Это рабочая копия, существует только на время жизни процесса. — На диске, в зашифрованном хранилище по пути /data/montana/keys/. Это резервная копия, расшифровывается только при загрузке узла. Ключ шифрования диска привязан к защищённому элементу Titan M2.
Принимается, что в фазах с первой по шестую узел подписывает блоки в обычной памяти процесса под блокировкой от подкачки. Это допустимо по Картам Безопасности при условии выполнения всех контрактов окружения из раздела 6.2.
В фазе семь, после отдельной валидации, защищённый элемент Titan M2 может использоваться напрямую как аппаратный модуль безопасности для подписания блоков. Эта возможность не входит в первый релиз и не блокирует выпуск версии 1.0 MontanaOS.
7. Сборка и поставка
7.1. Окружение сборки
Спецификация требует точного совпадения окружения:
— Дистрибутив операционной системы сборщика: Debian 12 «bookworm». — Виртуальная машина Java версии двадцать один в варианте без графического интерфейса. — Компилятор: Clang версии, фиксированной в исходном дереве GrapheneOS на момент фиксации версий. AOSP с версии Pie использует только Clang. Никакого GCC. — Аппаратные требования: тридцать два гигабайта оперативной памяти, четыреста гигабайт свободного дискового пространства, восемь и более ядер процессора.
Конкретный список пакетов Debian 12 — в файле scripts/setup-build-env.sh. Спецификация не дублирует этот список.
7.2. Воспроизводимая сборка
Все компоненты сборки фиксируются по криптографическому хэшу:
— Дерево исходных кодов GrapheneOS — по хэшу коммита. — Двоичный файл узла Монтаны — по хэшу коммита в основном репозитории Монтаны. — Ядро Linux — по хэшу коммита в дереве GrapheneOS. — Патчи MontanaOS — по хэшу коммита в данном репозитории.
Все хэши сведены в файле MONTANAOS_VERSIONS.lock. Файл переходит из состояния «шаблон» в состояние «зафиксирован» в момент закрытия Фазы 1.
Источник детерминизма метки времени — переменная окружения SOURCE_DATE_EPOCH, рассчитанная как метка времени коммита фиксации версий MontanaOS (не локальная дата сборщика). Имя выходного файла включает короткий хэш коммита, не дату.
Кэш компиляции (ccache) выключен в режиме воспроизводимой сборки. Сборщик включает ccache только в режиме разработки, итоговый воспроизводимый билд проходит без кэша.
Двое разработчиков, собирающих образ из одного и того же коммита репозитория MontanaOS в чистое окружение Debian 12, обязаны получить побайтово идентичный итоговый образ, включая имя файла. Расхождение — регрессия и блокирующий дефект.
7.3. Подпись релизов
Цепочка подписи MontanaOS — двухуровневая.
Корневой ключ MontanaOS: — Генерируется один раз на изолированной машине без сетевого подключения, никогда не имевшей доступа в публичную сеть. — Хранится оффлайн на двух физически разнесённых носителях с шифрованием при транспортировке. — Используется только для подписи промежуточного релизного ключа. — Не участвует в подписи каждого релиза.
Промежуточный релизный ключ: — Генерируется на отдельной машине, подписывается корневым ключом, срок действия двенадцать месяцев. — Используется для подписи итоговых образов MontanaOS. — Ротация по истечении срока: новый промежуточный ключ генерируется и подписывается корневым.
После ротации промежуточного ключа старые подписанные релизы остаются валидными до истечения срока действия предыдущего промежуточного ключа. Обновления, выпущенные в течение года под старым ключом, продолжают проверяться устройствами без вмешательства до момента, когда корневой ключ их публично отзовёт.
Канонический публичный ключ — в файле MONTANAOS_RELEASE_KEY.pub. Он содержит публичный корневой ключ. Список действующих и отозванных промежуточных ключей — в файле MONTANAOS_RELEASE_KEY.history, подписанный корневым ключом.
7.4. Обновления по воздуху
Обновления распространяются через хаб Монтаны как подписанные пакеты в формате архива zip. Устройство проверяет:
— Подпись пакета действующим промежуточным ключом из MONTANAOS_RELEASE_KEY.history. — Цепочку подписи: промежуточный ключ подписан корневым ключом из MONTANAOS_RELEASE_KEY.pub. — Контрольную сумму содержимого. — Цепочку версий: новая версия должна быть строго выше текущей или соответствовать утверждённому откату.
После проверки обновление применяется через стандартный движок обновлений, унаследованный от GrapheneOS.
8. Открытые вопросы
8.1. Использовать ли патч ядра пользовательской блокировки памяти, или ограничиться подъёмом жёсткого лимита через систему инициализации? Решение откладывается до Фазы 2 после эмпирических измерений на тестовом устройстве. Премисса о значении жёсткого лимита по умолчанию (раздел 5.2) подтверждается измерениями в той же Фазе.
8.2. Допустимо ли подписывать блоки в обычной памяти процесса узла под блокировкой от подкачки в фазах с первой по шестую, или Карты Безопасности требуют немедленной интеграции защищённого элемента Titan M2? Текущий ответ — допустимо (раздел 6.4). Решение подтверждается отдельной валидацией Карт Безопасности до Фазы 6.
8.3. Сценарий повторной сборки после применения патчей MontanaOS. Жёсткое поведение repo sync --force-sync затирает локальные изменения. Нужен либо скрипт стэширования патчей перед синхронизацией, либо отдельная ветка в каждом репозитории дерева. Решение — Фаза 1.
8.4. Поведение цепочки подписи после ротации промежуточного релизного ключа. Текущий ответ — старые релизы валидны до истечения срока промежуточного ключа (раздел 7.3). Открытый вопрос: нужен ли механизм досрочного отзыва промежуточного ключа на устройстве, не получившем OTA в течение года?
8.5. Эксплуатационная процедура замены аккумулятора каждые двенадцать-восемнадцать месяцев (Не-цель 3.3). Что происходит со слотом узла в сети Монтана во время сервисной замены? Нужен ли механизм передачи слота на резервное устройство? Решение — основная спецификация Монтаны, не MontanaOS.
8.6. Как обрабатывать смену генезис-сервера после первой загрузки? Вариант — переустановка туннеля через приложение статуса с повторной аттестацией устройства. Допустимо ли это без полной перепрошивки? Решение — Фаза 4.
8.7. Какой минимальный объём постоянной памяти достаточен для работы узла без внешнего твердотельного накопителя? Зависит от темпов роста состояния цепочки в живой сети. Решение — Фаза 6, после первого внешнего тестового запуска сети.
8.8. Стоит ли оставлять в составе MontanaOS среду исполнения Android-приложений, или собирать минимальный образ только под узел и системное приложение статуса? Решение принимается ближе к Фазе 5 на основании размера образа и поверхности атаки, без сбора пользовательской телеметрии.
8.9. Поддерживается ли горячая замена туннеля на собственный сервер пользователя, минуя три генезис-сервера? Технически да, но требует проверки совместимости с моделью аттестации устройства.
8.10. Применяется ли в сборке cargo или только нативная сборка AOSP? Двоичный файл узла Монтаны написан на языке Rust и собирается через cargo, но интеграция в дерево AOSP требует обёртки. Решение — Фаза 3.
9. Лицензия
Каждый компонент сохраняет свою исходную лицензию. Совокупный труд MontanaOS публикуется под лицензией Apache версии 2.0. Ядро Linux — под лицензией GPL версии 2, как требует исходное дерево.
10. Авторство и сопровождение
Архитектор: автор Монтаны. Сопровождение: команда хаба Монтаны. Канал обратной связи: репозиторий MontanaOS на хабе Монтаны.
Дорожная карта: ROADMAP.md. Этот документ — единственный источник истины по фазам и срокам, спецификация лишь ссылается.
Конец спецификации версии 0.2.