Роли и клиенты

Три роли в проде (owner / dm / player), как они разграничены в RLS, и что ждёт на горизонте: spectator, see_all, multi-DM как фундамент west-marches.

Членство в кампании хранится в таблице campaign_members (миграция 024): один row на пару (user_id, campaign_id) с полем role. Один пользователь может состоять в нескольких кампаниях с разными ролями. Ровно один owner на кампанию (unique partial index).


Таблица ролей

РольМожет читатьМожет писатьОсобенности
ownerвсё в кампаниивсёединственный owner; управляет ролями участников
dmвсё в кампаниивсё, кроме auth.users и чужих кампанийнесколько DM на кампанию — поддерживается
playerвсё в кампаниисвой PC + не-character ноды (031)создаёт и правит нод мира; чужой PC — нельзя

Несколько dm-аккаунтов на одну кампанию — уже работает. Это фундамент для west-marches со множеством DM: каждый ведёт свои сессии, видит общий мир.

Как роли проверяются

Три helper-функции SECURITY DEFINER (миграция 024):

  • is_member(campaign_id) — базовая проверка членства (для SELECT-политик).
  • is_dm_or_owner(campaign_id) — для write-политик и DM-эксклюзивных действий.
  • is_owner(campaign_id) — только для операций уровня кампании (состав участников, настройки).

Server actions используют resolveAuth(campaignId) из app/actions/transactions.ts — возвращает { role }, и дальше action бранчится: player-only actions требуют role='player' + isPcOwner.

Создание и управление ролями

Пользователи не регистрируются самостоятельно. Флоу:

  1. owner (или DM) создаёт аккаунт через скрипт / admin UI (scripts/seed-owner.ts — первый admin).
  2. user_profiles.must_change_password = true → при первом входе редирект на /onboarding.
  3. Owner назначает роль через /c/[slug]/members.
  4. Смена роли — через ту же страницу; понижение dm → player не лишает права читать историю, но лишает права писать DM-операции.

Инвайт-ссылки и самостоятельная регистрация — roadmap.

Защита PC через node_pc_owners

PC-ноды — особый случай. Помимо роли, используется таблица node_pc_owners (миграция 027): (node_id, user_id). Игрок может редактировать только PC, записанный на него в этой таблице. Функция can_edit_node(node_id) проверяет роль И наличие строки в node_pc_owners.

Несколько игроков могут контролировать один PC (совместный персонаж) — просто добавляется несколько строк в node_pc_owners.

Spectator — на горизонте

spectator — участник кампании без управляемых персонажей. Наблюдатель, зритель live-broadcast, ассистент DM. В проде роли пока нет.

Когда появится: spectator видит то же, что player (все события с mode='all'). Опциональный флаг participants.see_all: bool даст расширенный доступ — spectator с see_all=true видит и dm_only-события.

visibility.md

Партии — UI-агрегация, не security-граница

«Пачка» — это состав конкретной сессии (рёбра participated_in). Партия не является security-сущностью: нет «роли в партии», нет membership-таблицы пачки. Если событие адресовано «только этой пачке» — оно несёт явный список character_ids в поле visibility.


features/auth-and-membership/README.md — как работают приглашения и управление участниками. → dm-as-demiurge.md — что конкретно DM может делать и как это гейтится. → visibility.md — как visibility поле работает поверх ролевой системы.