При регистрации и в процессе использования сервиса сохраняются:
Europe/Moscow) — присылается браузером при входе и регистрации. От него зависит, какие сутки считаются у вас «сегодня» для серии дней и дневной цели: без него днём был бы UTC, и занятие в час ночи по Москве попадало бы во вчера;onboarded_at) — чтобы не показывать его повторно; это факт об аккаунте, а не о браузере;password_changed_at) — по ней сервер отличает токен входа, выданный до смены пароля, от честного, и обесценивает первый сразу, а не когда он истечёт сам по себе (до 7 дней);Чтобы упражнения, повторения и прогресс работали, сервис хранит:
Отдельно на сервере кешируются ответы внешнего словаря для функции поиска слов — подробности и на что это влияет см. в разделе «Что уходит наружу» ниже.
В разделе «Идеи» сервис хранит:
При удалении аккаунта ваш голос и ваша жалоба удаляются вместе с ним. Текст предложенной вами темы — нет: ссылка на вас как на автора обнуляется, а само предложение остаётся в сервисе, потому что за него могли проголосовать другие пользователи.
Файлов cookie среди этого нет: сервис не ставит ни одной. Всё перечисленное — localStorage и IndexedDB, то есть хранилища самого браузера, к которым сервер обращаться не может; данные оттуда уходят на сервер только в составе обычных запросов приложения.
В проекте нет аналитики, отслеживания и рекламного кода. Сам сайт не загружает в браузер ничего со сторонних доменов: страница, стили, шрифты и иконки раздаются только отсюда. Наружу обращается сервер, а не браузер, и таких обращений два — поиск слова в словаре и отправка писем.
api.dictionaryapi.dev) — используется функцией поиска слов. Этот запрос делает сервер, а не браузер: стороннему сервису уходит искомое слово, но не IP-адрес и не что-либо ещё, что могло бы опознать пользователя. Ответ кешируется на сервере в общей таблице на 30 дней — повторный поиск того же слова в этот период вообще не обращается к стороннему сервису. Этот кеш общий для всех пользователей и хранит только само слово и ответ словаря — привязки к конкретному аккаунту в нём нет.Шрифты раньше загружались браузером с fonts.googleapis.com и fonts.gstatic.com, и такой запрос по своей природе раскрывал Google IP-адрес каждого посетителя. Теперь их копии лежат на этом сайте, и этого запроса больше нет.
Отдельная оговорка про озвучку (кнопка 🔊) и проверку произношения (кнопка 🎤): обе используют встроенные в браузер механизмы синтеза и распознавания речи, а не что-либо написанное здесь. Что именно делает браузер, зависит от браузера: многие из них выполняют распознавание не на устройстве, а отправляют записанный фрагмент на серверы своего разработчика (например, Chrome — в Google). Этот обмен идёт мимо нашего сервера, мы не видим ни записи, ни результата, и повлиять на него с нашей стороны нельзя — но и молчать о нём было бы нечестно. Если это нежелательно, просто не пользуйтесь этими двумя кнопками: на остальную работу сайта они не влияют.
В двух местах, и второе менее очевидно, чем первое.
В журналах веб-сервера. Оба слоя nginx, через которые проходит любой запрос к сервису (edge-прокси и раздача статики фронтенда), пишут свой формат: время, идентификатор запроса, IP-адрес, метод, запрошенный адрес, код ответа, размер ответа и длительность. Это происходит для каждого запроса, независимо от того, вошли вы в аккаунт или нет. User-agent не записывается — стандартный формат nginx, который его пишет, в обоих конфигах заменён на свой.
Журналы никуда не выгружаются и не архивируются: они живут в логах самих Docker-контейнеров, и объём журнала ограничен — 50 МБ на каждый сервис, после чего старые записи вытесняются новыми. В пересчёте это около 250 тысяч запросов; сколько это дней, зависит от того, сколько людей пользуется сервисом, поэтому фиксированного срока тут нет и назвать его было бы неправдой.
Запрошенный адрес пишется целиком, вместе со строкой запроса. Ссылки из писем это не затрагивает: одноразовый токен в них передаётся во фрагменте адреса (#token=…), а фрагмент браузер на сервер вообще не отправляет — в журнале его нет и быть не может. Так сделано именно для этого.
В самой базе данных. Ограничение частоты обращений к входу и регистрации считается не в памяти процесса, а в общей таблице — иначе при двух работающих копиях приложения объявленный предел был бы вдвое выше, и переждать перебор паролей можно было бы до ближайшего обновления. Счётчик ключуется по IP-адресу, то есть адрес попадает в базу открытым текстом. Строки живут не дольше окна учёта — минуты — и вычищаются автоматически, когда окно сменяется.
В журналы самого приложения (не веб-сервера) не попадают ни заголовки, ни тела запросов вовсе: только метод, путь без строки запроса, код ответа, длительность, идентификатор запроса и, если вы вошли, идентификатор аккаунта. Это не мелочь: без такого ограничения оборванный JSON на форме входа записал бы в журнал пароль открытым текстом.
Раз в сутки на сервере снимается резервная копия всей базы данных — на случай сбоя, повреждения данных или ошибки при обновлении сервиса, а не для чего-либо ещё. Хранится не больше 14 последних копий: при появлении новой копии самая старая из лишних удаляется. Срок «примерно две недели» — не гарантия, а следствие того, что копии продолжают сниматься по расписанию: если снятие копий по какой-то причине прервётся (например, кончится место на диске или сервер будет выключен), уже существующие копии никуда не удаляются и могут пролежать дольше. Копии лежат на том же сервере, что и сам сервис, без шифрования, и наружу никуда не отправляются.
В Профиле, в разделе «Мои данные», есть выгрузка: один файл JSON со всем, что перечислено выше, — прогресс, повторения, «Мои слова», XP, достижения, история занятий, дуэлей, а также ваши предложения тем и голоса за них. Это данные, а не отчёт: имена полей повторяют внутренние, ничего не склеено в красивые строки и ничего не отброшено. Из чужого в файле только отображаемые имена друзей и соперников по дуэлям — то, что и так видно на экране; ни их почты, ни их прогресса там нет. Пароль не выгружается ни в каком виде: он хранится только хешем и не восстанавливается даже нами.
В Профиле, в разделе «Опасная зона», есть удаление аккаунта. Оно необратимо и удаляет сразу: сам аккаунт, весь прогресс, историю повторений, достижения, списки слов, друзей, XP и ваши голоса за предложения тем — а также очередь офлайн-ответов, сохранённую в браузере на этом устройстве. Отменить это действие нельзя.
Исключение — текст ваших собственных предложений тем: он остаётся в сервисе и после удаления аккаунта, только без ссылки на вас как на автора, потому что за предложение могли проголосовать другие пользователи.
Оговорка про резервные копии базы (раздел выше): из самой базы данные удаляются сразу и необратимо, но если к моменту удаления аккаунта существовала более ранняя резервная копия, ваши данные в ней сохраняются до тех пор, пока эта копия не будет вытеснена более новыми копиями (при исправно идущем снятии копий это происходит в пределах примерно двух недель, но не гарантированно — см. оговорку в разделе выше). В этот срок данные не используются ни для чего, кроме восстановления сервиса после сбоя, но полностью удалёнными в буквальном смысле слова их называть в это время нельзя: восстановление базы из такой копии (например, после аварии) вернёт и удалённый аккаунт вместе с его данными, если копия была снята до удаления.