# Как мы защищаем данные

Коротко для жюри и проверяющих: что защищено, чем и где это можно проверить. Ограничения указаны в конце, они не скрываются.

## Роли и права

- Ролей две: клиент (`customer`) и сотрудник (`operator`). Внутри выпускного альбома есть организатор и участник.
- **Права проверяет только сервер.** Приложение на экране роль показывает, но границей безопасности не является: на каждом запросе API проверяет роль и принадлежность объекта.
- Чужой объект возвращает `404`, а не `403`: так не раскрывается, существует ли он. Каждый такой отказ пишется в журнал как `access.denied`.
- Сотрудник видит заказы, обращения и журнал. Каталога, корзины и AI в его разделе нет.

Проверка: `api/test/operator.test.ts`, `api/test/albums.test.ts`, `api/test/orders.test.ts`.

## Вход и пароли

- Код входа хранится только в виде HMAC-хеша и действует ограниченное время. Приглашения в альбом хранятся как хеш токена: утечка таблицы не даёт доступа к альбомам.
- **Лимиты:** 5 неверных кодов и 3 запроса кода за 10 минут на контакт; 5 неверных паролей альбома за 10 минут на приглашение и пользователя. Дальше `429 TOO_MANY_ATTEMPTS`. Телефон или почта в журнал открытым текстом не попадают.
- **Пароль выпускного альбома** (4–64 символа) хранится как `scrypt` с солью, не логируется и не отдаётся в API. Чтобы вступить в альбом, нужны и ссылка-приглашение, и пароль: утечка ссылки не открывает фото детей.

Проверка: `api/test/auth.test.ts` («лимиты входа»), `api/test/albums.test.ts` («пароль альбома»).

## Фото и ссылки

- Фото отдаются по подписанной ссылке (HMAC), которая действует один час; испорченная или просроченная ссылка не открывается.
- Участник альбома видит и правит только свои фото; организатор видит все.
- Ответы API не кэшируются (`Cache-Control: no-store`), на всех ответах `X-Content-Type-Options: nosniff` и `Referrer-Policy: no-referrer`; Caddy добавляет `Strict-Transport-Security`. Доступ к сайту и API только по HTTPS.

## Журнал действий

Таблица `audit_log` только дополняется: пользователь, время, действие, объект, результат. Сотрудник видит журнал в своём разделе. Журнал переживает удаление пользователя, но содержит только идентификаторы, а не содержимое данных.

## AI и согласие

- Фото уходит во внешний сервис обработки (aiai.by) **только после явного согласия** пользователя и только для той правки, которую он запустил.
- Согласие можно отозвать в профиле («Мои данные и безопасность»): после этого AI снова просит согласие.
- К запросам применяется фильтр недопустимых инструкций; результат обработки не попадает в заказ без действия пользователя. Лимит: не больше 10 обработок в час на пользователя.

## Удаление данных

В профиле: «Мои данные и безопасность» показывает, сколько проектов, фото и заказов хранится, и позволяет удалить всё. Удаляются проекты, фото (вместе с файлами), AI-задачи и согласия; заказы остаются в истории без привязки к человеку. Проверка: `api/test/privacy.test.ts`.

## Данные в репозитории

Только синтетические данные. Секреты (`JWT_SECRET`, `FILE_URL_SECRET`, ключ aiai.by, пароль базы) лежат в `.env` на сервере, в репозитории только `.env.example` без значений.

## Ограничения

- **Демо-режим** (`DEMO_MODE=true`): код входа всегда `000000`. Он включён на демо-сервере, чтобы жюри могло войти без SMS; для настоящих пользователей его нужно выключить. Лимиты попыток при этом работают, а демо-код остаётся рабочим.
- Коды входа не отправляются по SMS и почте: до продакшена нужен провайдер рассылки.
- Фото хранятся на локальном диске сервера; для нескольких серверов нужно объектное хранилище.
- Права проверяются в коде API, политик на уровне базы (RLS) нет.
- Платежи имитируются, платёжных данных в системе нет.
