Обмен с ядром
Как части платформы говорят друг с другом: сокет и строки JSON-RPC, обёртка сообщения, знакомство функции с ядром, методы ядра и события. Нужен тому, кто пишет функцию платформы или разбирается в её устройстве.
Введение
Договор обмена между частями платформы: сокет Unix внутри машины и соединение мастерской по 127.0.0.1. Наружу платформа ничего не открывает. Версия договора — 1. Каналы и пересылка — Устройство платформы, 3.3, 3.7, форматы файлов — Форматы файлов.
Методы, чьё имя начинается на core., обрабатывает только ядро. Остальные принадлежат той функции, которая объявила их при знакомстве.
Договор разделён по хозяевам. Настоящая часть содержит общее для всех: провод, обёртку сообщения, знакомство с ядром, методы ядра и события. Остальные части договора:
Таблица 1 — Введение
| Файл | Что в нём |
|---|---|
| Методы графа | методы функции графа: процесс, программа, прогон, переменные, вопросы |
| Методы модулей | договор с модулем и методы функции модулей |
| Методы мастерской и пульта | данные мастерской, методы окна, пульт |
| Методы служб платформы | настройки, локализация, наблюдаемость, источники, вход для программ, помощник |
| Коды ошибок | коды ошибок: кто бросает и когда |
1 Область действия
- функция ↔ ядро — Unix-сокет
data/run/ogas.sock; - функция модулей ↔ модуль —
stdin/stdoutмодуля, которые ядро передаёт как байты; - мастерская ↔ функция
ui— TCP127.0.0.1, дальше через ядро к функциям (Методы мастерской и пульта, раздел 2).
Функции не вызывают код друг друга — только методы через ядро. Модуль к сокету ядра не подключается.
2 Передача сообщений
JSON-RPC 2.0 задаёт вид сообщения, но не способ передачи и не границу между сообщениями. Границу платформа задаёт сама: одно сообщение — одна строка (JSON Lines).
- UTF-8, без метки порядка байтов (BOM);
- одно JSON-значение на строку, в конце
\n;\rперед\nдопускается и отбрасывается; - не больше 4 МиБ на строку;
- пустые строки запрещены;
jsonrpcобязан быть"2.0";- несколько запросов одним массивом запрещены: массив JSON на верхнем уровне — это
Invalid Request(-32600) и закрытие канала; idзапроса — только строка вида<отправитель>-<счётчик>; числа и повторidв одном соединении запрещены;- ответ содержит тот же
id; - уведомление не имеет
idи ответа не получает.
Если строку не удалось разобрать, канал закрывается — но сначала, если ответ ещё можно отправить, уходит Parse error (-32700, id: null). Само ядро при этом продолжает работать.
Коды JSON-RPC -32700, -32600, -32601, -32602 и -32603 значат то же, что в спецификации. Ошибки по делу отдаются кодом -32000 и полем data.error_code (Коды ошибок).
Длина очередей ограничена настройкой core.rpc_queue. Запрос, пришедший в переполненную очередь, получает отказ backpressure; уведомления в такой момент могут склеиваться или отбрасываться. Вывод модуля в stdout не отбрасывается никогда: если он не успевает читаться, шаг завершается ошибкой transport_overflow. От stderr хранится только конец, ограниченной длины.
3 Общая обёртка сообщения
Запрос:
{
"jsonrpc": "2.0",
"id": "graph-17",
"method": "core.process.spawn",
"params": {
"command_id": "cmd-17",
"command": "python3",
"args": ["main.py"],
"cwd": "modules/echo"
}
}
Успех:
{
"jsonrpc": "2.0",
"id": "graph-17",
"result": {
"process_handle": "proc-1",
"pid": 1234,
"state": "running"
}
}
Ошибка:
{
"jsonrpc": "2.0",
"id": "graph-17",
"error": {
"code": -32000,
"message": "process spawn failed",
"data": {
"error_code": "process_spawn_failed",
"category": "runtime",
"message_key": "error.process_spawn_failed",
"retryable": false,
"trace_id": "trace-1"
}
}
}
Отправитель вызова. Вызов, который ядро передаёт функции, несёт поле source рядом с params: id функции, чей вызов ядро передаёт, или core, если вызывает само ядро. Ставит его ядро; поле source, записанное самим вызывающим, до хозяина метода не доходит. Хозяин, которому важно, кто вызывает, проверяет source, а не параметры (Защита, раздел 4).
command_id — защита от повторной доставки. Это не транспортный id JSON-RPC.
- У изменяющих методов ядра (
core.event.publish,core.process.spawn,core.process.stop,core.process.kill,core.document.write,core.document.remove,core.shutdown,core.space.places) поле обязательно; без него —invalid_request. - При потере ответа отправитель повторяет ту же команду с тем же
command_id, и ядро возвращает прежний результат. - Функция вставляет в
command_idметку своего запуска, чтобы после перезапуска её новые команды не совпали со старыми в кэше ядра. - Ключ — отправитель + метод +
command_id. При повторе ядро сравнивает отпечаток тела (без самогоcommand_id): другое тело с тем же ключом —command_id_conflict. - Пока команда выполняется, повтор ждёт того же результата и второго действия не запускает.
- Кэш не применяется к запросам и к
core.process.stdin.write. Из кэша вытесняются самые давние завершённые записи; выполняющиеся не вытесняются.
После аварии гарантия сохраняется только у тех команд, результат которых уже записан на диск. Гарантии выполнения внешнего действия ровно один раз платформа не даёт.
4 Знакомство функции с ядром (handshake)
Ядро передаёт функции одноразовый токен запуска в переменной OGAS_LAUNCH_TOKEN. Функция подключается к сокету из OGAS_SOCKET и первым сообщением отправляет:
{
"jsonrpc": "2.0",
"id": "graph-1",
"method": "function.handshake",
"params": {
"launch_token": "...",
"id": "graph",
"implementation_version": "1.0.0",
"contracts": { "core": 1, "graph": 1, "surface": 1 },
"commands": ["process.run.start", "process.run.stop"],
"queries": ["process.snapshot", "function.health"],
"events": ["run.changed"],
"surfaces": ["graph.editor"],
"capabilities": []
}
}
Успех:
{
"jsonrpc": "2.0",
"id": "graph-1",
"result": {
"status": "ready",
"function_id": "graph",
"granted_capabilities": []
}
}
Когда знакомство отклоняется:
- токен не тот —
invalid_launch_token; - версия договора в
contracts.coreне подходит —contract_incompatible; - пользователь или номер процесса на другом конце сокета (
SO_PEERCRED) не совпадает с тем OS-процессом, который ядро запускало, —peer_mismatch; - первым сообщением пришло не знакомство — канал закрывается.
capabilities — необязательный список прав; незнакомые имена не читаются. Право, которое функция просит, должно быть перечислено в её записи реестра, иначе — capability_denied. Ответ содержит granted_capabilities. Право process нужно для core.process.spawn, core.process.stdin.write, core.process.stop и core.process.kill; без него эти методы отвечают capability_denied.
locales — необязательный список папок со словарями, пути относительные. Ядро их запоминает и отдаёт по core.locales, а сами файлы не читает. Один сегмент пути может быть *: такая папка означает каждую папку этого места внутри папки установки, где есть остаток пути, — ядро находит их заново при каждом core.locales и пропускает скрытые (имя начинается с точки). Так функция модулей объявляет modules/*/locales, и тексты модуля, положенного в папку руками, созданного или добавленного импортом, видны без перезапуска и без нового объявления.
5 Методы ядра
Все они, кроме знакомства, вызываются функцией на уже принятом соединении.
5.1 core.status
Снимок здоровья ядра и функций (не модулей). Обязательных параметров нет:
{
"core": {
"pid": 12,
"started_at": "2026-08-30T12:00:00Z",
"data_dir": "/home/user/Concept/core/data",
"functions_ready": 6,
"functions_failed": 0
},
"metrics": {
"rpc_requests": 12,
"rpc_errors": 0,
"health_failures": 0,
"function_restarts": 0,
"document_writes": 3,
"processes_spawned": 1
},
"functions": [
{
"id": "graph",
"interactive": false,
"state": "ready",
"pid": 34,
"restart_count": 0,
"last_health_at": "2026-08-30T12:00:05Z",
"last_error": null,
"last_error_code": null,
"last_error_data": null
}
]
}
interactive — признак из описания функции: ей нужна мастерская. По нему другие части отличают функции окна, не зная их имён. state: starting | ready | failed | restarting. О последнем отказе функции — last_error (сообщение для журнала), last_error_code (код, его текст для человека — error.<код> в словаре ядра) и last_error_data (что код называет: при function_dependency_unavailable — { "dependency" }, недостающая зависимость). Функции, которой нет в реестре или которая в нём выключена, в списке нет. Журнал подробностей: data/logs/core.jsonl.
5.2 core.health
Запрос без обязательных параметров. Ответ:
{ "status": "ok", "contract": 1, "functions": 6, "processes": 2 }
5.3 function.health
Ядро вызывает этот метод у функции, а не наоборот. Ответ — { "status": "ok" } или ошибка. Если ответа нет дольше core.health_timeout_ms, функция считается отказавшей (failed).
5.4 core.process.spawn
Параметры:
Таблица 2 — core.process.spawn
| Поле | Тип | Обязательно | Смысл |
|---|---|---|---|
command_id |
строка | да | чтобы узнать повторную доставку |
command |
строка | да | имя исполняемого файла или путь от папки установки или папки данных |
command_base |
строка | нет | от чего считается путь command: install (по умолчанию) или data — папка данных |
args |
список строк | нет | аргументы командной строки |
cwd |
строка | нет | рабочая папка; по умолчанию папка установки |
cwd_base |
строка | нет | от чего считается cwd: install (по умолчанию) или data — папка данных; при data папка создаётся, если её нет |
env |
объект «имя → значение» | нет | дополнительные переменные окружения |
limits |
объект | нет | пределы группы программ: group — имя группы (маленькие латинские буквы, цифры, _), memory_mb — память всей группы в мегабайтах, cpu_cores — сколько ядер процессора она может занять; 0 — без предела |
Ядро сначала очищает окружение, потом добавляет PATH, LANG, LC_ALL и переменные из env. Переменных Python ядро не подставляет — их передаёт функция с правом process. Путь с косой чертой приводится к однозначному виду от своей папки и за её пределы выйти не может. В папке данных папки на пути проверяются по ссылкам, как у любого пути там; сама программа может быть ссылкой наружу — интерпретатор окружения процесса ссылается на Python, из которого сделан, — и должна вести к файлу (Защита, T-03). Ответ: process_handle, pid, state. Хозяин этого process_handle — функция, по чьему соединению пришёл запрос. Нет права process — capability_denied.
Сведений о Python ядро не содержит: интерпретатор и аргументы выбирает функция модулей.
Пределы. С limits ядро ставит программу в группу: пределы держит сам Linux — systemd пользователя, группа экземпляра своя, и все программы одного имени группы делят её память и процессоры. Программу, которая вывела группу за предел памяти, Linux останавливает, и process.io.closed говорит об этом полем limit: "memory". Если systemd пользователя не отвечает, запуск с пределами получает отказ process_limits_unavailable, а без пределов не выполняется: предел, который не держится, не обещается. Работает ли systemd, говорит строка process_limits команды doctor.
5.5 core.process.stdin.write
Параметры: process_handle и line — целая строка JSON, обязательно с \n на конце. Что внутри line, ядро не разбирает. Если ответ на запись потерялся, повторять её модулю, для которого повтор не безобиден, нельзя.
5.6 core.process.stop
Параметры: command_id (обязательно), process_handle. Штатная остановка: SIGTERM через pidfd, ожидание core.process_stop_grace_ms, затем SIGKILL через тот же pidfd, если OS-процесс ещё жив. Повторить остановку безопасно: для уже завершившегося OS-процесса приходит ответ { "state": "exited", "signal_sent": false }. То же у core.process.kill. Нет права process — capability_denied.
5.7 core.process.kill
Параметры: command_id (обязательно) и process_handle. Немедленный SIGKILL через pidfd — после истечения срока ожидания или по аварийной команде.
5.8 core.document.read
Параметры: path от OGAS_DATA_DIR, без ... Ответ — содержимое документа. Если файла нет, читается соседний {name}.bak, когда он есть: значит, запись оборвалась после переименования текущего файла в резервную копию. Если и его нет — document_not_found. Повреждённый текущий файл резервной копией без сообщения не подменяется.
5.9 core.document.list
Параметры: prefix от OGAS_DATA_DIR, без ..; sizes: true — посчитать размер каждой записи. Ответ — { "entries": [{ "name", "path", "directory", "modified_ms", "bytes" }] }: modified_ms — время изменения в миллисекундах от эпохи, bytes — только при sizes, для папки вместе с содержимым, ссылки считаются сами по себе. Если папки нет, список пустой. Смысл самих файлов ядро не разбирает.
5.10 core.document.write
Параметры: command_id (обязательно), path, document. Ядро проверяет путь, UTF-8 и размер (не больше core.document_max_bytes), а если у документа есть format на верхнем уровне — что он выглядит как <имя>/<номер> (document_invalid). Какие версии понятны, решает хозяин документа при чтении (Форматы файлов, раздел 15); ключи настроек с секретными именами отклоняет функция настроек, а ядро проверяет только обёртку. Запись идёт так: временный файл в той же папке, fsync файла, прежний файл переименовывается в .bak (не копируется), временный встаёт на его место, fsync папки. Сбой при записи не уничтожает прежнее поколение: на диске остаётся либо целый текущий файл, либо целая резервная копия.
Документы процесса разрешено писать с отступами для читаемости. Размер считается в байтах записанного UTF-8.
5.11 core.document.remove
Параметры: command_id (обязательно), path. Удаляет файл или папку целиком внутри папки данных. Символьная ссылка удаляется как ссылка, её цель остаётся на месте. Путь обязан состоять хотя бы из двух частей (runs/<run_id>, а не runs), чтобы один вызов не снёс целый раздел; иначе — document_remove_refused. Ответ — { "path", "removed" }; если удалять было нечего, приходит removed: false, а не ошибка.
5.12 core.locales
Отдаёт папки словарей: свою core/kernel/locales и те, которые функции назвали при знакомстве в поле locales; папка с сегментом * раскрыта в папки, которые есть в этот момент (раздел 4). Параметров у запроса нет. Ответ: { "dirs": ["core/kernel/locales", "functions/graph/locales", ...] } — пути считаются от OGAS_INSTALL_DIR, без .. и не от корня. Функция i18n читает только эти папки и свою собственную.
5.13 core.settings.schema
Схема ключей core.* — содержимое файла core/kernel/settings.schema.json (ogas.settings-schema/1). Параметров нет. Спрашивает её функция настроек при запуске; ядро само читает core.* по той же схеме (Настройки, раздел 3).
5.14 core.event.publish
Параметры: command_id (обязательно), event_type, payload. Ядро даёт событию номер event_id и рассылает уведомление event всем функциям. Ответ: { "event_id", "source" }.
5.15 core.event.subscribe
Параметры: last_event_id — последний полученный подписчиком номер или null. Номера растут не подряд: номер получает и событие, посланное одной функции, а видит его только она, — поэтому шаг номера больше единицы потерей не является. Если этот номер ещё есть в памяти последних событий, ядро досылает пропущенное; если нет, отвечает { "resync_required": true }, и подписчик запрашивает состояние заново. Событий process.io.* в этой памяти нет: поток модуля подпиской не восстанавливается.
5.16 core.shutdown
Параметры: command_id (обязательно). Останавливает экземпляр: новые запуски запрещаются, ядро публикует system.stopping и завершает OS-процессы. Ответ — { "status": "stopping" }.
5.17 core.space
Запрос: сколько места занимает платформа. Параметры: fresh — true, если нужен свежий счёт, а не сохранённый на короткое время. Ответ: { used_bytes, budget_bytes, free_bytes, keep_free_bytes, room_bytes, places: [{ path, bytes }] }. Считаются папка данных и папки, которые части назвали ядру (core.space.places); вложенная папка не считается второй раз, файл с несколькими именами (пакеты окружений — ссылки в один кэш) — тоже. budget_bytes — бюджет core.disk_budget_gib, null — без бюджета; free_bytes — сколько ещё вмещает самый тесный диск этих папок; room_bytes — сколько можно занять, не выйдя ни за бюджет, ни за core.disk_free_min_gib. Оба ключа ядро перечитывает при каждом вопросе, поэтому они действуют без перезапуска.
5.18 core.space.check
Запрос: поместится ли новое. Параметры: bytes — размер нового, 0 — размер неизвестен, fresh. Ответ { fits: true, room_bytes }; не помещается — отказ space_budget_exceeded (выйдет за бюджет) или space_disk_full (выйдет за место, оставленное свободным), в данных ошибки needed_bytes и room_bytes. Новое неизвестного размера помещается, пока остаётся хоть сколько-то места. Спрашивают те, кто занимает место: загрузка из источника, установка окружения, начало прогона.
5.19 core.space.places
Команда: папки, в которых часть хранит своё вне папки данных. Параметры: command_id (обязательно), places — список: полный путь или { path, backup }, где backup: true — папку берёт резервная копия; каждый вызов называет все папки части заново. Ядро держит названное и в data/places.json (ogas.places/1), чтобы копию можно было сделать и при остановленной платформе.
6 События о OS-процессе модуля
Уведомление ядра (без id):
{
"jsonrpc": "2.0",
"method": "event",
"params": {
"event_id": 42,
"event_type": "process.io.chunk",
"event_version": 1,
"time": "2026-08-30T12:00:01.250Z",
"source": "core",
"trace_id": null,
"payload": {
"process_handle": "proc-1",
"stream": "stdout",
"sequence": 0,
"chunk": "{\"jsonrpc\":\"2.0\",\"id\":\"module-3\",\"result\":{}}\n"
}
}
}
Когда закрыты оба потока:
{
"jsonrpc": "2.0",
"method": "event",
"params": {
"event_id": 43,
"event_type": "process.io.closed",
"event_version": 1,
"time": "2026-08-30T12:00:01.300Z",
"source": "core",
"trace_id": null,
"payload": {
"process_handle": "proc-1",
"exit_code": 0,
"termination": "exited"
}
}
}
Поля события, как у всякого события ядра, лежат в payload (раздел 7). termination принимает значения exited (вышел сам) и killed (завершён сигналом); limit: "memory" — его остановил предел памяти его группы (limits, раздел 5.4). OS-процесс, который не запустился, событий не порождает: у него нет process_handle, и отказ приходит ответом на core.process.spawn (process_spawn_failed). После process.io.closed ядро забывает этот OS-процесс, и core.health.processes считает только живые. У stderr свой счётчик sequence. Байты, которые не складываются в UTF-8, в stderr заменяются знаком U+FFFD.
process.io.chunk и process.io.closed ядро отправляет только той функции, которой принадлежит process_handle, и в общую память последних событий не пишет. Остальные события (function.ready, process.started, system.stopping и опубликованные через core.event.publish) получают все подключённые функции.
7 События
Каждое событие ядра: event_id, event_type, event_version, time, source (core или id функции, опубликовавшей событие), trace_id (из payload.trace_id, иначе null), payload. У каждого типа события есть текст для человека event.<тип> в словаре его хозяина — ядра или функции, объявившей событие при знакомстве: им событие называет журнал.
Обязательный набор: system.ready, system.stopping, system.activate, function.ready, function.failed, function.restarted, function.handshake_failed, function.handshake_timeout, process.started, process.io.chunk, process.io.closed, run.changed, node.changed, problem.opened, problem.resolved, logs.removed, sources.download.changed, profile.changed, profile.sending.changed, profile.checked, settings.changed, settings.schema.registered, settings.value_invalid, i18n.key_missing, function.work_failed. (resync_required — не событие, а поле ответа core.event.subscribe, раздел 5.)
system.ready — { functions }: экземпляр поднялся; functions — функции, которые ядро запустило. Функция, подключившаяся позже, получает его при догоняющей подписке (раздел 5).
function.handshake_failed — { error_code, message, function_id? }: ядро отказало соединению в знакомстве. function_id есть, только если соединение назвалось функцией из реестра: слова постороннего соединения на шину не попадают.
function.handshake_timeout — { function_id, error_code }: запущенная функция не познакомилась с ядром за core.function_start_timeout_ms; за ним следует function.failed.
system.activate — { reason: "second_launch" }: мастерскую запустили ещё раз, пока экземпляр работает. Функция окна открывает окно, если его нет (Устройство платформы, 3.6.3).
function.restarted — { function_id, cause, attempt, max, delay_ms, level: "warn" }: ядро перезапускает функцию; cause — почему: function_exited (вышла сама) или health_timeout (не ответила на проверку работоспособности, и ядро её остановило). Предупреждение, а не проблема: исчерпанный бюджет перезапусков даёт function.failed с error_code: function_restart_exhausted.
function.failed — { function_id, error_code, dependency? }: функция не работает и ядро её больше не запускает — исчерпаны перезапуски (function_restart_exhausted), функция вышла или замолчала, а перезапуск ей не положен (function_exited, health_timeout), её канал оборвался (function_lost), не запустилась, не познакомилась вовремя, другой версии договора или осталась без обязательной зависимости (function_dependency_unavailable, тогда dependency называет её). Функция наблюдения открывает по нему проблему с именем функции и причиной.
function.work_failed — { work, error_code, message, … }: функция не завершила работу, ответа на которую никто не ожидает (фоновую работу): выгрузку модулей после прогона, уборку старых прогонов, запись контрольной точки, срабатывание таймера или события по часам, остановку служб и модулей. work — что это была за работа, простыми словами для того, кто разбирает журнал; остальные поля говорят, о чём она была (process_id, run_id, module_id). Опубликовать его может любая функция, объявлять его отдельно ей не нужно. По error_code наблюдение открывает проблему, как по любому событию с кодом (Форматы файлов, раздел 13). Событие, которое ядро не приняло, функция говорит в свой stderr, а ядро держит stderr функций в своём журнале (Устройство платформы, 3.8.2), так что сбой не теряется бесследно.
node.changed — шаг прогона: { run_id, process_id, node_id, execution_id, status }, где status — queued — шаг пришёл к модулю и ждёт своей очереди: места у модуля заняты другими шагами (parallelism манифеста); running — модуль взял шаг; completed, failed или cancelled — шаг оборван остановкой прогона или его группы. Шаг, которому место нашлось сразу, queued не публикует. Публикуется для узлов модулей и для узлов, на которых ветка стоит, — вопроса, ожидания сигнала и паузы (Устройство платформы, 3.8.2): у таких status — waiting, когда ветка встала, затем completed (ветка пошла дальше) или cancelled (ожидание снято остановкой группы или тем, что взят другой пакет круга), а execution_id пуст — попытки шага у ожидания нет. Событие об ожидании публикуется после записи контрольной точки, в которой оно уже есть.
События живой программы (Программа: граф и запуски, разделы 2, 9):
program.changed—{ process_id, on }: программу включили или выключили;variable.changed—{ process_id, variable_id, value, partial }: переменную записали; значение больше 16 КиБ событие не несёт — вместоvalueв нёмlarge: true, и показывающий читает значение сам запросомprocess.variable.listсvariable_id;partial: true— пока модуль выдаёт результат частями;notice.posted—{ process_id, run_id, node_id, title, text }: узел «Уведомить» зовёт человека; окно кладёт уведомление поверх того, что открыто;show.changed—{ process_id, node_id, run_id }: узел «Показать» показал новое; что именно —process.show.get;event.dropped—{ process_id, node_id, waiting, reason, level: "warn" }: событие прогона не начало.reason—overflow, когда ждущих ужеgraph.queue_depth, илиskip, когда правило наложения события — «пропустить» (Программа: граф и запуски, раздел 5); уskipполеwaitingравно нулю;ask.openedиask.closed—{ process_id, run_id, node_id, ask_id, question, kind, choices, fill }: узелaskоткрыл вопрос человеку и вопрос закрыт.ask_id— по нему отвечают (process.ask.answer);kind— чего ждёт вопрос, параметр узла:text— строки,choice— одного изchoices,confirm— «да» или «нет»,number— числа,image,audio,video,file— ссылки на файл этого вида;fill— у вопроса строкой текст, с которого начинается поле ответа: поле данных вопроса по пути из параметраfillузла, иначеnull;signal.sent—{ process_id, name, key, run_id, node_id, delivered }: снаружи послали сигнал.delivered: falseзначит, что его никто не ждал и он не сохранён (Программа: граф и запуски, раздел 22).module.partial—{ run_id, node_id, execution_id, port, data }: часть результата шага; публикует функция модулей, слушает функция графа;module.emit—{ process_id, module_id, port, data, node_id? }: служба выдала пакет вне шага; сnode_id— для одного своего узла;module.signal—{ process_id, module_id, name, key, data }: модуль послал сигнал своей программе; публикует функция модулей, слушает функция графа;message.sent—{ process_id, name, delivered, events }: программе отправили сообщение — узлом «Отправить» или методомprocess.message.send;events— события, которые его взяли, с их ответом (прогон или очередь);delivered: false— не взяло ни одно;module.raised—{ module_id, process_id, run_id, mode, process_handle }: функция модулей подняла модуль;run_idнет у службы и у тестового вызова. Продолжает ли узел с сохранённой памяти, говорит его шаг (resumed), а не подъём: модуль один на все свои узлы (Модули, раздел 4);module.ended—{ module_id, process_id, run_id, mode, process_handle, reason, saved, nodes }: OS-процесс модуля завершён.reason—resources(место на видеокарте понадобилось другому),run_ended(прогон долгого модуля кончился),switched_off(программу службы выключили),step_ended(шаговый модуль сделал свой шаг),timeout(модуль не ответил в срок шага),lost(не ответил наping),exited(завершился сам);saved— сохранена ли память;nodes— узлы, чью память модуль обслуживал и сохранял;module.log—{ module_id, process_id, run_id, node_id, level, message }: модуль записал в свой журнал;run_idиnode_id— шага, который у него идёт, а между шагами — прогона долгого модуля или ничего;level—info,warnилиerror;module.progress—{ module_id, run_id, node_id, execution_id, ratio }: сколько сделано в шаге, от 0 до 1; функция модулей передаёт его не чаще двух раз в секунду и всегда — значение 1;packet.absorbed—{ run_id, process_id, node_id, port, packet_id }: пакет с выхода без рёбер никуда не передан; публикует функция графа один раз на пакет (Программа: граф и запуски, 10.2).
События документов. Каждая команда, которая записывает или удаляет документ, оставляет событие: функция наблюдения пишет его в журнал, и порядок изменений восстанавливается по журналу (Правила разработки, 2.7.3):
process.changed—{ process_id, generation, command }: граф процесса записан;command—process.create,process.saveилиprocess.import,generation— поколение графа после записи;process.exported—{ process_id, path }: пакет процесса записан в папкуpath;run.kept—{ run_id, kept }: прогон отмечен «Хранить» или отметка снята;runs.removed—{ process_id, removed, reason }: прошлые прогоны удалены.reason—clear, когда их удалил человек (process.runs.clear; без процесса — прогоны всех процессов,process_id: null), илиruns_keep, когда их отпустило правилоgraph.runs_keepпосле прогона; по правилу событие публикуется, только если что-то удалено;canvas.changed—{ process_id, generation }: пульт процесса записан; публикует функция пульта.canvas.images.changed—{ process_id, name }: рисунок пульта положен в папку процесса или убран из неё;name— имя рисунка илиnull, когда рисунки пришли с переданным процессом или изменился свой вид элемента. Публикует функция пульта; окно, показывающее пульт этого процесса, читает его рисунки заново.ui.face.asked—{ process_id }: запуск попросил показать лицо программы этого процесса (Мастерская, 6.6); публикует функция окна. По нему уже открытое окно выходит вперёд и показывает лицо.
8 Совместимость
Если меняется смысл обязательного поля или метода — растёт версия договора contract. Добавление необязательного поля версию не меняет. Незнакомые необязательные поля не читаются.
Договор не обещает ни JSON-RPC 1.0, ни нескольких запросов одним массивом, ни числовых id, ни работы поверх HTTP.
9 Авторизация
Входа по имени и паролю в платформе нет: на машине работает один человек. Функцию к ядру допускают четыре условия: права 0600 на сокете, одноразовый токен запуска, совпадение пользователя и номера процесса на другом конце сокета (SO_PEERCRED) с тем OS-процессом, который ядро запускало, и совпадение объявленного id с записью реестра. Модуль методов ядра не вызывает вообще. process_handle принадлежит только той функции, которая его получила. Окно мастерской пускают адрес 127.0.0.1, токен сессии функции ui и закрытый список методов ядра (Защита, 3.10). Программу человека пускает вход для программ: открытая настройка, адрес 127.0.0.1, ключ входа и закрытый список методов (Методы служб платформы, раздел 5).
10 Ограничения договора
- описания всех сообщений в машиночитаемом виде нет; поля заданы таблицами настоящей части;
- сколько последних событий ядро держит в памяти, задано только значением по умолчанию
1024и на нагрузке не проверено; - доступа к платформе по сети нет, и договор для него не описан.