По-настоящему важные данные нельзя держать в одном единственном месте — их нужно диверсифицировать.
Можно хранить всё дома и однажды вернуться с прогулки, а вместо дома и драгоценного компьютера — одни обугленные руины.
Можно носить всё с собой в телефоне, но лишиться его в первом же кафе, где аппарат вытащит у вас из кармана воришка.
Можно загрузить всё на надёжный сервер крупной корпорации, но в один прекрасный день эта корпорация введёт против вас санкции или просто заблокирует доступ к вашему же аккаунту.
У каждого отдельного хранилища есть свой собственный способ вас подвести.
Вот ровно эту проблему я и попытался решить.
Ниже — приложение для заметок, которое я собрал в паре с ИИ-агентом, а его безопасность гонял через «рой» независимых ИИ-агентов-тестировщиков (об этом честно расскажу ближе к концу).
По своей сути это схема, при которой критические для вас данные одновременно живут на нескольких ваших устройствах, синхронизируются между ними напрямую — без всякого центрального сервера, который можно было бы у вас отнять или заблокировать, — и зашифрованы так, что потеря любого отдельного устройства ничем вам не грозит.
А главное, система защищает ваши данные даже в том случае, если вас силой заставят её разблокировать. Иными словами, это решение ровно на тот случай, когда дома у вас уже нет.
Разберём шаг за шагом, как оно устроено под капотом.

Не одна программа, а две
Существует эта программа сразу в двух обличьях.
Десктопная версия написана на Python (PySide6/Qt) и работает на Windows и Linux.
Мобильная — на Flutter и Kotlin для устройств под Android.
И, забегая вперёд: это не одна программа, наспех портированная на телефон, а две совершенно независимые реализации одного решения, которые обязаны понимать друг друга бит в бит. Формат зашифрованного файла, кодировка операций синхронизации, правила разрешения конфликтов — всё это на двух языках написано отдельно и должно совпадать до последнего байта. Чтобы они не разошлись молча, есть отдельный набор кросс-языковых conformance-тестов: одни и те же входные данные прогоняются и через Python, и через Dart, а результат сверяется.
Интерфейс: если вы открывали Telegram, объяснять нечего
Слева узкая колонка папок, справа широкая лента, а внизу поле ввода. Заметки здесь ведут себя ровно как сообщения в чате: это может быть простой текст, форматированный текст, картинка, файл, целый альбом вложений или видео.

Ментальная модель предельно простая: папка — это чат, а заметка — сообщение в нём.
Поле ввода тоже знакомое: вложения откладываются в лоток, как в мессенджере, а отправленную заметку можно тут же поправить прямо на месте, без всяких отдельных окон. Форматирование, вставка картинок из буфера, drag-n-drop файлов в окно — всё как ожидаешь.
Для планирования есть календарь с цветными событиями, по которому удобно раскидывать дела по датам, — а сами события можно перетаскивать мышью с одного дня на другой.

Самое интересное прячется не в окне, а на диске
В основе всей программы лежит один принцип: источником истины являются файлы. Каждая ваша заметка — это обычный JSON-файл, который лежит в предсказуемой структуре каталогов:
<хранилище>/ folders/<id-папки>/ folder.json notes/<id-заметки>.json notes/attachments/<id-заметки>/ calendar/events.json
Папка — это каталог, вложения лежат рядом, события календаря собраны в один файл. Перенести всё на другую машину означает буквально скопировать одну папку. А для удобства можно просто нажать кнопку экспорта в zip-архив.

Из такого подхода бесплатно вытекает важное свойство: поисковый индекс программа строит сама для себя, и по сути это всего лишь кэш, выводимый из файлов. Потеряли, повредили, удалили индекс — не беда, он перестроится из файлов-первоисточников.
Сам поиск при этом устойчив к опечаткам: под капотом работает не чёткое сравнение строк, а нечёткое совпадение по буквам (SQLite FTS5 для быстрого отбора кандидатов плюс rapidfuzz для ранжирования). А ещё он понимает даты: стоит ввести запрос с датой — и в выдачу первым делом попадут все заметки, привязанные к этому дню или просто созданные в этот день.

Синхронизация без сервера, облака и единого аккаунта
Но самая магия начинается там, где в дело вступает синхронизация. Здесь работает собственный peer-to-peer движок поверх вашей локальной сети.
Устройства находят друг друга через mDNS в общей сети Wi-Fi, а первичное доверие устанавливается сканированием QR-кода с одноразовым токеном, у которого есть короткий срок жизни.
Дальше всё общение идёт через взаимный TLS, причём доверие строится не через какие-то центры сертификации, а через закрепление сертификата (certificate pinning) — ровно так же, как это сделано в Syncthing.
Идентификатор каждого устройства — это первые байты хэша от его собственного сертификата, и подделать его попросту нельзя. А само соединение остаётся открытым, так что любое изменение тут же улетает на другое устройство без всякого опроса и ожидания.

А вот как именно сходятся данные — это отдельная инженерная история.
Параллельно с файлами программа ведёт журнал операций (op-log). Каждая ваша правка пишет не только сам файл для интерфейса, но и запись в этот журнал. Когда два устройства встречаются, они обмениваются так называемыми версионными векторами — кратким описанием того, кто что уже знает о заметках, — и докачивают друг у друга недостающие операции:
Устройство A: vv = { A: 42, B: 17 } // «я видел 42 своих операции и 17 от B» Устройство B: vv = { A: 40, B: 19 } ↓ сверяем векторы A → B: свои операции №41 и №42 B → A: операции B №18 и №19
Конфликты разрешаются по простому правилу: при двух правках одной и той же заметки побеждает та, что новее по времени, а удаление побеждает всегда и безусловно. Это сознательное решение: если вы что-то удалили — оно остаётся удалённым, даже если на другом устройстве в тот же момент была правка. Ровно этот сценарий («правка на одном устройстве одновременно с удалением на другом») мой набор тестов проверяет отдельно, во всех порядках доставки операций, — потому что именно на таких стыках прячутся баги, из-за которых два устройства могут разойтись навсегда.
Шифрование: пять цифр, которые нельзя перебрать
Теперь перейдём к тому, ради чего подобные приложения нередко вообще существуют, — к шифрованию.
По умолчанию оно выключено, и пока вы его не включили, программа ведёт себя как самое обычное хранилище. Но стоит его активировать, как все данные на диске зашифровываются по алгоритму AES-256, причём у каждого файла свой собственный подключ, а путь к файлу намертво вшит в проверку подлинности (AAD). Это значит, что если злоумышленник просто перетащит зашифрованный кусок в другое место, расшифровка не пройдёт — и подмена сразу вскроется.
Самое интересное — это защита от перебора. ПИН-код здесь всего из пяти цифр.
Казалось бы, это 100 000 комбинаций, которые перебираются за считанные секунды. Но хитрость в том, что ПИН тут не используется как пароль напрямую. Он подаётся на вход аппаратному модулю, который вычисляет криптографический код на неизвлекаемом ключе. На десктопе эту роль играет TPM, а на телефоне — Android Keystore.
Если очень упростить, это отдельный специализированный криптографический чип на материнской плате устройства. Без этого конкретного железа значение попросту невозможно вычислить — а значит, украденные файлы превращаются в бесполезный мусор. Перебор нельзя даже начать, потому что у вора нет того самого чипа.

Поверх этого, на всякий случай, накручен ещё и медленный, требовательный к памяти алгоритм (scrypt), чтобы любая попытка угадывать стоила дорого.
А после нескольких неверных попыток включается нарастающая блокировка: сначала минута, потом 5 минут, потом полчаса, 2 часа и в итоге целые сутки.
Если же кто-то ввёл неправильный ПИН больше пяти раз подряд — данные стираются безвозвратно. И сам счётчик неудач защищён от обнуления: его не сбросить ни удалением файла, ни переводом системных часов назад.
А если у злоумышленника root?
Но что, если злоумышленник не просто унесёт ваши файлы, а получит root прямо на самом устройстве? С таким доступом он может прочитать любой файл приложения и даже обратиться к защищённому хранилищу ключей. Чего он сделать не в состоянии — это попасть в ту самую изолированную зону железа, где чип сверяет ваш отпечаток пальца или лицо и выносит вердикт, что аутентификация пройдена. Это решение принимается только внутри отдельной защищённой среды, а не в ядре системы, которым владеет root.
И вот ровно на этом держится дополнительная защита с биометрией. Если её включить, то проверка ПИН переезжает на особый ключ, которым чип соглашается воспользоваться только после свежей, подтверждённой железом биометрии. А поскольку главный ключ всего хранилища выводится именно через этот шлюз, то даже тот, кто знает ваш ПИН и обнулил счётчик попыток, не сможет развернуть его без вашего живого присутствия.
Однако важно оговориться, что это не панацея. Биометрия защищает данные, пока устройство заперто и вас нет рядом. Но если root уже пробрался в систему, у злоумышленника есть удалённый доступ, а вы сами сидите в открытом приложении — он по-прежнему способен вытащить ключ прямо из оперативной памяти. Поэтому элементарную цифровую гигиену вам по-прежнему нужно соблюдать. Я специально не пытаюсь продать вам «абсолютную защиту» — её не существует.
Режим «под принуждением»
И мы добрались до самой интересной функции во всей программе — до режима под принуждением. Это гипотетическая ситуация, когда преступники заставляют вас разблокировать устройство.
Вам достаточно ввести свой ПИН задом наперёд.
Снаружи это выглядит как абсолютно обычная разблокировка: никаких предупреждений, никаких ошибок, ничего подозрительного. Но в этот самый момент программа необратимо уничтожает все ваши настоящие данные и открывает вместо них правдоподобную подложку — свежую папку с парой случайных заметок, которая на каждом устройстве выглядит немного по-своему, чтобы её нельзя было опознать как заготовку.

Уничтожение при этом начинается с криптографического стирания. Программа в первую очередь удаляет ту самую обёртку, которой зашифрован главный ключ. После этого весь реальный шифротекст превращается в нечитаемый мусор — и даже этот мусор тоже затирается. При этом ваши другие данные, даже если они лежат в одной папке с шифротекстом, не пострадают: стирание идёт строго по явному списку того, чем владеет само приложение, и никогда не трогает соседние файлы.
Как это проверялось: «рой» тестировщиков и сетка регрессий
Стоит сказать, что вся эта система и инфраструктура обоих приложений прошла через три раунда настоящего аудита безопасности.
И вот здесь — про ИИ честно и без прикрас: код обеих версий я писал в паре с ИИ-агентом, а роль независимых тестировщиков безопасности играл целый рой ИИ-агентов, каждый из которых пытался сломать систему со своей стороны.
Ключевой принцип: каждое найденное слабое место закрывалось не просто заплаткой, а отдельным тестом, который следит, чтобы проблема больше не вернулась. Потеря данных при внезапном отключении питания, утечка шифротекста в интерфейс, попытки сбросить счётчик перебора, расхождение устройств при синхронизации, переполнение памяти по сети — всё это было поймано и закрыто, и на каждый случай теперь есть красный тест, который станет зелёным только когда баг действительно исправлен.
Отдельная гордость — та самая кросс-языковая сетка: набор сценариев вида «дан список конкурентных операций → вот каким обязано быть финальное состояние», который прогоняется и на Python, и на Dart, во всех возможных порядках доставки. Именно она гарантирует, что телефон и десктоп понимают правила синхронизации одинаково и не разойдутся на тонком краевом случае.
Честно про компромиссы
Ультимативная ли это система защиты? Конечно же нет.
Эта программа — компромисс между удобством и безопасностью. Очевидно, что ПИН-код вводить проще, чем трёхсотзначный пароль несколько раз под разные слои зашифрованного контейнера. А синхронизировать файлы автоматически через домашнюю сеть легче, чем передавать их по проводу или флешке. Поэтому я и не настаиваю на использовании подобного софта — это был ещё один эксперимент, в котором ИИ выступал писателем программ, а рой ИИ-агентов — независимыми тестировщиками безопасности.
Код открыт
Весь код обеих версий выложен в открытый доступ под лицензией GPL-3.0:
Десктоп (Python / Qt): https://github.com/HelpFreedom/note-desktop
Мобильная (Flutter / Kotlin): https://github.com/HelpFreedom/note-mobile
А целиком, со всеми демонстрациями вживую, приложение показано в видео:
Спасибо, что дочитали до конца.
cpud47
Что будет, если после ввода пинкода наоборот выйти и ввести его ещё раз? Откроется ли то же самое? Если нет, то это не имеет смысла
Edit: ну и в целом отличить пинкод и пинкод наоборот можно через скорость ввода (на отдельном устройстве)