Устройство платформы
Как устроена платформа: ядро и функции, слои, жизненный цикл экземпляра, каналы между частями, хранение и журналы, где что лежит в исходниках.
1 Общие сведения
1.1 Обозначение и наименование
Платформа личных программ O.G.A.S. (ОГАС).
1.2 Программное обеспечение, необходимое для функционирования
Платформа работает в Ubuntu внутри WSL2 на Windows 11; мастерская — программа Windows на системном WebView2 (см. раздел 4). Порядок установки и состав поставки — Сборка, установка и обслуживание.
1.3 Языки программирования
Ядро и функции платформы написаны на Rust, общий код модулей (OGAS SDK) — на Python и Rust, страница мастерской — на HTML, CSS и JavaScript без сборщика.
2 Функциональное назначение
Платформа даёт человеку собирать личные программы из узлов, модулей и пульта без программирования и пользоваться ими. Что она для этого делает, описывают С чего начать и Мастерская.
3 Описание логической структуры
3.1 Слои
3.1.1 Платформа состоит из четырёх слоёв; каждый слой работает в собственных OS-процессах (таблица 1).
Таблица 1 — Слои
| Слой | Где в репозитории | Чем владеет |
|---|---|---|
| Ядро | core/kernel; общий протокол — core/protocol |
замок экземпляра, сокет, пересылка сообщений, запуск OS-процессов, запись документов, журналы ядра |
| Функции платформы | functions/<id> |
каждая — своей задачей; перечень — таблица 2 |
| Модули | modules/<id> |
прикладная работа и её внешние действия |
| Мастерская | functions/ui/window |
окно в Windows: ввод, отрисовка, раскладка окон |
flowchart TD
user[Человек] --> window[Мастерская в Windows]
window -->|127.0.0.1, токен| ui[Функция ui]
ui --> core[Ядро]
graph[Функция graph] --> core
canvas[Функция canvas] --> core
settings[Функция settings] --> core
modules[Функция modules] --> core
observe[Функция observe] --> core
i18n[Функция i18n] --> core
sources[Функция sources] --> core
assistant[Функция assistant] --> core
programs[Программы человека] -->|127.0.0.1, ключ| gate[Функция gate]
gate --> core
core --> module[Модуль]
3.1.2 Функции не соединяются друг с другом напрямую: все сообщения проходят через ядро. Структура исходных текстов — приложение А.
3.2 Неизменные правила устройства
Правила устройства обязательны для всех частей платформы и показывают применение правил разработки (Правила разработки) к данной платформе:
1) ядро не содержит предметной логики, узлов графа, интерфейса, кода модулей и сведений о функциях платформы (правило 2.6);
2) функция является самостоятельным OS-процессом; функции не подключают код друг друга и взаимодействуют только через ядро (правило 2.3);
3) модуль не знает устройства платформы и других модулей: всё, что нужно для встраивания, он объявляет в манифесте (правило 2.3);
4) данные передаются через именованные порты и пакеты, а не через скрытое общее состояние (правило 2.7);
5) команда изменяет состояние, запрос только читает, событие сообщает о произошедшем (правило 2.7);
6) ошибка содержит код, категорию, место и признак допустимости повтора (правило 3.2);
7) отказ одного OS-процесса не останавливает остальные, если они могут работать без него (правило 2.3);
8) документы хранятся в файлах и записываются целиком или не записываются совсем; каждую папку данных записывает её хозяин (3.8.1, правила 2.4 и 3.5);
9) мастерская заменяется без изменения ядра (правило 2.3);
10) новая прямая зависимость Rust записывается с причиной в 3.9 (правило 2.8);
11) пределы ядра являются настройками core.*; протокольные величины (версия JSON-RPC, предел строки 4 МиБ) являются константами договора в core/protocol (правило 2.2).
3.3 Ядро
3.3.1 Ядро — один долгоживущий OS-процесс. Ядро:
- создаёт папку данных и замок экземпляра;
- читает реестр функций и описание каждой из них; сведений о функциях в коде ядра нет;
- запускает функции, следит за ними и перезапускает; модули запускает по просьбе функции с правом
process; - принимает соединения на Unix-сокете и пересылает команды, запросы и события;
- читает и записывает документы целиком (файловый сервис);
- ограничивает размер сообщений, очереди и время ожидания по настройкам
core.*; - выдаёт идентификаторы, пишет журнал запуска
boot.jsonlи журнал ядраcore.jsonl; - сообщает о падении, недоступности и восстановлении функций так, чтобы было видно, что сломалось и где.
3.3.2 Ядру известны протокол, идентификаторы, жизненный цикл, пределы, общая обёртка документа и объявленные права функций. Смысл модулей и данных ядру неизвестен. Интерпретатор Python ядро само не запускает и переменных Python не подставляет.
3.3.3 Порядок выбора получателя сообщения:
метод начинается с "core." → обрабатывает ядро
иначе → хозяин метода из handshake (commands, queries)
хозяина нет → method_not_found
хозяин недоступен → function_unavailable
иначе → переслать запрос, вернуть ответ отправителю
3.3.4 Функция не может объявить метод с префиксом core.. Любой другой метод принадлежит ровно одной функции.
3.4 Функции платформы
Таблица 2 — Функции платформы
| Функция | Чем владеет | Подробно |
|---|---|---|
graph |
граф, проверка, включённые программы, события, переменные, выполнение прогонов, контрольные точки, сквозные объекты, прошлые прогоны | Программа: граф и запуски |
modules |
манифесты, окружения Python, запуск и выгрузка модулей, распределение видеопамяти, разбор сообщений модулей | Модули |
canvas |
документ пульта workspace.json |
Мастерская, раздел 6 |
settings |
схемы, уровни и значения настроек | Настройки |
i18n |
словари и язык интерфейса | Настройки, раздел 7 |
observe |
журнал событий и список проблем | 3.8.2 |
sources |
источники — витрина O.G.A.S., Hugging Face, Civitai: поиск, файлы, загрузки и папка downloads/ |
Мастерская, 5.6; Защита, 3.14 |
profile |
профиль человека на ogas.space: ключи, посылки и их отправка, свои отправки, письма, что сайт говорит о людях | Защита, раздел 8; Методы служб платформы, раздел 7 |
gate |
вход для программ человека: ключ, порт на 127.0.0.1, пересылка их запросов |
Методы служб платформы, раздел 5 |
assistant |
помощник: задачи, разговор с сетью, инструменты платформы, права черновика и расширенного доступа, пределы | Методы служб платформы, раздел 6 |
ui |
связь с мастерской: порт, токен сессии, запуск окна | Мастерская, раздел 2 |
При знакомстве каждая функция объявляет свои команды, запросы, события и папки словарей, а затем регистрирует схему своих настроек у функции настроек — и снова всякий раз, когда та поднимается заново (Настройки, раздел 3). Ядро пересылает сообщения по объявленному списку, не зная их смысла. Добавление или замена функции состоит в том, чтобы положить её папку с описанием и исполняемый файл и изменить запись в реестре; ядро при этом не пересобирается.
3.4.1 Реестр функций и описание функции
Реестр data/functions.json содержит сведения, которые определяет экземпляр: id, путь к исполняемому файлу, enabled, capabilities. Описание functions/<id>/function.json в папке функции содержит сведения о самой функции: requires, optional, platform.min / max, contract, restart, interactive. Форматы обоих — Форматы файлов, раздел 3. Ядро соединяет их по id при запуске экземпляра; функцию, чьё описание не читается, оно не запускает (function_description_invalid), остальные работают.
requires— функции, от которых зависит данная: все функции, договором которых она пользуется (Правила разработки, 2.3.3). Функция запускается только после того, как все её зависимости, кроме необязательных, прошли handshake. Замкнутый круг зависимостей — ошибка настройки реестра.optional— те изrequires, без которых функция работает: она пользуется их договором, пока они доступны, а без них продолжает работу и сообщает, чего не хватает (правило 2.3.4). Что необязательной зависимости нет, функция узнаёт по отказу на вызов её метода:method_not_found— метода не объявил никто,function_unavailable— его хозяин не работает; в общем коде этоrequest_optional. Необязательная зависимость не задаёт порядок запуска и не входит в замкнутый круг; её отказ зависимую функцию не останавливает. Имя вoptional, которого нет вrequires, делает описание недействительным.- Если функция исчерпала перезапуски, ядро не останавливается; функции, для которых она не названа в
optional, останавливаются и не перезапускаются. interactive: true— функции нужна мастерская. Такие функции запускают глаголыstartиui; глаголы, описанные в реестре, их не запускают.restart: false— после завершения функция не перезапускается.capabilities— права, которые функция может запросить при знакомстве. Правоprocessвыдано только функции модулей.- Путь к исполняемому файлу считается от папки установки. Если файла там нет, ядро ищет файл с тем же именем рядом с собой. Этот поиск предназначен для разработки и не заменяет правильного пути в поставке.
- Глаголы командной строки, кроме глаголов ядра, их вызовы и аргументы записаны в реестре (поле
cli), а не в коде ядра (раздел 5).
Повреждённый или отсутствующий реестр не останавливает ядро: ядро работает без функций и отказывает командам, которым нужен реестр.
3.4.2 Знакомство и проверка работоспособности
Первое сообщение функции — function.handshake: id и версия, поддерживаемые версии договора (обязательна contracts.core), команды, запросы, события, права, папки словарей и имена поверхностей — последние нужны только для диагностики (Мастерская, раздел 8). Если первое сообщение другое, соединение закрывается. Одноразовый токен запуска после знакомства удаляется и повторно не принимается. Сообщение целиком — Обмен с ядром, раздел 4.
После знакомства ядро с интервалом core.health_interval_ms запрашивает у функции function.health. При отсутствии ответа в течение core.health_timeout_ms функция становится degraded: ядро её останавливает, и дальше она идёт тем же путём, что упавшая, — расходует одну попытку перезапуска (3.6.4). Обратным запросом core.health функция проверяет работоспособность самого ядра.
3.5 Распределение владения
- ядро — жизненный цикл OS-процессов, сокет, пути, запись документов целиком;
- функция графа — граф, включённые программы и их переменные, события, план выполнения, состояние сквозных объектов, контрольные точки, прошлые прогоны и их уборка;
- функция canvas — документ пульта;
- функция профиля — ключ компьютера и ключ писем профиля, свои отправки и письма; главного ключа она не хранит, а файл ключа пишет только туда, куда сказал человек;
- функция модулей — манифесты, окружения, смысл
process_handle, какие модули подняты и какие выгрузить, чтобы хватило видеопамяти, рабочие папки и файлы результатов прогонов; - функция настроек — значения всех уровней и схемы;
- функция observe — журнал событий и список проблем;
- мастерская — окно, ввод, фокус, раскладка окон и копия последнего полученного состояния;
- модуль — своя прикладная работа и её внешние действия.
Документы JSON функции записывают только через файловый сервис ядра и только свои. Папки с файлами (окружение Python, рабочую папку прогона, копии модулей для экспорта, журналы, память узла, файлы результатов) хозяин записывает сам и только в своей папке (3.8.1).
3.6 Жизненный цикл экземпляра
3.6.1 Запуск
- Ядро создаёт в папке данных папки
run,logsиconfig. Остальные папки создают их хозяева, когда они понадобятся. - Читает пределы
core.*изconfig/core.json, затем изconfig/system.json. Еслиcore.jsonнет, ядро записывает его пустым: умолчания остаются в схеме. Если файл не разбирается, старт отказывается с именем файла и номером строки. - Записывает начало запуска в
logs/boot.jsonl. - Берёт замок
ogas.lock: записывает в негоpid, время старта и случайную метку и держит на файлеflock. Замок занят — см. 3.6.3, «Повторный запуск». - Создаёт Unix-сокет с правами
0600. Путь —run/ogas.sock, если папка лежит на файловой системе Linux (не 9p, DrvFs, NFS, CIFS) и путь помещается в 107 байт. Иначе —XDG_RUNTIME_DIRили/tmpс теми же условиями. Ни один вариант не подходит — старт отказывается. Настоящий путь функции получают вOGAS_SOCKET. - Проверяет зависимости функций.
- Запускает функции в порядке зависимостей с окружением из 3.7.4.
- Ждёт handshake каждой функции с ограничением по времени. Зависимая функция не стартует раньше своих зависимостей.
- Сообщает о готовности — полной или неполной, если часть функций недоступна.
Ошибка функции не останавливает ядро. Если функция A зависит от функции B, а B не запустилась, не прошла знакомство или исчерпала перезапуски, A не запускается (или останавливается) и помечается недоступной. Необязательная зависимость (3.4.1) этого не вызывает.
3.6.2 Остановка
Ядро перестаёт принимать новые запуски, публикует system.stopping, ждёт завершения порождённых функциями OS-процессов в пределах core.process_stop_grace_ms и принудительно завершает оставшиеся. Затем ядро закрывает каналы функций: функция дочитывает то, что ей пришло, — system.stopping в том числе — и завершается сама, поэтому то, что она делает на остановку, успевает сделаться: граф останавливает прогоны, функция ui передаёт событие окну, и окно закрывается. Функцию, не завершившуюся до конца core.shutdown_grace_ms, ядро завершает принудительно. Замок снимается при выходе.
При выходе из мастерской функция ui отправляет core.shutdown, и экземпляр останавливается; закрытие окна только прячет его к значку у часов (Мастерская, 2.1). Отказ функции ui или окна экземпляр не останавливает; restart: false только запрещает перезапуск.
3.6.3 Восстановление
Отказавшая функция получает ограниченное число перезапусков подряд — core.function_restart_max — с растущей паузой: первая core.function_restart_base_ms, каждая следующая вдвое длиннее. Успешный handshake обнуляет счётчик. После исчерпания попыток функция отключается до следующего запуска ядра. Незавершённая команда к ней получает function_lost; автоматически повторяются только команды, которые безопасно выполнить повторно.
Замок — это flock(LOCK_EX) на открытом ogas.lock. OS-процесс, завершившийся без снятия замка, освобождает его при закрытии файла системой. JSON внутри файла содержит сведения для диагностики и замком не является. Два одновременных запуска замок не получат.
После завершения функции ядро удаляет запись о её OS-процессе, чтобы завершившаяся функция не отображалась работающей.
Повторный запуск. После отказа мастерской экземпляр продолжает работать без окна, и человек запускает ярлык повторно. Повторный запуск не завершается отказом на замке, а активирует работающий экземпляр:
- Глагол
startилиuiне получил замок. Он берётpidизogas.lock, проверяет по/proc/<pid>/exe, что это та же программа, и посылает ей сигналSIGUSR1. Сам глагол выводит «уже запущено» и завершается с кодом 0. - Ядро рассылает всем функциям событие
system.activateи заново запускает каждую функцию сinteractive: true, которая не работает. Функции ядро выбирает по признакуinteractiveиз их описаний. - Функция окна, получив
system.activate, открывает окно, если его нет: окно не подключено и его OS-процесс не работает. Работающее или запускающееся окно повторно не открывается; событие доходит до страницы окна, и окно, спрятанное у часов, показывается. - Глагол реестра, которому нужна мастерская (5.3), при занятом замке не завершается отказом, как остальные глаголы реестра, а оставляет работающему экземпляру записку — глагол и значения его аргументов (
run/launch.json, Форматы файлов, раздел 3) — и посылает тот же сигнал. Разбуженный экземпляр записку забирает и выполняет вызовы глагола сам, если его реестр описывает этот глагол как требующий мастерской и принимает его аргументы. Отказ записывается в журнал ядра, экземпляр продолжает работать. Записка читается один раз; нечитаемая, другого формата или пролежавшая дольше полуминуты просьбой не считается, а экземпляр, который запускается, записку прежнего не берёт.
Функция окна отказавшее окно не перезапускает: окно, отказывающее при старте, перезапускалось бы непрерывно. Окно открывается снова по запросу человека. Глаголы, описанные в реестре, на занятом замке завершаются отказом: они выполнили бы работу, о которой работающий экземпляр не знает.
3.6.4 Состояния функции
stateDiagram-v2
[*] --> Discovered
Discovered --> Starting
Starting --> Handshake
Starting --> Failed
Handshake --> Ready
Handshake --> Failed
Ready --> Degraded
Ready --> Restarting
Ready --> Stopping
Degraded --> Restarting
Degraded --> Stopping
Restarting --> Starting
Restarting --> Failed
Failed --> [*]
Stopping --> [*]
Discovered — найдена в реестре и ждёт своей очереди: функция запускается после тех, без которых не работает; Starting — запускается OS-процесс; Handshake — процесс запущен, ожидается первое сообщение; Ready — работает; Degraded — не ответила на проверку работоспособности, ядро её останавливает; Restarting — вышла сама или остановлена за молчание и ждёт паузу перед перезапуском; Failed — не работает, и ядро её больше не запускает: не запустилась, не познакомилась вовремя, исчерпала перезапуски, осталась без обязательной зависимости или вышла без права на перезапуск; Stopping — экземпляр останавливается. В core.status состояния называются теми же словами строчными буквами (discovered, starting, handshake, ready, degraded, restarting, failed, stopping), их тексты — core.state.<состояние> в словаре ядра.
3.6.5 Состояния модуля
stateDiagram-v2
[*] --> Missing
Missing --> Invalid
Missing --> NeedsEnvironment
NeedsEnvironment --> Stopped
Stopped --> Starting
Starting --> Ready
Starting --> Crashed
Ready --> Executing
Executing --> Ready
Executing --> Crashed
Crashed --> Restarting
Restarting --> Starting
Ready --> Stopping
Stopping --> Stopped
Missing — модуля нет, Invalid — манифест с ошибками, NeedsEnvironment — нужно окружение, Stopped — готов к запуску, Starting — запущен и ждёт init, Ready — готов к работе, Executing — выполняет шаг, Crashed — завершился с отказом, Restarting — запускается снова, Stopping — останавливается.
3.7 Каналы
3.7.1 Функция — ядро
Unix-сокет, одно соединение на функцию. Чтение и запись идут в разных потоках, сообщения — JSON-RPC 2.0 по одному на строку, очереди ограничены, права сокета 0600. Что это действительно та функция, которую запустило ядро, подтверждают одноразовый токен запуска и SO_PEERCRED: uid экземпляра и pid порождённого OS-процесса. Правила строки и обёртки сообщения — Обмен с ядром, разделы 2, 3.
Срок ответа ставит только тот, кто спрашивает. Ядро, функция ui и программа окна — посредники: вызов, который они передают хозяину метода, они ждут столько, сколько нужно хозяину, и своего срока не ставят. Иначе посредник прерывал бы работу, которую хозяин выполняет в пределах своего срока, например шаг модуля, которому манифест разрешил десять минут. Вызов без ответа не ожидает бесконечно: при отключении или отказе хозяина метода все его вызовы без ответа сразу получают function_lost; при отключении спросившего ответ ему не передаётся. Сколько вызовов одна функция может держать в ожидании одновременно, ограничивает core.rpc_queue; сверх этого — отказ backpressure.
3.7.2 Канал модуля через ядро
graph → ядро → modules : module.execute
modules → ядро : core.process.spawn, core.process.stdin.write
ядро → stdin модуля : сообщения JSON-RPC модуля строками
stdout / stderr модуля → ядро → событие process.io.chunk → modules
sequenceDiagram
participant G as graph
participant C as ядро
participant M as modules
participant P as модуль
G->>C: module.execute
C->>M: module.execute
M->>C: core.process.spawn
C->>P: OS-процесс
M->>C: core.process.stdin.write
C->>P: строка JSON в stdin
P-->>C: байты stdout
C-->>M: событие process.io.chunk
M-->>C: результат
C-->>G: ответ execute
Ядро проверяет только свою обёртку и не разбирает JSON-RPC внутри stdout модуля: строки собирает и разбирает функция модулей. stdout модуля отдан протоколу, диагностика идёт в stderr. Когда закрыты оба потока, ядро отправляет process.io.closed с кодом завершения. События process.io.* получает только функция, которая запустила этот OS-процесс.
3.7.3 Мастерская — функция ui
TCP на 127.0.0.1, одно окно за раз, тот же JSON-RPC 2.0 по строкам с пределом строки 4 МиБ. Функция ui запускает программу окна и передаёт ей порт и токен сессии. Первое сообщение окна — ui.window.hello с токеном. Запросы окна функция ui передаёт ядру через своё соединение; из методов ядра окну доступны только core.status, core.health, core.event.subscribe и core.shutdown. События ядра приходят окну уведомлением event. Угрозы и защита — Защита, 3.10.
sequenceDiagram
participant W as Мастерская (Windows)
participant U as ui (WSL)
participant C as Ядро
U->>W: запуск: --port, --token
W->>U: ui.window.hello {token}
U-->>W: {status: ready}
W->>U: process.run.start
U->>C: process.run.start
C-->>U: ответ
U-->>W: ответ
C->>U: событие run.changed
U-->>W: событие run.changed
Если окно пропустило события или переподключилось, оно заново запрашивает всё состояние.
3.7.4 Окружение запуска функции
Ядро запускает функцию с пустым окружением и передаёт только:
Таблица 3 — Окружение запуска функции
| Переменная | Назначение |
|---|---|
OGAS_SOCKET |
полный путь к сокету ядра |
OGAS_LAUNCH_TOKEN |
одноразовый токен для handshake |
OGAS_FUNCTION_ID |
id из реестра |
OGAS_INSTALL_DIR |
папка установки (в ней ogas, bin/, modules/) |
OGAS_DATA_DIR |
папка данных |
PATH |
PATH родителя или /usr/local/bin:/usr/bin:/bin |
HOME |
домашняя папка, если она есть у родителя |
LANG, LC_ALL |
как у родителя; если не заданы — C.UTF-8 |
http_proxy, https_proxy, no_proxy, all_proxy и они же заглавными |
как у родителя, если заданы: через них функция ходит в сеть по команде человека, например при установке окружения процесса |
Функция ui получает то же окружение; переменные экрана Linux ей не нужны. Поток stdin функции — /dev/null; stdout наследуется от ядра; stderr ядро перехватывает и записывает в свой журнал (3.8.2); протокол передаётся только через сокет.
3.7.5 Окружение запуска модуля
Ядро запускает модуль с пустым окружением и добавляет PATH, LANG / LC_ALL и переменные из поля env запроса core.process.spawn. Функция модулей сама передаёт PYTHONUNBUFFERED=1, PYTHONPATH, HOME, переменные прокси, если они заданы (3.7.4), OGAS_MODULE_ID, OGAS_MODULE_VERSION, OGAS_MODULE_DIR и, кроме службы, OGAS_RUN_DIR; пути к нейросетям, которые выбрали параметры узла, приходят не в окружении, а с шагом (Модули, раздел 11). Папка памяти узла приходит не в окружении, а с каждым шагом: модуль один на все свои узлы, а память у каждого узла своя (Модули, раздел 4). Модуль не получает OGAS_LAUNCH_TOKEN и OGAS_SOCKET и к шине не подключается. Программы, которые части просят запустить, ядро запускает из одного своего потока, живущего столько же, сколько ядро, и каждая получает SIGKILL, когда ядро завершается, в том числе аварийно: модуль посреди долгого шага не держит видеокарту после ядра.
3.8 Хранение и наблюдение
3.8.1 Папка данных
Всё, что относится к рабочему процессу, лежит в одной его папке processes/<id>/: документы, прогоны и их результаты, файлы, данные ему человеком, память его узлов и окружение Python. Удалённая папка уносит процесс целиком, и ничего от него не остаётся в других местах. Прогон называется местными датой и временем начала — 2026-10-08_07-29-12, при совпадении в ту же секунду -2, -3, — и человек находит его результаты в results/ по дате. Папка процесса называется по его идентификатору; переименование процесса папку не меняет: на неё ссылаются вызовы и пути.
Таблица 4 — Папка данных
| Путь | Что там | Хозяин | Как пишется |
|---|---|---|---|
functions.json |
реестр функций | человек, поставка | вручную |
ogas.lock |
замок экземпляра | ядро | само |
config/core.json |
пределы ядра | ядро при первом старте, потом функция настроек | через файловый сервис |
config/system.json, config/user.json |
настройки уровней system и user; system.json мастерская не пишет |
функция настроек | через файловый сервис |
config/layout.json |
раскладка окон мастерской между запусками | функция ui |
через файловый сервис |
config/library.json |
библиотека мастерской: панели программ и что скрыто от колонки графа | функция ui |
через файловый сервис |
library/pieces/<piece_id>.json |
заготовки интерфейса в библиотеке | функция canvas | через файловый сервис |
processes/<id>/process.json, generation.json |
документ процесса и его поколение | функция графа | через файловый сервис |
processes/<id>/workspace.json |
пульт | функция canvas | через файловый сервис |
processes/<id>/settings.json |
настройки процесса | функция настроек | через файловый сервис |
processes/<id>/state.json |
значения переменных «помнить» | функция графа | через файловый сервис |
processes/<id>/state/<имя>/, <имя>.saved.json |
память узла — своя или общая нескольких узлов — и отметка, что она сохранена целиком | модуль узла; отметку — функция модулей | сам; отметку — через файловый сервис |
processes/<id>/modules.lock.json |
версии и отпечатки модулей | функция модулей | через файловый сервис |
processes/<id>/.venv, requirements.txt, requirements.freeze.txt |
окружение Python процесса: ссылки на общий кэш пакетов | функция модулей | сама, программой uv |
processes/<id>/inputs/ |
файлы, которые человек дал процессу | человек | сам |
processes/<id>/runs/<run_id>/checkpoint.json, keep.json |
контрольная точка и отметка «Хранить» | функция графа | через файловый сервис |
processes/<id>/runs/<run_id>/work/ |
рабочая папка модуля | функция модулей | сама |
processes/<id>/runs/<run_id>/events.jsonl |
журнал прогона | функция observe | сама |
processes/<id>/results/<run_id>/ |
файлы результатов прогона под их именами | функция модулей | сама |
processes/<id>/runs/<run_id>/ и results/<run_id>/ целиком |
прогон | функция графа — только она удаляет прогон | через файловый сервис |
module-tests/<module_id>/ |
рабочая папка и результаты тестового вызова модуля вне процесса | функция модулей | сама |
cache/uv/ |
общий кэш пакетов Python, на который ссылаются окружения процессов | функция модулей | сама, программой uv |
exports/<id>/package.json, documents/ |
экспорт процесса | функция графа | через файловый сервис |
exports/<id>/modules/ |
копии модулей для экспорта | функция модулей | сама |
exports/<id>/ целиком |
экспорт процесса | функция графа — только она заменяет экспорт | через файловый сервис |
problems.json, logs/events-<ГГГГ-ММ>.jsonl |
проблемы и общий журнал по месяцам | функция observe | сама |
logs/boot.jsonl, logs/core.jsonl |
журналы ядра | ядро | само |
run/ |
сокет ядра | ядро | само |
Базы данных нет. Файловый сервис ядра записывает документ целиком: временный файл, fsync, прежний файл переименовывается в .bak, временный встаёт на его место, fsync папки. У набора файлов процесса есть номер поколения; после сбоя принимается только полное поколение. Форматы — Форматы файлов.
3.8.2 Распределение наблюдения
- Ядро наблюдает за собой и за функциями: здоровье, перезапуски, ошибки handshake, очереди, запись документов. Журнал
logs/core.jsonl, снимок состоянияcore.status. Вывод функции вstderrядро записывает в этот журнал построчно с именем функции (function.stderr) и повторяет в свойstderr: у функции, запущенной с ярлыка, нет терминала, и без этого её последнее сообщение (отказ при старте, сбой, который не удалось опубликовать) было бы потеряно. Служебный поток ядра, который не запустился, также оставляет запись (core.thread_failed). Запись с кодом причины оставляет и каждый отказ, после которого от функции может ничего не остаться: соединение, не прошедшее знакомство (function.handshake_failed), отказ проверки собеседника и соединение сверхcore.connections_max(connection.refused; сверх предела — одна запись, пока он держится, чтобы стучащая программа не заполнила журнал), функция, которую ядро больше не запускает или не перезапускает. - Каждая функция сообщает об отказе своей фоновой работы событием
function.work_failed(Обмен с ядром, раздел 7). - Функция модулей наблюдает за модулями: запуск, потоки вывода, окружение, манифест.
- Функция observe собирает общий поток событий и список проблем для мастерской.
Журналы пишутся в формате JSON Lines:
boot.jsonlиcore.jsonl— ядро, в том числе до запуска функции наблюдения;logs/events-<ГГГГ-ММ>.jsonl— общий журнал функции observe, по месяцам, по времени записи;logs/events.jsonl, если он есть, читается как самая старая часть;runs/<run_id>/events.jsonl— журнал запуска, еслиrun_idгодится как имя папки.
События, которые ядро разослало до подключения функции наблюдения, — например, что функция не запущена без своей зависимости, — она берёт при запуске из памяти последних событий ядра (core.event.subscribe), начиная после последнего события этого экземпляра в своём журнале: ни один отказ не остаётся без записи и проблемы, и ни одно событие не пишется дважды.
Журналы не укорачиваются: записанное остаётся. Журнал запуска удаляется только вместе с запуском, по правилу graph.runs_keep (Программа: граф и запуски, раздел 12). Старые месяцы общего журнала удаляются только командой человека — observe.logs.remove, «Удалить журнал до месяца» в окне журнала платформы — или настройкой observe.logs_keep_months, по умолчанию — хранить всё (Настройки, раздел 8); нынешний месяц не удаляется никогда. Удаление записывается в журнал событием logs.removed; поиск читает журнал с самого нового месяца назад, пока не наберёт нужное.
Одна запись журнала уходит на диск одним вызовом записи, поэтому записи разных потоков не перемешиваются. Если прошлый запуск оборвали посреди записи и файл не заканчивается переводом строки, следующая запись сначала дописывает перевод строки: оборванная строка остаётся одной испорченной строкой и не портит следующую. Читатели пропускают строку, которую не удалось разобрать.
Запись журнала ядра содержит time, source (core или id функции), event, error_code при ошибке и короткое message. По записи определяются место и причина отказа без чтения исходных текстов.
Перезапуск функции отражается событием function.restarted с номером попытки и паузой; после исчерпания попыток — событием function.failed с кодом function_restart_exhausted.
Функция графа публикует node.changed для модульных узлов: queued, когда шаг ждёт своей очереди к модулю, running, когда модуль его взял, и completed, failed или cancelled (шаг оборван остановкой) после. Узлы платформы (условие, слияние, цикл) таких событий не публикуют, так как не вызывают модуль и срабатывают сразу. Исключение — узлы, на которых ветка стоит: вопрос, ожидание сигнала, пауза. О них функция графа публикует waiting, когда ветка встала, и completed или cancelled, когда она пошла дальше или ожидание снято, — после того как это записано в контрольную точку: иначе никто не узнал бы, что прогон стоит именно здесь.
У событий номер event_id, который только растёт, но не обязательно на единицу: номер получает и событие, посланное одной функции (process.io.*), а в память ядра и к остальным оно не попадает. Подписчик передаёт последний полученный номер, каким бы ни был шаг до него; если такого номера уже нет в памяти ядра (её размер — core.event_ring_size), ответ говорит resync_required, и подписчик заново читает всё состояние. Совместимость формата журналов с OpenTelemetry не обеспечивается.
3.9 Потоки и зависимости
Ядро использует стандартные потоки Rust, без Tokio: поток приёма соединений, поток чтения на каждое соединение, отдельный поток на обработку каждого запроса функции (одновременно не больше core.rpc_queue на соединение, сверх — отказ backpressure), поток наблюдения за каждым порождённым OS-процессом и потоки чтения stdout и stderr модулей. Записи в одно соединение идут под замком. Для экземпляра на одного человека этого достаточно.
Замок удерживается только на время обращения к защищаемым данным. Не допускается брать второй замок, не отпустив первый. Единственное отступление — шлюз видеопамяти функции модулей: под ним освобождается и занимается место на видеокарте с захватом других замков, а сам шлюз никогда не берётся под другим замком, поэтому взаимная блокировка исключена.
Прямые зависимости Rust и их назначение:
- ядро и функции:
serde,serde_json— чтение и запись JSON; - ядро дополнительно:
libc—SO_PEERCRED, pidfd,flock, сигналы и тип файловой системы сокета; - функция графа дополнительно:
libc— смещение местного времени (localtime_r) для событий по часам; - функция
ui: толькоserde_jsonиogas-protocol; - функция профиля дополнительно:
ed25519-dalek— подпись посылок;crypto_box— письма, запечатанные для профиля, как их запечатывает сторона сайта (libsodium);sha2— задачка на счёт: миллионы SHA-256 за секунды;argon2,chacha20poly1305— пароль на файл ключа;getrandom— случайность системы для ключей; своя криптография была бы недопустимым риском; - общий код протокола под Unix и функция источников:
ed25519-dalek— проверка подписи сайта ogas.space; своя реализация подписи была бы недопустимым риском; - мастерская (отдельный набор пакетов Cargo под Windows, цель
x86_64-pc-windows-msvc):tauri2,tauri-build,serde_json; страница окна — без внешних JS-библиотек.
Пакеты Cargo в основном наборе: ogas-protocol, ogas-core, ogas-graph, ogas-modules, ogas-settings, ogas-observe, ogas-i18n, ogas-canvas, ogas-sources, ogas-profile, ogas-ui, ogas-gate, ogas-assistant, ogas-module — SDK для модулей на Rust. Отдельный набор — ogas-window в functions/ui/window. Точные версии всех зависимостей фиксирует Cargo.lock.
4 Используемые технические средства
- Ядро, функции, модули и данные работают в Ubuntu внутри WSL2.
- Мастерская — программа Windows 11
ogas-window.exeна Tauri 2 и системном WebView2. Её запускает функцияuiчерез механизм WSL, который позволяет запускать программы Windows из Linux (интероп). - При отсутствии такого механизма (например, в обычном Linux) платформа работает без мастерской, через командную строку.
- Сокет ядра лежит только на файловой системе Linux, не на дисках Windows.
- Диски Windows видны в WSL как
/mnt/<диск>, если/etc/wsl.confне задаёт другой корень; функцияuiузнаёт корень уwslpath. Модулю они доступны, но работают заметно медленнее диска Linux.
5 Вызов и загрузка
5.1 Одна программа ogas-core содержит ядро и его глаголы командной строки:
ogas-core start [--data-dir PATH]
ogas-core ui [--data-dir PATH]
ogas-core doctor [--data-dir PATH]
ogas-core <глагол из реестра> [--<аргумент> ЗНАЧЕНИЕ …] [--data-dir PATH]
5.2 Глаголы ядра:
startзапускает функции по реестру, включая функции с признакомinteractive, если их запись включена. Обычный запуск с мастерской —./ogasилиogas-core start;uiзапускает функции с признакомinteractive, даже если их запись выключена;doctorвыводит собственные признаки ядра — что значит каждый из них, сказано в Сборка, установка и обслуживание, 4.1; при отсутствии реестра или подходящего пути сокета завершается с кодом 1. Затем он выполняет проверки среды, которые функции реестра объявили в своих описаниях (Форматы файлов, раздел 3), не зная их смысла, и по каждой выводит строку<функция>.<проверка>=passedили=failedс текстом из словаря функции — на языке системы (LC_ALL,LC_MESSAGES,LANG), а если такого словаря у функции нет, на английском. Итоги проверок функций кода выхода не меняют. Файлыdoctorтолько читает; запускает он только программы, которые называют проверки функций, и ждёт каждую не дольше внутреннего срока ядра. Интерпретатор Pythondoctorне ищет: выбор интерпретатора принадлежит функции модулей (настройкаmodules.python_candidates). Какие проверки объявляет поставка — там же, в Сборка, установка и обслуживание, 4.1.backupделает резервную копию того, чего платформа не сделает заново, и убирает копии старше хранимого числа (Сборка, установка и обслуживание, 5.6); работает и при остановленной платформе;stopпросит работающий экземпляр остановиться, как при выходе из окна, и ждёт, пока он не закончит; не работает — так и говорит;
5.3 Остальные глаголы описываются в реестре функций (поле cli, Форматы файлов, раздел 3): имя глагола, предварительные вызовы, вызываемый метод, ожидание итога и аргументы с признаками обязательности и умолчаниями. Ядро разбирает аргументы по этому описанию и подставляет их значения в вызов, не зная их смысла. Без обязательного аргумента глагол завершается с кодом 1, не запуская экземпляр. Так же, не запуская экземпляр, отклоняются глагол, которого нет ни у ядра, ни в реестре, флаг, которого нет среди аргументов глагола, флаг без значения, повторённый флаг и второе слово вместо флага; глаголы ядра не принимают флагов, кроме --data-dir (Правила разработки, 3.2.2). При необъявленном методе или ошибке вызова выводится сообщение и возвращается код 1. Флаг --help выводит глаголы ядра и глаголы реестра папки данных с их аргументами. Собственного срока глаголы не устанавливают: они ожидают итога, сколько бы ни длились установка или прогон; ожидание прерывается клавишами Ctrl+C.
Глагол реестра запускает экземпляр без мастерской и после своего вызова останавливает его. Глагол с признаком interactive — «нужна мастерская» — запускает экземпляр вместе с мастерской, как start, и после своих вызовов экземпляр не останавливает; отказ его вызова при запуске завершает запуск с кодом 1. Если экземпляр уже работает, вызовы такого глагола выполняет он (3.6.3). Что именно открывает мастерская по такому глаголу, ядру неизвестно: оно знает только описание вызова.
5.4 Реестр поставки описывает глаголы прогона процесса без мастерской, установки окружения процесса и запуска программы с её лицом (Сборка, установка и обслуживание, 5.2).
5.5 Сведений о функциях ядро не содержит (Правила разработки, 2.6). ./ogas — обёртка над ogas-core (С чего начать, 2.2).
6 Входные и выходные данные
Форматы документов, которые платформа читает и пишет, — Форматы файлов. Сообщения между частями платформы — Обмен с ядром.
Приложение А (справочное). Структура исходных текстов
Приложение указывает, куда помещать новые файлы. Структура исходных текстов подчинена правилам 2.1 и 2.2 (Правила разработки):
- у каждой части есть главный файл — точка входа; он запускает часть и передаёт работу файлам задач, а сам её не делает;
- одна задача — один файл; задача с подзадачами — папка со своим главным файлом;
- файл делят, когда в нём появляется вторая задача, а не потому, что он длинный;
- настройки части описаны в её файле схемы
settings.schema.json, тексты для человека — в еёlocales/ru.jsonиlocales/en.json, коды ошибок — в еёcodes.rs, остальные константы договора и внутренние величины — в её файле константconstants.rs. Код берёт значения оттуда и своих копий не держит.
А.1 Дерево репозитория
репозиторий
README.md лицо репозитория: что это и как запустить
LICENSE, NOTICE лицензия платформы; что под какой лицензией, имя и знак
CONTRIBUTING.md на каких условиях принимают правку со стороны
docs/ документация; частью платформы не является
README.md карта документации: какие документы есть и кому они
guide/ тому, кто собирает программы: с чего начать, мастерская
reference/ справочник: виды узлов, форматы, настройки, коды ошибок, термины
sdk/ OGAS SDK: руководство автору модулей и сценариев
platform/ устройство, программа, модули, защита, сборка, правила
contract/ договоры: обмен с ядром и методы функций
en/ английский перевод с той же раскладкой и своей картой
core/protocol общий код обмена: JSON-RPC 2.0, JSON Lines, ошибки, идентификаторы
core/kernel ядро: цикл, пересылка сообщений, замок экземпляра
core/data данные экземпляра разработки: реестр и настройки поставки; процессы и личное человека в git не входят
functions/<id> функция платформы — отдельный OS-процесс
modules/<id> модуль человека — отдельный OS-процесс; платформа своих модулей сюда не кладёт
sdk OGAS SDK: общий код модулей на Python и Rust, его проверки и тестовые модули
scripts/ сборка поставки, перечень чужих лицензий, замеры и проверка правил с её собственными проверками;
installer/ установщик Windows: сценарий NSIS, `setup.sh` — что он делает внутри Linux, его тексты на двух языках
сами проверки правил — в scripts/rules/, файл на группу правил
assets/ марка для ярлыка
work file/ макеты мастерской
.github/workflows/ CI: запускает `make ci`
Документы лежат только в docs/; их состав — карта документации. В корне лежат рабочие файлы репозитория, а не документы продукта: README.md, лицензия LICENSE и сведения NOTICE о том, что под какой лицензией, условия правки со стороны CONTRIBUTING.md, Makefile, запуск ogas, ogas.cmd и ярлык ogas.desktop, файлы сборки Rust и версия Rust rust-toolchain.toml, рабочая область редактора Concept.code-workspace. Проверки общего кода модулей — в sdk/tests/, тестовые модули, на которых они идут, — рядом, в tests/doubles/: элемент приходит и уходит вместе со своими проверками (правило 2.10). Автор проверяет свой модуль без платформы помощником SDK sdk/python/ogas_check.py и держит такие проверки в папке модуля (test_<id>.py); make ci проверяет платформу, а не модули человека.
А.2 Ядро — core/kernel/
Ядро содержит только то, на чём держится платформа. Новая функция добавляется записью в реестре, а не файлом в ядре.
core/kernel/
settings.schema.json схема ключей core.*: умолчания и допустимые значения
locales/{ru,en}.json названия и подсказки ключей core.* для человека
src/
main.rs точка входа: разбор командной строки, сигналы, выбор глагола
codes.rs коды ошибок ядра
constants.rs внутренние величины ядра и сроки, которые не настраиваются
Таблица А.1 — Ядро — core/kernel/
| Файл | За что отвечает |
|---|---|
main.rs |
точка входа: разбор командной строки, сигналы, выбор глагола |
state.rs |
состояние ядра: подключённые функции, их OS-процессы, счётчики, снимок core.status |
connections.rs |
учёт функций: регистрация после знакомства, права, проверка собеседника, недоступность |
routing.rs |
пересылка вызова хозяину метода и ожидание ответа |
methods.rs |
разбор методов core.*: таблица «метод → файл задачи» |
commands.rs |
память команд: повторная доставка по command_id |
events.rs |
события: номер, рассылка, память последних, досылка подписчику |
health.rs |
проверка здоровья функций: вопрос по сроку, отказ и остановка молчащей |
paths.rs |
пути внутри папки установки и папки данных, граница папки данных |
boot.rs |
замок экземпляра, папки, чтение реестра, журнал запуска |
lifecycle.rs |
порядок запуска функций, перезапуски, пробуждение, каскад недоступности, остановка всего запущенного экземпляром |
server.rs |
приём соединений, handshake и разбор сообщений соединения, работа сервера до остановки |
cli.rs |
глаголы командной строки: разбор, глаголы реестра и их проверка, подстановки в вызовы |
doctor.rs |
глагол doctor: собственные признаки ядра и проверки среды, объявленные функциями |
process.rs |
методы core.process.*: запуск модулей, pidfd, потоки ввода-вывода |
document.rs |
методы core.document.*: чтение, запись, список и удаление файлов в папке данных |
settings.rs |
значения core.*: схема, файлы core.json и system.json, проверка пределов |
os.rs |
обращения к системе: pidfd, flock, путь сокета, замена файла целиком |
codes.rs |
коды ошибок ядра (Коды ошибок) |
constants.rs |
внутренние величины ядра с объяснением, почему выбрано такое значение |
tests.rs |
проверки ядра без запуска OS-процессов |
Сквозные проверки — в core/kernel/tests/: hostile.rs (испорченные реестры, замок, отказ handshake), transfer.rs (экспорт и добавление процесса), runs.rs (хранение прогонов), wake.rs (повторный запуск), cli.rs (глаголы командной строки на процессах из fixtures/processes/), latency.rs (замер задержки, запускается вручную), common/ (общий каркас этих проверок), fixtures/ (тестовые функции на Python).
Функции запускает lifecycle.rs, модули — process.rs по просьбе функции с правом process.
Ядро стартует раньше функции настроек, поэтому ключи core.* оно читает само — по своей схеме и своим файлам уровней.
А.3 Функция платформы — functions/<id>/
У всех функций одно устройство:
functions/<id>/
Cargo.toml
function.json что функция такое: зависимости, договор, окно, перезапуск (Форматы файлов, раздел 3)
settings.schema.json схема настроек функции, если они есть: ключи, умолчания, допустимые значения
locales/{ru,en}.json тексты для человека: названия и подсказки настроек, ошибки, подписи
src/
main.rs точка входа: `start_function` — запуск и знакомство с ядром, таблица «метод → файл задачи»
state.rs состояние службы: что функция держит в памяти, пока работает
codes.rs коды ошибок функции
constants.rs форматы документов и внутренние величины функции
<задача>.rs одна задача — один файл
<задача>/ задача с подзадачами — папка: mod.rs и по файлу на подзадачу
Таблица А.2 — Функция платформы — functions/<id>/
| Функция | Файлы и их задачи |
|---|---|
graph |
state (состояние службы: включённые программы, идущие прогоны и их итоги, остановленное, замки узлов), check (проверка процесса по запросу, вместе с тем, во что разворачиваются его вызовы), process (создание, открытие, сохранение и список процессов), summary (что библиотека показывает о программах: размер, узлы, модули, очертание графа), run (запуск, остановка, повтор, продолжение, шаг узла), program (включённая программа: включение и выключение, её события и правила их наложения), variables (переменные программы и запомненные значения в state.json), asks (вопросы человеку), signals (сигналы извне, которых ждут ветки), notices (части результата шага и пакеты службы), objects (сквозные объекты программы для пульта), schedule (ближайший срок события по часам), changes (события о записанных документах), worker (исполнитель идущего прогона: вызовы модулей, запись контрольных точек, события), modules (что граф знает о модулях: порты, типы, семантика, выгрузка), nodes (набор видов узлов, их шаблоны и допустимые соединения узла), kinds/ (по папке на вид узла — описание вида, шаг, проверка или разворачивание вызова, слова, знак, проверки; правило 2.10), settings (свои настройки), storage (чтение и запись документов процесса), generation (поколения файлов), insert (вставка графа другого процесса), wrap (обёртка узлов в группу), runs (прошлые прогоны в папках процессов и уборка), transfer (экспорт и добавление процесса), runtime/ (выполнение: types, ports — порты видов из их описаний, validate, queue, field — поле пакета по пути, groups — группы, их дерево и лицо, region — рамки и части, candidates, checkpoint, substitute, read — документ процесса, как его пишет эта версия, rename — переименование переменной вместе со всем, что её называет, run_variables — переменные прогона, steps — шаги модулей, идущие рядом с прогоном, execute — цикл прогона; upgrade — сокращённая запись графов в тестах движка, test_support — общее для тестов движка и видов) |
modules |
state (поднятые модули и чьи они, идущие шаги, сохранившие состояние, шлюз видеопамяти), catalog (установленные модули, проверка манифеста, список), create (новый модуль по шаблону OGAS SDK sdk/templates/python-basic/), manifest (что говорит манифест и верно ли), texts (слова, которые модуль приносит в своих словарях), networks (готовые сети помощника из OGAS SDK, их установка и секреты модуля в .env), states (память узлов процесса, как её видит человек), install (установка окружения процесса по команде), lock (modules.lock.json: версии и отпечатки модулей процесса), run/ (запуск модуля, по файлу на задачу: step — шаг и отмена, process — OS-процесс модуля, unload — выгрузка, gpu — бюджет видеопамяти, service — службы, ping — проверка, отвечают ли долгие модули и службы, stream — stdout и stderr модуля; fake_kernel — ядро в проверках: отвечает на вызовы функции и отдаёт ответы модуля как его stdout), pyenv (интерпретатор, окружение процесса, план запуска), artifacts (рабочая папка и файлы результатов), digest (отпечаток модуля), transfer (копирование модулей и просмотр экспорта), models (папка нейросетей: виды, список, приём скачанной, удаление, каких нейросетей не хватает программе), hardware (что есть у компьютера: видеокарты с их памятью, память, ядра, место на диске нейросетей) |
settings |
state (схемы хозяев, значения сессии, уже названные отказы), levels (уровни значений), schema (сборка схем хозяев, схема core.* от ядра при запуске), values (чтение, запись и сброс значений), validate (проверка значения по схеме) |
observe |
state (журнал, его размер и список проблем), journal (запись и чтение журнала событий), problems (проблемы и problems.json) |
canvas |
state (соединение, через которое функция отвечает), workspace (документ пульта workspace.json: чтение, запись, пустой пульт), document (форма документа: что отклоняется и что дополняется — идентификатор, раздел, размер и место элемента), snapshot (данные для окна: разделы и элементы вместе со сквозными объектами процесса, отметки, числа листа), renames (привязки идут за переименованиями графа), settings (числа листа, вид пульта по умолчанию и предел размера рисунка — настройки функции), images (рисунки пульта в папке процесса: проверка, чтение, запись, перенос с процессом), looks (виды элементов — свои, из папки процесса, и встроенные: проверка документа вида, чтение, запись), elements/ (набор элементов: папка на элемент с описанием, словарями и встроенными видами) |
i18n |
state (где лежат словари), catalog (сборка словарей из папок хозяев) |
sources |
state (идущие и прошлые загрузки сеанса), settings (адреса источников и пределы), net (обращение в сеть через curl системы и контрольная сумма через sha256sum), names (имена и пути файлов, пришедших из сети, и что брать по умолчанию), thing (вещь источника в одном виде для всех источников), query (по чему каждый вид источника отбирает и в каком порядке отдаёт, и проверка запроса поиска), hub (Hugging Face), civitai (Civitai), catalog (витрина ogas.catalog/1), download (загрузка: файлы, пределы, проверка, передача хозяину) |
gate |
door (слушатель, ключ, вошедшие программы и что им можно), settings (открыт ли вход, порт, сколько программ) |
profile |
state (соединение, папка данных, неотправленное, что идёт), keys (код восстановления, ключ компьютера с поручительством, ключ писем), keyfile (файл ключа и пароль на нём), profile (профиль: создать, подключить, карточка, отозвать, удалить, выйти), parcel (поля видов посылок, тело, подпись), work (задачка на счёт), send (неотправленное, отправка, свои отправки), actions (посылки от ключа компьютера и вещи для «Предложить»), offer (файлы вещи: что уходит и что остаётся), check (взгляд на сайт: состояния, письма, доска, отзывы, профили), site (файлы сайта с проверкой подписи), letters (письма профилю), store (документы профиля через ядро), settings (свои настройки) |
assistant |
task (задача, её поток и пределы), tools (инструменты и права), prompt (указание сети), store (документы помощника), settings (пределы) |
ui |
relay (приём окна: порт, токен сессии, методы, события), launch (запуск программы окна и повторное открытие), face (лицо программы, которое попросил запуск), layout (раскладка окон между запусками), library (библиотека: панели программ и скрытое), locales (словари функции и окон), font (файл шрифта для окна), settings (схема ui.* и умолчания для окна); window/ — сама программа окна |
Новая команда — строка в таблице методов main.rs и функция в файле своей задачи, а не новый пакет кода. Новый файл появляется, когда у функции появляется новая задача.
Общий код — в core/protocol, по файлу на задачу; lib.rs только перечисляет их:
frame.rs— строки JSON Lines и запись журнала целыми записями;layout.rs— где что лежит в папке процесса: запуски, результаты, память узлов; имя запуска по местному времени;media.rs— вид файла по окончанию имени: им модули называют свои файлы, а граф — файлы результатов;message.rs— сообщение JSON-RPC, ошибка, поля;client.rs— клиент функции: соединение с ядром, знакомство, цикл методов; только Unix;registry.rs— реестр функций и описание функции;time.rs— время в документах;constants.rs— константы договора платформы, среди них категории отказов, и внутренние сроки ожидания, общие для всех частей;codes.rs— общие коды ошибок и их категории;report.rs— что функция говорит сама: опубликованное событие и отказ фоновой работы, которого никто не ждёт;contracts/<хозяин>/— константы и коды договора хозяина, которые называют и зависимые части:codes.rs,constants.rs;settings.rs— схема настроек и помощник чтения, который берёт запасное умолчание из схемы хозяина (правило 2.2);site.rs— ключи сайта ogas.space и проверка подписи его файлов; только Unix;web.rs— обращение в сеть программойcurlсистемы: GET и POST, толькоhttps; только Unix.
Программа окна подключает этот же код, кроме частей для Unix, — так предел строки и другие константы у окна те же, что у всех.
А.4 Мастерская — functions/ui
Функция ui работает в WSL, программа окна — в Windows. Устройство окна — Мастерская, раздел 4.
functions/ui/
src/ функция ui (Linux): main, relay, launch, face, layout, library, locales, font, settings
window/ программа окна (Windows, Tauri 2): свой набор пакетов Cargo, цель x86_64-pc-windows-msvc
src/ main.rs — окно Windows и команды страницы; link.rs — связь с функцией ui: ждущие ответа вызовы,
состояние связи; connect.rs — соединение и знакомство, чтение ответов и событий, конец связи
tauri.conf.json, capabilities/, icons/
cdp.py проверка страницы из WSL средствами отладки WebView2 (только для разработки)
ui/ страница, встроенная в исполняемый файл
index.html, style.css, app.js вход в страницу: связывает части между собой
start.js запуск страницы, когда есть связь: вид, данные платформы, события, отложенные настройки
desk.js перерисовка: вид и надпись о связи, раскладка после замера хоста, окна
keys.js клавиатура страницы
shell.js области: марка, полоса сверху, бары по краям, хост; окна и перетаскивание
faces.js общие формы: бар в трёх состояниях, вкладка окна
windows.js окна на хосте: размер, прилипание, разворот, свёртка
events.js события платформы → состояние; пропуск событий → запрос всего состояния
lib/ dom (обновление страницы), rpc (запросы), look (`ui.*` → CSS), glyphs (значки), labels (тексты), font,
units (размеры словами)
state/ общее состояние мастерской — то, что читают или делают несколько окон, по файлу на задачу: session,
documents (документы процесса в правке и шаги назад), process (открытый процесс: документы от хозяев,
программа, сохранённое, проверка), processes (переход к другому процессу, создание, ввоз и вывоз),
pulpit (полоса пульта и её ячейки), run (программа в работе), overlay (состояние прогона), settings,
modules (каталог модулей), workspace (данные платформы вокруг процесса), parts (какие части платформы
не работают и почему), layout
host/ окна на хосте; каждое — своя папка (правило 2.10): index.js — описание окна (название, знак, бар, части платформы,
которые окно показывает, свои поля сессии, крючки),
view.js — вид, state.js — то, что делает только это окно, locales/ — слова, которые говорит только оно,
style.css — свой вид, tests.js — свои проверки;
index.js — единственный список окон; frame.js — тело окна по Мастерская, 5.1; controls.js — то, что показывают несколько окон;
окна — по папке на окно, их список — host/index.js, их состав — Мастерская, таблица 1
tests/ проверки общего состояния и каркаса и их приспособления (fixtures.js); run.js запускает и их, и проверки окон; всё — в WebView2 окна
fonts/ встроенный шрифт (DejaVu Sans) и его лицензия
Новое окно на хосте — новая папка в host/ и строка о ней в host/index.js, единственном списке окон: геометрия окон и каркас берут виды окон оттуда, а не держат свои списки. Соседнее окно при этом не правится (правило 2.10). Текст, который говорит одно окно, лежит в его locales/; общий словарь functions/ui/locales — для оболочки, общих частей страницы и самой функции, и make ci проверяет, что в нём нет текстов одного окна. Тело окна собирается из host/frame.js, а не рисуется заново. В бар окно встаёт по своему описанию (bar, order), и голову бара открывает окно, которое её назвало (heads); новый бар — элемент в shell.js; новая кнопка вкладки — элемент списка в файле этого окна.
Марка для ярлыка — assets/ogas-mark.svg (используется в ogas.desktop); в окне — window/ui/mark.svg, значок программы — window/icons/icon.ico.
А.5 Модуль — modules/<id>/
modules/<id>/
manifest.json порты и параметры модуля
main.py точка входа: разбор сообщения и вызов своей задачи
locales/{ru,en}.json название, описание и подсказки модуля
test_<id>.py проверки модуля без платформы, если они есть
sdk/ OGAS SDK
ogas-sdk команда автора: new, check, try, pack, status, catalog, run
python/ SDK для Python; лежит на PYTHONPATH модуля
ogas_runtime.py модуль на Python: протокол, чтение шага, ответ, отказ, слова, память узла
ogas_models.py общее модулей с нейросетями и картинками
ogas_assistant.py общее сетей помощника
ogas_check.py проверка модуля без платформы: договор по файлам и разговор по протоколу
ogas/ пакет для программ снаружи: вход в платформу, программа сценарием, команда ogas-sdk
rust/ пакет ogas-module: то же для модуля на Rust
templates/ шаблоны модулей
networks/ сети помощника — готовые модули
examples/ примеры модулей и программ
tests/ проверки самого SDK: отказ, сбой в работе шага, падение, шум в stdout, отмена
doubles/<id>/ тестовые модули: устроены как модуль человека, но не ставятся и не едут в поставку
live/<задача>/ испытание на живой задаче: modules/<id>/ — его модули, process/ — процессы, samples/ или make_reference.py — исходные данные;
не ставятся, в поставку не едут, весов моделей не содержат, make ci их не запускает
Модуль не подключает код соседнего модуля. Общее — только OGAS SDK (sdk/) и обмен по stdin / stdout. Что лежит в папке модуля — Модули, раздел 2.
А.6 Документы
Состав документации — карта документации. Документация частью платформы не является и в поставку не входит (Правила разработки, 2.11).
Оформление мастерской — токены в lib/look.js и style.css внутри functions/ui/window/ui/; рамку окна рисует каркас (shell.js), окно на хосте — только своё содержимое. Правило — Мастерская, раздел 7, «Единый вид оболочки».
А.7 Ограничения структуры
- Новая внешняя библиотека Rust — только с записанной причиной (3.9).
- Набор технологий: файлы JSON, стандартные потоки Rust, модуль — OS-процесс, окно — программа Windows на Tauri 2 и WebView2, страница без сборщика и без готовой библиотеки интерфейса.
- Документ лежит в папке своих читателей:
docs/guide/,docs/reference/,docs/sdk/,docs/platform/,docs/contract/(Правила разработки, 5.1.3). - Бары и вкладки окон рисует каркас (
shell.js,faces.js); окно на хосте своей капсулы не рисует и цвет числом не задаёт. - Файл не делят только из-за числа строк, в том числе в проверках.
- Главный файл не делает работу задачи: задачу, у которой есть свой файл, не пишут в
main.rs. - Умолчание, предел или текст для человека не пишут в коде: для них есть файл схемы и словари части.
Приложение Б (справочное). Допущения
- Модуль — доверенный код и работает с правами пользователя.
- Нагрузка не задана; о больших значениях судят по замерам.
- Жёсткого предела числа узлов и рёбер нет: предел задают ресурсы машины.
- Сообщение JSON до 4 МиБ — для служебных данных; большие данные передаются файлами результатов (артефактами), размер одного по умолчанию до 64 МиБ.
- Обязательные языки интерфейса — русский и английский.
- Установка зависимостей Python может требовать сети; установка без сети не обещается.