Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,056 changes: 1,056 additions & 0 deletions BACKEND_OPERATIONS_AUDIT_2026-07-10.md

Large diffs are not rendered by default.

14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ Copy-Item .\env.example .\.env

At minimum, you should review these values before regular use:

- `TALESPINNER_ACCESS_MODE` — keep `local` for a trusted machine or use
`public` with the complete hardened configuration;
- `TOKENS_MASTER_KEY` — master key used to encrypt stored tokens;
- `PORT`, `DATA_DIR`, `DB_PATH` if you need custom runtime paths;
- `CHROMA_*` only if you plan to use RAG/ChromaDB.
Expand All @@ -103,11 +105,12 @@ Vite prints the exact frontend URL in the terminal after startup.
### First working flow

1. Open the app in your browser.
2. Create your first profile.
3. Connect an LLM provider in `LLM Settings`.
4. Select a token and model.
5. Create or open a chat.
6. Configure `World Info` and `Operations` if needed.
2. Create the first user account. In local mode its password may be empty.
3. Create your first character profile.
4. Connect an LLM provider in `LLM Settings`.
5. Select a token and model.
6. Create or open a chat.
7. Configure `World Info` and `Operations` if needed.

## Useful commands

Expand All @@ -126,6 +129,7 @@ yarn --cwd server test
The repository includes separate documentation for users and developers.

- User onboarding: `docs/docs/user/getting-started.md`
- Accounts and secure hosting: `docs/docs/user/accounts-and-security.md`
- Everyday chat workflow: `docs/docs/user/chat-basics.md`
- World Info: `docs/docs/user/world-info.md`
- Operations: `docs/docs/user/operations.md`
Expand Down
174 changes: 174 additions & 0 deletions USER_ACCOUNTS_IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# Система пользователей и режимы доступа TaleSpinner

## Цель

Добавить несколько изолированных аккаунтов без ухудшения локального UX:

- в режиме `local` разрешить аккаунты без пароля, быстрый выбор и автоматический вход;
- в режиме `public` включать обязательную аутентификацию и полный набор защит;
- всегда определять владельца данных на сервере, не доверяя `ownerId` из HTTP-запроса;
- сохранить существующие данные с владельцем `global` при обновлении приложения.

## Инварианты

1. Количество аккаунтов не зависит от режима доступа.
2. Изоляция данных обязательна в `local` и `public`.
3. Режим меняет проверку личности и защиту HTTP, но не правила владения данными.
4. `user_persons` остаются игровыми персонами внутри аккаунта и не заменяют `users`.
5. Клиент не может выбрать произвольный `ownerId`.
6. Публичный режим не запускается при неполной security-конфигурации.
7. Пароли, session tokens, API-ключи и setup secrets не попадают в логи или API DTO.

## Конфигурация

Основной переключатель:

```env
TALESPINNER_ACCESS_MODE=local # local | public
```

Планируемые параметры публичного режима:

```env
TALESPINNER_SESSION_SECRET=
TALESPINNER_SETUP_TOKEN=
TALESPINNER_ALLOW_REGISTRATION=false
TALESPINNER_SESSION_TTL_DAYS=30
TALESPINNER_TRUST_PROXY=false
```

`public` должен активировать согласованный набор защит целиком. Опциональные флаги
не должны позволять по отдельности отключать обязательные secure cookies, CSRF
или rate limiting.

## Модель данных

### `users`

- `id`;
- `username` и нормализованное уникальное значение для входа;
- `display_name`;
- nullable `password_hash` только для локальных passwordless-аккаунтов;
- роль `admin | user`;
- статус `active | disabled`;
- версия credentials для отзыва сессий;
- даты создания, изменения и последнего входа.

### `auth_sessions`

- идентификатор сессии;
- ссылка на пользователя;
- только hash session token;
- способ входа `local | password`;
- даты создания, последней активности, истечения и отзыва.

Первый пользователь принимает существующий scope `global`, чтобы старые чаты и
настройки не потерялись. Новые пользователи получают собственный UUID scope.
Это действие должно быть атомарным и идемпотентным.

## Этапы реализации

### 1. Фундамент backend — реализован

- [x] Зафиксировать архитектурный план.
- [x] Добавить единый resolver политики `local | public`.
- [x] Закрывать запуск `public` при неполной security-конфигурации.
- [x] Добавить таблицы `users` и `auth_sessions`.
- [x] Расширить request context типизированным authenticated actor.
- [x] Добавить репозитории пользователей и сессий без выдачи секретных полей.
- [x] Добавить интеграционные тесты миграции и ограничений уникальности.

### 2. Credentials и сессии

- [x] Выбрать и подключить поддерживаемую реализацию Argon2id.
- [x] Хэшировать пароль только на сервере.
- [x] Генерировать криптографически случайные session tokens.
- [x] Хранить в БД только hash токена.
- [x] Реализовать создание, продление, отзыв и очистку истёкших сессий.
- [x] Отзывать сессии при смене пароля или отключении пользователя.

### 3. Setup и локальный вход

- [x] Добавить endpoint состояния первоначальной настройки без утечки данных.
- [x] Создавать первого администратора и принимать legacy scope `global`.
- [x] В `local` разрешить пустой пароль.
- [x] Автоматически входить в единственный passwordless-аккаунт.
- [x] Для нескольких аккаунтов поддержать быстрый выбор. Последний аккаунт
восстанавливается серверной session cookie, отдельный неподписанный owner-id не хранится.
- [x] Не позволять local auto-login выбирать аккаунт через неподписанный `ownerId`.

### 4. Публичный режим

- [x] Требовать пароль и валидную session-конфигурацию.
- [x] Защитить первоначальный setup токеном из окружения.
- [x] Использовать `HttpOnly`, `Secure`, `SameSite` cookies.
- [x] Добавить CSRF-защиту для изменяющих запросов.
- [x] Добавить rate limiting только неуспешных попыток и безопасные ошибки входа.
- [x] Настроить security headers, proxy trust и проверку HTTPS.
- [x] Добавить безопасное восстановление доступа администратора.

### 5. Перевод API на trusted owner scope

- [x] Auth middleware устанавливает actor и owner scope до маршрутизации.
- [x] Игнорировать клиентский `ownerId`; репозитории берут владельца только из
authenticated AsyncLocalStorage scope.
- [x] Все пользовательские get/update/delete выполняются по `id + ownerId` либо
проходят эквивалентную owner-проверку родительского ресурса.
- [x] Проверять одинакового владельца у связанных сущностей.
- [x] Сохранить отдельный явно привилегированный admin API только для управления аккаунтами.

### 6. Аудит хранилищ

- [x] Чаты, ветки, сообщения, варианты и bulk-операции.
- [x] Персоны и entity profiles.
- [x] World Info, instructions и operation profiles.
- [x] RAG, Chroma collections и knowledge store.
- [x] LLM presets, provider credentials и runtime state.
- [x] UI settings, темы, фоны, bundles и импорты.
- [x] Загруженные файлы, аватары и защита путей.

### 7. Frontend

- [x] Мастер первого запуска.
- [x] Вход, выход и восстановление сессии.
- [x] Локальный account picker и auto-login.
- [x] Базовое создание аккаунтов администратором.
- [x] Переключение аккаунта с полным сбросом Effector state/cache.
- [x] RU/EN локализация добавленных экранов.

### 8. Миграция и совместимость

- [x] Автоматически связать legacy `global` data с первым аккаунтом.
- [x] Не создавать публичного администратора без setup token.
- [x] Проверить обновление существующей БД и чистую установку.
- [x] Добавить резервную копию и диагностику перед необратимой миграцией.

### 9. Проверки готовности

- [x] Unit-тесты политики доступа, credentials и session lifecycle.
- [x] Интеграционные тесты cross-owner read/write/delete для chats, entries,
operations, LLM, RAG/Chroma, file-backed settings, media и backgrounds.
- [x] API-тесты local/public setup и login/logout.
- [x] Тесты CSRF, cookies, rate limiting и session revocation.
- [x] API-интеграционные сценарии первого запуска и нескольких аккаунтов.
- [x] Аудит отсутствия секретов в auth DTO, публичных ошибках и session storage.
- [x] Обновить RU/EN документацию и `env.example`.

## Критерий завершения

Фича готова, когда два одновременно созданных аккаунта не могут получить данные
друг друга ни через UI, ни через прямые HTTP-запросы, локальный пользователь может
работать без пароля, а публичный сервер отказывается запускаться или обслуживать
запросы без полностью настроенной защиты.

## Текущее состояние

Последнее обновление: 2026-07-27. Рабочая ветка: `agent/user-access-modes`.

Реализация и security-review завершены. Итоговая проверка:

- server: typecheck, lint и production build пройдены;
- server tests: 123 файла, 595 тестов пройдены;
- web: typecheck, lint, 43 файла/139 тестов и production build пройдены;
- E2E: smoke 5, full matrix 9 и black-box 1 сценарий пройдены;
- docs: генерация API, RU/EN parity и production builds пройдены.
Loading
Loading