Загрузка 0
ПОДЕЛИТЬСЯ

Мой блог

Листай вниз

Менеджер задач Dela: сквозное шифрование, self-hosted сервер и код от Claude Code

Менеджер задач Dela: сквозное шифрование, self-hosted сервер и код от Claude Code

Разработчик представил проект Dela — менеджер задач со сквозным шифрованием, созданный для развёртывания на собственном сервере. Ключевая концепция сервиса заключается в полном шифровании информации непосредственно на пользовательских устройствах: сервер выступает исключительно в роли релея и хранилища шифротекстов, не имея доступа к содержимому записей. Синхронизация правок между клиентами происходит по отдельным полям с применением меток гибридных логических часов, а вся кодовая база проекта была сгенерирована нейросетевым инструментом Claude Code.

Поводом для создания собственной системы стали ограничения бесплатного тарифа Todoist по числу проектов и нежелание оформлять платную подписку, а также потребность в строгой приватности, исключающей чтение списков дел третьими лицами или администратором хостинга. В результате получился кроссплатформенный менеджер задач Dela с открытым исходным кодом под лицензией AGPL-3.0, доступный в браузере, а также на платформах Windows и Android.

Архитектура безопасности: что доступно серверу

Поскольку серверная часть Dela часто размещается на одном VPS с другими веб-сервисами и ботами, потенциальная компрометация соседних контейнеров или получение root-прав злоумышленником не должны приводить к утечке задач. По этой причине сквозное шифрование встроено в архитектуру на постоянной основе и не подлежит отключению в настройках.

Реклама

Сервер обрабатывает минимально необходимый объём метаданных. Ему известны:

  • адреса электронной почты учетных записей, идентификаторы подключенных устройств и списки участников совместных проектов;
  • количество поступивших операций и их временные метки;
  • объём каждой операции с выравниванием (pad) с точностью до 64 байт.

В то же время от сервера полностью скрыты названия задач, описания, комментарии, вложения, проекты, разделы, метки, дедлайны, правила повторения, приоритеты, точное время напоминаний, а также конкретные таблицы, записи и поля, модифицируемые операцией.

Криптографические ключи формируются на устройстве пользователя из мастер-пароля через алгоритм Argon2id. Из 64 байт результата 32 байта становятся секретом входа (сервер хранит только его хеш), а остальные 32 байта формируют ключ обёртки (KEK), расшифровывающий мастер-ключ. Мастер-ключ шифрует ключи личных пространств и секретный ключ X25519. Сам исходный пароль никогда не покидает клиент. При его смене перешифровывается исключительно мастер-ключ, тогда как тело задач остаётся нетронутым. Для предотвращения перебора и проверки наличия аккаунта в базе сервер возвращает соль для Argon2id: если запрошенного адреса не существует, генерируется детерминированная поддельная соль через HMAC от внутреннего секрета сервера.

Реклама

Попольная синхронизация и разрешение конфликтов

Базовой единицей модификации данных выступает операция записи нового значения в конкретное поле отдельной строки таблицы. Каждое действие сопровождается меткой гибридных логических часов (HLC), состоящей из физического времени в миллисекундах, счётчика операций внутри одной миллисекунды и уникального идентификатора устройства. Метки имеют строго фиксированную ширину и сопоставляются как строки: при конфликте побеждает операция с большей меткой.

Благодаря разделению по ячейкам изменение разных параметров одной и той же задачи на двух устройствах в офлайн-режиме не приводит к потере данных. Если на смартфоне без сети был изменён срок выполнения, а на ПК в это же время отредактировано название, после подключения к интернету применятся обе правки. Если же оба клиента меняли один и тот же атрибут, сохранится вариант с более поздней меткой HLC. Для защиты от рассинхронизации системного времени сервер отклоняет операции, опережающие его часы более чем на 60 секунд, транслируя клиенту корректное время для применения поправки.

В модели данных также заложены специальные правила для стабильной работы без связи:

  • метки задачи представлены отдельными бинарными полями формата label:<id>, благодаря чему теги, добавленные независимо на разных клиентах, корректно суммируются;
  • идентификатор системного списка «Входящие» совпадает с идентификатором пространства, что предотвращает дублирование папки при её создании в офлайне;
  • позиционирование задач внутри списков опирается на дробные ключи, поэтому перестановка меняет значение только одного поля;
  • циклические перемещения подзадач друг в друга разрешаются на этапе чтения путём игнорирования наиболее старой операции перемещения.

Формат конвертов и серверный журнал

На сервер операции передаются внутри защищённых конвертов. В качестве идентификатора ячейки выступает слот — хеш BLAKE2b-128 от пути к ячейке с использованием ключа пространства. Сервер понимает, что два конверта направлены в одну и ту же ячейку, но не может узнать её адрес. В открытом виде передаётся только HLC-метка, необходимая для определения актуальности пакета.

Шифрование полезной нагрузки осуществляется связкой XChaCha20-Poly1305 с выравниванием длины до 64 байт. Слот и метка HLC входят в блок аутентифицированных дополнительных данных (AAD): любая попытка подменить время или привязать данные к другому слоту приведёт к ошибке расшифровки на клиенте. Сервер сохраняет исключительно последний конверт для каждого слота и выдаёт изменения клиентам страницами по возрастанию порядкового номера. Детерминированное тестирование ядра релея включает симуляцию на десятках сценариев с намеренным сдвигом часов на клиентах до 30 секунд и потерей сетевых пакетов.

Реклама

Совместная работа и ротация ключей

Каждый общий проект функционирует внутри отдельного криптографического пространства. Приглашая участника, владелец получает его открытый ключ по почте, шифрует ключ проекта через crypto_box и передаёт отпечаток для ручной сверки по независимому каналу связи. Это предотвращает атаку типа «человек посередине», при которой скомпрометированный сервер мог бы сгенерировать собственную пару ключей.

В случае удаления пользователя из проекта Dela запускает процедуру ротации ключа. Владелец проекта генерирует новый ключ, заново перешифровывает все существующие данные и передаёт обновлённый ключ оставшимся участникам. Исключённый пользователь сохраняет доступ к локально сохранённой копии старых записей, но лишается возможности расшифровывать любые новые правки. Процесс ротации выполняется пакетами с верификацией курсора изменений, предотвращая гонки при одновременной записи.

Интеллектуальный ввод и синтаксис Todoist

Модуль быстрого создания задач выполняет синтаксический разбор вводимого текста на лету на русском и английском языках. Система автоматически определяет даты, время, цикличность, целевой проект, метки и приоритет, подсвечивая распознанные токены в интерфейсе ввода. Нажатие на подсвеченный фрагмент возвращает слово в текст названия, если парсер ошибся с контекстом.

Правила повторения сохраняются в стандарте RRULE. Дополнительно поддерживаются относительные повторы с отсчётом интервала от фактической даты выполнения задачи. Поисковые фильтры полностью совместимы с синтаксисом запросов Todoist и поддерживают русскоязычные ключевые слова, а перенос существующей базы задач реализован через прямое обращение к API Todoist прямо из браузера пользователя без участия сервера Dela.

Клиентские приложения для Android и Windows

Десктопный клиент для Windows построен на базе Tauri 2, а мобильное приложение для Android использует фреймворк Capacitor 8. Оба решения используют единый веб-интерфейс на React, дополненный нативными компонентами для интеграции с операционной системой:

  • на ПК реализованы работа в системном трее и глобальная комбинация клавиш Ctrl+Shift+Space для вызова окна ввода;
  • на Android добавлены виджет рабочего стола, тайл быстрых настроек, голосовой ввод и автономные локальные напоминания;
  • фоновая доставка уведомлений с других устройств работает через децентрализованный протокол UnifiedPush (включая ntfy) без обязательной привязки к Google Play Services.

Безопасность локальных данных обеспечивается PIN-кодом с хешированием Argon2id, поддержкой биометрии и прогрессивными задержками ввода. После десяти неверных попыток ввода PIN-кода локальное хранилище полностью очищается, требуя повторной авторизации по основному паролю.

Разработка через Claude Code и исправление ошибок

Создание кодовой базы проекта происходило при помощи Claude Code. Разработка велась поэтапно на основе заранее согласованной архитектурной спецификации, разделённой на четыре фазы с чёткими критериями приёмки.

Для отладки мобильного приложения нейросеть взаимодействовала с физическим смартфоном разработчика через USB-кабель и команды adb shell input, анализируя состояние через логи Chrome DevTools и системные дампы Android. В процессе тестирования ассистент выявил и устранил несколько специфических проблем платформы:

  • Падение плагинов Capacitor: функция registerPlugin возвращала прокси-объект, у которого асинхронный контекст вызывал метод then(), что приводило к ошибке из-за отсутствия реализации промиса на стороне Android;
  • Зависание виджета: лаунчер Android блокировал отрисовку стандартного класса разметки <View> в качестве разделителя, что потребовало его замены на FrameLayout;
  • Сброс напоминаний: из-за погрешности неточных будильников внутренний таймер приложения обгонял системный вызов и отменял расписание; логику скорректировали, оставив удаление только неактуальных будущих событий и добавив запрос разрешения на точное время.

Текущие ограничения и развёртывание через Docker

В текущей версии Dela веб-клиент держит расшифрованные данные в IndexedDB, а мобильный виджет помещает отображаемые задачи в закрытую системную директорию без дополнительного шифрования. В случае гипотетического взлома хостинга сервер не сможет прочесть зашифрованные задачи, но потенциально способен подменить JS-скрипты веб-интерфейса, поэтому использование нативных клиентов для Windows и Android обеспечивает более высокий уровень изоляции. Приложение для iOS в настоящий момент отсутствует.

Окно быстрого добавления задачи в Dela с распознаванием параметров
Синтаксический разбор фразы при быстром создании задачи с подсветкой распознанных сущностей.

Развёртывание серверной части требует установленного Docker и настроенного доменного имени. Встроенный веб-сервер Caddy автоматически выпускает TLS-сертификаты. Контейнер функционирует с правами доступа к файловой системе только для чтения, не требует привилегий суперпользователя и автоматически создаёт резервные копии базы данных SQLite раз в сутки и перед выполнением миграций.

01.