temporality
Изменить страницу↗
Инженерные документы

Организационная структура и модель доступа Temporality

#1. Цель

Ввести единую модель организационной иерархии и доступа, на которой строятся видимость, использование и управление всеми сущностями Temporality:

Модель должна поддерживать:

Ключевой принцип:

Оргструктура определяет область видимости и наследования, а не является единственным механизмом авторизации.


#2. Проблематика текущей модели

Сейчас в системе сосуществуют три несвязанных механизма:

Это не масштабируется, поскольку отсутствуют:


#3. Основные понятия

#3.1. Org Unit

Узел организационной структуры.

Примеры:

Org Unit образуют дерево.

Компания A
├── Разработка
│   ├── Платформа
│   │   ├── Temporality
│   │   └── Infrastructure
│   └── Продукт
└── Качество

Компания B
└── ...

#3.2. Resource

Ресурс, которым можно управлять или который может использовать агент:


#3.3. Project

Проект не является частью дерева оргструктуры.

Проект может объединять:


#3.4. Identity

В системе существуют разные субъекты:

Нельзя автоматически приравнивать права одного субъекта к другому.


#4. Дерево организационной структуры

Один корневой узел на инсталляцию.

Organization A
├── Development
│   ├── Platform
│   └── Product
└── QA

Organization B
└── ...

#Таблица

org_unit
    id
    parent_id
    kind
    name
    path
    created_at
    updated_at

#Поля

id            TEXT PRIMARY KEY

parent_id     TEXT NULL
              REFERENCES org_unit(id)

kind          TEXT
              -- organization | department | team | ...

name          TEXT

path          TEXT
              -- materialized path предков

head_user_id  TEXT NULL
              -- руководитель узла: цель эскалаций и дефолтный адресат
              -- командных запросов; наследуется снизу вверх по дереву

created_at
updated_at

parent_id = NULL разрешён только у корневого узла.

Глубина дерева в первой версии — до 7–8 уровней.


#5. Материализованный путь

Для определения ancestor/descendant использовать materialized path.

Пример:

a
a.b
a.b.c
a.b.c.d

Для узла a.b.c.d предки:

a
a.b
a.b.c
a.b.c.d

Перемещение узла требует пересчёта path всего поддерева.

Перемещение считается редкой административной операцией.

#Требования


#6. Наследование

Основной принцип:

Ресурс, привязанный к Org Unit, доступен этому узлу и всему его поддереву.

Например:

Development
├── Platform
└── Product

Агент привязан к Development.

Он доступен:

#Глобальный ресурс

org_unit_id = NULL

означает:

ресурс доступен всей инсталляции.

Это сохраняет обратную совместимость с текущей моделью.


#7. Запрет отрицательного наследования

В первой версии отсутствуют:

То есть модель только аддитивная:

родитель
   ↓
поддерево

Если ресурс привязан к Development, невозможно сделать его доступным Platform, но скрытым от Product.

Это должно быть явно зафиксировано, чтобы не возникла необходимость реализовывать ACL с отрицательными правилами.


#8. Привязка ресурсов

Распределяемые ресурсы получают:

org_unit_id TEXT NULL REFERENCES org_unit(id)

Минимально:

workspace_agent.org_unit_id
workspace_skill.org_unit_id
workspace_mcp_server.org_unit_id
workspace_provider.org_unit_id
org_unit_trigger.org_unit_id

В дальнейшем аналогичный механизм должен быть доступен для других распределяемых ресурсов.


#9. Видимость ресурса

Для пользователя U ресурс R видим, если:

R.org_unit_id IS NULL

или:

R.org_unit_id

является самим Org Unit пользователя либо одним из его предков.

Иными словами:

resource scope
       ↓
     parent
       ↓
      user

#10. Видимость и полномочия — разные понятия

Это принципиальное требование.

Видимость ресурса не означает автоматически право:

Минимально различать:

visibility
use
manage
administer

#Пример

Пользователь может видеть Agent Reviewer, но не иметь права:

POST /runs

с этим агентом.

Аналогично пользователь может видеть MCP Server, но не иметь права привязать его к своему агенту.


#11. Пользователи

В первой версии пользователь имеет одну основную организационную принадлежность:

workspace_user.org_unit_id

Это его primary_org_unit.

Права и видимость наследуются от этого узла вверх по дереву.

Например:

Company A
└── Development
    └── Platform
        └── User

Пользователь получает область видимости:

Company A
Development
Platform

#12. Будущая поддержка нескольких Org Unit

Модель должна быть спроектирована так, чтобы в дальнейшем можно было добавить:

workspace_user_org_unit
    user_id
    org_unit_id
    membership_type

без изменения основной модели ресурсов.

Основной workspace_user.org_unit_id остаётся primary membership.

Дополнительные memberships нужны для случаев:

В MVP достаточно одной основной принадлежности.


#13. Роли

Существующие роли:

admin
operator
writer
reader

сохраняются.

Но область действия роли должна быть явной.

#Уровни

installation
org_unit
project

#Installation role

Действует на всей инсталляции.

Например:

installation admin

не ограничивается Org Unit.

#Org Unit role

Действует на Org Unit и его поддерево.

#Project role

Действует только внутри конкретного проекта.


#14. Проекты

Проект не является Org Unit.

Проект может быть связан с несколькими узлами:

workspace_project_org_unit

project_id
org_unit_id

PRIMARY KEY (project_id, org_unit_id)

Например:

Project X
├── Company A / Development
├── Company A / QA
└── Company B / Product

#15. Доступ к проекту

Проект виден пользователю, если существует пересечение между:

Иными словами, если проект связан с:

Company A / Development

он доступен пользователям:

Development
Development / Platform
Development / Product
...

но не:

Company A / QA
Company B / Development

если для них нет отдельной связи.


#16. Явное членство в проекте

Организационной принадлежности недостаточно для всех сценариев.

Добавить нормальную таблицу:

workspace_project_member

project_id
user_id
role
created_at
created_by

Явное членство позволяет дать доступ конкретному пользователю независимо от его Org Unit.

Например:

Project X
  ├── Development
  └── explicit member: external consultant

Это заменяет:

workspace_project.allowed_users

JSONB.

#Правило

Доступ к проекту:

org-unit access
OR
explicit project membership

#17. Внешние пользователи

Не создавать для каждого подрядчика искусственный полноценный Org Unit.

Пользователь должен иметь признак:

user_type:
    internal
    external

Для внешнего участника предусмотреть membership с:

sponsor_org_unit
expires_at

Доступ внешнего пользователя должен иметь возможность автоматически истекать.

В MVP допускается использовать explicit project membership.

Полноценная модель внешних организаций может быть реализована позже.


#18. Триггеры

Trigger — самостоятельная сущность, запускающая Agent Run на основании внешнего события.

Примеры:

Trigger также должен иметь область видимости:

org_unit_id

#19. Trigger не является пользователем

Критически важно разделить:

кто создал trigger

и:

от чьего имени выполняется операция

Например:

Azure DevOps webhook
       ↓
Trigger
       ↓
Reviewer Agent
       ↓
Execution Identity
       ↓
MCP / Provider / Project

Trigger не должен автоматически получать права администратора или права создавшего его пользователя.


#20. Execution Identity

Автоматический Run должен выполняться в контексте отдельной execution identity.

Пример:

CI Reviewer
Test Automation
PR Reviewer

Execution identity определяет:

#Главное правило

Автоматический Run не наследует автоматически права пользователя, создавшего Trigger.


#21. Авторизация Trigger

При создании Trigger необходимо проверить:

При каждом автоматическом запуске необходимо повторно проверить актуальность критических разрешений.


#22. Authorization на уровне операции

Фильтрация UI/API недостаточна.

Каждая чувствительная операция должна выполнять authorization check.

Минимально:

GET
POST
PUT
PATCH
DELETE
EXECUTE
BIND
UNBIND

Например:

POST /runs
{
    agent_id: ...
}

должен проверить:

user
    ↓
can use agent?
    ↓
can use project?
    ↓
can use skills?
    ↓
can use MCP?
    ↓
can use provider?

Нельзя полагаться на то, что ресурс уже был отфильтрован в предыдущем GET.


#23. Provider и credentials

Provider нельзя рассматривать просто как ещё один ресурс.

Необходимо разделить:

Provider definition
Credential / Secret
Usage policy

Видимость Provider не означает доступ к его credentials.

Нельзя возвращать секреты через обычные API ресурсов.

Использование credentials должно происходить только внутри разрешённого execution context.


#24. Политики

Архитектура должна поддерживать отдельную сущность Policy.

Даже если часть политик реализуется позже, необходимо предусмотреть возможность наследования:

Org Unit
├── Resources
├── Roles
└── Policies

Фактический набор политик (workspace.Policy):

Политики наследуются сверху вниз и сливаются рестриктивно; deny-измерения политики (filesystem/network/intake) не отменяются дочерними уровнями. Файловая политика фиксируется снимком при старте рана (policy_version) и наследуется при делегировании.


#25. Human-in-the-loop

Агент может в процессе Run запросить дополнительный контекст у человека.

Для этого используется специальный инструмент:

ask_human(...)

Это не сообщение в конкретный чат.

Это системное взаимодействие:

Agent Run
   ↓
Human Request
   ↓
User / Role / Project owner
   ↓
Communication channel
   ↓
Response
   ↓
Agent Run

#26. Получатель Human Request

Агент не должен напрямую выбирать транспорт:

Matrix
Telegram
...

Агент выбирает логического получателя.

Например:

project_owner
qa_lead
specific_user

Система определяет конкретного пользователя и канал.


#27. Канал коммуникации

Настройки пользователя определяют предпочтительный канал.

Например:

User
├── Matrix      preferred
├── Telegram    enabled
└── Email       enabled

Система использует:

preferred channel

и при необходимости fallback.

Агент не должен зависеть от реализации конкретного транспорта.


#28. Human Request как отдельная сущность

human_request

id
run_id
recipient
question
options
status
channel
created_at
delivered_at
answered_at
expires_at
response

Статусы:

pending
delivered
answered
rejected
expired
cancelled

(waiting слит в delivered; rejected — отказ идентичности/получателя.)


#29. Приостановка Run

После ask_human:

running
    ↓
waiting_for_human

При этом:

После ответа:

waiting_for_human
    ↓
resuming
    ↓
running

#30. Human Request и права

Агент не может произвольно обратиться к любому пользователю.

Необходимо проверить:

Получателем может быть:

user:…        (id или имя)
role:…        (роль)
org:…         (подразделение)
project_owner
title:…       (должность — групповой адрес)

Всё перечисленное реализовано (резолвер получателя в уведомлениях).


#31. Контекст Human Request

Запрос человеку должен быть самодостаточным.

Он должен содержать:

Не следует передавать весь технический контекст Run.


#32. Структурированные ответы

Если вопрос допускает варианты:

options:
    - id
      label

Ответ должен сохранять структурированное значение.

Свободный текст также поддерживается.


#33. Таймаут Human Request

Human Request должен иметь timeout.

После истечения система выполняет заданную policy:

Политика должна быть частью модели выполнения, а не транспортного адаптера.


#34. Изменение оргструктуры во время Run

Изменение Org Unit не должно автоматически уничтожать уже созданный Run.

При создании Run необходимо зафиксировать execution context:

agent
skills
MCPs
provider
project
execution identity
authorized_by (кто санкционировал)
effective policies (файловая политика — замороженный снимок + policy_version)

Снимок файловой политики наследуется дочерними ранами при делегировании — изменение политик не повышает полномочия уже работающего дерева.

Это позволяет восстановить контекст, в котором Run был создан.

При этом критические действия внутри Run могут повторно проверять актуальные права.


#35. Snapshot execution context

Run должен позволять ответить на вопрос:

Почему операция имела право использовать этот ресурс в момент выполнения?

Для этого необходимо сохранять как минимум идентификаторы:

agent_id
skill_ids
mcp_ids
provider_id
project_id
execution_identity_id
policy context

Полное копирование ресурсов не требуется.


#36. Аудит

Изменения модели доступа должны иметь отдельный audit trail.

Минимальные события:

org_unit.created
org_unit.updated
org_unit.moved
org_unit.deleted

user.org_unit_changed

resource.bound
resource.unbound

project.org_unit_added
project.org_unit_removed

project.member_added
project.member_removed

role.granted
role.revoked

trigger.created
trigger.updated
trigger.deleted

execution_identity.created
execution_identity.updated
execution_identity.deleted

Каждое событие должно содержать:

actor
timestamp
target
old_value
new_value

По возможности также:

reason

#37. Audit Human-in-the-loop

Human interaction также является частью траектории Run.

Фиксировать:

human.requested
human.answered
cancel/reject/timeout варианты: human.cancelled, human.rejected, human.timed_out,
human.reasked; факт доставки — notification.sent
run.resumed

Это позволит анализировать:


#38. API организационной структуры

GET    /v1/org/units
POST   /v1/org/units

GET    /v1/org/units/{id}
PUT    /v1/org/units/{id}
DELETE /v1/org/units/{id}

GET    /v1/org/units/{id}/resources
GET    /v1/org/units/{id}/roles
PUT    /v1/org/units/{id}/roles/{userID}
DELETE /v1/org/units/{id}/roles/{userID}
GET    /v1/org/units/{id}/effective-policy

Изменять дерево может только соответствующий администратор.


#39. API привязки ресурсов

PUT    /v1/org/resources/{kind}/{id}/binding
DELETE /v1/org/resources/{kind}/{id}/binding

Пример:

PUT /v1/org/resources/agents/reviewer/binding

{
    "org_unit_id": "development"
}

org_unit_id = null означает глобальную доступность.


#40. API проектов

Фактически реализовано (префикс /v1/workspace):

GET    /v1/workspace/projects/{id}/members
POST   /v1/workspace/projects/{id}/members/{userID}
DELETE /v1/workspace/projects/{id}/members/{userID}

Орг-связи проекта меняются replace-семантикой через PUT /v1/workspace/projects/{id} с полем org_units — отдельных /org-units-эндпоинтов нет.

Старые:

workspace_project.allowed_users
workspace_user.projects

после миграции не используются.


#41. API списков

Существующие API:

GET /v1/workspace/agents
GET /v1/workspace/skills
GET /v1/workspace/mcp-servers
GET /v1/workspace/providers
GET /v1/workspace/projects
GET /v1/workspace/triggers

должны возвращать только сущности, доступные вызывающему субъекту.

#Маршрутизация моделей

GET /v1/workspace/model-config?model={model}&project={project}[&provider={provider_id}]

Возвращает провайдер, обслуживающий модель для проекта. Без provider — первый из видимых проектову провайдеров (глобальные + привязанные к оргединицам проекта), у которого модель есть в списке models. С provider={id} (pinned-вызов) — только этот провайдер: явная привязка агента к провайдеру — контракт маршрутизации, дублирование модели у другого провайдера не перехватывает вызов. Ответ:

{"provider_id":"prov-...","provider_name":"...","base_url":"https://...","api_key":"...","model":"...","max_concurrent_requests":4}

404 — модель не обслуживается ни одним видимым провайдером (или пиновый провайдер её не декларирует). 409 — обслуживающий провайдер выключен. Ядро вызывает этот эндпоинт в call_model для нестандартных моделей (TTL-кеш 60 с); ответ содержит ключ провайдера, поэтому эндпоинт закрыт гейтом RoleReader.

Для системного runtime/API, работающего с внутренней execution identity, допускается отдельный privileged API.

Важно:

privileged API не должен использоваться для обхода authorization в пользовательском API.


#42. UI — Организация

Добавить раздел:

Организация

Возможности:

Перемещение должно требовать подтверждения, поскольку оно меняет область видимости.


#43. UI — Ресурсы

В формах:

добавить:

Доступность

Варианты:

Вся организация
[Org Unit]

Для администратора отображать эффективную область действия.

Например:

Доступно:
Development
└── 14 дочерних узлов

#44. UI — Проект

В проекте:

Организационные области

Мультивыбор Org Unit.

Отдельно:

Участники

для explicit project membership.

Это две разные модели и не должны объединяться в одно поле.


#45. UI — Пользователь

Показывать:

Основное подразделение
Роли
Проекты

Основное подразделение выбирается вместо старого списка проектов.

Проекты показываются как вычисленная область доступа плюс явные memberships.


#46. UI — Trigger

При создании Trigger пользователь должен выбрать:

  1. источник события;
  2. событие;
  3. агента;
  4. проект/область;
  5. execution identity;
  6. policy;
  7. правила human-in-the-loop.

Не требовать от пользователя написания YAML для типового сценария.


#47. UI — Execution Identity

Отдельная сущность для автоматических запусков.

Например:

PR Reviewer
Test Automation
Work Item Analyst

Показывать:


#48. Миграция

Миграция должна быть постепенной.

#Этап 1

Создать корневой Org Unit:

Organization

Все существующие пользователи:

org_unit_id = Organization

Все существующие ресурсы:

org_unit_id = NULL

Поведение системы не меняется.


#Этап 2

Создать новую модель проектов:

workspace_project_org_unit
workspace_project_member

Перенести существующие связи.

Старые:

workspace_user.projects
workspace_project.allowed_users

пока сохраняются.


#Этап 3

Переключить чтение на новую модель.

Старые JSONB-колонки больше не участвуют в authorization.


#Этап 4

Провести проверку расхождений:

old access model
vs
new access model

До удаления старых полей необходимо убедиться, что новая модель не изменила существующие права неожиданным образом.


#Этап 5

После стабилизации удалить:

workspace_user.projects
workspace_project.allowed_users

#49. Совместимость

NULL org_unit_id у ресурсов сохраняет текущую семантику:

ресурс глобальный.

Это позволяет постепенно вводить организационное ограничение без обязательной миграции всех существующих ресурсов.


#50. Производительность

Основные authorization-запросы должны быть рассчитаны на выполнение большого количества раз.

Необходимо предусмотреть индексы для:

Не следует выполнять полное построение дерева при каждом tool call.

При необходимости использовать:

Конкретный механизм оптимизации может быть выбран при реализации после измерений.


#51. Тестирование

Обязательны интеграционные тесты следующих сценариев.

#Иерархия

A
└── B
    └── C

Ресурс A:

Ресурс C:


#Глобальный ресурс

org_unit_id = NULL

виден всем.


#Кросс-функциональный проект

Проект связан:

Development
QA
Company B / Product

Проверить пользователей каждого подразделения.


#Explicit project membership

Пользователь вне организационной области проекта получает доступ только после explicit membership.


#Роли

Проверить:


#Trigger

Проверить:


#Run

Проверить:


#Human-in-the-loop

Проверить:

  1. агент создаёт Human Request;
  2. система определяет получателя;
  3. выбирается preferred channel;
  4. Run переходит в waiting_for_human;
  5. ответ сохраняется;
  6. Run возобновляется;
  7. ответ становится результатом ask_human.

#Изменение Org Unit

Проверить:

  1. пользователь имел доступ к ресурсу;
  2. пользователя переместили;
  3. effective visibility изменилась;
  4. новый Run использует новые права;
  5. существующий Run не исчезает;
  6. критическое действие корректно проходит актуальную authorization check.

#52. Фазы реализации

#Фаза 1 — Org Model


#Фаза 2 — Project Model


#Фаза 3 — Authorization


#Фаза 4 — Triggers


#Фаза 5 — Human-in-the-loop


#Фаза 6 — Policies


#Фаза 7 — UI


#Фаза 8 — Migration & Cleanup


#53. Критерии готовности

Система считается реализованной, если можно воспроизвести следующий сценарий:

Company A
└── Development
    └── Platform
        └── User

Company B
└── QA

Создан ресурс:

Reviewer Agent
scope = Company A / Development

Пользователь Platform его видит и может использовать.

Пользователь Company B его не видит и не может использовать через прямой API-вызов.

Создан проект:

Omni
├── Company A / Development
└── Company B / QA

Пользователь из обеих областей получает доступ.

В проект добавлен внешний пользователь через explicit membership.

Создан Trigger:

Azure DevOps Pull Request
        ↓
Reviewer
        ↓
Execution Identity = PR Reviewer

Webhook запускает Run.

Run использует только ресурсы, разрешённые execution identity.

Reviewer обнаруживает недостаток контекста и вызывает:

ask_human

Система:

  1. определяет допустимого получателя;
  2. смотрит настройки пользователя;
  3. отправляет запрос через Matrix или Telegram;
  4. переводит Run в waiting_for_human;
  5. получает ответ;
  6. возобновляет Run;
  7. передаёт ответ агенту;
  8. завершает Run.

В Temporality при этом видна единая траектория:

trigger
  ↓
run.created
  ↓
agent execution
  ↓
human.requested
  ↓
human.answered
  ↓
waiting_for_human
  ↓
human.reasked
  ↓
run.resumed
  ↓
run.completed

Все изменения оргструктуры, membership, ролей, bindings, Trigger и execution identity аудируются.

#54. Архитектурные ограничения первой версии

Чтобы не превратить первую реализацию в полноценную IAM-платформу, явно не реализовывать:

При этом архитектура должна оставлять возможность добавить эти возможности позднее без переделки базовой модели.

#55. Ключевой принцип архитектуры

Все механизмы должны сходиться к одной модели:

                    ┌──────────────┐
                    │  Org Units   │
                    └──────┬───────┘
                           │
             ┌─────────────┼─────────────┐
             ▼             ▼             ▼
         Resources      Projects       Policies
             │             │             │
             └─────────────┼─────────────┘
                           ▼
                         User
                           │
                           ▼
                     Authorization
                           │
             ┌─────────────┴─────────────┐
             ▼                           ▼
        Manual Run                  Trigger Run
                                         │
                                         ▼
                                Execution Identity
                                         │
                                         ▼
                                      Agent Run
                                         │
                              ┌──────────┴──────────┐
                              ▼                     ▼
                           Tools              Human Request
                                                    │
                                                    ▼
                                           Matrix / Telegram / ...

Org Unit определяет область наследования. Project определяет область совместной работы. Role определяет полномочия. Policy определяет ограничения. Execution Identity определяет права автоматической операции. Authorization принимает окончательное решение. Temporality фиксирует контекст и историю этого решения.