Форматы файлов
Форматы файлов платформы: программа, пульт, память, контрольная точка, настройки, манифест модуля, экспорт, витрина и другие — что в каждом поле и кто файл пишет.
Введение
Документ описывает файлы, которые пишет платформа, и их содержимое. Назначение документов изложено в описаниях частей: граф — Программа: граф и запуски, модули — Модули, пульт — Мастерская, раздел 6, настройки — Настройки, папка данных целиком — Устройство платформы, 3.8.
1 Общие правила
- Документ — это JSON в UTF-8, без комментариев и без запятой перед закрывающей скобкой.
- У каждого формата есть поле
format, напримерogas.process/4. Незнакомое значение делает документ недействительным, а документ более новой версии программа прежней версии не открывает. - Незнакомое необязательное поле текущая версия не читает.
- Идентификаторы — строки. Идентификатор выполнения второй раз в пределах экземпляра не выдаётся.
- Время записывается по Гринвичу в виде ISO 8601 с миллисекундами и буквой
Z, например2026-08-30T10:15:30.123Z. - Двоичные данные в JSON не помещаются: для них служит артефакт и ссылка на него
artifact_handle. - Путь внутри документа считается от папки его хозяина и за её пределы не выходит.
- Килобайт в пределах — это 1024 байта.
2 Идентификаторы
Таблица 1 — Идентификаторы
| Идентификатор | Что обозначает |
|---|---|
function_id |
функция платформы из реестра |
module_id |
модуль; совпадает с именем папки modules/<module_id>/: [a-z0-9][a-z0-9_-]{0,63} — строчные латинские буквы, цифры, _ и -, первый знак — буква или цифра; runtime — папка общего кода модулей, а не модуль. Другой module_id функция модулей отклоняет, не читая файлов |
process_id |
рабочий процесс, [a-z0-9-]{1,64} (Программа: граф и запуски, раздел 1) |
node_id, edge_id, group_id, port_id |
части графа |
object_id |
сквозной объект |
surface_id |
имя вида, которое функция объявила для диагностики |
run_id |
логический прогон: местные дата и время его начала, 2026-10-08_07-29-12, при совпадении — -2, -3; единственный среди прогонов всех процессов. Имя папок прогона runs/<run_id>/ и results/<run_id>/ в папке процесса он даёт, только если состоит из латинских букв, цифр, - и _ и не длиннее 128 знаков; другой run_id остаётся лишь в общем журнале |
attempt_id |
попытка прогона; после сбоя новая, run_id прежний |
operation_id |
логический шаг узла: прогон + узел + packet_id + итерация. Повтор той же итерации сохраняет его, новая итерация цикла — новый |
execution_id |
конкретный вызов модуля; у повтора новый |
packet_id |
логический пакет; при разветвлении один на всех получателей. Пакет внутри рамки, в которую он вошёл из круга другой рамки, носит прежний packet_id и номер того круга через «/» (Программа: граф и запуски, раздел 11) |
delivery_id |
конкретная доставка пакета одному получателю |
artifact_handle |
файл результата прогона: его имя в results/<run_id>/, без / и \, не начинается с точки |
command_id |
стабильный ключ изменяющей команды при повторной доставке (Обмен с ядром, раздел 3) |
process_handle |
конкретный OS-процесс модуля; смысл знает функция модулей |
event_id |
монотонный номер события ядра |
trace_id |
связь всех записей одного действия человека |
problem_id |
проблема в списке функции observe |
3 Реестр функций
Сведения о функции содержатся в двух файлах. Реестр экземпляра определяет размещение её программы, включение и выданные права. Сама функция в своей папке описывает свои свойства: зависимости, версию договора, работу с окном и перезапуск (правило 2.10). Ядро читает оба файла и соединяет их по id.
Файл реестра: data/functions.json. Формат: ogas.functions/2.
{
"format": "ogas.functions/2",
"cli": {
"run": {
"args": {
"process": { "required": true },
"text": { "required": false, "default": "hello from O.G.A.S." }
},
"before": [
{ "method": "process.program.on", "params": { "command_id": "cli-run-on", "process_id": "$process" } }
],
"method": "process.event.fire",
"params": {
"command_id": "cli-run",
"process_id": "$process",
"data": { "text": "$text" }
},
"wait": {
"method": "process.run.status",
"params": { "run_id": "$run_id" },
"field": "status",
"success": ["completed"],
"failure": ["failed", "cancelled", "stopped", "interrupted", "unknown"]
}
},
"install": {
"args": { "process": { "required": true } },
"method": "module.install",
"params": { "process_id": "$process" }
}
},
"functions": [
{
"id": "settings",
"path": "bin/ogas-settings",
"enabled": true
}
]
}
cli — необязательные глаголы командной строки, кроме глаголов ядра (start, ui, doctor), в виде { "<глагол>": описание } (Устройство платформы, раздел 5):
- описание глагола —
{ "args", "before", "method", "params", "wait", "interactive" }; interactive— необязательный признак «глаголу нужна мастерская», по умолчаниюfalse: экземпляр запускается вместе с функциями, которым нужна мастерская, и после вызовов глагола остаётся работать, а работающий экземпляр выполняет вызовы сам (Устройство платформы, 3.6.3 и 5.3). Опросаwaitу такого глагола нет: конца работы, которого он ждал бы, нет;args— аргументы глагола:{ "<имя>": { "required", "default" } },requiredпо умолчаниюfalse; аргумент передаётся флагом--<имя> ЗНАЧЕНИЕ. Без обязательного аргумента глагол завершается с кодом 1, не запуская экземпляр; необязательный аргумент без флага принимает значениеdefault(строка), поэтому необязательный аргумент безdefaultне допускается;- имя глагола и имя аргумента состоят из строчных латинских букв, цифр,
_и-и начинаются с буквы;data-dirиhelp— флаги ядра и именами аргументов не являются; before— вызовы до основного, каждый{ "method", "params" }: действия, которые человек выполнил бы предварительно, например включение программы перед нажатием кнопки её пульта. Ответы этих вызовов не читаются, отказ любого из них является отказом глагола;- подстановки в
paramsосновного вызова и вызововbefore: строка$<имя>целиком заменяется значением аргумента с этим именем; строка, в которой кроме$<имя>есть другие знаки, не заменяется. Имя, которого нет среди аргументов глагола, не допускается; wait— необязательный опрос после вызова:method,params,field,success,failure. Вparamsопроса доступны аргументы глагола и строковые поля ответа первого вызова ($run_id); имя, которого нет ни среди них, ни в ответе, — отказ глагола с кодом 1. Значениеfieldизsuccess— код выхода 0, изfailure— 1.
Глагол с именем глагола ядра, имя глагола или аргумента, не отвечающее этим требованиям, необязательный аргумент без умолчания, вызов без method, подстановка без аргумента и опрос wait у глагола, которому нужна мастерская, делают реестр недействительным (function_registry_invalid): ядро отказывает всем глаголам реестра и работает без функций (Устройство платформы, 3.4.1).
Записка повторного запуска — run/launch.json в папке данных, ogas.launch/1: { "format", "verb", "args": { "<имя>": "<значение>" }, "asked_ms" }. Её оставляет запуск глаголом, которому нужна мастерская, когда экземпляр уже работает, и читает разбуженный экземпляр (Устройство платформы, 3.6.3). asked_ms — время просьбы в миллисекундах от начала эпохи. Файл доступен только пользователю, записывается целиком и удаляется при чтении.
Запись функции в реестре — только решения экземпляра:
Таблица 2 — Поля записи функции в реестре
| Поле | Тип | Умолчание | Смысл |
|---|---|---|---|
id |
string | — | id функции; её папка — functions/<id>/ |
path |
string | — | исполняемый файл относительно папки установки |
enabled |
bool | — | запускать ли |
capabilities |
string[] | пусто | права, которые handshake может запросить |
Состояние функций во время работы в этот файл не входит.
Описание функции — файл functions/<id>/function.json в папке установки, формат ogas.function/1. Его пишет автор функции вместе с её кодом; в поставку описание входит вместе с функцией.
{
"format": "ogas.function/1",
"id": "ui",
"requires": ["settings", "i18n", "graph", "modules", "canvas", "observe"],
"optional": ["graph", "modules", "canvas", "observe"],
"platform": { "min": 1, "max": 1 },
"contract": 1,
"restart": false,
"interactive": true
}
Таблица 3 — Поля описания функции
| Поле | Тип | Умолчание | Смысл |
|---|---|---|---|
format |
string | — | ogas.function/1 |
id |
string | — | та же функция, что в реестре; другое id — описание чужое |
requires |
string[] | пусто | функции, договором которых пользуется данная |
optional |
string[] | пусто | те из requires, без которых функция работает: запуск их не ожидает, отказ их её не останавливает |
platform.min / max |
u32 | 1 |
поддерживаемые версии платформы |
contract |
u32 | — | версия контракта |
restart |
bool | true |
перезапускать ли после выхода |
interactive |
bool | false |
нужна мастерская |
checks |
object[] | пусто | проверки среды для ogas-core doctor |
Функция ui описывает себя с interactive: true и restart: false; списка идентификаторов функций в ядре нет. Смысл полей — Устройство платформы, 3.4.1.
checks — проверки среды, которые функция объявляет для ogas-core doctor (Устройство платформы, раздел 5). Каждая — { "id", <вид> } из закрытого набора видов, которые умеет выполнять ядро:
file— список путей: проверка пройдена, если есть хотя бы один файл или папка; относительный путь считается от папки установки;program— программа и её аргументы: проверка пройдена, если программа завершилась с кодом 0 в пределах внутреннего срока ядра; программа запускается из папки установки, её вывод не читается.
id состоит из строчных латинских букв, цифр и _ и не повторяется в описании; у проверки ровно один вид. Иначе описание недействительно. Что означает итог, функция говорит в своём словаре ключами <id функции>.check.<id>.passed и <id функции>.check.<id>.failed на русском и английском языках.
"checks": [
{ "id": "wsl_interop", "file": ["/proc/sys/fs/binfmt_misc/WSLInterop", "/proc/sys/fs/binfmt_misc/WSLInterop-late"] },
{ "id": "window", "file": ["bin/ogas-window.exe", "functions/ui/window/target/x86_64-pc-windows-msvc/release/ogas-window.exe"] }
]
Если описание отсутствует, не является JSON, имеет другой формат или другое id, ядро функцию не запускает, так как не знает её требований и договора. Запуск отказывает с function_description_invalid и называет файл, остальные функции работают, а зависимые от неё остаются без неё, как без любой недоступной. Реестр другого формата ядро не читает, а называет его версию: молча выбросить из него, например, restart: false значило бы поменять поведение экземпляра без слова.
4 Манифест модуля
Файл: modules/<module_id>/manifest.json. Формат: ogas.module/1.
{
"format": "ogas.module/1",
"id": "echo",
"version": "1.0.0",
"name_key": "module.echo.name",
"description_key": "module.echo.description",
"runtime": "python",
"entrypoint": "main.py",
"inputs": [
{ "name": "input", "required": true, "type": "text" }
],
"outputs": [
{ "name": "success", "type": "text" }
],
"input_policy": "all",
"parallelism": 1,
"process": "step",
"resources": { "gpu_mb": 0 },
"suspend": false,
"execution_semantics": "idempotent",
"retry": { "max_attempts": 1, "delay_ms": 0, "exponential": false },
"parameters": {
"suffix": { "type": "string", "default": "" }
},
"permissions_info": { "network": false, "filesystem": "module-and-run-only" },
"timeout_ms": 10000
}
Таблица 4 — Манифест модуля
| Поле | Тип | Обязательно |
|---|---|---|
format |
"ogas.module/1" |
да |
id |
string | да |
version |
string | да |
name_key |
string | да |
description_key |
string | нет |
runtime |
"python" | "native" |
да |
entrypoint |
string относительно папки модуля | да |
inputs / outputs |
массив портов | да, может быть пустым для outputs |
input_policy |
all | any |
да |
parallelism |
целое ≥ 1 | да |
process |
step | long | service |
да |
resources.gpu_mb |
целое ≥ 0: сколько видеопамяти нужно поднятому модулю | нет, по умолчанию 0 |
suspend |
bool: умеет ли модуль сохранить промежуточное состояние перед выгрузкой и продолжить с него | нет, по умолчанию false |
execution_semantics |
idempotent | non_idempotent | unknown |
да |
retry.max_attempts |
целое ≥ 1 | да |
retry.delay_ms |
целое ≥ 0 | да |
retry.exponential |
bool | нет |
timeout_ms |
целое ≥ 1 | да |
parameters |
объект: имя параметра → { type, default?, min?, max?, choices? }, у нейросети — { type: "model", kind, default? } |
нет |
permissions_info |
{ network, filesystem } |
нет |
role |
assistant_network — модуль служит сетью помощника мастерской: вход request, выход reply |
нет |
secrets |
имена секретов, которые модуль читает из .env своей папки: заглавные буквы, цифры и _; окно спрашивает их у человека |
нет |
runtime_python |
string, например ">=3.11" |
нет |
Порт: name, type — имя вида данных из таблицы 11 (16.1), required. У входа может быть join_key — путь через точку. Входы и выходы манифеста — это порты узла на графе: рёбра соединяют их по имени, а имя выхода модуль называет в поле port ответа (Методы модулей, раздел 1).
Параметр:
type—string,integer,number,string[]илиcolor, как у настроек (Настройки, раздел 2);boolean— да или нет, безmin,maxиchoices, выключатель пульта подаёт его как есть;model— нейросеть одного вида;default— значение, которое получает модуль, если узел параметр не задал; безdefaultпараметр необязательный, и модуль его не получает;minиmax— для чисел,choices— для закрытого набора;- у нейросети
kind— вид, имя подпапки папки нейросетей: строчные латинские буквы, цифры,_и-;default— имя файла или папки нейросети в ней; пределов и закрытого набора у неё нет — выбирают среди того, что лежит (Модули, раздел 11); - необязательное
sourcesнейросети — откуда взять еёdefault: список{ "source", "id", "files" }, гдеsource— вид источника (huggingface,civitaiилиogas),id— то, что источник так называет (unsloth/Qwen2.5-7B-Instruct-bnb-4bit), а необязательноеfiles— какие файлы взять; без него — те, что источник предлагает сам (раздел 17).
Поля models — списка нейросетей, которые модуль берёт всегда, — у манифеста нет: нейросеть берут параметром, и выбирает её узел. Что такое параметр и где его тексты — Модули, раздел 10.
Тексты в манифесте отсутствуют: название, пояснение и подсказки хранятся в словарях модуля.
permissions_info — только сведения для человека: системные вызовы по ним не ограничиваются.
5 Рабочий процесс
Папка data/processes/<process_id>/ — всё, что относится к процессу: process.json, workspace.json, settings.json, state.json, запомненные списки kept/<id переменной>/, modules.lock.json, generation.json, память узлов state/<имя>/, рисунки пульта и виды его элементов pulpit/, файлы человека inputs/, файлы постоянных и переменных files/ (16.3), прогоны runs/<run_id>/, их результаты results/<run_id>/ и окружение .venv. Кто чем владеет — Программа: граф и запуски, раздел 1.
5.1 process.json — ogas.process/4
{
"format": "ogas.process/4",
"generation": 1,
"id": "chat",
"name": "Chat",
"description": "",
"variables": [
{ "id": "history", "type": "list", "initial": [], "remember": true },
{ "id": "style", "type": "text", "initial": "watercolor", "remember": true }
],
"nodes": [
{
"id": "send",
"kind": "event",
"x": 40, "y": 80,
"parameters": { "source": "pult", "overlap": "queue" }
},
{
"id": "reply",
"kind": "module",
"module_id": "llm",
"x": 240, "y": 80,
"parameters": { "temperature": { "variable": "temperature" } }
},
{
"id": "remember_reply",
"kind": "write",
"x": 440, "y": 80,
"parameters": { "variable": "history", "mode": "append" }
},
{
"id": "history_now",
"kind": "variable",
"x": 240, "y": 200,
"parameters": { "variable": "history" }
},
{
"id": "retry_image",
"kind": "repeat",
"x": 240, "y": 320,
"parameters": { "max_iterations": 3, "max_loop_ms": 600000 }
},
{
"id": "draw",
"kind": "module",
"module_id": "image_gen",
"region": "retry_image",
"x": 280, "y": 360
}
],
"edges": [
{
"id": "e1",
"source_node": "send",
"source_port": "event",
"target_node": "reply",
"target_port": "input",
"parts": null,
"join_key": null
},
{
"id": "e2",
"source_node": "history_now",
"source_port": "value",
"target_node": "reply",
"target_port": "history"
}
],
"groups": []
}
kindузла — вид из каталога узловой системы (Виды узлов, приложение А); вид, которого программа не знает, отклоняется сgraph_node_not_supported. Группа видом узла не бывает. Параметры каждого вида — Программа: граф и запуски, разделы 5, 11, 18, 22.name— название узла, данное человеком: обычный текст, как имя группы. Без поля узел называется по умолчанию — видом, а узел-модуль названием модуля (Программа: граф и запуски, раздел 4). Поле необязательное.description— описание узла словами человека: что узел делает и зачем он здесь; обычный текст, может занимать несколько строк. На прогон не влияет (Программа: граф и запуски, раздел 4). Поле необязательное.region— id рамки, внутри которой расположен узел; у узла вне рамок поля нет.collapsed— у рамки: свёрнута ли она в один узел (Мастерская, 5.3); при отсутствии поля рамка развёрнута. Прогон поле не читает.shown_parameters— необязательно: параметры, которые узел показывает на холсте строками, по порядку отметки — у узла-модуля имена параметров манифеста, у узла платформы пути полей его вида (Мастерская, 5.3). Прогон поле не читает, и функция графа пишет его запросомprocess.node.showи у включённой программы, не меняя поколения (Методы графа).face_marks— необязательно, у узла верхнего уровня: отметки заранее — на лицо группы, которую из него соберут, —inputsиoutputs— имена его портов,variables— переменные, с которыми он работает (Программа: граф и запуски, 17.1). Когда узел оказывается в группе, отметки переходят на её лицо и поле уходит.memory— у узла-модуля, который делит память с другими узлами своего модуля: имя общей памяти, латинские буквы, цифры,-и_. Без поля память узла названа его идентификатором (Программа: граф и запуски, раздел 13).overlap— у узла службы, которая сама выдаёт пакеты: правило наложения её пакетов —queue,skip,replaceилиparallel; без поля —queue. У других узлов поля нет: у события правило наложения — его параметр (Программа: граф и запуски, раздел 5).- Параметры событий и узлов задаёт их вид (Виды узлов, приложение А). У узла
notify—title, заголовок уведомления, иpath, поле его текста (Программа: граф и запуски, 24.6). У события сsource: "message"—name, имя сообщения; сsource: "change"—variable, переменная, за которой оно следит; у узлаsend—process_id,mode(message|signal),nameи, у сигнала,key(Программа: граф и запуски, раздел 23). У события сsource: "call"и у узлаresultнеобязательноеname— имя входа и имя выхода, под которым они стоят портами у узла, вызывающего процесс; без имени —inputиsuccess. У узлаprocess—process_idи необязательноеvalues: значения переменных вызываемого процесса в этом месте вызова,{ "<id переменной>": <значение её типа> }(Программа: граф и запуски, раздел 18). Поля необязательные. - Узел хранит только свои значения. Порты узла платформы задаёт его вид (Программа: граф и запуски, раздел 4), порты и схему параметров узла-модуля — манифест, версию модуля —
modules.lock.json; в узел они не копируются. Узел-модуль хранитmodule_id, заданные параметры, место, рамку и группу. Параметр, которого нет вparameters, получает умолчание манифеста.input_policyиretryузла, если заданы, перекрывают те же поля манифеста (Программа: граф и запуски, 10.3, 12.4). - Значение параметра — само значение, подстановка настройки
"${имя}"или переменная{ "variable": "<id>" }(Программа: граф и запуски, разделы 6, 15). variables— переменные программы:id(идентификатор[A-Za-z_][A-Za-z0-9_]*),type— имя вида данных из таблицы 11 (16.1),initial— значение этого типа илиnull: у картинки, звука, видео и файла — пока файла нет,remember— сохраняется ли значение после выключения,scope— необязательно,run— переменная прогона: своё значение у каждого прогона, безremember; без поля — переменная программы (Программа: граф и запуски, раздел 7).x,yузла — место на холсте графа, любые числа, отрицательные тоже: у холста нет краёв; раскладка графа — часть документа процесса.- Ребро обязано иметь
source_node,source_port,target_node,target_port.idребра необязателен; если его нет, функция графа назначает его при сохранении. Холодное ребро отличается не полем, а источником: оно идёт от узлаvariableилиconstant. partsребра — массив имён частей для отбора илиnull(передать всё содержимое пакета). Имя ищется сперва в частях пакета (его полеparts), потом в самом пакете — как путь (Программа: граф и запуски, раздел 8). На холодном ребре отбор действует на значение переменной или постоянной.viaребра — необязательно: точки на холсте, через которые связь ведут, по порядку, каждая — пара чисел[x, y](Мастерская, 5.1). Прогон поле не читает: данные идут по связи, как без него.- Заметка — запись среди узлов с
kind: "note", местомx,y,group_idилиregion, как у узла, и параметромtext— словами человека. Портов у неё нет, связей к ней не бывает, и прогон к ней не приходит: это вид-сокращение УС-06 без смысла для работы (Виды узлов, приложение А).
Группа:
{
"group_id": "g1",
"name": "Зажигание",
"node_ids": ["reply"],
"group_id_parent": null,
"x": 120.0,
"y": 40.0,
"collapsed": true,
"ports_in": [{ "node_id": "reply", "port": "input", "name": "Картинка" }],
"ports_out": [{ "node_id": "reply", "port": "success", "name": "Готово" }],
"variables": ["style"]
}
name— имя группы обычным текстом: его пишет человек, и перевода оно не имеет. Ключ словаря и собственная папка с текстами есть у элементов, поставляемых готовыми, — у модуля (Программа: граф и запуски, 17.1).description— описание группы словами человека, как у узла: что она делает и зачем. Поле необязательное.node_ids— узлы, лежащие прямо в группе. То же говорит полеgroup_idузла; если узел назван и там и там, это одна и та же группа, иначе —graph_invalid(Программа: граф и запуски, 17.1).group_id_parent— группа, внутри которой расположена данная;nullили нет поля — группа верхнего уровня. Вложенность не ограничена (Программа: граф и запуски, 17.1).x,y— место свёрнутой группы на холсте: левый верхний угол того, что в ней лежит (Программа: граф и запуски, 17.1); у развёрнутой место занимает её область, как у рамки. Группа без этих полей стоит у угла своих узлов.collapsed— свёрнута ли группа. При отсутствии поля группа развёрнута.ports_in,ports_out— лицо: порт внутреннего узла, вынесенный наружу, со своим именем. Ребро снаружи в документе по-прежнему идёт в узел, а не в группу: свёртка — способ показа, и прогон от неё не меняется.nameстроки необязательно: без него строку называют по порту.variables— идентификаторы переменных программы, показанных на лице. Группа ими не владеет (Программа: граф и запуски, раздел 7).- Отметки заранее у группы верхнего уровня (Программа: граф и запуски, 17.1):
marked: trueу строкиports_inилиports_out— строка встанет на лицо группы, которую соберут из этой, иface_marks.variables— переменные лица, отмеченные так же. Когда группа оказывается в другой, отметки переходят на лицо той и уходят.
5.2 state.json — ogas.state/2 и запомненные списки
Значения переменных с отметкой remember, хозяин — функция графа. Читаются при включении программы и попадают на диск при каждой записи такой переменной:
state.jsonдержит значения запомненных переменных, которые не списки, и поколение каждого запомненного списка;- запомненный список держит свои элементы в папке
kept/<id переменной>/<поколение>/: список, записанный целиком, — одним документомall.json(ogas.state-items/1, полеitems, элементы нумеруются с 1 по порядку), элемент, добавленный после него или изменённый, — своим документом<номер>.json(ogas.state-item/1, полеvalue), который стоит поверх элемента с тем же номером; порядок списка — порядок номеров; - запись добавленного письма — один маленький документ, запись по ключу — документ этого элемента; элемент, добавленный после целого и убранный, — его документ удалён вместе с копией
.bak, которую ядро держит у переписанного документа; элемент целого убранный, вставленный не в конец, и список, заменённый целиком, записываются целиком новым поколением; так же — пачка: запись, которая добавила или изменила больше 64 элементов сразу (папка из тысяч снимков — один документ, а не тысячи маленьких, каждый со своим ожиданием диска). Новое поколение пишется раньше, чемstate.jsonего называет, а прежнее удаляется после: оборвись запись между ними, программа начнёт с прежнего списка или с нового, но не с половины каждого. Убранное на диске не остаётся; - записи запомненных значений одной программы ложатся на диск в том порядке, в каком менялись значения.
{
"format": "ogas.state/2",
"values": { "style": "watercolor" },
"lists": { "history": 3 }
}
Значение переменной, которой больше нет в process.json или тип которой не совпадает, при включении не берётся: переменная получает initial. Переменная, переименованная после последней записи, находит своё значение под прежним именем — по записи renamed_objects документа процесса; следующая её запись кладёт значение уже под новым, а список — целиком в папку нового имени. state.json и kept/ принадлежат человеку, который работает с программой: в экспорт процесса они не входят.
5.3 Память узла
state/<имя>/ — память узла: то, что модуль узла сохраняет перед выгрузкой (Модули, раздел 4). Память принадлежит узлу, а не модулю: модуль один на все свои узлы. Имя — идентификатор узла или имя общей памяти из поля memory узла, которую называют несколько узлов одного модуля. Что в ней, решает модуль; путь к ней он получает с каждым шагом узла в поле state_dir. Платформа её не читает, переносит в экспорт, только если человек попросил (10.1), и удаляет, когда её не называет ни один узел.
Рядом с папкой лежит отметка о сохранённой памяти state/<имя>.saved.json — ogas.module-state/1: она называет модуль, который сохранил память. Её пишет функция модулей, а не модуль: целиком, после того как модуль ответил, что сохранил память, и убирает перед тем, как попросить его сохраниться снова. Имя папки — латинские буквы, цифры, - и _; у узла с другим идентификатором и без memory памяти нет.
{
"format": "ogas.module-state/1",
"module_id": "painter",
"version": "1.0.0",
"digest": "sha256:…",
"saved_at": "2026-10-07T17:30:00Z"
}
version и digest — версия и отпечаток модуля, сохранившего состояние (Модули, раздел 3). Модуль, поднятый с теми же идентификатором, версией и отпечатком, получает resumed: true; без отметки или при расхождении — resumed: false, даже если папка не пуста.
6 Сквозной объект
Сквозные объекты не хранятся отдельным списком: их задают узлы и переменные графа (Программа: граф и запуски, раздел 16).
Таблица 5 — Сквозной объект
object_id |
Откуда берётся |
|---|---|
event:<node_id>, cancel:<node_id>, retry:<node_id>, status:<node_id> |
узел event с source: pult |
var:<variable_id> |
переменная из variables |
ask:<node_id> |
узел ask |
program |
сама программа |
Пульт хранит только object_id в своих привязках (раздел 7).
7 Пульт
workspace.json — ogas.workspace/3: граф интерфейса программы (Мастерская, 6.8). Владелец — функция canvas, смысл объектов — в process.json.
{
"format": "ogas.workspace/3",
"generation": 8,
"process_id": "comic",
"pulpit_id": "main",
"theme": { "accent": "#c8892b" },
"bindings": { "open": "var:screen" },
"groups": [
{ "id": "window_1", "name": "Комикс", "holds": "tabs" },
{ "id": "tab_1", "name": "Рисовать", "parent": "window_1" },
{ "id": "tab_2", "name": "Галерея", "parent": "window_1", "bindings": { "shown": "var:has_frames" } },
{ "id": "group_1", "name": "Сцена", "parent": "tab_1" }
],
"elements": [
{ "id": "field_1", "kind": "field", "group": "group_1", "bindings": { "value": "var:scenario" },
"label": "Сценарий", "parameters": { "lines": 6 } },
{ "id": "file_1", "kind": "file", "group": "group_1", "bindings": { "value": "var:hero" },
"label": "Герой", "parameters": { "kind": "image" } },
{ "id": "button_1", "kind": "button", "group": "group_1",
"bindings": { "act": "event:draw", "carries": { "scenario": "var:scenario", "hero": "var:hero" }, "opens": "tab_2" },
"label": "Нарисовать" },
{ "id": "view_1", "kind": "view", "group": "tab_2", "bindings": { "value": "show:comic" } }
]
}
generation— поколение документа пульта: каждая запись функцииcanvas— следующее поколение после записанного;canvas.snapshotназывает егоrevision.pulpit_id— имя пульта, идентификатор. У процесса один пульт, его имя —main; идентификаторы вместилищ и элементов единственны в пределах пульта.theme— необязательная тема пульта:{ "<ключ>": значение }. Ключи — закрытый перечень функцииcanvas:background,surface,text,accent,ok,stop— цвета вида#rrggbbили#rgb;font—bundled,system,serifилиmono;letters— высота букв, целое от 8 до 48 единиц листа;radius— скругление, целое от 0 до 40 единиц листа. Перечень и допустимое значение каждого ключа — разделcanvas.themeсхемы настроек функции пульта: ключ<ключ>описан там какcanvas.theme_<ключ>(Настройки, раздел 8). Ключ вне перечня и значение, которого ключ не принимает, — отказ. Ключ, которого в документе нет, берётся из настроек; без поля пульт рисуется видом по умолчанию.bindings— необязательные привязки верхнего уровня:open— переменная типаtext: какое окно открыто, поidокна. Без неё программа открывается на первом окне.groups— вместилища, не меньше одного. Окно — вместилище безparent; вкладка — вместилище в том, что показывает своё по одному; группа и подгруппа — вместилище в том, что показывает всё сразу. Окна показываются по одному, программа начинается с первого. У вместилища:id— идентификатор, единственный в пульте среди вместилищ и элементов; даёт функция пульта;name— название, заданное человеком; необязательное: окно без названия называется именем процесса, вкладка — текстом словаря функции пульта «Основное», группа остаётся без заголовка;parent—idвместилища, в котором оно лежит; у окна поля нет;holds— как вместилище показывает то, что в нём лежит:together(по умолчанию) — всё сразу,tabs— по одному, с переключателем;bindings— необязательные привязки к переменным программы:shown— переменная типаboolean: приfalseвместилище со всем, что в нём, не показывается;open— только у вместилища сholds: tabs— переменная типаtext: какое из вложенных открыто, по егоid; переключив вкладку рукой, человек пишет эту переменную, как поле пишет свою. Так процесс открывает вкладку и прячет группу записью в переменную (Мастерская, 6.8);look— необязательное имя вида вместилища (7.2);screen— лист страницы — окна без вкладок или вкладки окна:sheet—[ширина, высота]в единицах листа,background— подложка:colorиimage, оба необязательны. Лист у страницы есть всегда: страница без него получает его при записи и при чтении (Мастерская, 6.3). У вкладок одного окна лист один: функция пульта даёт им самый широкий и самый высокий из их листов. Вместилище внутри страницы — рамка вокруг того, что в нём лежит: её считает функция пульта (boxвcanvas.snapshot), своих листа и размера у него нет;place— только у пустого, там, где оно стоит;placed—falseу рамки, которую человек убрал с листа: она и всё, что в ней лежит, не стоят на листе, а место, которое у них было, сохраняется (Мастерская, 6.3). Только у того, что лежит на листе страницы; без поля — на листе;layer— место вместилища на холсте графа интерфейса[x, y],collapsed— свёрнуто ли оно там (Мастерская, 6.8). Развёрнутое вместилище — область вокруг того, что в нём лежит;layer— место, где стоит свёрнутое или пустое вместилище; пишет его рука, которая переносит вместилище за шапку;reflections— необязательные отражения: то, что рабочий граф отдаёт интерфейсу, узлами графа интерфейса (Мастерская, 6.4 и 6.8). У отражения:id— идентификатор, единственный в пульте среди вместилищ, элементов и отражений; даёт функция пульта:reflection_<n>;objects— его строки:object_idсквозных объектов, не меньше одного. Элемент связан с отражением общим объектом;source— что отражено:node:<node_id>— узел целиком,in:<node_id>/<вход>,out:<node_id>/<выход>,param:<node_id>/<параметр>— параметр, который берёт переменную из строки,var:<переменная>,group:<group_id>— лицо собранного узла,program— включение программы; по нему отражение названо и от него переходят к оригиналу;group— необязательное вместилище, в котором отражение лежит на графе интерфейса; без него — в первом окне. Вместилище, в котором на любой глубине лежат одни отражения и ни одного элемента, на листе не стоит;layer— место на холсте графа интерфейса[x, y]; пишет его рука;elements— элементы. Порядок в списке — порядок внутри вместилища, порядок рисования на листе (последний сверху) и порядок обхода клавишейTab. У элемента:id— идентификатор, единственный в пульте; даёт функция пульта: вид и номер;kind— вид элемента из набора (таблица 6);group—idвместилища, в котором элемент лежит;bindings— привязки к сквозным объектам (Программа: граф и запуски, раздел 16):{ "<имя привязки>": "<object_id>" }; привязки, которую вид требует, может не быть: такой элемент записывается и помечаетсяunbound— его поставили на граф интерфейса и ещё не связали; привязка к нескольким объектам называет каждый своим именем —{ "<имя>": "<object_id>", … }, имя — идентификатор. Имена привязок и допустимые виды объектов называет вид элемента (таблица 6). У украшения поля нет;label— подпись, заданная человеком; необязательная и не пустая: без неё окно называет элемент по его объекту — текстом словаря графа по виду объекта. У украшения поля нет;hint— подсказка, заданная человеком: что элемент задаёт и куда его крутить; необязательная и не пустая. Окно показывает её над элементом при наведении и говорит экранному чтецу. У украшения поля нет;screen—place—[x, y]левого верхнего угла иsize—[ширина, высота], целые числа в единицах листа: только у элемента, который лежит на листе вместилища на любой глубине; там элемент лежит на листе целиком и не меньше наименьшего размера своего вида;anchor— необязательное: как элемент держится в окне больше нарисованного листа (Мастерская, 6.3):x—left,right,middleилиstretch,y—top,bottom,middleилиstretch; чего нет, то говорит вид элемента;placed—falseу элемента, который не размещён: человек убрал его с листа или ещё не положил на него. Он работает, как всякий, но не нарисован, функция пульта места ему не даёт, аscreen, который у него был, сохраняет как есть — числами, которые могут и не лежать на нынешнем листе. Только у того, что лежит на листе страницы; без поля — на листе;layer— место графического узла на холсте графа интерфейса[x, y], в мерах рабочего графа; пишет его рука, которая ставит и переносит узел. Без него узел стоит там, где его ставит начальная раскладка: окна одно под другим справа от рабочего графа, элементы вместилища столбиком в нём;look— необязательное имя вида элемента (7.2); без него элемент рисуется видом набора по умолчанию;parameters— необязательные параметры вида элемента (таблица 6).
Таблица 6 — Первый набор элементов пульта
kind |
Элемент | Род | Привязки | Параметры |
|---|---|---|---|---|
blank |
пустой элемент | — | holds — что держит: любой объект программы — событие, вопрос, «Показать», выход, вход, связь, ход шага, переменная. Пустой элемент — графический узел, вид которого ещё не выбран: функция пульта пишет ему placed: false, на лист он не ложится, а canvas.snapshot помечает его no_kind, пока он к чему-то привязан (Мастерская, 6.8) |
— |
button |
кнопка | действие | act — событие, его остановка или повтор, или сигнал, которого ждёт узел (signal:<node_id>); carries — необязательные переменные, значения которых кнопка несёт событию, каждая под именем поля пакета (Программа: граф и запуски, раздел 5); допустима только у кнопки события и сигнала: сигналу они уходят его данными; opens — необязательное вместилище этого же графа: кнопка открывает вкладку или окно, а при act ещё и нажимает событие; resets — необязательное вместилище этого же графа: кнопка возвращает к начальному значению каждую переменную, которую пишут элементы ввода в нём на любой глубине (process.variable.reset), — так у настроек есть «Вернуть как было». Кнопка называет хотя бы одну из act, opens, resets |
— |
toggle |
выключатель | ввод | value — переменная типа boolean или включение программы |
— |
rows |
строки | ввод | value — переменная типа list: человек правит её строками — меняет клетку, добавляет строку под последней и убирает строку; поля записи — столбцами, у списка не из записей — один столбец. Клетка берёт то, что держит её столбец в первой строке, где он задан: «да» или «нет» — выключателем, число — числом, текст — строкой; столбец, которого нет ни в одной строке, — строкой; иное показано по правилам показа (16.5) и здесь не правится. Добавленная строка — запись из столбцов, каждый пустой своего вида: пустая строка, «нет»; число не ставится, пока его не набрали. Переменная получает весь список, когда набор клетки закончен, строка добавлена или убрана |
fields — список полей, которые видны столбцами, по порядку; пусто — поля, которые есть в записях, в порядке появления; titles — слова над столбцами, по порядку; пустая строка или её нехватка — имя поля; lines — сколько строк текста видно в ячейке, целое от 1 до 12, по умолчанию 1: больше одной — ячейка многострочная, и Enter переводит строку |
field |
поле | ввод | value — переменная типа text, time, json или list; время выбирают выбором даты и времени системы, значение json и list набирают текстом JSON |
lines — сколько строк видно, целое от 1 до 40, по умолчанию 1; больше одной — поле многострочное; writes — когда поле пишет переменную: done — когда набор закончен, по Enter или уходу из поля, по умолчанию; typing — ещё и на каждой паузе в наборе, только у текста: данные, набранные наполовину, значением не стали |
file |
файл | ввод | value — переменная картинки, звука, видео, файла или json: выбранный файл ложится в файлы процесса, а переменная получает ссылку на него (Программа: граф и запуски, раздел 24) |
kind — вид файла: image, audio, video или file (по умолчанию) |
number |
число | ввод | value — переменная типа number |
min, max, step — необязательные числа |
choice |
выбор | ввод | value — переменная типа text |
options — список значений; пока он пуст, выбирать не из чего; titles — слова, которыми значения показаны человеку, по порядку; пустая строка или её нехватка — само значение: так переменная держит код параметра модуля, а человек видит слово; models — вид нейросетей: тогда выбирают из нейросетей этого вида в библиотеке, и options не нужен |
ask |
вопрос | ввод | question — вопрос человеку; ответ — того вида, какой просит узел вопроса: текст, число, выбор, да или нет, область, точки или закрашенное кистью на картинке вопроса, файл своего вида (Программа: граф и запуски, раздел 9) |
— |
value |
значение | показ | value — переменная любого типа, выход узла (out:<node_id>/<выход>), вход узла (in:<node_id>/<вход>) или связь (edge:<edge_id>) |
— |
view |
просмотр | показ | value — узел «Показать» (show:<node_id>), выход узла (out:<node_id>/<выход>), вход узла, связь или переменная: показывает так же, как узел «Показать» на холсте — текст, число, список, картинку, галерею, проигрыватель звука и видео (16.5) |
— |
table |
таблица | показ | value — переменная типа list, узел «Показать», выход узла, вход узла или связь: записи списка строками, поля столбцами, ячейка — по правилам показа (16.5), картинка — картинкой; picked — необязательная переменная, в которую щелчок по строке пишет её ключ; выбранная строка — та, чей ключ равен значению переменной; act — необязательное событие: щелчок по строке после выбора нажимает его, как кнопка, у выключенной программы только выбирает; carries — как у кнопки, допустима только при act; переменная выбора в нём уже держит ключ этой строки; only — необязательная переменная: видны только записи, у которых поле by равно её значению, а пока она пуста — ни одной. Длинный список стоит частями по 200 строк: следующая часть приходит, когда человек прокрутил таблицу к концу нарисованного, или по кнопке «Показать ещё» с числом оставшихся (лента из тысяч снимков, нарисованная целиком, задерживает каждую перерисовку окна на 0,7 с) |
fields — список полей, которые видны столбцами, по порядку; пусто — все поля первой записи; titles — слова над столбцами, по порядку; пустая строка или её нехватка — имя поля (проверка пульта пустую строку принимает только здесь, в fields — нет); sort — поле, по которому строки стоят по порядку: числа по величине, остальное как текст, запись без поля — после остальных; пусто — в порядке списка, а сам список порядка не меняет; order — up по возрастанию или down по убыванию, по умолчанию up; key — поле записи, которое пишется в picked; пусто — вся запись; pick — one: щелчок выбирает одну строку, по умолчанию; many: у каждой строки метка, щелчок отмечает строку или снимает отметку, picked держит список ключей отмеченных строк, а событие щелчок не нажимает; by — поле записи, которое сравнивают со значением only; lines — one: ячейка в одну строку, длинное обрезается, по умолчанию; all: текст читается целиком, ячейка растёт; follow — no: таблица стоит, где её оставили, по умолчанию; end: новая строка внизу сама уходит в вид, как реплика разговора |
bar |
полоса | показ | value — переменная типа number или ход шага узла модуля (progress:<node_id>): доля от нуля до целого |
min, max — границы полосы для переменной; по умолчанию 0 и 100 |
indicator |
индикатор | показ | state — состояние прогона события |
shows — color (по умолчанию) или words: состояние цветом или словами |
label |
надпись | украшение | — | text — текст надписи, необязательный: без него надпись называет себя; size — необязательная высота букв в единицах листа; align — left (по умолчанию), center или right |
picture |
рисунок | украшение | — | image — необязательное имя файла рисунка (7.2): без него на листе пустое место; fit — contain (по умолчанию), cover или stretch: рисунок целиком, во всю площадь или растянут |
Привязка, не названная необязательной, обязательна. Набор пополняет платформа: новый вид элемента формат не меняет. Рамки в наборе нет: собрать элементы вместе — дело группы.
Что функция пульта отклоняет (canvas_invalid, с названием элемента или вместилища и поля): документ без вместилищ; повторяющийся id; parent или group, которых нет; вместилище, лежащее само в себе прямо или через другие; holds вне перечня; привязку вместилища, кроме shown и open, open у вместилища без вкладок и привязку вместилища не к переменной; opens, который называет не вместилище; kind вне набора; привязку, которой нет у вида элемента, отсутствие обязательной привязки, несколько объектов там, где объект один, и наоборот; opens или resets, которые называют не вместилище этого пульта; параметр, которого нет у вида элемента, или его значение вне допустимого; пустую hint; place и size у того, что лежит не на странице; лист у вместилища, которое само стоит на листе другого; size у вместилища; элемент на листе, выходящий за лист или меньше наименьшего размера; placed, который не true и не false, и placed: false у того, что не лежит на листе; anchor, который называет что-то, кроме x и y, или значение вне их перечня; отражение без objects или с пустым списком, group отражения, которого нет, и повторяющийся id отражения; label, hint или привязку у украшения; ключ темы вне перечня или с недопустимым значением; имя рисунка, которое не является именем файла рисунка (7.2); два элемента одного вида с одними и теми же привязками в одном вместилище (Мастерская, 6.2) — кроме украшений.
Что функция пульта не отклоняет, а помечает. Объект, которого в графе нет, и объект, вид или тип которого элементу или вместилищу не подходит, отказом записи не являются: граф меняется после пульта, и пульт не должен из-за этого терять документ. Такой элемент или вместилище canvas.snapshot отдаёт помеченным, и мастерская показывает его на месте (С чего начать, 4); помеченный элемент ничего не отправляет и не записывает, а вместилище с помеченной привязкой показывается, как без неё. Так же помечается то, рисунка которого в папке процесса нет (7.2): why — image_gone; подложка такого листа показана одним цветом.
Что функция пульта дополняет при записи:
- элемент или вместилище без
idполучают его; элемент безgroupпопадает в первое вместилище, в котором нет вложенных; вместилище безholdsпоказывает всё сразу; - страница без листа получает лист размера нового листа;
- объект, к которому привязан элемент и которого нет ни в одном отражении, получает своё отражение (
source— сам объект); отражение безidполучает его. Так же — при чтении: пульт, собранный раньше или помощником, читается с отражениями; - то, что лежит на странице без места и не убрано с листа (
placed: false), получает место там, куда страница положила бы его сама (Мастерская, 6.8): элементы вместилища сверху вниз, под тем, что в нём уже стоит, — то, что читают и набирают, во всю ширину, кнопки подряд в ряд, остальное шириной своего вида; затем его вместилища — рамкой с головой высотой 32 единицы и полями в промежуток листа; вкладки вместилища вкладок — одна над другой. Места — на шаге листа, с отступом от краёв и промежутком — настройками функции пульта (Настройки, раздел 8); - когда положенное в стоящую рамку делает её выше, то, что стоит под ней, опускается на столько же; лист растёт вниз до того, что на нём стоит;
- лист у вместилища внутри страницы снимается, и то, что на нём стояло, получает места заново.
Место и размер, записанные в документе, функция пульта иначе не меняет. Пульт без листов при чтении раскладывается так же.
Документ читают canvas.get; canvas.snapshot — поверхность для окна, а не документ.
7.1 Раскладка окон мастерской
data/config/layout.json — ogas.layout/1. Владелец — функция ui, записывает его по запросу страницы (ui.window.layout.set, Методы мастерской и пульта, раздел 2). Документ хранит расположение окон, оставленное человеком, и настройкой не является: в окне настроек его нет. Как и user.json, он принадлежит человеку, который здесь работает, и в репозиторий и поставку не входит.
{
"format": "ogas.layout/1",
"host": [1180, 758],
"windows": [
{ "kind": "graph", "x": 0, "y": 0, "w": 1180, "h": 379, "z": 4, "maximized": false, "folded": false, "minimized": 0, "restore": null },
{ "kind": "journal", "x": 0, "y": 379, "w": 1180, "h": 379, "z": 3, "maximized": false, "folded": false, "minimized": 0, "restore": null },
{ "kind": "settings", "x": 40, "y": 40, "w": 614, "h": 417, "z": 2, "maximized": false, "folded": false, "minimized": 1, "restore": null }
],
"panels": { "graph": [573, 0, 340, 703] }
}
host— размер хоста, на котором раскладку записали,[ширина, высота]в пикселях CSS;null, пока ничего не записано.windows— открытые окна на хосте, не больше 96. У окна:kind— вид окна;process— у окна процесса (граф, пульт, журнал программы) идентификатор его процесса (Мастерская, раздел 5); окно без него показывает процесс, открытый при запуске;x,y,w,h— прямоугольник на хосте;z— порядок наложения;maximizedиfolded— развёрнуто и свёрнуто на месте;minimized—0или место в нижнем баре;restore— прямоугольник до разворота илиnull. Поляtiled(да или нет) иsnap({ kind, side }илиnull) функцияuiпринимает и хранит, а страница не читает: окно стоит там, куда его поставили (Мастерская, 5.1).panels— где человек поставил панель выбранного в окне каждого вида:{ "<вид окна>": [x, y, w, h] }— прямоугольник в рабочем поле окна, в пикселях CSS, не больше 32 видов. Вида, у которого панель стоит на своём месте внизу поля, вpanelsнет (Мастерская, 5.1). Поле необязательное. Вид окна, которого страница не знает, и место без размера пропускаются; внутри поля панель держит страница, какого бы размера оно ни было.- При запуске страница применяет раскладку на хост этого запуска: прямоугольники пересчитываются пропорционально прежнему хосту и остаются внутри нового. Вид окна, которого страница не знает, и повтор вида пропускаются; пара, у которой не вернулась вторая половина, распадается.
7.2 Рисунки пульта и вид элемента
Рисунки пульта и свои виды его элементов лежат в папке процесса, в папке pulpit/; хозяин — функция canvas. Они принадлежат процессу: удаление папки процесса уносит их, передача процесса переносит.
processes/<process_id>/pulpit/
images/<файл> рисунки подложек и украшений
looks/<look_id>/look.json свой вид элемента
looks/<look_id>/<файл> рисунки этого вида
- Документ пульта называет рисунок именем файла в
images/: без папок, имя не начинается с точки, не длиннее 120 байт и кончается на.png,.jpg,.jpeg,.gif,.webpили.svg. - Рисунок — файл PNG, JPEG, GIF, WebP или SVG: вид называет окончание имени, и файл должен начинаться так, как начинается файл этого вида. Размер — не больше настройки
canvas.image_max_bytes(Настройки, раздел 8). Окно показывает рисунок только как изображение: сценарии внутри SVG не исполняются, ссылки наружу не загружаются. - Рисунок кладёт в папку функция пульта: окно отдаёт ей сам файл, выбранный человеком, одним сообщением (Методы мастерской и пульта, раздел 3). Файл записывается рядом и получает своё имя, когда записан целиком; файл с тем же именем заменяется. Файл, положенный в папку руками, функция видит так же, а неподходящий — не отдаёт. Ссылки в папке не читаются и не переносятся.
lookэлемента и вместилища называет вид поlook_id. Вид ищется сначала среди своих видов процесса, затем среди встроенных видов этого элемента у функцииcanvas. Вид, которого нет (look_gone), вид, сделанный для другого элемента (look_unfit), и свой вид, описание которого не проходит проверку (look_broken), элементу не мешают работать: элемент рисуется видом набора по умолчанию, а поверхность называет причину вlook_why.- Встроенный вид — такой же документ с рисунками в папке элемента у функции
canvas:elements/<вид элемента>/looks/<look_id>/(Мастерская, 6.2). Название встроенного вида для человека — в словаре элемента:canvas.element.<вид элемента>.look.<look_id>.
Папка pulpit/ переносится с процессом (10.1).
look.json — ogas.look/1. Вид — данные: сценариев и ссылок за пределы своей папки в нём нет, поле вне перечня — отказ.
{
"format": "ogas.look/1",
"look_id": "brass",
"kind": "button",
"name": "Латунь",
"size": [200, 60],
"states": { "rest": "rest.svg", "hover": "hover.svg", "pressed": "down.svg", "disabled": "off.svg" },
"slice": [18, 18, 18, 18],
"content": [0, 14, 4, 14],
"words": "#2a1c05"
}
look_id— идентификатор, он же имя папки вида;kind— вид элемента, для которого вид сделан;name— необязательное название для человека; без него вид называется своимlook_id;size— необязательный размер[ширина, высота], под который вид нарисован, в единицах листа, не меньше наименьшего размера элемента: элемент, которому дали вид, принимает этот размер — так круглая лампа остаётся круглой;states— рисунки по состояниям элемента:{ "<состояние>": "<файл>" }. Состояния называет вид элемента (таблица 7). Рисунок первого состояния вида обязателен, когда вид не нарисован (paint); состояние без рисунка показывается рисунком первого;slice— необязательные четыре целых: отступы сверху, справа, снизу и слева, в точках рисунка, по которым рисунок режется на девять частей. Углы сохраняют размер, края тянутся вдоль, середина — в обе стороны. Без поля рисунок растягивается на элемент целиком. Одна точка рисунка — одна единица листа;content— необязательные четыре целых: отступы для слов и полей элемента от его краёв, в единицах листа;words— необязательно: цвет слов элемента поверх рисунка,#rrggbb, илиfalse— вид говорит всё рисунками, и элемент своих слов не показывает;value— необязательно и только у элемента, который показывает количество (таблица 7): как вид рисует долю между границами. Способ один из трёх:{ "fill": "<файл>", "slice": […], "inset": […], "from": "left" }— рисунок-заполнение, растянутый на долю:inset— отступы области заполнения от краёв элемента,from—left(по умолчанию) илиbottom;{ "frames": "<файл>", "count": 11, "along": "row" }— лента изcountравных кадров в строку (row, по умолчанию) или в столбец (column); кадр выбирается по доле. Кадров от 2 до 256;-
{ "pointer": "<файл>", "turn": [-120, 120], "pivot": [0.5, 0.5] }— стрелка: рисунок во весь элемент, повёрнутый на угол междуturn[0]иturn[1]градусов по доле вокруг точкиpivot— долей ширины и высоты элемента, по умолчанию середины. -
paint— необязательно: вид нарисован, а не составлен из рисунков (Мастерская, 6.5). Его делают на глаз в панели элемента, и документ вида — одинlook.json. Поля: states— цвета по состояниям:{ "<состояние>": { "fill", "stroke", "words" } }— фон, рамка и слова, каждый#rrggbbи необязателен; первое состояние вида обязательно, остальные берут недостающее у него;radius— скругление, целое от 0 до 64 единиц листа;stroke_width— толщина рамки, от 0 до 16;pattern— узор поверх фона:{ "kind": "stripes" | "checks" | "dots", "color": "#rrggbb", "size": 2…64 }— полосы, клетка или точки, шаг в единицах листа;font—bundled,system,serifилиmono, как в теме пульта;letters— высота букв, от 8 до 48;value— цвет, которым заполняется количество, только у элемента, который его показывает.
Вид может быть и нарисован, и составлен из рисунков: рисунок состояния ложится поверх нарисованного.
Каждый рисунок, названный в states и в value, лежит в папке вида и проходит ту же проверку, что рисунок пульта; в папке своего вида нет ничего, кроме look.json и названных им рисунков. Общий размер вида — не больше canvas.image_max_bytes.
Таблица 7 — Состояния элементов для вида
kind |
Состояния | Когда |
|---|---|---|
button |
rest, hover, pressed, disabled |
покой; под указателем; нажата; команду сейчас не выполнить: событие — программа выключена, остановка — прогон не идёт, повтор — последний прогон события не упал |
toggle |
off, on |
выключен; включён |
field, rows, number, choice, file |
rest, focus |
покой; в поле или клетке стоит курсор или выбор файла в фокусе |
ask |
rest, asked |
никого не спрашивают; вопрос открыт |
value, view, table |
rest |
— |
bar |
rest; показывает количество |
рисунок подложки; доля — поле value |
indicator |
idle, running, completed, failed, stopped |
запуска не было; идёт; прошёл; сбой; остановлен, прерван или отменён |
group — вместилище |
rest |
— |
label, picture |
нет | видов у них нет |
Состояния hover, pressed и focus окно узнаёт само — по указателю и курсору — и показывает поверх состояния, в котором элемент находится. Встроенные виды первого набора: button — key, toggle — lever, indicator — lamp, bar — tube (заполнение) и dial (стрелка), вместилище — plate. Вид вместилища описывают так же, с kind: "group".
7.3 Библиотека мастерской
data/config/library.json — ogas.library/1. Владелец — функция ui, записывает его целиком по запросу страницы после каждой правки (ui.window.library.set, Методы мастерской и пульта, раздел 2). Документ хранит, что человек держит на виду из своих программ и модулей (Мастерская, 5.6): панели программ справа и то, чего колонка графа не предлагает. Как и раскладка (7.1), он принадлежит человеку, настройкой не является и в репозиторий и поставку не входит.
{
"format": "ogas.library/1",
"bars": [
{ "id": "programs", "name": "", "programs": ["telegram", "process-5"] },
{ "id": "bar-2", "name": "Игры", "programs": [] }
],
"hidden": { "programs": ["process-3"], "modules": ["echo"] },
"views": { "programs": "tiles", "models": "table" },
"groups": { "programs": true, "models": false },
"fits_only": true
}
bars— панели программ в их порядке сверху вниз, не больше 32. У панели:id— строчные латинские буквы, цифры и-, не длиннее 40 и не повторяется;name— название, которое дал человек, не длиннее 80 букв; пустое название окно показывает словом раздела «Программы»;programs— идентификаторы программ на панели в их порядке, каждый один раз.hidden.programsиhidden.modules— программы и модули, которых колонка графа не предлагает, каждый один раз. Скрытое остаётся установленным и работает там, где уже стоит.views— каким видом человек смотрит раздел библиотеки: ключ — раздел (programs,modules,models,pieces,sources), значение —tiles(плитка) илиtable(таблица). Раздела, поля или другого значения нет — плитка.groups— показывает ли человек раздел группами: ключ — раздел, как уviews, значение —trueилиfalse. Раздела или поля нет — нейросети группами по видам, остальное одним списком.fits_only— выключатель «Только то, что запустится»:trueпрячет вещи, которым нужно больше видеопамяти, чем есть у видеокарты компьютера. Поля нет —false.- Каждый список — не больше 1024 идентификаторов. Документ, который не сходится с этим, функция
uiне записывает и не отдаёт:document_invalidс названием поля. - До первой записи
ui.window.library.getотдаёт одну пустую панельprogramsс пустым названием и пустыеhidden. Какие программы и модули есть, говорят их хозяева: идентификатор, которого больше нет, документ хранит, а окно не показывает; программу и модуль, удалённые из мастерской, окно убирает из документа само.
7.4 Заготовка интерфейса
data/library/pieces/<piece_id>.json — ogas.piece/1: вместилище пульта со всем, что в нём лежит, сохранённое в библиотеке, чтобы поставить его в другую программу (Мастерская, 6.8). Владелец — функция canvas: делает заготовку из записанного пульта процесса (canvas.piece.save, Методы мастерской и пульта, раздел 3), отдаёт и удаляет её. Заготовка принадлежит человеку, как библиотека (7.3), и в репозиторий и поставку не входит.
{
"format": "ogas.piece/1",
"piece_id": "razgovor",
"name": "Разговор",
"groups": [
{ "id": "talk", "name": "Разговор", "holds": "together", "layer": [640, 120] },
{ "id": "more", "parent": "talk", "holds": "together" }
],
"elements": [
{ "id": "field_1", "kind": "field", "group": "talk", "label": "Сообщение", "parameters": { "lines": 3 }, "layer": [660, 180] },
{ "id": "button_1", "kind": "button", "group": "talk", "label": "Ещё", "bindings": { "opens": "more" } }
]
}
piece_id— имя заготовки, оно же имя файла: строчные латинские буквы, цифры и-, не длиннее 40. Даёт его функция пульта из названия латиницей; название без латинских букв даётpiece; занятое получает номер через-.name— название: присланное при сохранении, иначе название вместилища, иначе егоid.groups— сохранённое вместилище первым, затем вложенные в него на любой глубине, в порядке пульта;elements— элементы, которые в них лежат, в порядке пульта. Поля — те же, что у пульта (раздел 7), со снятым:- у сохранённого вместилища нет
parent, а у егоscreen—placeиsize: место на листе того, в чём оно лежало, принадлежит тому; - у вместилищ нет
bindings: переменные, которые их показывают и открывают, — переменные своей программы; - у элемента из
bindingsостаются только привязки к вместилищу —opensиresets, — если они называют вместилище самой заготовки; остальное снимается, и элемент, поставленный в другую программу, помеченunbound, пока к нему не протянут связь; look, который называет свой вид процесса, снимается; встроенный вид остаётся.- Ставит заготовку окно: вместилища получают новые
id, свободные в пульте, сохранённое вместилище ложится во вместилище, выбранное человеком, элементы приходят безidи получают его от функции пульта при записи,opensиresetsпереводятся на новые имена, местаlayerсдвигаются вместе. Поставленное — часть пульта программы: с заготовкой оно больше не связано.
8 Настройки
Файлы: data/config/core.json (только core.*), data/config/system.json, data/config/user.json, processes/<id>/settings.json. Уровень сессии в файле не хранится. Формат ogas.settings/1.
{
"format": "ogas.settings/1",
"generation": 1,
"values": {
"core.health_interval_ms": 5000,
"i18n.language": "ru"
}
}
Уровни, порядок чтения, схема ключей и их перечень — Настройки. Секретов в этих файлах нет.
Схема настроек хозяина — файл settings.schema.json рядом с кодом хозяина, формат ogas.settings-schema/1. Программа хозяина встраивает его при сборке.
{
"format": "ogas.settings-schema/1",
"owner": "graph",
"group": "other",
"sections": [
{
"id": "graph.run",
"keys": {
"graph.max_iterations": { "type": "integer", "default": 1000, "min": 1, "max": 1000000, "restart": false },
"graph.runs_keep": { "type": "integer", "default": 20, "min": 0, "max": 10000, "restart": false }
}
}
]
}
Таблица 8 — Настройки
| Поле | Смысл |
|---|---|
owner |
хозяин; каждый ключ начинается с <owner>. |
group |
крупный раздел окна настроек: core, ui или other |
sections |
подразделы в том порядке, в каком их показывает окно; id подраздела начинается с <owner>. |
keys |
ключи подраздела; порядок в окне — по имени ключа |
type |
string, integer, number, string[] или color |
default |
значение по умолчанию; обязательно и само проходит проверку по схеме |
default_by |
необязательно: { "key": "<ключ того же хозяина>", "values": { "<значение того ключа>": <умолчание> } } — умолчание, когда тот ключ принял указанное значение; иначе действует default |
restart |
нужен ли перезапуск, чтобы значение подействовало |
min, max |
для integer и number — допустимые границы включительно |
choices |
закрытый набор допустимых строк |
pick |
необязательно, только для string: folder или file — путь, который человек может выбрать в системном окне Windows, а не печатать; значение — путь так, как его пишет Windows |
Названия и пояснения в схеме отсутствуют: они хранятся в словарях хозяина под ключами setting.<ключ>.title, setting.<ключ>.help, setting.<ключ>.choice.<значение> и setting.section.<id> (Настройки, раздел 2).
9 Версии модулей и поколение документов
modules.lock.json — ogas.modules-lock/1:
{
"format": "ogas.modules-lock/1",
"generation": 1,
"modules": [
{
"id": "echo",
"version": "1.0.0",
"digest": "sha256:9f2b…"
}
]
}
generation.json — ogas.generation/1. Владелец — функция графа; в files только документы, которые она пишет:
{
"format": "ogas.generation/1",
"generation": 1,
"files": ["process.json"]
}
workspace.json, settings.json и modules.lock.json принадлежат функциям canvas, settings и modules. Полноту поколения граф проверяет только по своим файлам; записанные версии модулей он сверяет запросом module.lock.get.
После сбоя принимается поколение, для которого все files существуют, имеют то же generation и проходят разбор. Незавершённое поколение откатывается к предыдущему .bak набору, если он полный.
Истории версий сверх нынешнего поколения и предыдущего нет: правку в мастерской возвращают шагами истории правок, а сохранённое берегут передачей процесса папкой.
10 Пакет на ребре и артефакт
{
"packet_id": "packet-1",
"run_id": "run-1",
"iteration_id": 0,
"delivery_id": "delivery-1",
"source": { "node_id": "echo_one", "port": "success" },
"parts": { "text": "пример" },
"artifacts": [
{
"artifact_handle": "sheet-1.svg",
"kind": "file",
"size": 1234,
"media_type": "application/octet-stream"
}
]
}
Файл результата — data/processes/<process_id>/results/<run_id>/<artifact_handle>, размер по умолчанию до 64 МиБ (modules.artifact_max_bytes). Ссылка — имя файла среди результатов прогона, которое дал модуль: без папок, не начинается с точки, занятое в прогоне получает номер (Модули, раздел 6). В JSON абсолютных путей нет. Поле name — путь, под которым файл создал модуль в рабочей папке.
10.1 Экспорт процесса
Экспорт процесса — папка data/exports/<process_id>/ формата ogas.export/3. Документы процесса хранятся в виде JSON, файлы модулей — как есть, без base64 и без предела размера документа. Вместе с процессом в экспорт входят процессы, которые он вызывает, на любой глубине (Программа: граф и запуски, раздел 18): вызов — ссылка, и без них процесс там, куда его перенесли, не работал бы.
exports/workshop/
package.json что внутри
documents/process.json
documents/workspace.json
documents/settings.json
documents/modules.lock.json
calls/resize/documents/process.json
calls/resize/documents/modules.lock.json
modules/echo/manifest.json
modules/echo/main.py
modules/thumb_image/…
pulpit/images/face.svg рисунки и виды пульта, если они есть
files/7f3a91c2-кот.png файлы постоянных и переменных, если они есть
state/paint/… сохранённая память узла, если её просили
state/paint.saved.json и её отметка
package.json:
{
"format": "ogas.export/3",
"process_id": "workshop",
"name": "Workshop pack",
"exported_at": "2026-09-17T10:15:30.123Z",
"documents": ["process.json", "workspace.json", "settings.json", "modules.lock.json"],
"calls": [
{ "process_id": "resize", "name": "Resize", "documents": ["process.json", "modules.lock.json"] }
],
"modules": [
{
"id": "echo",
"version": "1.0.0",
"digest": "sha256:9f2b…",
"files": 4,
"bytes": 3812,
"links_left_out": []
}
]
}
- В экспорт модуля попадает то же, что считает отпечаток: без
.venv,__pycache__,*.pyc,requirements.freeze.txtи.git. Поэтому у копии отпечаток источника, и импорт это проверяет: при несовпадении модуль удаляется, ответmodule_changed. - Символьные ссылки не переносятся. При экспорте ссылка внутри модуля не копируется и перечисляется в
links_left_out; экспорт со ссылкой при добавлении отклоняется (module_invalid) — иначе копирование могло бы перенести вmodules/любой файл принимающей машины. calls— процессы, которые вызывает экспортируемый, каждый один раз, с его собственными документами вcalls/<process_id>/documents/. Модули всех процессов пакета лежат в общейmodules/. При импорте каждый вызываемый процесс становится новым процессом со свободным id, вызовы переводятся на новые id, и вызываемые записываются раньше вызывающего.- Папка
pulpit/— рисунки пульта и свои виды его элементов (7.2) — входит в пакет, когда в ней есть файлы: её копирует хозяин, функция пульта, без ссылок и без недописанных файлов, аpackage.jsonназываетpulpit—{ files, bytes }. При импорте она становится папкойpulpit/нового процесса. У вызываемых процессов пакета пульт не переносится, и папки нет. - Папка
files/— файлы, которые держат постоянные и переменные (16.3), — входит в пакет, когда в ней есть файлы: её копирует функция графа, под теми же именами, без ссылок и без недописанных частей; вpackage.jsonтогдаfiles—{ files, bytes }. Импорт кладёт их вfiles/нового процесса, и ссылки в документах продолжают их называть. Файлы вызываемых процессов, как и рисунки их пультов, в пакет не входят. generation.jsonв пакет не входит: принимающий экземпляр пишет своё поколение.- Окружение Python не входит: оно пересобирается на месте (Модули, раздел 5).
- Сохранённая память входит, только если человек попросил (
include_state): папкиstate/<имя>/с отметками рядом, а вpackage.json—state(какая память лежит) иstate_left_out(какая осталась дома: без отметки или у поднятого модуля). Импорт делает её памятью с теми же именами нового процесса; модуль, у которого здесь другие файлы, её своей не считает — отметка названа другим отпечатком (5.3). - Повторный экспорт того же процесса заменяет прежний целиком.
11 Контрольная точка
data/processes/<process_id>/runs/<run_id>/checkpoint.json — ogas.checkpoint/2: голова контрольной точки; рядом с ней — запечатанные части checkpoint.part-<n>.json (ниже).
{
"format": "ogas.checkpoint/2",
"run_id": "run-1",
"attempt_id": "attempt-1",
"process_id": "demo",
"process_generation": 1,
"status": "running",
"updated_ms": 1789640130123,
"queued": [
{
"node_id": "echo_two",
"port": "input",
"delivery_id": "delivery-2",
"packet_id": "packet-1",
"iteration_id": 0,
"payload": { "text": "x" },
"artifacts": []
}
],
"executions": [
{
"node_id": "echo_one",
"packet_id": "packet-1",
"delivery_id": "delivery-1",
"operation_id": "run-1-operation-echo_one-packet-1-0",
"execution_id": "run-1-operation-echo_one-packet-1-0-execution-1",
"attempt": 1,
"iteration": 0,
"status": "completed",
"port": "success",
"input_port": "input"
}
],
"outputs": [],
"resume": { "allowed": true, "reason": null }
}
- Части. Записи о шагах и выходы копятся в голове; когда записей о шагах в ней набирается сто — внутренняя величина функции графа, — они вместе с выходами запечатываются в часть
checkpoint.part-<n>.jsonформатаogas.checkpoint.part/1(run_id,part— номер с 1,executions,outputs), и голова копит заново. Часть пишется один раз и раньше головы, которая её называет: оборвись запись между ними, прежняя голова ещё держит эти шаги, и часть пишется снова.parts— необязательно: сколько частей лежит рядом с головой. Читатель — продолжение, повтор,process.snapshot, восстановление после остановки платформы — собирает целое: шаги и выходы частей по порядку, затем головы; в собранном поляpartsнет, и он такой же, каким был документ со всеми шагами. Прогон, оборванный остановкой платформы, отмечается прерванным в голове, части остаются как были. Причина: голова переписывается на каждом шаге, и в одном документе со всеми шагами запись росла бы с квадратом их числа (разделы 4 и 7). queued— пакеты в очередях: узел и порт,delivery_idдоставки,packet_idиiteration_idпакета, его данныеpayloadи ссылки на файлыartifacts(раздел 10). По ним продолжение восстанавливает очереди.status:running|completed|failed|cancelled|interrupted.resume.allowedиresume.reasonопределяют допустимость продолжения прогона (Программа: граф и запуски, раздел 14).stopped_by— присутствует только у прогона с итогомcancelled:person|replace|switched_off— кто его остановил (Программа: граф и запуски, 12.5).error— присутствует только у прогона с итогомfailed:{ error_code, message, node_id, message_key, category }— почему прогон отказал;message_key— слова, которые назвал отказ (у каждого отказа есть хотя быerror.<код>),category— чей это отказ, напримерmodule(Программа: граф и запуски, 12.5).node_id— узел, на котором прогон остановился; поля нет, если отказ случился не на узле. Отказ шага здесь тот же, что в записи шага.event_node— необязательно: id узлаevent, который начал прогон. При отсутствии поля прогон начат запуском узла или группы (Программа: граф и запуски, 12.1).event_port— необязательно, присутствует, только когда прогон начался не с выходаevent: прогон, начатый пакетом службы, начинается с узла её модуля и с того выхода, который назвала служба (Модули, раздел 7). Продолжение начинает прогон оттуда же.asks— необязательно, присутствует, только когда в прогоне открыт вопрос человеку:{ node_id, packet_id, iteration_id, question }на каждый открытый вопрос (Программа: граф и запуски, раздел 9). После сбоя и продолжения вопрос открыт снова, под новымask_id, и срок ответа отсчитывается заново: пока платформа не работала, человек вопроса не видел.signals— необязательно, присутствует, только когда ветка ожидает сигнала извне:{ node_id, packet_id, iteration_id, payload, artifacts, key }на каждую ожидающую ветку (Программа: граф и запуски, раздел 22).key— ключ, с которым ветка ждёт, когда узел называет путьkeyи значение по нему есть: по нему окно даёт человеку выбрать, какой ветке послать сигнал. Остальные параметры ожидаемого сигнала в запись не входят: они читаются из узла, как у вопроса, и при продолжении прогона ключ тоже читается заново.run_variables— необязательно, присутствует, только когда процесс объявляет переменные прогона:{ "<id>": значение }— что они держат в этом прогоне. Продолжение прогона начинает с них; значение не того типа, что объявлен, заменяется начальным (Программа: граф и запуски, раздел 7).waits— необязательно, присутствует, только когда в прогоне выполняется пауза:{ node_id, packet_id, iteration_id, until_ms, payload, artifacts }на каждую ожидающую ветку, гдеuntil_ms— момент реального времени, до которого ветка ожидает (Программа: граф и запуски, раздел 19). Продолжение досиживает остаток; момент, который уже прошёл, отпускает ветку сразу.frames— необязательно, присутствует, только когда в прогоне работает рамка «Для каждого»:{ node_id, packet_id, items, results, done, sent, files, result_files }на каждую такую рамку, гдеpacket_id— с которым её элементы идут в тело (Программа: граф и запуски, раздел 11),items— её список,results— собранное к текущему моменту,done— какие элементы вернулись,sent— сколько отправлено в тело,files— файлы, пришедшие со списком,result_files— файлы каждого вернувшегося результата; два последних поля, если их нет, считаются пустыми (Программа: граф и запуски, 11.2, 14). По этой записи продолжение восстанавливает рамку и досылает оставшиеся элементы.outputs— пакеты, которые не ушли ни в одно ребро: выход узла без рёбер. Запись —{ node_id, port, packet_id, payload, artifacts, code: "packet_absorbed" }; о каждой такой записи функция графа один раз публикуетpacket.absorbed, и журнал прогона её называет (Программа: граф и запуски, 10.2).dropped— необязательно, присутствует, только если были отброшенные пакеты: пакеты, которых не выбрало правило входовanyили слияниеfirstи которые не могли уйти черезoverflow(Программа: граф и запуски, 10.3). Запись —{ node_id, port, packet_id, delivery_id, iteration_id, reason }.reason—unselected(правило входов взяло другой пакет того же круга) илиstalled(прогон кончился, а пакет так и стоял в очереди, ожидая напарника, который не пришёл — Программа: граф и запуски, 10.4), илиwithdrawn— ожидание вопроса, сигнала или паузы снято, потому что узел, который берёт первый пакет круга, взял другой (10.3 там же); у такой записи вместоport—decided_by, узел, который решил; илиstopped— узел лежит в группе, которую остановил человек: снято ожидание вопроса, сигнала или паузы или не взят пришедший к узлу пакет (Программа: граф и запуски, 12.3).updated_ms— когда контрольная точка записана, в миллисекундах от эпохи; по нему упорядочены прошлые прогоны (Программа: граф и запуски, 12.6). Прогон без поля упорядочен по времени изменения его папки.- Ветка, ожидавшая вопроса, сигнала или конца паузы и пошедшая дальше, записана шагом своего узла:
status: completedи выход, по которому она пошла, —answer,declined,timeout,signalилиdone. По этой записи узел ожидания показан выполненным. - Шаг модуля, идущий рядом с прогоном, стоит в
queuedсвоего узла с данными, которые он взял, пока не кончится (Программа: граф и запуски, раздел 14). Данные, собранные правиломallс нескольких входов, стоят на каждом своём входе. - Запись о шаге, чей отказ ушёл на выход
failedузла-модуля, — запись о завершённом шаге:status: completed,port: failedи полеcaughtс самим отказом (Программа: граф и запуски, 12.4). Продолжение такой шаг не повторяет. - Запись о шаге, оборванном остановкой прогона или группы, —
status: cancelled, с полямиpayload,input_portиerror— тем, что ответил модуль (Программа: граф и запуски, 12.3). Продолжение возвращает такой шаг в очередь, если его операция не выполнена позже. - Запись о завершённом шаге хранит идентификаторы, а не входные данные: продолжению нужно знать, какие операции уже сделаны. Входные данные (
payload) хранит только отказавший или оборванный шаг — при продолжении их возвращают в очередь. Контрольная точка переписывается на каждом шаге, и данные каждого шага делали бы её размером со все данные прогона. - Запись о каждом шаге сохраняется, поэтому контрольная точка растёт с числом шагов, около 280 байт на шаг: предел документа 8 МиБ наступает около 30 000 шагов за прогон.
Отметка «Хранить» — runs/<run_id>/keep.json в папке процесса, формат ogas.run-keep/1. Пишет и удаляет её функция графа (Программа: граф и запуски, 12.6); уборка прогонов отмеченный прогон не удаляет.
{ "format": "ogas.run-keep/1", "run_id": "run-1", "kept_at": "2026-09-17T10:15:30.123Z" }
12 Замок экземпляра
data/ogas.lock, права 0600, действующий замок — flock на открытом дескрипторе:
{
"pid": 1234,
"started_at": "2026-08-30T10:15:30.123Z",
"nonce": "n-3f9a1c0b7e2d4a65"
}
nonce — случайная метка этого запуска экземпляра: восемь байт случайности системы в шестнадцатеричной записи.
13 Журнал и проблема
Запись общего журнала событий data/logs/events-<ГГГГ-ММ>.jsonl — по месяцу времени записи — и журнала прогона runs/<run_id>/events.jsonl в папке процесса:
{
"time": "2026-08-30T10:15:30.123Z",
"level": "info",
"source": "graph",
"trace_id": "trace-1",
"run_id": "run-1",
"node_id": null,
"event_id": 42,
"event_type": "run.changed",
"payload": { "run_id": "run-1", "status": "running" }
}
Запись — событие шины, как его разослало ядро: event_id, event_type и payload. К нему добавлены уровень level (info, warn или error; уровень, названный в payload, или error у отказа) и поля, по которым журнал ищут: source, trace_id, run_id, node_id. Своего ключа словаря у записи нет: человеку событие называет текст event.<event_type> из словаря его хозяина.
Журнал ядра data/logs/core.jsonl пишет само ядро — отказы функций, знакомство, перезапуски; функция observe его не записывает:
{
"time": "2026-08-30T10:15:30.123Z",
"source": "graph",
"event": "function.health_failed",
"error_code": "rpc_timeout",
"message": "Function did not answer the health check in time"
}
Поля записи: time, source (ядро или id функции, о которой запись), event, error_code (или null) и message. Строка, которую функция написала в stderr, лежит здесь же записью function.stderr — не длиннее 4 КиБ, длинная обрезается. message — короткая подсказка тому, кто разбирает отказ по журналу; это не текст для человека в мастерской, поэтому он не переводится и ключа словаря у него нет. Человеку то же самое объясняет проблема из problems.json по своему code.
Список проблем — data/problems.json, формат ogas.problems/1: { "format", "problems": [проблема, …] }; пишет его только функция наблюдения. Проблема описывается полями problem_id, code, severity (info, warn или error), указателем на место pointer (function_id, process_id, run_id, node_id, trace_id; у пропавшего текста ещё key), признаком open, счётчиком count, временами first_seen и last_seen, а также событием, которое её открыло: event_type и payload. Закрытая проблема несёт open: false и время закрытия resolved_at. Повтор того же отказа увеличивает count, а новой строки не добавляет; тот же отказ — то же событие с тем же кодом в том же месте: у той же функции или в том же узле того же процесса, а у пропавшего текста — с тем же ключом, на каком бы языке его ни искали. Повтор закрытой проблемы открывает её снова: open становится true, resolved_at убирается. Список проблем отдаёт observe.problems.list, события журнала — observe.logs.query.
- Ядро пишет только
core.jsonlиboot.jsonl; функцияobserve—logs/events-<ГГГГ-ММ>.jsonlиprocesses/<process_id>/runs/<run_id>/events.jsonl; файлlogs/events.jsonl, если он есть, она только читает. Процесс прогона функция узнаёт из первого события прогона, которое его называет, — начала прогона. - Журнал читается только по строкам, завершённым
\n. - Уровень
warnу записи сам по себе проблему не открывает.
14 Связи
Связи принадлежности приведены в таблице. Связь «один ко многим» означает, что у одного рабочего процесса таких предметов может быть любое число, а каждый из них принадлежит одному процессу.
Таблица 9 — Связи
| Целое | Сколько частей | Часть |
|---|---|---|
| рабочий процесс | сколько угодно | узел |
| рабочий процесс | сколько угодно | ребро |
| рабочий процесс | сколько угодно | группа |
| рабочий процесс | сколько угодно | переменная |
| рабочий процесс | сколько угодно | сквозной объект — из узлов и переменных |
| рабочий процесс | ровно один | файл запомненных значений (state.json) |
| рабочий процесс | по одной на имя памяти, которую сохранил модуль: узла или общую нескольких узлов | память узла (state/<имя>/) |
| рабочий процесс | по одной на прогон | папка прогона (runs/<run_id>/) и папка его результатов (results/<run_id>/) |
| включённая программа | сколько угодно | прогон |
| рабочий процесс | ровно один | пульт (workspace.json) |
| рабочий процесс | ровно один | свои настройки (settings.json) |
| рабочий процесс | ровно один | окружение Python (.venv) |
| рабочий процесс | сколько угодно, и наоборот | версия модуля — через modules.lock.json |
| прогон | сколько угодно | выполнение шага |
| прогон | сколько угодно | пакет |
| прогон | сколько угодно | артефакт |
| прогон | ровно одна | текущая контрольная точка |
15 Хранение
База данных не используется.
Запись документа выполняется в следующем порядке:
1) хозяин документа проверяет его по схеме;
2) ядро проверяет путь, размер, UTF-8 и format, если он есть на верхнем уровне;
3) временный файл, fsync файла, текущий файл переименовывается в .bak, временный встаёт на его место, fsync папки;
4) в наборе процесса generation.json обновляется последним.
Документ другой версии формата не читается. Ядро при записи проверяет только вид строки — <имя>/<номер>, — потому что смысла документа не знает; какую версию понимать, решает хозяин документа при чтении. Хозяин отвечает document_invalid, называя найденную и ожидаемую версию. Хозяева форматов:
- функция графа — процесс
ogas.process/4, памятьogas.state/2,ogas.state-item/1,ogas.state-items/1, поколениеogas.generation/1, контрольная точкаogas.checkpoint/2иogas.checkpoint.part/1, экспортogas.export/3, отметка «Хранить»ogas.run-keep/1; canvas— пультogas.workspace/3;settings— настройкиogas.settings/1;modules— версии модулейogas.modules-lock/1;- ядро — реестр функций
ogas.functions/2и описание функцииogas.function/1; - функция наблюдения — список проблем
ogas.problems/1; - общий код протокола — схема настроек
ogas.settings-schema/1.
Имена форматов объявлены в файлах констант их хозяев; make ci проверяет, что в коде нет их строковых копий и что каждый записан здесь.
16 Виды данных, файлы процесса и показ
Раздел описывает виды данных, файлы процесса и то, как данные показаны человеку (Программа: граф и запуски, раздел 24; Мастерская, 5.5).
16.1 Имена видов данных
В форматах вид данных называют именем из таблицы 11: у порта модуля (type, раздел 4), у переменной (type, 5.1), у постоянной, у события и у вопроса (16.6).
Таблица 11 — Имена видов данных
| Вид | Имя | Значение в JSON |
|---|---|---|
| текст | text |
строка |
| число | number |
число |
| да/нет | boolean |
true или false |
| дата и время | time |
строка ISO 8601 — 2026-10-17T09:00, с секундами или без, с поясом Z или +03:00 или без него — тогда по часам этой машины; пустая строка — время не задано. Поле ввода — выбор даты и времени системы |
| список | list |
массив |
| картинка | image |
ссылка на файл (16.2) |
| звук | audio |
ссылка на файл |
| видео | video |
ссылка на файл |
| файл | file |
ссылка на файл |
| что угодно | json |
любое значение |
Ребро соединяет порты, когда виды одинаковые, когда один из них json и когда картинка, звук или видео идут на порт file: картинка — тоже файл. Обратно — файл на порт картинки — ребро не соединяет: что в файле, неизвестно. Других превращений нет: текст на порт числа не идёт.
16.2 Ссылка на файл
Картинка, звук, видео и файл в данных — ссылка, а не содержимое:
{ "file": "files/7f3a91c2-кот.png", "name": "кот.png", "media_type": "image/png", "size": 48213 }
file— где файл лежит (16.3);name— имя, под которым его дали или создали: его показывают человеку и предлагают при сохранении;media_type— тип содержимого; по нему узел «Показать» решает, как показать файл (16.5);size— размер в байтах.
Ссылка идёт в пакете, лежит в переменной и в постоянной, как любое значение. Модуль получает файл по ссылке так же, как файл результата: путём в artifact_files (Модули, раздел 6). Отдаёт модуль файл той же ссылкой: в file — путь в рабочей папке, тот же, что в artifacts; функция модулей, перенеся файл, ставит в file его имя среди результатов и дописывает остальные поля (Модули, раздел 6).
16.3 Где лежат файлы
Файлы лежат в папке процесса, в двух местах:
- файлы прогона —
results/<run_id>/(раздел 10): то, что создал модуль, и то, что человек дал событию или вопросу этого прогона. Полеfileссылки — имя файла среди результатов прогона, какartifact_handle. Живут столько, сколько прогон (уборка — Программа: граф и запуски, 12.6); - файлы процесса —
files/: то, что держит постоянная или переменная. Полеfile—files/<имя>; имя — восемь шестнадцатеричных знаков, дефис и имя, под которым файл дали, без папок и не с точки.
Файл после появления не изменяется (Программа: граф и запуски, 24.4). Поэтому, когда узел «Запись» кладёт в переменную ссылку на файл прогона, функция графа копирует файл в files/ — на том же диске жёсткой ссылкой, без второй копии содержимого, — и в переменную ложится ссылка на копию: прогон уберут, а переменная держит файл дольше.
Уборка файлов процесса. Файл из files/ живёт, пока его называет что-то из: значение постоянной в записанном документе процесса, начальное значение переменной, нынешнее значение переменной включённой программы, запомненное значение (state.json). Функция графа убирает файл, который ничего не называет и который старше суток: сутки покрывают файл, данный в несохранённую правку графа. Убирает при включении программы и после записи документа процесса.
Экспорт процесса уносит папку files/ вместе с процессом, как папку рисунков пульта (10.1). Данные, которые узел «Отправить» передаёт другой программе, несут её файлы с собой: каждый файл, который они называют, копируется в files/ получателя тем же способом, и ссылка называет копию (Программа: граф и запуски, раздел 23).
16.4 Как файл попадает в процесс
Файл Windows человек даёт двумя способами: выбирает в системном окне выбора — оно открывается по нажатию поля для файла с папки настройки ui.open_dir и показывает сначала файлы нужного вида, потом все, — или отпускает над полем файл, перетащенный из Проводника. В обоих случаях окно получает путь, а не содержимое: функция ui переводит путь Windows в путь Linux (ui.window.path), и функция графа копирует файл сама (process.file.take с process_id, path, place и run_id) — целиком, один раз, рядом с местом назначения и затем на место, под своим именем; предел размера и место на диске проверяются до копирования. Перетащенный файл, отпущенный там, где файл не берут, не теряется молча: окно говорит, куда его отпустить.
Файл, который сделала сама страница (маска рисунка), она передаёт функции графа содержимым, частями: строка обмена ограничена (MAX_LINE_BYTES, раздел 1), а видео бывает большим. Запрос process.file.put несёт process_id, name, media_type, часть содержимого в base64 (data), upload_id — один на весь файл, offset — с какого байта эта часть, и last — последняя ли она. Части складываются в файл .<upload_id>.part рядом с местом назначения; последняя часть переименовывает его в постоянное имя, и ответ несёт ссылку (16.2). Пропущенная или повторная часть — отказ с причиной; недописанный файл старше суток убирается. Куда ложится файл, говорит place: files — для постоянной и переменной, run с run_id — для ответа на вопрос идущего прогона. Предел размера — настройка функции графа, по умолчанию 1 ГиБ.
16.5 Узел «Показать»
Вид узла show (Программа: граф и запуски, 24.3):
- вход
value— что угодно (json), выходsuccess— тот же пакет без изменений: узел ставят и в конец ветки, и в середину, ничего не перецепляя; - параметр
path— какое поле пакета показать, по правилам путей (Программа: граф и запуски, раздел 8); без него показывается весь пакет. Поля, которого в пакете нет, узел не требует: показано, что его нет, и прогон идёт дальше — узел смотрит и не меняет того, на что смотрит; - что пришло, узел держит: последнее показанное значение каждого узла
showвключённой программы с прогоном, который его принёс. При выключении программы показанное забывается; - показ выбирает вид по значению: строка — текстом, а время ISO 8601 — датой и временем по часам человека, число — числом, логическое значение — словом «да» или «нет» на языке человека, массив — списком, ссылка на файл — по
media_type: картинка, проигрыватель звука или видео, имя и размер файла; остальное — устройством данных. Элемент списка и поле записи показываются по тем же правилам: картинка в списке и в поле — картинкой, файл — именем и размером, а не устройством ссылки; список, в котором каждый элемент — файл или запись с одним файлом, — галереей: плитка показывает файл, её подсказка — остальные поля записи; так кадры с подписями — результат «Для каждого», который всегда записи, — видны картинками; - значение больше предела — настройка функции графа, по умолчанию 256 КиБ, — держится началом: у текста первые знаки, у списка первые элементы, и отмечено «показано не всё»;
- сквозной объект узла —
show:<node_id>: к нему привязан элемент интерфейса, который показывает то же на пульте (Мастерская, 6.8); - окно узнаёт о новом значении событием
show.changed(process_id,node_id,run_id) и читает значения запросомprocess.show.getсprocess_id: ответ{ values: { <node_id>: { value, cut, run_id } } }.
Узел «Показать» проверка графа не требует ни к чему подключать: без входа он не сработает, как любой узел, и process.validate назовёт его предупреждением.
16.6 Поле на узле
Что задаёт поле на узле холста и где это хранится:
- постоянная — параметр
type: вид её значения (16.1); без него вид следует из значения — строка, число,true/false, массив, объект. Поле правитvalueв документе процесса: это правка графа, она отменяется шагом назад и записывается «Сохранить». У картинки, звука, видео и файла поле выбирает файл, и вvalueложится ссылка на файл процесса; - переменная — поле показывает значение, а у включённой программы и пишет его (
process.variable.set), как поле пульта. У выключенной поле только показывает начальное значение: его правят в панели переменной; - событие — параметр
takes: что событие принимает, список{ name, type }. Поле на узле — по строке на каждое имя, и знак «Запустить» запускает событие с набранным: пакет{ <name>: значение }. Безtakesу события пульта, сообщения и вызова одна строкаtextвида «текст»: пакет{ "text": "…" }; у события таймера, часов, включения и изменения полей нет — его данные даёт источник. Набранное хранит окно до закрытия мастерской, а не документ; - вопрос — параметр
kindпринимает, кромеtext,choiceиconfirm, ещёnumber,area,points,mask,image,audio,videoиfile: - область (
area) — вместо данных пакета показана картинка из них, и человек обводит на ней область: нажал и протянул; «Ответить» доступно, когда область есть, а без картинки в данных вопрос только отклоняют; - точки (
points) — щелчок по картинке ставит точку «вот это», сShiftили правой кнопкой — «а это не брать»; «Убрать последнюю» и «Очистить» правят поставленное; «Ответить» доступно, когда есть точка «вот это»; - кисть (
mask) — человек закрашивает нужное; ширину кисти выбирают три кнопки, правая кнопка и «Стирать» стирают, «Очистить» убирает всё; «Ответить» доступно, когда закрашено хоть что-то, и закрашенное уходит картинкой в файлы прогона; - на узле открытый вопрос показан данными пакета, как их показал бы узел «Показать», и под ними — поле ответа его вида со знаками «Ответить» и «Отказаться»; картинка вопроса об области, точках или кисти стоит под данными, а узел вдвое шире обычного. Файл ответа ложится в файлы прогона;
- ожидание сигнала — своих параметров у поля нет. Поле на узле показывает ветки прогона, которые ждут на нём, — у узла с ключом по ключу каждой из контрольной точки, у узла без ключа их число, — строку того, что послать, и «Послать сигнал»:
process.signal.sendсnode_idузла, выбранным ключом и набранным текстом вdata; пустая строка шлётnull. Без ждущих веток поле говорит, что сигнала никто не ждёт; - проверочный запуск события любого источника. С холста событие запускают и тогда, когда оно не от пульта: таймер, часы, включение, сообщение, вызов. Запрос
process.event.fireнесётby_hand: true; пакет — набранное в поле, а без полей — то, что дал бы источник:{ "time": … }у часов,{}у включения. Пульт запускает только события пульта.
16.7 Отдельно запущенная часть
Узел или группу запускают отдельно (process.run.start, Программа: граф и запуски, 12.1). Вход части, к которому ведёт ребро снаружи, получает:
- от постоянной и переменной — их значение, как в любом прогоне: это холодные рёбра (Программа: граф и запуски, раздел 6);
- от события с полем — набранное в поле этого события, по ребру и с отбором частей ребра (
parts); от события без полей — то, что дал бы его источник (16.6); - от любого другого узла снаружи — ничего; запуск отклоняется (
graph_no_entry,message_keygraph.part.input_cut) и называет такой вход вnames: «узел (вход)». Слова: «этот вход ничего не получит снаружи части — подайте на него постоянную или запускайте с события».
Окно передаёт набранное во всех событиях графа (events: { <id события>: пакет }, у события без полей — null), кладя в пакет только поля, которые читаются как их вид. Какие из событий ведут в часть, знает функция графа, и только их набранное она сверяет с takes: поле, которого нет или которое не того вида, — отказ (graph_no_entry, message_key graph.part.event_unfit, в names — «событие: поле»). Событие, которое в часть не ведёт, ни о чём не спрашивают: недонабранное поле в соседней ветке проверке не мешает.
Узел части, к которому не ведёт ни одно горячее ребро — ни изнутри, ни от события снаружи, — начинает прогон с пустого пакета {} и читает свои холодные входы: так проверяют узел, у которого на входе только постоянная.
Общего ввода у окна нет.
17 Источники
17.1 Витрина — ogas.catalog/1
Витрина — документ JSON, который источник O.G.A.S. отдаёт по своему адресу: по умолчанию https://ogas.space/catalog.json. Её читает функция источников (Мастерская, 5.6).
{
"format": "ogas.catalog/1",
"name": "O.G.A.S.",
"items": [
{
"id": "sobesednik",
"what": "program",
"title": "Собеседник",
"description": "Разговор с локальной сетью в манере образа",
"version": "1.0.0",
"author": "Виктор Овчинников",
"license": "Apache-2.0",
"files": [
{ "path": "process.json", "url": "https://ogas.space/files/sobesednik/1.0.0/process.json", "size": 298115, "sha256": "…" }
]
}
]
}
Таблица 12 — Вещь витрины
| Поле | Тип | Обязательно | Что значит |
|---|---|---|---|
id |
string | да | имя вещи в витрине: строчные латинские буквы, цифры, _, - и .; под ним вещь ложится в downloads/ |
what |
string | да | program — программа, папка экспорта процесса (Форматы файлов, 10.1); module — папка модуля; network — нейросеть; piece — заготовка интерфейса: ровно один файл .json, документ ogas.piece/1 (7.4) |
title, description |
string | title — да |
название и описание словами человека |
kind |
string | у нейросети | вид нейросети — подпапка папки нейросетей, куда она встанет |
version, author, license |
string | нет | версия, автор, лицензия как её называют |
profile |
integer ≥ 1 | нет | номер профиля автора на ogas.space (21.5): по нему библиотека показывает автора и говорит, тот ли автор у новой версии, что у прежней |
category |
string | нет | раздел витрины, в котором стоит вещь, словами витрины: по нему библиотека показывает найденное группами |
gpu_mb |
integer ≥ 0 | нет | сколько видеопамяти нужно программе или модулю, в МиБ: по нему библиотека говорит, запустится ли вещь на этом компьютере |
files |
object[] | да | файлы вещи: path — относительный путь внутри её папки, url — откуда скачать (https; у витрины-файла на этом компьютере — file:), size — байты, sha256 — контрольная сумма; необязательные role — чем файл приходится вещи (weights, part:<папка>, adapter, config, tokenizer, vae, docs, picture, other) и variant — чем этот вариант отличается от других (fp16, Q4_K_M, pruned…); без них роль и вариант читаются по имени файла |
preview |
object | нет | картинка вещи для плитки: url, size, sha256, как у файла; PNG, JPEG, WebP или GIF не крупнее 2 МиБ |
Витрина другого формата, вещь без обязательного поля, заготовка не из одного файла .json, путь файла, выходящий из папки вещи, файл без size или sha256, gpu_mb не целым числом, роль файла, которой нет в списке, не читаются: источник отказывает source_answer_bad и называет, что не так.
Витрина O.G.A.S. может назвать и новую версию платформы: поле platform — { "version": "1.0.1", "page": "https://ogas.space/…" }; номер — числа через точку, страница — только страница ogas.space. Его читает только проверенная витрина (Методы служб платформы, раздел 4).
Витрина ogas.space лежит рядом со своей подписью catalog.json.sig (21.5); без верной подписи она не читается (source_signature_bad). Программы, модули и заготовки на ней — все файлы лежат на самом ogas.space; нейросетей на ней нет, их библиотека берёт с Hugging Face и Civitai.
17.2 Загрузки
Функция источников пишет скачанное в папку данных, в downloads/:
downloads/<загрузка>/— папка идущей загрузки; файл качается в<имя>.partрядом и встаёт на место переименованием, когда скачан и проверен;- нейросеть после загрузки уходит в папку нейросетей — её ставит туда функция модулей (
module.model.take, Методы модулей), и папка загрузки исчезает; - программа остаётся в
downloads/<id>/, а повторная загрузка той же программы — вdownloads/<id>-2/, не трогая прежней; модуль — вdownloads/<загрузка>/<id>/: модуль принимают из папки, названной его идентификатором; оттуда их принимают, как принесённые папкой.
Остановленная или отказавшая загрузка убирает свою папку целиком: недокачанного на диске не остаётся. Список загрузок живёт, пока работает функция, и в файл не пишется.
18 Вход для программ — ogas.gate/1
data/gate.json. Владелец — функция gate (Методы служб платформы, раздел 5). Программа человека читает его, чтобы войти.
{ "format": "ogas.gate/1", "port": 50123, "key": "9f…e1" }
port— порт на127.0.0.1, на котором вход слушает;null, пока вход закрыт;key— ключ входа: 64 шестнадцатеричных знака.
19 Помощник
Владелец — функция assistant (Методы служб платформы, раздел 6).
data/assistant/network.json—ogas.assistant-network/1:{ format, module_id, params, environment }— сеть помощника, её параметры и программа, в чьём окружении она идёт.data/assistant/own.json—ogas.assistant-own/1:{ format, programs, modules }— id черновиков и модулей, которые помощник создал сам.data/assistant/before/<task_id>/<process_id>.json—ogas.assistant-before/1:{ format, process_id, graph, pulpit }— программа человека, какой она была до того, как помощник в расширенном доступе впервые за задачу записал поверх неё.data/assistant/tasks/<task_id>.json—ogas.assistant-task/1:{ format, task_id, title, created, state, access, messages, log, usage, budget_from, error }.messages— разговор:{ role: "user" | "assistant" | "tool", text, calls?, call_id?, name? };usage—{ input_tokens, output_tokens, steps };budget_from— с какого расхода считается нынешний предел.
20 Места частей — ogas.places/1
data/places.json пишет ядро, когда часть называет свои папки вне папки данных (core.space.places, Обмен с ядром, 5.19):
{ "format": "ogas.places/1", "parts": { "modules": [
{ "path": "/opt/ogas/modules", "backup": true },
{ "path": "/opt/ogas/models", "backup": false }
] } }
parts — по части список её папок; backup: true — папку берёт резервная копия (Сборка, установка и обслуживание, 5.6). Документ нужен, когда платформа остановлена: по нему ядро знает, что взять в копию.
21 Профиль, посылки и файлы сайта
Владелец — функция profile (Методы служб платформы, раздел 7); что от чего защищает — Защита, раздел 8. Строки base64 — стандартный алфавит с =.
21.1 Код восстановления и файл ключа — ogas.key/1
Код — 55 знаков алфавита 0123456789ABCDEFGHJKMNPQRSTVWXYZ группами по пять через -: 34 байта — зерно главного ключа (32 байта) и первые два байта его SHA-256 — с тремя нулевыми битами в конце. При чтении пробелы, переводы строк и - пропускаются, строчные буквы читаются как прописные, O — как 0, I и L — как 1.
Файл ключа — документ JSON там, где его сохранил человек:
{ "format": "ogas.key/1", "code": "T2RBT-8P06Y-…-JMAWR" }
или, под паролем:
{ "format": "ogas.key/1",
"kdf": { "name": "argon2id", "salt": "…", "m": 65536, "t": 3, "p": 1 },
"sealed": "…" }
sealed — 24 байта nonce и шифр кода (XChaCha20-Poly1305) ключом 32 байта, который Argon2id выводит из пароля с salt (16 байт) и параметрами m (КиБ), t, p.
21.2 Профиль на компьютере
Папка data/profile/; файлы пишет функция profile.
keys.json—ogas.profile-keys/1, права только для пользователя:{ format, key, device, vouch, device_secret, letter_secret }— открытая часть главного ключа, ключа компьютера, поручительство (подпись главного ключа надogas.device/1\nи строкойdevice), зерно ключа компьютера и секрет ключа писем; всё base64. Секрет ключа писем — SHA-256 отogas.box/1\nи зерна главного ключа. Нет файла — нет профиля.card.json—ogas.profile-card/1:{ format, name, about, links, picture, shown, number }— карточка, какой её отправили последней посылкойprofile;number— номер профиля на сайте, когда сайт его назвал.sent.json—ogas.sent/1:{ format, sent: [{ id, what, made, title, thing?, request?, state?, number?, reason?, seen? }] }— свои отправки:id— SHA-256 посылки;state,number,reason— что последний взгляд на сайт сказал о ней (21.5).letters.json—ogas.letters/1:{ format, letters: [{ id, kind, opened, … }] }— открытые письма:id— SHA-256 запечатанного, поля — письма (21.4).outbox/<id>.json—ogas.outbox/1:{ format, id, what, title, made, bits, raw }— посылка, подписанная и ещё не ушедшая:raw— её текст как есть,bits— сколько нулевых бит просит задачка на счёт. Посылка больше предела документа ядра ждёт в памяти функции до остановки платформы.
21.3 Посылка — ogas.parcel/1
{ "format": "ogas.parcel/1",
"key": "…", "device": "…", "vouch": "…",
"body": "{\"what\":\"review\",\"lang\":\"ru\",\"made\":\"2026-11-06T12:00:00Z\",\"thing\":\"rask\",\"version\":\"1.0\",\"works\":true}",
"sign": "…" }
key,device— открытые части главного ключа и ключа компьютера, 32 байта;vouch— поручительство (21.2), 64 байта; посылку, которую подписывает сам главный ключ, он подписывает как свой же компьютер:deviceравенkey.body— текст объекта JSON: подпись берётся над его байтами в UTF-8 как они есть, без нового порядка полей;sign— подпись Ed25519 ключомdevice, 64 байта.- У тела всегда
what,lang(ruилиen) иmade— времяYYYY-MM-DDTHH:MM:SSZ; прочие поля — по таблице 13. Поле, которого вид не называет, — отказ.
Таблица 13 — Виды посылок
what |
Поля | Подписывает |
|---|---|---|
profile |
name (до 80 знаков), about?, links? — до пяти адресов https://, picture? — { type: ".png" \| ".jpg" \| ".webp", base64 } до 2 МиБ, box — открытая часть ключа писем, device — открытая часть ключа компьютера, с которого пришла карточка: сайт принимает подпись только названных так компьютеров, shown? — показывать ли страницу |
главный ключ |
review |
thing, version, works (boolean), platform?, gpu?, text?, incognito? |
компьютер |
request |
title, wants, input?, output?, network?, gpu?, example?, incognito? |
компьютер |
take |
request — номер обращения, about?, incognito? |
компьютер |
offer |
request?, title, about?, note?, incognito?, things — до 20 вещей { what: "program" \| "module" \| "piece", id, version, files: [{ path, text } \| { path, base64 }] } |
компьютер |
bug |
where — platform или id вещи, version?, did?, expected?, happened, doctor?, journal?, box |
компьютер |
comment |
bug — номер, text |
компьютер |
solved |
bug, version, note? |
компьютер |
reply |
review — номер отзыва о своей вещи, text |
компьютер |
withdraw |
parcel — SHA-256 своей посылки |
компьютер |
revoke |
keep — открытая часть ключа этого компьютера: остальные компьютеры профиля отзываются |
главный ключ |
delete |
— | главный ключ |
Пределы: посылка — до 28 МиБ; файлы вещей — до 20 МиБ и 500 штук, путь — до 200 знаков, относительный, из обычных имён, без .., скрытых и \, один путь — один раз без учёта регистра; base64 — только у картинок .png, .jpg, .jpeg, .webp, .gif, и первые байты должны быть картинкой этого вида; заготовка — ровно один файл .json текстом. Тексты: имя — до 80 знаков, строка — до 500, текст — до 20 000, doctor и journal — до 100 000.
Посылку отправляют запросом POST https://ogas.space/api/send с заголовком X-OGAS-Work: <день>:<число>: SHA-256 от строки <день>:<число>:<hex SHA-256 посылки> должен начинаться с 20 нулевых бит, у посылки profile — с 24; <день> — сегодня или вчера по UTC, YYYY-MM-DD. Ответ — { "ok": true, "id": "<hex SHA-256 посылки>" } или { "ok": false, "error", "detail" }; коды: bad_format, bad_kind, bad_path, not_a_picture, missing, too_large, main_key_only, no_work, blocked, too_often, full.
21.4 Письмо — ogas.letter/1
Письмо — запечатанный ящик libsodium (crypto_box_seal: X25519 и XSalsa20-Poly1305) для открытой части ключа писем получателя; внутри — объект JSON { "format": "ogas.letter/1", "kind", … }.
Таблица 14 — Письма
kind |
Поля | Кому |
|---|---|---|
answer |
parcel — SHA-256 сообщения об ошибке, text |
сообщившему |
bug |
bug — номер, thing, version, did, expected, happened, doctor, journal |
автору вещи |
review |
review — номер отзыва, thing |
автору вещи, когда отзыв встал на сайт |
21.5 Файлы сайта и их подпись
Таблица 15 — Что платформа читает с ogas.space
| Файл | Формат | Что в нём |
|---|---|---|
catalog.json |
ogas.catalog/1 |
витрина (17.1) |
requests.json |
ogas.requests/1 |
requests: { number, made, by, title, wants, input, output, network, gpu, example, takes, results, state }; takes — { number, by, about, made }; results — { number, title, by, note, where }; state — open или done; by — номер профиля или null |
reviews.json |
ogas.reviews/1 |
reviews — по id вещи список { number, works, made, by, version, platform, gpu, text, reply? }, reply — ответ автора вещи; bugs — по id вещи известные ошибки { number, text, version, made, solved?, solved_note?, comment? } |
profiles.json |
ogas.profiles/1 |
profiles: { number, name, about, links, picture?, things, results, reviews, of_things }; of_things — по своей вещи её отзывы и ошибки |
states.json |
ogas.states/1 |
states — по SHA-256 посылки { state, number, reason?, profile? }; state — waiting, shown, accepted, done, rejected, withdrawn, answered, refused |
mail.json |
ogas.mail/1 |
letters — запечатанные письма base64 (21.4) за последние 30 дней |
Рядом с каждым — <имя>.sig: строка base64 подписи Ed25519, 64 байта, над байтами ogas.site/1\n<имя>\n и байтами файла. Открытые части ключей сайта — в общем коде платформы.