Конвенции справочника
Общие понятия, на которые ссылаются страницы методов. Здесь — канон; на страницах методов даются только ссылки сюда.
Уровни доступа (middleware)
Каждый эндпоинт защищён одним или несколькими middleware (server/auth/middleware.js).
В блоке инвентаря страницы метода поле Auth показывает их как есть из кода.
| Middleware | Смысл |
|---|---|
optionalAuth | Аноним (гость) и авторизованный допускаются. Гостю PG отдаёт только его guest-данные. |
requireAuth | Только авторизованный пользователь (есть сессия-cookie или валидный токен). |
requireProjectAccess | Загружает проект и проверяет доступ читателя; уровень (read/write) кладёт в req.projectAccess. Точную запись (owner/spaceman/org_admin) досматривает PG-функция. |
requireSiteAdmin | Только site-admin с активной TOTP-сессией. Токену недоступно ни на одном scope. |
Внутри write-хендлеров часто есть дополнительная проверка: для guest-спейса —
заголовок X-Guest-Projects (claim на конкретный проект), для владельческих
проектов — req.projectAccess === 'write'. Точные правила ролей — в матрице
RP (источник истины разрешений) и в секции «Для человека»
каждого метода.
Scope токена (агентный доступ)
Агент ходит под токеном владельца — с теми же правами, что у него в UI.
| Scope | Что можно |
|---|---|
read | Только GET. |
read_write | GET + мутации (POST/PUT/PATCH/DELETE), в пределах роли владельца. |
Никакому токену на любом scope недоступны site-admin-операции (by design).
Часть org-admin write-операций в v1 закрыта на уровне agent-gate
(server/auth/agentGate.js). Детали — в путеводителе для агента.
Формат ответа для агента
- Cookie-клиент (браузер) всегда получает JSON.
- Токен с
Accept: text/markdown(или?format=md) получает Markdown-зеркало ресурса (server/agent/negotiate.js). У ресурсов естьETag; повтор сIf-None-Match→304(экономия токенов). - Блок
## Actionsв Markdown-ответе перечисляет только действия, доступные данному токену (его scope и роль владельца) — см.server/agent/affordances.js.
Агентные конвенции записи
Эти правила касаются только запросов под токеном (Bearer). Cookie-клиент
(браузер) ими не затронут: у фронта свои подтверждения и оптимистичная блокировка.
Реестр операций и их риск — server/auth/opRegistry.js.
Ось риска (risk)
Каждая write-операция помечена риском. Метка видна в блоке ## Actions
Markdown-ответа (см. server/agent/affordances.js):
| Риск | Что это | Примеры |
|---|---|---|
SAFE | Обычная запись/обновление/восстановление | create/update/upsert, restore из корзины или версии, set-context, cancel-job |
DESTRUCTIVE | Обратимое удаление/снятие/отзыв | soft-delete в корзину, remove участника, revoke, удаление ребра |
IRREVERSIBLE | Необратимое уничтожение/сброс | permanent-delete из корзины, reset спейса к исходному |
COSTLY_LLM | Запускает платную LLM-генерацию | generate/regenerate графа, doc-content generate/inline-edit, suggest-prompt |
Confirm-гейт (409)
Операции с риском IRREVERSIBLE или COSTLY_LLM требуют от токена явного
подтверждения: пришлите {"confirm": true} в теле. Без него — 409 (RFC 9457
application/problem+json) с полями op и risk. Принимается только литеральное
булево true собственным свойством тела (не строка "true", не 1). Гейт стоит
выше роутеров, поэтому SSE-генератор (COSTLY_LLM) получает 409 до открытия
text/event-stream — ответ приходит обычным JSON (server/auth/confirmGate.js).
Оптимистичная блокировка (If-Match → 412)
Опционально: перед PUT /api/projects/{id}, PUT /api/collections/{id} или
PATCH …/doc-content/{node} можно прислать If-Match: "<ETag>" (ETag берётся из
предыдущего чтения ресурса). Если ресурс изменился параллельно — 412
(precondition_failed), перечитайте и повторите. Заголовок опциональный: без него
поведение прежнее. Маркер версии — updated_at ресурса; граф проекта не двигает
updated_at — If-Match защищает метаданные (заголовок/промпт/спейс, поля
коллекции, содержимое блока), не топологию графа (server/agent/ifMatch.js).
Асинхронный режим долгих генераций (?mode=async → 202 + poll)
Долгие генерации по умолчанию стримят SSE. Токен может вместо этого запросить
фоновую постановку: добавьте ?mode=async к запросу генерации — вместо SSE придёт
202 {job_id, poll}. Дальше опрашивайте статус по poll-URL
(GET …/jobs/{jobId}); статусы pending → running → succeeded|failed|canceled.
Отменить — POST …/jobs/{jobId}/cancel (best-effort: генератор без канала
прерывания может доработать, результат сохранится). Poll-ответ leak-безопасен:
он не отдаёт actor_snapshot, request_body, token_id — только статус, прогресс
и время. Задача принадлежит своему ресурсу (IDOR-защита по resource_type +
resource_id). Async-режим доступен только токену; cookie-клиент всегда получает
SSE. Инфраструктура — server/services/jobRunner.js, таблица generation_job.
Экспонирующие операции — только через человека (403)
Шеринг (/share), перенос проекта (/transfer, /transfer-owner) и публичность
коллекции (is_public) недоступны токену на любом scope — они делают ресурс
видимым за пределами текущих прав и требуют одобрения человека. Токену такой запрос
отвечает 403 (для is_public — жёсткий 400/403). Это не confirm-гейт: агент
не может подтвердить сам, канала внешнего одобрения пока нет
(server/auth/opRegistry.js → WRITE_DENY_HUMAN_APPROVAL).
display label ≠ system key
Видимая надпись (заголовок H1, текст кнопки) — это отображение; слаг id:,
имя PG-функции, ключ в коде — это системные ключи. Переименование надписи не
меняет системный ключ, и наоборот. Слаги страниц — системные ключи: они
постоянны, на них ссылаются снаружи.
Роли
Шесть ролей системы: guest, employee, spaceman (менеджер спейса),
supervisor (наблюдатель, read-only), org_admin, site_admin. Подробно —
roles.