BASE_THINK.md

Да, тут лучше сразу зафиксировать не как “страничку с плитками”, а как **отдельный Control Plane / Launcher NODE.DC**: единая точка входа, витрина приложений, управление клиентами, выдача доступов, связка с Authentik и дальше — прокидывание пользователей в Plane/Task Manager, NodeDC и будущие сервисы.

Я бы раскладывал так.

---

## 1. Главная мысль по продукту

Launcher — это **не часть NodeDC** и **не часть Task Manager**. Это отдельное React-приложение, которое управляет всей экосистемой.

Функционально он отвечает за:

1. вход пользователя через Authentik / OIDC;
2. определение, к какому клиенту / компании относится пользователь;
3. показ только доступных сервисов;
4. управление клиентами, пользователями, группами и сервисами;
5. выдачу доступов на уровне клиента, группы или конкретного участника;
6. передачу пользователя в нужный сервис по SSO;
7. хранение каталога приложений: название, описание, URL, slug, статус, превью, порядок отображения;
8. в будущем — синхронизацию доступов с Authentik, Plane, NodeDC и другими сервисами.

Это хорошо совпадает с базовым документом бэкендеров: там уже выделены сущности **Клиент**, **Участник**, **Группа**, **Сервис** и **Доступ**, а доступ может выдаваться всему клиенту, группе или отдельному участнику.  

---

## 2. Важное архитектурное решение: Authentik не должен быть всей бизнес-логикой

Authentik лучше использовать как **Identity Provider и SSO-слой**, а не как единственное место хранения бизнес-иерархии.

То есть:

| Слой                 | За что отвечает                                                                                       |
| -------------------- | ----------------------------------------------------------------------------------------------------- |
| Authentik            | логин, пароль, OIDC, группы, claims, SSO, политики входа                                              |
| Launcher Backend     | клиенты, компании, участники, сервисы, доступы, исключения, демо-периоды, статусы, каталог приложений |
| Plane / Task Manager | workspace, проекты, роли внутри workspace, доски, задачи                                              |
| NodeDC               | workflows, sharing workflow, агентные сценарии, runtime-доступы                                       |
| Launcher Frontend    | витрина, админка, доступы, запуск сервисов                                                            |

Почему так: в Authentik действительно есть пользователи, группы, роли, приложения, политики, application bindings и entitlements. Группы — это коллекции пользователей, группы могут иметь parent groups, роли могут назначаться группам и наследоваться вниз по иерархии. ([authentik][1]) Но бизнес-сущности типа “ООО Ромашка купила Task Manager до 01.09”, “у Васи исключение из доступа”, “этому клиенту доступен демо-модуль”, “у сервиса вот такое превью” — лучше держать в собственной модели Launcher.

Authentik должен получать из Launcher только то, что нужно для SSO и доступа: группы, application bindings, claims, entitlements или синхронизированные атрибуты. Scope mappings в Authentik как раз позволяют передавать данные пользователя и групп в OAuth/OIDC claims. ([authentik][2])

---

## 3. Доменная модель Launcher

Я бы зафиксировал такие сущности.

### 3.1. Client / Клиент

Клиент — владелец доступа. Это может быть компания или частное лицо.

Поля:

```ts
Client {
  id: string;
  type: "company" | "person";
  name: string;
  legalName?: string;
  status: "active" | "suspended" | "demo" | "expired";
  demoEndsAt?: string;
  contactName?: string;
  contactEmail?: string;
  notes?: string;
  createdAt: string;
  updatedAt: string;
}
```

Пример:

```ts
{
  id: "client_romashka",
  type: "company",
  name: "ООО Ромашка",
  status: "active",
  demoEndsAt: "2026-06-01"
}
```

---

### 3.2. User / Участник

Пользователь берётся из Authentik, но в Launcher хранится его бизнес-привязка.

```ts
LauncherUser {
  id: string;
  authentikUserId: string;
  email: string;
  name: string;
  avatarUrl?: string;
  globalStatus: "invited" | "active" | "blocked";
  createdAt: string;
}
```

---

### 3.3. ClientMembership / Членство в клиенте

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

```ts
ClientMembership {
  id: string;
  clientId: string;
  userId: string;
  role: "client_owner" | "client_admin" | "member";
  status: "active" | "disabled";
}
```

Роли:

| Роль            | Что может                                 |
| --------------- | ----------------------------------------- |
| `root_admin`    | ваш главный NODE.DC-админ, видит всё      |
| `support_admin` | ваш внутренний оператор, можно ограничить |
| `client_owner`  | главный админ клиента                     |
| `client_admin`  | админ клиента                             |
| `member`        | обычный пользователь                      |

Важно: **root_admin Launcher** не обязательно должен быть тем же самым, что **super-user Authentik**. В Authentik super-user даётся через группу с super-user правами, и эти права наследуются descendant-группами, поэтому с этим нужно аккуратно. ([authentik][1])

---

### 3.4. ClientGroup / Группа внутри клиента

Это не обязательно один-в-один Authentik group. Это бизнес-группа внутри клиента.

```ts
ClientGroup {
  id: string;
  clientId: string;
  name: string;
  description?: string;
  memberIds: string[];
}
```

Примеры:

* Руководство
* Менеджеры
* Бухгалтерия
* Демо-команда
* IT-администраторы

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

---

### 3.5. Service / Сервис

Сервис — это приложение в экосистеме.

```ts
Service {
  id: string;
  slug: string;
  title: string;
  subtitle?: string;
  description: string;
  url: string;
  launchUrl?: string;
  iconUrl?: string;
  coverImageUrl?: string;
  previewVideoUrl?: string;
  ambientVideoUrl?: string;
  accentColor?: string;
  status: "active" | "maintenance" | "hidden" | "disabled";
  order: number;
  authentikApplicationSlug?: string;
  authentikGroupName?: string;
}
```

Примеры сервисов:

* NodeDC Agent Platform
* NODE.DC Task Manager
* Tender Agent
* 1C Assistant
* Digital Twin Viewer
* Demo Gallery
* Internal Tools

В базовом документе уже есть поля сервиса: название, описание, иконка, URL, технический slug, статус, связанная группа Authentik, порядок отображения. 

---

### 3.6. ServiceAccess / Доступ к сервису

Доступ должен быть трёхуровневым:

```ts
ServiceGrant {
  id: string;
  serviceId: string;
  targetType: "client" | "group" | "user";
  targetId: string;
  appRole: "viewer" | "member" | "admin" | "owner";
  status: "active" | "disabled";
}
```

Отдельно нужны исключения:

```ts
ServiceAccessException {
  id: string;
  serviceId: string;
  userId: string;
  type: "deny" | "allow";
  reason?: string;
}
```

Правило вычисления:

```txt
effectiveAccess =
  client is active
  AND service is active
  AND user is active
  AND user has grant via client/group/user
  AND user does not have deny exception
```

Это прямо нужно, потому что в документе бэкендеров есть сценарий: сервис подключен всему клиенту, но конкретному участнику его надо убрать — это должно отображаться как исключение. 

---

## 4. Иерархия прав в интерфейсе

### 4.1. Root Admin NODE.DC

Видит всё:

* все клиенты;
* всех пользователей из Authentik / Launcher;
* все сервисы;
* все доступы;
* все исключения;
* статусы синхронизации;
* ошибки provisioning;
* аудит действий;
* может банить, блокировать, приостанавливать клиента;
* может создавать сервисы и редактировать каталог;
* может назначать клиента, админов клиента и доступы к приложениям.

---

### 4.2. Client Owner / Client Admin

Видит только свою компанию.

Может:

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

Не может:

* видеть других клиентов;
* создавать глобальные сервисы;
* менять URL сервисов;
* выдавать доступ к сервису, который клиенту не куплен / не разрешён;
* менять Authentik-группы напрямую;
* видеть системные ошибки других клиентов.

---

### 4.3. Member

Видит только пользовательский Launcher:

* доступные сервисы;
* карточку профиля;
* выход из аккаунта;
* возможно — выбор компании, если он состоит в нескольких клиентах.

Никаких глобальных настроек.

---

## 5. UX-структура приложения

Ты правильно смотришь в сторону референса. Его можно взять не буквально, а как композиционный принцип:

> полноэкранный медиа-фон + верхний минимальный бар + нижняя карусель сервисов + glass-detail выбранного сервиса + отдельный админский слой.

### 5.1. Пользовательский экран

Композиция:

```txt
┌─────────────────────────────────────────────┐
│ Logo                      Search/Profile/⚙  │
│                                             │
│                                             │
│        Ambient preview выбранного сервиса   │
│        / абстрактная анимация / видео       │
│                                             │
│                  Glass Detail Card          │
│                  Название                   │
│                  Описание                   │
│                  Статус                     │
│                  [Открыть]                  │
│                                             │
│                                             │
│      [NodeDC] [Task Manager] [1C] [Demo]    │
└─────────────────────────────────────────────┘
```

Внизу — не “проекты” в смысле Plane Projects, а лучше назвать:

* **Контуры**
* **Сервисы**
* **Приложения**
* **Рабочие среды**

Я бы выбрал **“Сервисы”** или **“Контуры”**, потому что “Проекты” уже есть внутри Plane.

---

### 5.2. Поведение выбора сервиса

При клике на карточку в нижней панели:

1. центральный фон меняется на `ambientVideoUrl` / `previewVideoUrl` / `coverImageUrl`;
2. поверх появляется большая glass-карточка;
3. справа или по центру показывается описание;
4. кнопка “Открыть” ведёт на SSO launch URL;
5. если сервис на техработах — кнопка disabled, статус “Техработы”;
6. если сервис скрыт — обычный пользователь его не видит;
7. root admin может видеть скрытые сервисы с бейджем.

---

### 5.3. Верхняя панель

Минимальная:

* логотип NODE.DC / H&H DC;
* выбранный клиент, если у пользователя несколько компаний;
* кнопка “Администрирование”, если есть права;
* профиль пользователя;
* выход.

По дизайн-коду верхние панели должны держать единую горизонтальную ось, одинаковую высоту кнопок и нормальные радиусы. 

---

## 6. Админка: не отдельная страница, а слой поверх Launcher

Так как приложение одностраничное, админку лучше делать не как `/admin`, а как **режим / overlay**:

```txt
[Launcher View]
    ↓
[Admin Command Center overlay / drawer]
```

Варианты:

1. правая большая glass-панель;
2. полноэкранный matte glass overlay;
3. split view: слева список, справа детали;
4. command-center в стиле системной панели.

Для MVP я бы делал **полноэкранный админский overlay** с внутренними вкладками.

---

## 7. Разделы админки

### Для root admin

```txt
Администрирование
├── Клиенты
├── Участники
├── Группы
├── Каталог сервисов
├── Доступы
├── Инвайты
├── Синхронизация
└── Аудит
```

Это расширяет базовый документ, где уже есть разделы “Клиенты”, “Участники”, “Группы внутри клиента”, “Каталог сервисов”, “Доступы” и “Статус Authentik”. 

---

### Для client admin

```txt
Администрирование компании
├── Участники
├── Группы
├── Доступы к сервисам
├── Инвайты
└── Профиль компании
```

---

## 8. Экран “Клиенты”

Список клиентов:

| Клиент      | Тип          | Статус  | Участники | Сервисы | Демо до | Контакт |
| ----------- | ------------ | ------- | --------- | ------- | ------- | ------- |
| ООО Ромашка | Компания     | Активен | 18        | 2       | —       | Иван    |
| Иван Петров | Частное лицо | Демо    | 2         | 1       | 01.06   | Иван    |

Детальная карточка клиента:

* название;
* тип;
* статус;
* демо-доступ;
* контактное лицо;
* заметки;
* участники;
* группы;
* подключенные сервисы;
* история действий;
* синхронизация.

---

## 9. Экран “Каталог сервисов”

Сервис — это настраиваемая витрина.

Поля в форме:

```txt
Название
Slug
Краткое описание
Полное описание
URL
Launch URL
Статус: активен / скрыт / техработы / отключен
Иконка
Обложка
Превью-видео
Ambient-видео
Цветовой акцент
Порядок отображения
Связанная группа Authentik
Связанное приложение Authentik
Доступен всем новым клиентам: да/нет
```

Очень важно: медиа нужно сразу предусмотреть мультиформатно:

```ts
ServiceMedia {
  icon?: AssetRef;
  thumbnail?: AssetRef;
  coverImage?: AssetRef;
  previewVideo?: AssetRef;
  ambientVideo?: AssetRef;
  fallbackGradient?: string;
}
```

Поддержать:

* `.png`
* `.jpg`
* `.webp`
* `.gif`
* `.mp4`
* `.webm`

На фронте:

```ts
type MediaKind = "image" | "video" | "gif" | "gradient";
```

---

## 10. Экран “Доступы”

Это ключевой экран.

Я бы делал не просто таблицу, а **матрицу доступа**:

```txt
Клиент: ООО Ромашка

Участник        Task Manager    NodeDC      1C Assistant
---------------------------------------------------------
Иван            Admin           Admin       —
Вася            Member          —           —
Лена            Member          Member      Deny
Бухгалтерия     Member          —           Member
```

И рядом объяснение effective access:

```txt
Лена / NodeDC:
Нет доступа, потому что есть индивидуальное исключение deny.

Вася / Task Manager:
Есть доступ, потому что сервис подключен всему клиенту.

Пётр / 1C Assistant:
Есть доступ, потому что состоит в группе “Бухгалтерия”.
```

Это прямо соответствует требованию из PDF: надо отображать итоговый доступ пользователя и объяснять, почему он есть или отсутствует. 

---

## 11. Как это ложится на Plane / Task Manager

Здесь лучше зафиксировать жёсткое правило:

> Один клиент в Launcher = один workspace в Task Manager.

Например:

```txt
Client: ООО Ромашка
Plane Workspace: romashka
```

Дальше:

1. root admin создаёт клиента;
2. клиенту подключается сервис `task-manager`;
3. backend создаёт / связывает Plane workspace;
4. все пользователи клиента, которым выдан доступ к Task Manager, автоматически появляются в этом workspace;
5. внутри Plane админ клиента уже создаёт проекты;
6. распределение людей по проектам остаётся штатной логикой Plane.

То есть Launcher **не должен управлять каждым проектом Plane**. Он управляет только:

* доступом к приложению;
* принадлежностью пользователя к клиенту;
* provisioning в workspace.

А уже Plane управляет:

* проектами;
* досками;
* задачами;
* ролями внутри workspace;
* project-level access.

Это правильная граница, потому что вы не ломаете внутреннюю модель Plane.

---

## 12. Как это ложится на NodeDC

Для NodeDC логика похожая, но глубже:

```txt
Client: ООО Ромашка
NodeDC Tenant / Workspace: romashka
NodeDC Workflows: выдаются через NodeDC sharing
```

Launcher отвечает за:

* доступ к приложению NodeDC;
* роль пользователя в NodeDC: member/admin;
* привязку пользователя к клиентскому пространству.

NodeDC отвечает за:

* workflow;
* sharing workflow;
* runtime-права;
* агенты;
* запуск / мониторинг;
* доступ к конкретным сценариям.

То есть в MVP Launcher не должен пытаться управлять каждым workflow. Он должен только открыть пользователю вход в NodeDC и передать идентичность / клиент / роль.

---

## 13. Authentik: как лучше состыковать

В Authentik есть Applications, Providers, Groups, Roles, Policies, Application bindings и Entitlements. Для приложений важно помнить: если на приложение не задано bindings, то по умолчанию все пользователи могут получить к нему доступ, поэтому для ваших сервисов лучше всегда задавать bindings явно. ([authentik][3])

Рекомендуемая схема:

```txt
Authentik
├── Application: launcher
├── Application: task-manager
├── Application: nodedc
├── Group: nodedc-root-admins
├── Group: client-romashka-users
├── Group: client-romashka-admins
├── Group: service-task-manager-romashka
└── Group: service-nodedc-romashka
```

Но бизнес-правило всё равно должно жить в Launcher.

Пример flow:

```txt
Launcher Backend:
user Вася имеет доступ к Task Manager
↓
создать / обновить группу в Authentik
↓
добавить Васю в group service-task-manager-romashka
↓
Authentik application binding разрешает вход в Task Manager
↓
Task Manager получает пользователя через OIDC
↓
Task Manager backend проверяет / создаёт пользователя в workspace romashka
```

OIDC scopes в Authentik уже включают `openid`, `profile`, `email`, `entitlements`, `offline_access`; `profile` включает базовую информацию пользователя и group membership, а entitlements можно использовать для application-level прав. ([authentik][4])

---

## 14. Что фронту делать сейчас без настоящего Authentik

Фронт сейчас надо делать так, будто Authentik уже есть, но через mock contract.

### Mock endpoint `GET /api/me`

```ts
type MeResponse = {
  user: {
    id: string;
    name: string;
    email: string;
    avatarUrl?: string;
  };
  launcherRole: "root_admin" | "support_admin" | "client_owner" | "client_admin" | "member";
  memberships: {
    clientId: string;
    clientName: string;
    role: "client_owner" | "client_admin" | "member";
  }[];
  activeClientId: string;
  permissions: {
    canOpenAdmin: boolean;
    canManageClients: boolean;
    canManageOwnClient: boolean;
    canManageServiceCatalog: boolean;
    canInviteUsers: boolean;
  };
};
```

### Mock endpoint `GET /api/launcher/services`

```ts
type LauncherServiceView = {
  id: string;
  slug: string;
  title: string;
  description: string;
  status: "active" | "maintenance" | "hidden";
  userAccess: "allowed" | "denied";
  appRole?: "viewer" | "member" | "admin" | "owner";
  openUrl?: string;
  media: {
    icon?: string;
    thumbnail?: string;
    coverImage?: string;
    previewVideo?: string;
    ambientVideo?: string;
  };
};
```

### Mock endpoint `GET /api/admin/clients`

Для root admin.

### Mock endpoint `GET /api/admin/client/:id/access-matrix`

Для матрицы доступов.

---

## 15. Frontend state-модель

Я бы делал так:

```ts
LauncherState {
  mode: "user" | "admin";
  activeClientId: string;
  selectedServiceId?: string;
  serviceRail: LauncherServiceView[];
  me: MeResponse;
}
```

Для админки:

```ts
AdminState {
  activeSection:
    | "clients"
    | "users"
    | "groups"
    | "services"
    | "access"
    | "invites"
    | "sync"
    | "audit";

  selectedClientId?: string;
  selectedUserId?: string;
  selectedServiceId?: string;
}
```

Технически:

* React;
* TypeScript;
* Tailwind;
* Zustand или Redux Toolkit для UI state;
* React Query / TanStack Query для API;
* mock API adapter, который потом меняется на реальный backend;
* без реального Authentik на первом этапе.

---

## 16. UI-компоненты

### Базовые

```txt
AppShell
TopBar
ProfileMenu
ClientSwitcher
ServiceRail
ServiceTile
ServiceStage
ServiceDetailGlassCard
LaunchButton
StatusBadge
AdminOverlay
AdminSidebar
AdminSectionHeader
GlassTable
AccessMatrix
UserPicker
GroupPicker
ServiceEditor
InviteModal
ConfirmModal
ActionDropdown
MediaPreviewField
```

### Shared design components

```txt
GlassSurface
GlassCard
RoundIconButton
AccentButton
SecondaryButton
DangerButton
Checker
Chip
PortalDropdown
SearchInput
SegmentedControl
```

Это важно, потому что в вашем дизайн-коде прямо написано: новый экран или popup не стилизуется локально “на глаз”; сначала используется shared-class или shared-component, а если shared-слоя нет — создаётся reusable-компонент. 

---

## 17. Дизайн-канон для Launcher

Из текущего HDESIGN-CODE надо взять:

1. matte black glass для popup, dropdown, modal, sidebar overlays и settings-карточек;
2. blur / backdrop-filter;
3. мягкую стеклянную границу;
4. большие радиусы;
5. отсутствие жёстких outline;
6. круглые action-кнопки;
7. акцентные CTA с правильным контрастом текста;
8. dropdown только через portal, если он внутри scroll/detail/card/sticky header.  

Радиусы можно взять напрямую:

```css
--launcher-radius-xl: 1.75rem;
--launcher-radius-card: 1.35rem;
--launcher-radius-control: 1.25rem;
--launcher-radius-circle: 999px;
```

Из дизайн-кода:

* большие surface-контейнеры: `1.75rem`;
* glass-карточки: `1.35rem`;
* поля, селекты, кнопки, chip-кнопки: `1.25rem`;
* малые круглые action-кнопки: `999px`. 

---

## 18. Как адаптировать референс

Референс даёт правильную композицию:

* большой экран;
* затемнённый / заблюренный фон;
* центральная медиа-зона;
* мягкая glass-панель;
* нижняя timeline/rail-панель;
* плавающий правый assistant-like модуль.

Для Launcher это можно перевести так:

```txt
Reference Video Editor → Launcher

Storyboard              → список сервисов / контуров
AI Assistant card       → detail выбранного сервиса / админ-панель
Moodboard               → медиа-превью / related apps
Timeline bottom         → service rail
Main preview            → ambient preview выбранного сервиса
```

То есть визуально это будет не “админка”, а **входная операционная панель экосистемы**.

---

## 19. MVP-границы

На первом этапе не надо делать всё.

### MVP 1 — пользовательский Launcher

* mock login state;
* верхний бар;
* профиль;
* список доступных сервисов;
* нижняя карусель;
* выбранный сервис;
* glass-detail;
* кнопка “Открыть”;
* статусы `active / maintenance / hidden`;
* mock media assets.

### MVP 2 — root admin shell

* кнопка “Администрирование”;
* overlay;
* разделы:

  * Клиенты;
  * Участники;
  * Каталог сервисов;
  * Доступы;
* CRUD на mock data;
* access matrix;
* service editor.

### MVP 3 — client admin shell

* ограниченная админка;
* участники своей компании;
* группы своей компании;
* инвайты;
* выдача доступов только к разрешённым сервисам.

### MVP 4 — backend contract ready

* заменить mock adapter на API;
* оставить те же UI-компоненты;
* добавить loading / error / empty states;
* добавить sync-status;
* добавить audit log.

---

## 20. Инвайты и регистрация

Лучше сейчас зафиксировать:

> MVP работает только по invite-only модели.

Сценарий:

```txt
1. Root admin создаёт клиента.
2. Root admin создаёт client_owner.
3. Client owner заходит в Launcher.
4. Client owner создаёт invite.
5. Пользователь переходит по одноразовой ссылке.
6. Authentik создаёт / активирует пользователя.
7. Launcher связывает пользователя с clientId.
8. Пользователь видит только сервисы, доступные ему.
```

Если позже появится свободная регистрация, то новый пользователь должен попадать в состояние:

```txt
unassigned / pending / no services
```

То есть он вошёл, но ничего не видит, пока root admin не привяжет его к клиенту.

---

## 21. Синхронизация

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

```ts
SyncStatus {
  target: "authentik" | "task-manager" | "nodedc";
  status: "synced" | "pending" | "error";
  lastSyncAt?: string;
  errorMessage?: string;
}
```

В PDF уже есть статусы Authentik: “синхронизировано”, “ожидает синхронизации”, “ошибка синхронизации”, а также действия “повторить” и “открыть детали ошибки”. 

В UI это нужно показывать не техническим логом, а понятной плашкой:

```txt
Authentik: синхронизировано
Task Manager: ожидает создания workspace
NodeDC: ошибка синхронизации — открыть детали
```

---

## 22. Главные риски

### Риск 1. Смешать роли Launcher и роли приложений

Нельзя делать одну роль “admin” на всё.

Нужно разделить:

```txt
launcherRole: root_admin / client_admin / member
taskManagerRole: owner / admin / member
nodeDcRole: admin / editor / viewer
```

---

### Риск 2. Назвать сервисы “проектами”

В Plane уже есть Projects. В NodeDC есть Workflows. В Launcher лучше использовать “Сервисы” или “Контуры”.

---

### Риск 3. Полагаться только на Authentik groups

Authentik groups хороши для SSO и грубой авторизации, но ваша коммерческая модель доступа должна жить в Launcher.

---

### Риск 4. Не сделать deny exceptions

Без исключений будет больно: “всему клиенту доступ дали, но Васе нельзя”.

---

### Риск 5. Сделать админку как обычный скучный CRUD

У вас визуальная система сильная. Админка должна быть такой же glass/control-plane, но с нормальной читаемостью таблиц.

---

## 23. ТЗ для Codex — первый проход

Можно дать Кодексу так:

```md
# ТЗ: NODE.DC Launcher Frontend MVP

## Цель
Создать отдельное одностраничное React-приложение Launcher для входа в экосистему NODE.DC. Приложение должно работать без реального Authentik на первом этапе, но иметь mock API-контракт, совместимый с будущей OIDC/Authentik-интеграцией.

## Основные режимы
1. User Launcher View — витрина доступных сервисов.
2. Root Admin View — глобальная админка NODE.DC.
3. Client Admin View — админка конкретного клиента.

## Дизайн
Использовать NODE.DC glass-canon:
- matte black glass;
- backdrop blur;
- крупные радиусы;
- без browser outline;
- круглые action-кнопки;
- portal dropdown;
- русскоязычный UI;
- reusable shared components, без локальной стилизации “на глаз”.

## User Launcher View
Сделать:
- fullscreen shell;
- top bar с логотипом, выбранным клиентом, профилем;
- центральную media-stage зону;
- нижнюю service rail панель;
- service tile карточки;
- detail glass-card выбранного сервиса;
- кнопку “Открыть”;
- статусы сервиса: доступен / техработы / скрыт;
- поддержку image / gif / mp4 / webm preview.

## Admin View
Сделать overlay-панель администрирования:
- Клиенты;
- Участники;
- Группы;
- Каталог сервисов;
- Доступы;
- Инвайты;
- Синхронизация.

## Mock Data
Добавить mock:
- ООО Ромашка;
- ООО Рога и Копыта;
- NodeDC Agent Platform;
- NODE.DC Task Manager;
- 1C Assistant;
- несколько пользователей с разными ролями.

## Access Engine
Реализовать функцию computeEffectiveAccess:
- учитывать client status;
- service status;
- grants на client/group/user;
- deny exceptions;
- user status.

## Acceptance Criteria
- member видит только доступные сервисы;
- client_admin видит админку только своей компании;
- root_admin видит все разделы;
- сервис на техработах виден, но кнопка “Открыть” disabled;
- hidden-сервис не виден member-у;
- deny exception перекрывает client-level grant;
- dropdown/menu рендерятся через portal;
- UI не содержит смешанных английских подписей.
```

---

## 24. Мой вердикт по структуре

Делать надо так:

```txt
Launcher
├── витрина сервисов
├── клиентская админка
├── root-админка
├── каталог сервисов
├── access matrix
├── invite flow
├── sync status
└── SSO launch layer
```

А не так:

```txt
просто React-страница с кнопками на разные сайты
```

Потому что у вас уже не набор сайтов, а экосистема:

```txt
Информационный сайт
↓
Launcher / Auth
↓
NodeDC Agent Platform
↓
Task Manager / Plane
↓
дальше 1C, тендеры, digital twins, демки, внутренние сервисы
```

И Launcher становится тем самым верхним слоем, который делает из отдельных приложений единый веб-суперапп.

[1]: https://docs.goauthentik.io/users-sources/groups/manage_groups/ "Manage groups | authentik"
[2]: https://docs.goauthentik.io/add-secure-apps/providers/property-mappings/ "Provider property mappings | authentik"
[3]: https://docs.goauthentik.io/add-secure-apps/applications/manage_apps/ "Manage applications | authentik"
[4]: https://docs.goauthentik.io/add-secure-apps/providers/oauth2/ "OAuth 2.0 provider | authentik"
