АРХ - NODEDC PLATFORM: каркас платформенного репозитория
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# Architecture: NODE.DC Multi-App Platform
|
||||
|
||||
## Цель
|
||||
|
||||
Собрать единую платформенную основу для Launcher, Task Manager / Plane и будущих приложений NODE.DC.
|
||||
|
||||
Целевой пользовательский сценарий:
|
||||
|
||||
1. Пользователь открывает Launcher.
|
||||
2. Если сессии нет, его отправляет в Authentik.
|
||||
3. После логина Launcher получает identity и app access.
|
||||
4. Launcher показывает только доступные приложения.
|
||||
5. Переход в Task Manager не требует повторного логина.
|
||||
6. Прямой URL Task Manager тоже защищен.
|
||||
|
||||
## Роли компонентов
|
||||
|
||||
`Authentik` является единым Identity Provider:
|
||||
|
||||
- логин;
|
||||
- пароль;
|
||||
- активность пользователя;
|
||||
- группы;
|
||||
- app access;
|
||||
- OIDC claims;
|
||||
- будущие invite/enrollment/MFA flows.
|
||||
|
||||
`Launcher` является control plane:
|
||||
|
||||
- входная точка пользователя;
|
||||
- список доступных приложений;
|
||||
- admin UI управления пользователями и доступами;
|
||||
- backend-интеграция с Authentik API;
|
||||
- audit log админских действий.
|
||||
|
||||
`Task Manager / Plane` остается отдельным приложением:
|
||||
|
||||
- собственная БД;
|
||||
- собственные workspace/project/task/comment модели;
|
||||
- собственные роли workspace/project;
|
||||
- OIDC login через Authentik;
|
||||
- mapping внешней identity на существующего Plane user.
|
||||
|
||||
`Reverse proxy` является внешним защитным и маршрутизирующим слоем:
|
||||
|
||||
- единая точка входа;
|
||||
- HTTPS в staging/production;
|
||||
- routing по app domain;
|
||||
- forward headers для Authentik;
|
||||
- deny для пользователей без app access.
|
||||
|
||||
## Repository layout
|
||||
|
||||
Рабочая схема:
|
||||
|
||||
```text
|
||||
NODEDC/
|
||||
DOC/
|
||||
platform/
|
||||
docs/
|
||||
infra/
|
||||
packages/
|
||||
tasks/
|
||||
|
||||
/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher
|
||||
/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER
|
||||
```
|
||||
|
||||
`platform/` не забирает исходники Launcher и Plane внутрь себя. Он хранит только платформенный слой и договоренности между приложениями.
|
||||
|
||||
## Data ownership
|
||||
|
||||
Не создается единая общая таблица `users` для всех приложений.
|
||||
|
||||
Логическая схема:
|
||||
|
||||
```text
|
||||
authentik_db -> identity, groups, app access
|
||||
launcher_db -> app registry, local profiles, audit
|
||||
plane_db -> workspace, projects, tasks, comments, app roles
|
||||
future_app_db -> доменная логика будущих приложений
|
||||
```
|
||||
|
||||
Связь между identity и локальными пользователями выполняется через explicit mapping, а не через прямое чтение чужих таблиц.
|
||||
|
||||
## Auth flow
|
||||
|
||||
Для приложений, которые контролируются кодом:
|
||||
|
||||
- OIDC Authorization Code Flow + PKCE;
|
||||
- backend/session или BFF layer;
|
||||
- JWT/JWKS validation server-side;
|
||||
- проверка `issuer`, `audience`, `exp`, `sub`;
|
||||
- проверка app access group;
|
||||
- локальный user profile или external identity link.
|
||||
|
||||
Для legacy/временных приложений допускается reverse proxy forward-auth, но это временный внешний слой, а не единственная долгосрочная модель.
|
||||
|
||||
## Plane migration rule
|
||||
|
||||
Существующий Plane user нельзя пересоздавать.
|
||||
|
||||
Нужен mapping:
|
||||
|
||||
```text
|
||||
authentik_user.sub -> existing plane_user.id
|
||||
```
|
||||
|
||||
Plane должен логинить существующего пользователя по link, сохраняя все старые workspace, assignee, owner, created_by, comments и attachments.
|
||||
@@ -0,0 +1,108 @@
|
||||
# Auth Model
|
||||
|
||||
## Identity source
|
||||
|
||||
Единственный источник identity: Authentik.
|
||||
|
||||
Launcher, Plane и будущие приложения не хранят пароли пользователей и не становятся главным источником truth по логину.
|
||||
|
||||
## Required claims
|
||||
|
||||
Минимальный normalized user object:
|
||||
|
||||
```ts
|
||||
type AuthUser = {
|
||||
sub: string;
|
||||
email: string;
|
||||
name?: string;
|
||||
groups: string[];
|
||||
entitlements?: string[];
|
||||
};
|
||||
```
|
||||
|
||||
Обязательные проверки:
|
||||
|
||||
- `iss` совпадает с настроенным issuer;
|
||||
- `aud` совпадает с client/application audience;
|
||||
- `exp` не истек;
|
||||
- `sub` непустой и стабилен;
|
||||
- `email` присутствует для user-facing приложений;
|
||||
- `groups` или `entitlements` содержат требуемый app access.
|
||||
|
||||
## Groups
|
||||
|
||||
Минимальная группа верхнего уровня:
|
||||
|
||||
```text
|
||||
nodedc:superadmin
|
||||
```
|
||||
|
||||
Launcher:
|
||||
|
||||
```text
|
||||
nodedc:launcher:access
|
||||
nodedc:launcher:admin
|
||||
nodedc:launcher:user-manager
|
||||
```
|
||||
|
||||
Task Manager:
|
||||
|
||||
```text
|
||||
nodedc:taskmanager:access
|
||||
nodedc:taskmanager:admin
|
||||
```
|
||||
|
||||
Future apps:
|
||||
|
||||
```text
|
||||
nodedc:agents:access
|
||||
nodedc:tender:access
|
||||
nodedc:onec:access
|
||||
nodedc:dm:access
|
||||
```
|
||||
|
||||
## Access levels
|
||||
|
||||
Уровень 1: Authentik app access.
|
||||
|
||||
Отвечает на вопрос, можно ли открыть приложение.
|
||||
|
||||
Уровень 2: Launcher admin roles.
|
||||
|
||||
Отвечает на вопрос, можно ли управлять пользователями, группами, app registry и audit.
|
||||
|
||||
Уровень 3: Application roles.
|
||||
|
||||
Например, Plane workspace owner/admin/member/viewer. Эти роли остаются внутри Plane.
|
||||
|
||||
## Launcher backend requirements
|
||||
|
||||
Backend должен:
|
||||
|
||||
- хранить Authentik service token только server-side;
|
||||
- выполнять admin calls к Authentik API;
|
||||
- хранить audit log;
|
||||
- возвращать frontend только нормализованные данные пользователя и разрешенные действия;
|
||||
- не отдавать access/refresh/service tokens в browser bundle.
|
||||
|
||||
## Plane identity link
|
||||
|
||||
Минимальная таблица или эквивалентная модель в Plane:
|
||||
|
||||
```text
|
||||
external_identity_link
|
||||
provider = authentik
|
||||
authentik_sub
|
||||
email
|
||||
plane_user_id
|
||||
created_at
|
||||
last_login_at
|
||||
status
|
||||
```
|
||||
|
||||
Правило миграции:
|
||||
|
||||
- сначала искать link по `authentik_sub`;
|
||||
- если link найден, логинить связанный `plane_user_id`;
|
||||
- если link не найден, но email совпадает и включен migration auto-link, создать link;
|
||||
- если доступа нет, вернуть deny.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Local Deployment Plan
|
||||
|
||||
## Domains
|
||||
|
||||
Для локальной разработки целевая схема:
|
||||
|
||||
```text
|
||||
127.0.0.1 auth.local.nodedc
|
||||
127.0.0.1 launcher.local.nodedc
|
||||
127.0.0.1 task.local.nodedc
|
||||
127.0.0.1 agents.local.nodedc
|
||||
127.0.0.1 tender.local.nodedc
|
||||
127.0.0.1 onec.local.nodedc
|
||||
127.0.0.1 dm.local.nodedc
|
||||
```
|
||||
|
||||
## Current local apps
|
||||
|
||||
Launcher сейчас запускается как Vite app из:
|
||||
|
||||
```text
|
||||
/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher
|
||||
```
|
||||
|
||||
Task Manager сейчас доступен как Plane self-host runtime:
|
||||
|
||||
```text
|
||||
http://localhost:8090
|
||||
```
|
||||
|
||||
Runtime:
|
||||
|
||||
```text
|
||||
/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-app
|
||||
```
|
||||
|
||||
Source fork:
|
||||
|
||||
```text
|
||||
/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/plane-src
|
||||
```
|
||||
|
||||
## Target local flow
|
||||
|
||||
Первый local infra milestone:
|
||||
|
||||
```text
|
||||
auth.local.nodedc -> Authentik
|
||||
launcher.local.nodedc -> Launcher web/backend
|
||||
task.local.nodedc -> Plane proxy/runtime
|
||||
```
|
||||
|
||||
Все внешние запросы идут через reverse proxy.
|
||||
|
||||
## Environment
|
||||
|
||||
Базовые переменные должны жить в:
|
||||
|
||||
```text
|
||||
platform/infra/.env
|
||||
```
|
||||
|
||||
Пример значений хранится в:
|
||||
|
||||
```text
|
||||
platform/infra/.env.example
|
||||
```
|
||||
|
||||
## Implementation order
|
||||
|
||||
1. Согласовать локальные domain/port bindings.
|
||||
2. Добавить reverse proxy config.
|
||||
3. Поднять Authentik server, worker, Postgres и Redis.
|
||||
4. Прокинуть Authentik за proxy с корректными headers.
|
||||
5. Подключить текущий Launcher как внешний сервис или через compose service.
|
||||
6. Подключить текущий Plane runtime как внешний service target.
|
||||
7. После стабилизации собрать воспроизводимый compose.
|
||||
|
||||
## Notes
|
||||
|
||||
Текущий Plane runtime не переносится на нулевом этапе. Любое изменение `plane-app/plane.env`, volumes или startup scripts делается только после backup и отдельной задачи.
|
||||
@@ -0,0 +1,106 @@
|
||||
# Discovery Report: NODE.DC Platform
|
||||
|
||||
Дата: 2026-05-04
|
||||
|
||||
## Источники
|
||||
|
||||
- Базовое ТЗ: `/Users/dcconstructions/Downloads/mnt/NODEDC/DOC/BASE/tz_codex_platform_authentik_launcher_plane.md`
|
||||
- Краткое ТЗ: `/Users/dcconstructions/Downloads/mnt/NODEDC/DOC/BASE/nodedc_auth.md`
|
||||
- Launcher repo: `/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher`
|
||||
- Task Manager repo: `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER`
|
||||
|
||||
## Workspace
|
||||
|
||||
`/Users/dcconstructions/Downloads/mnt/NODEDC` используется как внешний workspace-корень для платформенной работы.
|
||||
|
||||
Текущая папка `NODEDC` до нулевого этапа содержала только базовые документы в `DOC/BASE`. Отдельного git-репозитория в этой папке нет.
|
||||
|
||||
## Launcher
|
||||
|
||||
Путь: `/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher`
|
||||
|
||||
Факты discovery:
|
||||
|
||||
- отдельный git-репозиторий;
|
||||
- Vite/React приложение;
|
||||
- основной entrypoint: `src/main.tsx`;
|
||||
- команды из `package.json`: `npm run dev`, `npm run build`, `npm run preview`, `npm run test`;
|
||||
- backend слоя в текущем Launcher не найдено;
|
||||
- значит OIDC/session handling, Authentik service token, app registry и audit log нельзя хранить только во frontend.
|
||||
|
||||
Минимальный вывод: для production-like auth нужен Launcher backend или BFF слой.
|
||||
|
||||
## Task Manager / Plane
|
||||
|
||||
Путь: `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER`
|
||||
|
||||
Факты discovery:
|
||||
|
||||
- отдельный git-репозиторий;
|
||||
- self-host runtime лежит в `plane-app`;
|
||||
- локальный fork исходников лежит в `plane-src`;
|
||||
- Plane CE self-host запущен через Docker;
|
||||
- локальный URL стенда: `http://localhost:8090`;
|
||||
- текущий compose/env: `plane-app/docker-compose.yaml` и `plane-app/plane.env`;
|
||||
- текущий fork Plane: `plane-src`, версия `1.3.0`;
|
||||
- основной runtime-контейнер API: `plane-app-api-1`;
|
||||
- активные контейнеры Plane: web, proxy, api, worker, beat-worker, db, redis, minio, mq, admin, space, live.
|
||||
|
||||
Текущий workspace в Plane:
|
||||
|
||||
- slug: `nodedc`;
|
||||
- name: `NODE DC`;
|
||||
- owner: `dcctouch@gmail.com`;
|
||||
- codex user: `codex@nodedc.local`.
|
||||
|
||||
Существующие проекты в workspace `nodedc` на момент discovery:
|
||||
|
||||
- `NODEDCLAUN` / `NODEDC LAUNCHER`
|
||||
- `CODEX` / `DCTM-WT-CODEX`
|
||||
- `NODEDCTASK` / `NODEDC TASKMANAGER`
|
||||
- `MGR` / `Менеджмент`
|
||||
- `BUH` / `Бухгалтерия`
|
||||
|
||||
## NDC details в карточках
|
||||
|
||||
Канон карточек описан в `/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER/AGENTS.md`.
|
||||
|
||||
Структурированные блоки карточки хранятся в `Issue.detail_layout` под ключом:
|
||||
|
||||
```text
|
||||
nodedc_structured_blocks
|
||||
```
|
||||
|
||||
Типы блоков:
|
||||
|
||||
- `text`: текстовый блок с `title` и `body`;
|
||||
- `checker`: чекер с `title` и пунктами `items`.
|
||||
|
||||
Frontend отображает эти блоки через компоненты:
|
||||
|
||||
- `plane-src/apps/web/core/components/issues/issue-detail-widgets/structured-content.helpers.ts`
|
||||
- `plane-src/apps/web/core/components/issues/issue-detail-widgets/structured-content-blocks.tsx`
|
||||
|
||||
## Решение по переносам
|
||||
|
||||
Физически переносить Launcher и Task Manager внутрь `NODEDC` на нулевом этапе не нужно.
|
||||
|
||||
Причины:
|
||||
|
||||
- оба приложения уже являются отдельными git-репозиториями;
|
||||
- Plane runtime завязан на текущие `plane-app`, `plane.env`, volumes и backup;
|
||||
- перенос может сломать локальный self-host стенд;
|
||||
- платформенный слой должен управлять интеграцией, а не становиться монорепозиторием всех исходников.
|
||||
|
||||
Допустимый вариант позже: добавить symlink или workspace manifest из `NODEDC` на внешние репозитории, если это потребуется для удобства навигации.
|
||||
|
||||
## Риски
|
||||
|
||||
- Нельзя пересоздавать существующего Plane admin/user, иначе потеряются связи workspace, задач, комментариев и assignee.
|
||||
- Нельзя делать Authentik admin token доступным frontend-коду Launcher.
|
||||
- Нельзя полагаться только на reverse proxy как единственный долгосрочный auth layer для приложений, которые мы контролируем кодом.
|
||||
- Нельзя публиковать наружу Postgres, Redis, MinIO и внутренние API в staging/production.
|
||||
|
||||
## Следующий технический шаг
|
||||
|
||||
Создать локальный `platform/infra` слой с Authentik и reverse proxy, но сначала согласовать конкретную схему доменов, ports и secrets.
|
||||
@@ -0,0 +1,108 @@
|
||||
# Plane User Migration Plan
|
||||
|
||||
## Цель
|
||||
|
||||
Связать существующего пользователя Plane с Authentik identity без потери workspace, projects, tasks, comments, attachments и истории.
|
||||
|
||||
## Current baseline
|
||||
|
||||
Текущий рабочий Plane workspace:
|
||||
|
||||
```text
|
||||
workspace slug: nodedc
|
||||
workspace name: NODE DC
|
||||
workspace owner: dcctouch@gmail.com
|
||||
codex user: codex@nodedc.local
|
||||
```
|
||||
|
||||
Перед реализацией OIDC нужно дополнительно снять точный snapshot:
|
||||
|
||||
- users;
|
||||
- profiles;
|
||||
- workspace memberships;
|
||||
- project memberships;
|
||||
- issue ownership;
|
||||
- issue assignees;
|
||||
- comments;
|
||||
- attachments;
|
||||
- uploaded files/storage.
|
||||
|
||||
## Backup before changes
|
||||
|
||||
Перед любыми изменениями Plane auth:
|
||||
|
||||
```text
|
||||
plane_db dump
|
||||
plane-app/plane.env
|
||||
uploads / MinIO volumes
|
||||
redis, если в нем есть важные очереди/сессии
|
||||
rabbitmq, если есть невыполненные jobs
|
||||
```
|
||||
|
||||
## Mapping model
|
||||
|
||||
Минимальная модель:
|
||||
|
||||
```text
|
||||
external_identity_link
|
||||
id
|
||||
provider = authentik
|
||||
authentik_sub
|
||||
email
|
||||
plane_user_id
|
||||
created_at
|
||||
last_login_at
|
||||
status
|
||||
```
|
||||
|
||||
Уникальность:
|
||||
|
||||
- `provider + authentik_sub`;
|
||||
- `provider + plane_user_id`;
|
||||
- желательно `provider + email`, если email используется для migration auto-link.
|
||||
|
||||
## Login flow
|
||||
|
||||
```text
|
||||
Authentik login
|
||||
-> Plane OIDC callback
|
||||
-> validate state/nonce/token
|
||||
-> validate issuer/audience/exp/sub
|
||||
-> check nodedc:taskmanager:access
|
||||
-> find external_identity_link by authentik_sub
|
||||
-> login existing plane_user_id
|
||||
```
|
||||
|
||||
Migration auto-link допускается только контролируемо:
|
||||
|
||||
```text
|
||||
if no link and email matches existing Plane user and migration mode enabled:
|
||||
create external_identity_link
|
||||
else:
|
||||
deny or provisioning flow
|
||||
```
|
||||
|
||||
## Forbidden during migration
|
||||
|
||||
- Не пересоздавать существующего Plane user.
|
||||
- Не менять `created_by`.
|
||||
- Не менять `owner_id`.
|
||||
- Не менять `assignee_id`.
|
||||
- Не менять `member_id`.
|
||||
- Не делать bulk update связей задач без отдельной проверки.
|
||||
|
||||
## Management command target
|
||||
|
||||
Целевая команда:
|
||||
|
||||
```bash
|
||||
python manage.py link_authentik_user --email admin@example.ru --sub <authentik-sub>
|
||||
```
|
||||
|
||||
Требования:
|
||||
|
||||
- idempotent;
|
||||
- проверяет конфликтующий mapping;
|
||||
- ничего не меняет в задачах/workspace;
|
||||
- выводит summary;
|
||||
- поддерживает dry-run.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Security Checklist
|
||||
|
||||
## Network
|
||||
|
||||
- [ ] Наружу опубликованы только reverse proxy ports.
|
||||
- [ ] Postgres не опубликован наружу в staging/production.
|
||||
- [ ] Redis не опубликован наружу в staging/production.
|
||||
- [ ] MinIO/storage не опубликован наружу в staging/production.
|
||||
- [ ] Внутренние API не доступны напрямую извне.
|
||||
- [ ] Authentik получает корректные `X-Forwarded-Proto`, `X-Forwarded-For`, `Host`.
|
||||
- [ ] WebSocket headers настроены для Authentik/Plane/live.
|
||||
|
||||
## Authentik
|
||||
|
||||
- [ ] Каждое приложение имеет отдельный Authentik Application.
|
||||
- [ ] Каждое приложение имеет отдельный Provider.
|
||||
- [ ] Для каждого приложения задана отдельная access policy.
|
||||
- [ ] Группы app access заведены отдельно от app-local ролей.
|
||||
- [ ] MFA/enrollment policy вынесены в отдельный этап.
|
||||
|
||||
## Launcher
|
||||
|
||||
- [ ] Authentik service token хранится только server-side.
|
||||
- [ ] Frontend не получает service token.
|
||||
- [ ] Admin endpoints требуют `nodedc:superadmin` или `nodedc:launcher:admin`.
|
||||
- [ ] Все admin actions пишутся в audit log.
|
||||
- [ ] Удаление пользователя реализовано как deactivate/disable, не hard delete.
|
||||
|
||||
## Plane
|
||||
|
||||
- [ ] Перед изменениями сделан backup DB/env/uploads/storage.
|
||||
- [ ] Существующий Plane user не пересоздается.
|
||||
- [ ] `owner_id`, `created_by`, `assignee_id`, `member_id` не меняются без отдельной миграции.
|
||||
- [ ] Публичный signup отключен.
|
||||
- [ ] Лишние обходные auth сценарии закрыты или явно оставлены как временные.
|
||||
- [ ] OIDC login проверяет state/nonce/token.
|
||||
- [ ] `authentik_sub` связан с существующим `plane_user_id`.
|
||||
|
||||
## Tokens and secrets
|
||||
|
||||
- [ ] Secrets не попадают в git.
|
||||
- [ ] Access/refresh tokens не логируются.
|
||||
- [ ] Session cookies имеют `secure=true` в staging/production.
|
||||
- [ ] В production включены HTTPS и HSTS.
|
||||
|
||||
## Acceptance scenarios
|
||||
|
||||
- [ ] Без логина Launcher отправляет в Authentik.
|
||||
- [ ] Пользователь без `nodedc:taskmanager:access` не видит Task Manager в Launcher.
|
||||
- [ ] Пользователь без `nodedc:taskmanager:access` получает deny на прямой `task.local.nodedc`.
|
||||
- [ ] Пользователь с доступом открывает Task Manager.
|
||||
- [ ] Старый Plane admin после OIDC видит старые workspace/tasks/comments.
|
||||
- [ ] Деактивированный пользователь теряет доступ.
|
||||
- [ ] Admin action появляется в audit log.
|
||||
Reference in New Issue
Block a user