Технические детали
Общая схема
Офлайн-доступ в Пассворке — локальный кэш отдельных записей, а не полная копия сейфа(ов). Он формируется после авторизации и синхронизации с сервером.
Как это работает:
- Пассворк хранит список выбранных записей, добавленных в офлайн-доступ.
- Десктопное приложение получает этот список через API.
- Локально сохраняются выбранные записи.
- Пароль и дополнительные поля записи перед сохранением шифруются отдельным локальным ключом.
- Если соединение с сервером пропадает, приложение использует локальный кэш.
- Когда соединение с сервером восстанавливается, приложение отправляет накопленные события и обновляет локальные данные.
Окно при недоступности сервера
Во время работы десктопное приложение отдельно отслеживает состояние сети и доступность сервера Пассворка. Потеря интернета, недоступный VPN, тайм-аут API-запроса или другая транспортная ошибка ещё не переводят приложение в офлайн-режим автоматически. Сначала приложение выполняет контрольный запрос к серверу, чтобы исключить кратковременный сбой.
Если сервер всё ещё недоступен, открывается окно Ошибка подключения. В нём отображается адрес текущего хоста и список технических причин, которые стоит проверить. Окно нельзя закрыть кликом по фону — пользователь должен явно выбрать дальнейшее действие.
В окне доступны действия:
- Подключиться — повторно выполнить контрольный запрос к серверу. При успешном ответе приложение закрывает окно и продолжает онлайн-сессию.
- Офлайн-режим — доступен только пользователям, которым разрешён офлайн-доступ. Приложение закрывает окно, переключает интерфейс в офлайн-режим и перестаёт выполнять серверные запросы до повторного подключения.
- Выйти — выполняет локальный выход без обращени я к серверу. Приложение отменяет активные запросы, очищает локальную сессию и переводит пользователя на экран входа.
После перехода в офлайн-режим в верхней панели отображается состояние недоступного сервера и кнопка повторного подключения. Эта кнопка снова проверяет доступность сервера, обновляет сессию и только после успешного ответа возвращает приложение в онлайн-режим.
Холодный запуск без сети обрабатывается отдельно: если офлайн-доступ был включён заранее, приложение восстанавливает сохранённые авторизационные данные из локального хранилища и открывает офлайн-интерфейс без показа окна.
Где хранится кэш
Офлайн-данные хранятся в том же локальном файле БД, что и остальные служебные данные Пассворка:
- Windows —
%APPDATA%\@passwork\app\app-data.db; - macOS —
~/Library/Application Support/@passwork/app/app-data.db; - Linux —
~/.config/@passwork/app/app-data.db(или путь изXDG_CONFIG_HOME).
Как шифруются данные
В офлайн-доступе используется два уровня защиты.
Шифрование локальных записей
Чувствительные значения в app-data.db сохраняются через safeStorage. Это обёртка над хранилищем секретов операционной системы:
- Windows — DPAPI/хранилище учётных данных, привязанное к аккаунту пользователя;
- macOS — Связка ключей (Keychain);
- Linux — совместимый Secret Service (GNOME Keyring, KWallet и т.п.).
Ключи шифрования управляются операционной системо й и не сохраняются в файлах приложения. В app-data.db попадают только зашифрованные base64-значения.
Шифрование полей записи
Перед сохранением каждой отдельной записи приложение дополнительно шифрует её секретную часть: значение пароля и дополнительные поля.
Для локального офлайн-кэша приложением генерируется отдельный AES-256 ключ и сохраняется в safeStorage. Затем секретная часть записи шифруется AES-GCM и сохраняется внутри локальной копии.
Что сохраняется
Для каждой синхронизированной записи:
- идентификаторы записи и связанной сущности;
- название;
- логин;
- URL-адреса;
- теги;
- заметка;
- цвет;
- директория;
- зашифрованный пароль;
- зашифрованные дополнительные поля.
Пароль и дополнительные поля шифруются отдельным AES-256 слоем. Остальные данные сохраняются как метаданные локальной копии и защищаются через safeStorage.
Что не сохраняется
Локальный кэш это не полная копия всех связанных данных записи. Не сохраняются:
- вложения;
- редакции записи;
- история событий записи;
- структура сейфов и папок;
- иконки Записей.
Состояния локальной копии
У офлайн-записи есть состояние, которое определяет, можно ли открыть её без сети:
| Состояние | Значение |
|---|---|
synchronized | Запись сохранена локально и доступна для просмотра. |
not_synchronized | Запись есть в офлайн-списке, но локальная копия не была синхронизирована. |
revoked | Доступ к записи был отозван на сервере. |
exceeded | Запись выходит за лимит количества для роли. |
outdated | Локальная копия устарела по правилу retentionHours. |
Состояния в приложении не отображаются — это внутренняя логика приложения при работе с офлайн-записями.
Состояние outdated вычисляется локально: приложение сравнивает текущее время с последней синхронизацией и временем добавления записи. Если срок офлайн-доступа без синхронизации истёк, запись считается устаревшей.
Синхронизация
Синхронизация выполняется только когда приложение находится онлайн.
При первичном включении офлайн-режима:
- Приложение сохраняет признак включённого офлайн-режима.
- Получает с сервера список записей, добавленных в офлайн-доступ.
- Отправляет на сервер список текущих локальных версий.
- Получает изменённые записи и список недоступных записей.
- Обновляет локальный кэш записей и список для офлайн-раздела.
- Сохраняет время успешной синхронизации.
При восстановлении сети:
- приложение повторно запускает синхронизацию;
- отправляет накопленные события;
- обновляет локальные копии записей.
Если запись была удалена из офлайн-доступа или стала недоступна, приложение удаляет её локальную копию при следующей синхронизации.
Ограничения и доступы
Офлайн-доступ зависит от следующих настроек в роли пользователя:
- Сохранение паролей офлайн
- Ограничить количество офлайн-записей на одном устройстве
- Ограничить время офлайн-доступа без синхронизации (в часах)
Если администратор отзовёт доступ к записи или изменит права пользователя, приложение узнает об этом только при следующем подключении к серверу и синхронизации. До восстановления связи доступна последняя локально кэшированная версия записи.