Інженерія

Застосунок здоровʼя, який не читає користувачок

ПР

Павло Рубановський

June 12, 2026 · 7 хв читання

Застосунок здоровʼя, який не читає користувачок

Обмеження, з якого ми починали

Більшість застосунків про здоровʼя спершу просять створити акаунт, а вже потім пояснюють щось про приватність. Створюючи Kvit, наш трекер циклу та вагітності, ми зробили навпаки: застосунок мав бути корисним ще до того, як дізнається про вас бодай щось, і мав лишатися корисним, якби наші сервери зникли завтра.

Це не маркетингова позиція. Це архітектурне обмеження, і воно визначає майже все далі.

Чого насправді коштує «локально на пристрої»

Сказати «ваші дані лишаються на телефоні» легко. Жити з цим — ні. Ось що це рішення забрало:

  • Ніякої серверної аналітики вмісту. Ми не можемо порахувати, скільки користувачок фіксують певний симптом, бо ми цих даних не маємо. Продуктові рішення спираються лише на агреговані знеособлені сигнали.
  • Ніякого відновлення пароля. Немає акаунта — нема чого відновлювати. Втрата пристрою без резервної копії означає втрату записів. Це чесний наслідок ключа, який не покидає телефон.
  • Ніякої дешевої синхронізації між пристроями. Її доводиться будувати як обмін зашифрованими пакетами, а не як читання з бази.

Ми прийняли всі три. У категорії, де витік — це не незручність, а питання особистої безпеки, цей обмін і є продуктом.

Шифрування, назване точно

Розмиті твердження гірші за їх відсутність. «Захист банківського рівня» не каже користувачці нічого, а аудитору — ще менше. Механізм, точно:

1. При першому запуску застосунок генерує 256-бітний ключ криптографічно стійким джерелом випадковості. 2. Ключ потрапляє в апаратне сховище платформи — Keychain на iOS, Keystore на Android — і не покидає пристрій. Його немає ні в коді, ні в конфігу, ні в резервній копії, яку ми могли б прочитати. 3. База на пристрої шифрується AES-256 цим ключем. Записи шифруються ще до того, як щось наблизиться до мережі.

`dart final key = await secureStorage.read(key: 'db_key') ?? await _generateAndPersistKey(secureStorage);

final db = await openEncryptedDatabase( path: await _databasePath(), encryptionKey: base64Decode(key), ); `

Найважливіший тут другий пункт. Шифрування ключем, який лежить поруч із даними, не захищає нікого. Уся гарантія тримається на тому, де живе ключ.

Жодного рекламного SDK — і чому це рішення про безпеку

Kvit виходить без рекламної бібліотеки. Зазвичай це подають як турботу про UX, але в застосунку про здоровʼя це рішення про потоки даних: рекламний SDK — це сторонній бінарник із доступом до мережі, що працює всередині вашого процесу. Кожен доданий — це ще одна сторона, за яку ви тепер ручаєтесь.

Рекламній мережі нічого передати, бо рекламної мережі в бінарнику немає.

Що це означає, якщо ви замовляєте застосунок

Якщо ви будуєте щось у сфері здоровʼя, фінансів або будь-де, де користувач не хотів би, щоб його дані зачитали вголос, варто рано поставити інженерному партнеру три питання:

  • Де живе ключ шифрування? Якщо відповідь не «в апаратному сховищі платформи» — шифрування декоративне.
  • Що працює з вимкненою мережею? Локальна архітектура не додається пізніше, вона змінює модель даних.
  • Які сторонні SDK працюють усередині процесу? Кожен — це додаткова обіцянка про приватність, яку ви даєте за когось іншого.

Ми побудували Kvit так, щоб відповідати на ці три питання без застережень. Той самий стандарт тримаємо і в клієнтських проєктах — і та сама логіка лишає розпізнавання чеків Qivvo на пристрої.

Розпочати проєкт

Сподобалося?
Давайте будувати разом.

Відповідь протягом 24 годин · NDA готові · Повна власність на код