diff --git a/10-platform/auth.md b/10-platform/auth.md index 5026d2a..7f0d505 100644 --- a/10-platform/auth.md +++ b/10-platform/auth.md @@ -1,16 +1,45 @@ # Аутентификация: gnexus-auth (SSO) Все пользовательские сервисы Gnexus подключаются к центральному SSO — gnexus-auth -(Laravel, auth.gnexus.space). Сервис не создаёт собственную систему пользователей. +(Laravel). Сервис не создаёт собственную систему пользователей и не имеет собственной +«страницы входа» — в сервисе только гейт (заглушка с кнопкой **Войти** по OAuth PKCE). ## Правила -- Регистрация OAuth-клиента в gnexus-auth (client_id + client_secret). -- redirect_uri — постоянный HTTPS-адрес сервиса. +- Регистрация OAuth-клиента в gnexus-auth (client_id + client_secret) — владелец делает в админке `/admin/clients`. +- redirect_uri — постоянный HTTPS-адрес сервиса (`<сервис>/auth/callback`). - Секреты клиента — в `.env` сервиса и в gnexus-creds, не в git. -- Webhook-события `user.*` (создание/изменение пользователя) подписаны HMAC (`GAUTH_WEBHOOK_SECRET`). - Готовые клиентские библиотеки: `gnexus-auth-client-py` (Python), `gnexus-auth-client-php` (PHP). +- Личность пользователя — `sub` из gnexus-auth; локальная запись пользователя зеркалируется (`user_id`, email, имя, профиль), профиль gnexus-auth — источник истины. + +## Что у gnexus-auth есть для сервиса +- OAuth 2.0 PKCE (`/auth/login` → callback), scopes `openid email profile roles permissions`. +- Webhook-события `user.*` с HMAC-подписью (см. ниже). +- Язык аккаунта (`profile.locale`) — источник языка интерфейса по умолчанию (см. [i18n.md](i18n.md)). +- Опциональный allowlist сервиса: пусто = пускаем любого залогиненного, иначе список email через запятую. + +## Каркас клиента (паттерн gnexus-creds / hard-panel) +- Вход через `/auth/login` только **по явному клику** — никаких авто-редиректов на загрузку страницы. +- SPA-гейт: splash → `GET /me` → `denied` (гейт с кнопкой входа) или `ready` (интерфейс). +- Сессия — httpOnly-cookie своего имени (например `ghard_session`) из SQLite, TTL ~7 суток; state/PKCE тоже в SQLite (`oauth_states`). +- `401` из API → ивент `unauthenticated` → сессии нет → гейт (не редирект). +- `GET /me` возвращает `{auth_enabled, user: {user_id, email, display_name, avatar_url}, locale…}`. +- Личная настройка пользователя (`PATCH /me`) — **только по cookie-сессии**: у Bearer-токенов нет личности (Bearer-админ → 401). +- При auth-off (client_id пуст) панель/сервис открыты без логина, личные настройки — в localStorage браузера. + +## Вебхуки gnexus-auth (обязательно для клиента) +Роут `/webhooks/gnexus-auth`, подпись HMAC в заголовках (`t=,v1=`), секрет — `*AUTH_WEBHOOK_SECRET`: +- **profile.update** — обновить в локальной зеркальной записи имя, аватарку и профиль (`profile` хранится verbatim JSON). Не должен затирать локальные override-ы пользователя (например override языка). +- **global_logout** — удалить все сессии сервиса, чей user_id совпал (авто-логаут после смены пароля/выхода). +- Роут регистрируется со двумя спеллингами (со слэшем и без) — защита от SPA catch-all. + +## Матрица доступа сервиса +| Кто | Как | Для чего | +|---|---|---| +| Браузер | cookie-сессия через SSO | UI, `PATCH /me` | +| Скрипты, MCP, ИИ-агенты | Bearer API-токен сервиса (см. [mcp.md](mcp.md)) | REST API | +| Машинные агенты | ключи агрегатов (`ghm_…`, `X-Server-Key`) | отдельные каналы (ingest) | ## Ссылки - Репозиторий: https://git.gnexus.space/root/gnexus-auth - Клиенты: `gnexus-auth-client-py`, `gnexus-auth-client-php` -- Факты о сервисе: gnexus-book → `10-systems/services/gnexus-auth.md` +- Факты о сервисе: gnexus-book → `10-systems/services/gnexus-auth.md` \ No newline at end of file diff --git a/10-platform/i18n.md b/10-platform/i18n.md new file mode 100644 index 0000000..6e1c9d1 --- /dev/null +++ b/10-platform/i18n.md @@ -0,0 +1,39 @@ +# Мультиязычность интерфейса + +Конвенция, применённая в gnexus-creds и hard-panel: без vue-i18n, один общий +модуль, три языка. + +## Правила +- **Три языка: en, uk, ru** — все сервисы экосистемы поддерживают их все. +- Язык по умолчанию берётся из аккаунта gnexus-auth (`profile.locale`). +- Пользовательский выбор языка — в настройках сервиса; выбор перекрывает язык + аккаунта **только для этого сервиса** («Авто» возвращает язык аккаунта). +- Override хранится на сервере у записи пользователя (`locale`) и переживает + logout и обновление профиля через вебхуки (upsert профиля не трогает locale). +- Если SSO не настроен (сервис открыт) — язык в localStorage браузера, + `auto` нет. +- Опции языка всегда подписываются родными именами, инвариантными к текущему + языку: English / Українська / Русский. + +## Реализация (паттерн gnexus-creds / hard-panel) +- Модуль `src/i18n/index.js`: `locale = ref("en")`, `normalizeTag` («en-US» → «en»), + `t(slug, params)` с интерполяцией `{param}` и **fallback на английский**, + `setLocale` + `watch(locale)` → `` и ``. +- Словари — `src/i18n/messages/{en,uk,ru}.js`: плоские ключи по областям + (`dash.*`, `server.*`, `storage.*`…), одинаковые slugs во всех словарях. +- Плюрализация — Slavic-категории (one/few/many), считается **только по + `params.n`**; строки с несколькими счётчиками собираются вложенными `t()`, + а не плюральным слагом. +- Backend: `GET /me` возвращает выбранный и эффективный язык + (`locale` + `locale_effective` = override → account → en); `PATCH /me` + меняет override (по cookie-сессии — см. [auth.md](auth.md)). + +## Грабли +- Модуль-уровень массивы с уже вычисленным `t()` застывают — таблицы колонок, + опции селектов и списки навигации обязательно **computed**. +- Пустой override («») и авто — одно и то же значение; не путать «настройка + не выбрана» с «выбран английский». + +## Ссылки +- Референс: `panel/frontend/src/i18n/index.js` и `gnexus-creds/frontend/src/i18n/index.js` +- Язык аккаунта меняется в gnexus-auth (LocaleController, синхронизация — webhook user.updated). \ No newline at end of file diff --git a/10-platform/mcp.md b/10-platform/mcp.md new file mode 100644 index 0000000..b9d73b4 --- /dev/null +++ b/10-platform/mcp.md @@ -0,0 +1,50 @@ +# MCP: мультиюзер доступ для ИИ-агентов + +Схема, применённая в gnexus-creds: сервис — сам MCP-сервер, доступ — через +собственные API-токены сервиса (не через access-токены gnexus-auth), каждый +токен принадлежит пользователю сервиса. + +## Правила +- Сервис с MCP-функционалом предоставляет **streamable HTTP** эндпоинт + (`/mcp-protocol/`, слэш в конце значим — реверс-прокси не буферизует и не + переписывает тело). Legacy HTTP/SSE-адаптер — только для клиентов без + streamable HTTP. `stdio` не используется: агент и сервис на разных хостах. +- Авторизация MCP — **Bearer токен сервиса** в заголовке `Authorization`. +- Токены выдаются в UI на **отдельной странице «Токены»**: имя, набор scopes, + ревок, `last_used_at`. Токен показывается ровно один раз при создании, + в БД — только хэш. +- Токен привязан к пользователю: действия через MCP выполняются от его имени + и видны в аудите (`channel=mcp`), а недействительный/заблокированный + пользователь gnexus-auth блокирует и его токены (403). +- Доступ ограничивается scopes; лишний scope не выдавать. Чего не хватит — + 403, а не молчаливое сужение выдачи. +- Rate-limit на чувствительные операции. + +## Scopes (паттерн gnexus-creds) +| Скоупы | Доступ | +|---|---| +| `mcp, read` | чтение/поиск метаданных | +| `mcp, read, reveal` | + расшифровка секретов | +| `mcp, read, reveal, write` | полный MCP (создание/обновление) | + +`read`/`reveal`/`write` наполняются в контексте конкретного сервиса; без +скоупа `mcp` всё закрыто 403. + +## Подключение агента +```json +{ + "mcpServers": { + "<сервис>": { + "type": "streamable-http", + "url": "https://<сервис>.gnexus.space/mcp-protocol/", + "headers": { "Authorization": "Bearer <api-token-сервиса>" } + } + } +} +``` + +## Ссылки +- Референс: gnexus-creds (`gnexus_creds/mcp*.py`, страница Tokens в UI) +- hard-panel пока на едином Bearer GHARD_ADMIN_TOKEN — при переходе на + мультиюзер: токены по этой конвенции, страница «Токены» в UI (см. + [auth.md](auth.md) про матрицу доступа). \ No newline at end of file diff --git a/10-platform/ui.md b/10-platform/ui.md index 7ccdba9..b590a0c 100644 --- a/10-platform/ui.md +++ b/10-platform/ui.md @@ -7,6 +7,24 @@ - Изменения стиля делаются в ui-kit, а не копируются в сервисы. - Эстетика: брутализм, тёмная тема, геометрические гротески, JetBrains Mono. +## Каркас веб-сервиса +- Shell (App.vue) = nav + гейт авторизации + контент; каждая страница начинается + с `GnPageHeader` (title/subtitle/kicker; кнопки действий — в шапке страницы, + не внутри карточек), модалки/confirm — `GnModal`/`GnConfirmDialog`, тосты + через `useToast`. + +## Каркас навигации +- Навигация — drawer слева; порядок сверху вниз: основной контент, в самом + низу «Настройки» и «Выйти» (отдельным блоком). +- Пункт навигации = иконка `ph-` + подпись. Подпись из i18n (см. + [i18n.md](i18n.md)); **список пунктов — computed** (module-level массив + с `t()` застывает). +- Подсветка активного пункта следует за роутом, а не за последним кликом: + после `onSelect` → `router.push` сверять `route.path` (для вложенных + страниц — `startsWith`). +- Роуты страниц регистрируются **до catch-all** (SPA-trap). + ## Ссылки - Репозиторий: https://git.gnexus.space/root/gnexus-ui-kit - Документация: `docs/index.md` в репозитории +- Референс каркаса: hard-panel `App.vue`/`router.js`, gnexus-creds `App.vue` diff --git a/README.md b/README.md index 5d7a919..1cc22fc 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ - [00-principles.md](00-principles.md) — базовые принципы платформы - `10-platform/` — конвенции интеграции с общей инфраструктурой - [auth.md](10-platform/auth.md) — SSO через gnexus-auth + - [i18n.md](10-platform/i18n.md) — мультиязычность интерфейса + - [mcp.md](10-platform/mcp.md) — MCP и API-токены для ИИ-агентов - [ui.md](10-platform/ui.md) — UI и визуальный стиль - [health.md](10-platform/health.md) — health-эндпоинт сервисов - [notifications.md](10-platform/notifications.md) — уведомления через Gnexus Synapse