rilo
Лис-маскот Rilo стоїть біля щита з замком на ньому

Тепер трохи технічно.

Усе, що каже головна сторінка, виписане як те, з чого це зроблено. Справжня база даних, як насправді виникає контакт, і хто ще торкається дзвінка.

Ця сторінка не потрібна, щоб користуватися Rilo, і ніщо тут не суперечить простішій версії. Це той самий продукт, описаний точно.

Уся база даних

Усе, що наш сервер знає про твою сім’ю.

Не стислий виклад. Це сімнадцять таблиць, які має сервер, з тими стовпцями, які в них справді є. Чотири про твою сім’ю. Хто вони, хто може діяти за дитину, з яких телефонів вони говорять і куди їм дзвонити. Чотири тримають щось для телефона, який був вимкнений, і всі чотири стирають себе самі. Три про те, хто може діяти за дитину і який телефон перестав бути довіреним. Ще дві зберігають підписаний слід завершення. Розірваний контакт або людина, яка пішла. Вони лишаються, щоб телефон, який був офлайн, міг перевірити їх сам, а не вірити нам на слово. В одній лежать витрачені одноразові числа, які прибирають, щойно вони згасають. Одна про підписку, і в ній немає нічого, що когось називає. Ще в одній лежить скарга, яку надіслав дорослий, і це єдина таблиця тут, яка існує, щоб щось сталося. Остання зовсім не про твою сім’ю.

accounts зберігається, поки ти не видалиш
  • idхеш відкритого ключа, не ім’я
  • public_keyключ пристрою, який і є обліковим записом
  • typeдорослий або дитина
  • type_certпідпис, який це доводить. Дитячий обліковий запис підписує батько або мати, тому тут немає прапорця, який можна перемкнути
  • created_at
  • last_activeостанній раз, коли якийсь із твоїх телефонів привітався
  • dormant_warned_atпорожньо
  • consent_sealedтвоя пошта з реєстрації, запечатана ключем, який лежить на папері у засновника. Жоден сервер не може її прочитати
  • uncovered_sinceпорожньо, якщо твій план перестав охоплювати цю дитину — тоді день, коли ми це помітили, щоб місяць до того, як щось зупиниться, рахувався з цієї дати
Рядок твого облікового запису лишається, поки ти його не видалиш, а видалення в застосунку забирає цей рядок і все, що до нього прикріплене. Ми працюємо над тим, щоб прибирати сім’ї, які зовсім перестали користуватися Rilo. Поки це не ввімкнено, тут ніщо не зникає само.
server_state зберігається
  • keyщо саме запам’ятовується
  • valueі що воно каже
Єдина таблиця тут, яка зовсім не про твою сім’ю. Сьогодні в ній одна річ, мить, коли ця версія сервера вперше запустилася. Кожне питання про те, хто затих, відмірюється від неї, тож базу, написану раніше, ніж ми могли це бачити, ніколи не прочитають як місяці тиші.
guardians зберігається
  • account_id
  • guardian_keyключ, якому дозволено діяти за цю дитину
  • added_at
  • removed_at
Видалення позначають, а не витирають. Контакти перевіряють проти того, хто був опікуном тоді, коли контакт підписали, тож батько чи мати, які йдуть з облікового запису, не розривають тихцем кожну дружбу, яку колись схвалили.
devices зберігається
  • account_id
  • device_keyвласний ключ одного телефона. Людина може говорити більше ніж з одного
  • agreement_keyключ, на який запечатують повідомлення, щоб відкрити його міг лише цей телефон
  • certпідпис, який впустив цей телефон, збережений слово в слово, щоб твій телефон міг його перевірити, а не вірити нам
  • kindтелефон або надрукований аркуш для відновлення
  • added_at
  • removed_at
Телефони й планшети, з яких говорить одна людина. Ця таблиця каже, скільки пристроїв має твоя сім’я, і зовсім нічого про те, з ким ти говориш, не більше за дві таблиці над нею. Телефон впускає підпис від пристрою, який уже в списку, ніколи не ми, і цей підпис лежить тут, щоб будь-який телефон міг його перевірити, а не вірити списку. Виведення телефона з обігу позначає рядок, а не витирає його, з тієї самої причини, з якої позначають дорослого, який пішов. Усе, що телефон підписав, поки був твоїм, має лишатися перевірним.
push_tokens зберігається
  • account_id
  • device_keyкотрий із твоїх телефонів будить цей рядок
  • platformapns або fcm
  • tokenнепрозорий ідентифікатор від Apple або Google
  • updated_at
По одному на пристрій, щоб дзвінок міг розбудити телефон, який спить. Пуш не несе ні імені, ні вмісту.
used_nonces прибирається
  • nonce
  • expires_at
Список одноразових чисел, які вже витрачені, щоб перехоплений запит не можна було відтворити знову. Стирається, щойно згасає.
denied_edges прибирається
  • edge_hashхеш пари, не двох людей
  • denied_at
  • expires_at
Коли дорослий каже «ні» на знайомство, це пам’ятає те «ні» три дні, щоб телефон другого з батьків, вимкнений на той момент, не завершив його випадково. Свіже сканування між тими самими сім’ями стирає це раніше. Ні імен, ні ключів, лише хеш і дві дати.
pending_grants прибирається
  • edge_hashхеш пари, не двох людей
  • grant_recordдозвіл, про який ідеться, і підписи, зібрані досі
  • waiting_keyчийого підпису ще бракує
  • requested_byтелефон, який попросив, щоб один телефон не міг залити сім’ю питаннями
  • originвідскановано особисто або батько чи мати діють за свою дитину
  • delivered_toякі телефони це вже мають
  • declaredвласне ім’я тієї сторони, яка просить, запечатане так, що прочитати його може лише сім’я, яку вона відсканувала
  • created_at
  • expires_at
Це єдине місце, де сервер тримає щось схоже на соціальний граф, і тримає лише поки знайомство не завершене. Ніхто не відповів за 72 години, і воно згасає. Відповіли, але телефон, для якого це було, так і не вийшов у мережу, тоді 30 днів. Щойно обидва телефони мають завершений контакт, рядок стирають. Саме тому на цій сторінці немає таблиці контактів. Таблиці контактів не існує.
messages стирається при доставці
  • id
  • recipient_idдля маршрутизації цей потрібен відкритим
  • recipient_deviceдля котрого з їхніх телефонів запечатано коробочку. Кожен телефон має свій ключ, тож кожен отримує свою коробочку
  • sender_idщоб телефон знав, чий ключ її відкриває
  • sender_deviceкотрий із їхніх телефонів її запечатав, щоб правильна копія знайшла правильний замок
  • edge_hashпара, у вигляді хешу, щоб це не читалося як карта того, хто з ким говорить
  • sealedшифротекст. Стовпця з текстом немає і ніколи не буде
  • sent_at
  • expires_at
Телефон вимкнений більшу частину дня, а повідомлення, яке тихо зникає, це зламаний продукт. Тому запечатана коробочка чекає тут, найдовше 30 днів, і стирається в ту мить, коли отримувач підтверджує, що вона в нього. Жодну з них ми відкрити не можемо.
revocations зберігається
  • edge_hashпара, у вигляді хешу
  • revoked_at
  • recordпідписаний запис, щоб телефон, який був офлайн, перевірив розрив сам, а не вірив нам на слово
Єдине, що ми тримаємо безстроково. Він фіксує що контакт розірвано, не будучи читабельним списком того, хто когось знав.
roster_changes зберігається
  • id
  • account_idчий список телефонів і дорослих змінюється
  • kindприєднання або прибирання
  • subject_keyтелефон або дорослий, про якого йдеться
  • subject_kindкотре з цих двох
  • agreement_keyдругий ключ, потрібний телефону, порожньо для паперу
  • recordсам підпис, збережений слово в слово, щоб телефон міг його перевірити, а не вірити нам
  • signed_byякий ключ це підписав
  • by_authorityчи підписав це аркуш для відновлення, і саме через це воно чекає
  • issued_atмить, яку заявляє той, хто підписав, ніколи наш годинник
  • reasonпорожньо, якщо той, хто підписав, нічого не написав
  • proposed_at
  • effective_atколи це стає правдою, через дві ночі після того, як запропонували
  • contested_at
  • contested_byхто заперечив і яким підписом він заперечив
  • contest_record
  • resolved_at
  • resolutionзастосовано або скасовано, коли все вирішилося
Дві ночі між тим, як хтось підписав зміну дорослих у дитини, і тим, як ця зміна стала правдою. Телефон, що приєднується до сім’ї, або той, що його виганяють, чекає тут, де кожен інший телефон в обліковому записі це бачить і може заперечити. Рядок лишається після того, як усе вирішилося, бо телефон, який був вимкнений, мусить мати змогу перевірити, що сталося, а не вірити нам на слово. Він тримає обліковий запис і задіяні ключі, а це більше, ніж більшість таблиць тут.
roster_change_votes зберігається
  • change_idяка саме зміна чекає
  • voter_keyякий дорослий
  • decisionпропустити або зупинити
  • recordйого підпис
  • at
Один рядок на дорослого, а не лічильник. Більшість дорослих дитини може завершити дві ночі очікування раніше, і чи є більшість, доводиться питати знову проти тих дорослих, які є зараз. Той, хто пішов, не голос, а лічильник цього не розрізнив би.
device_revocations зберігається
  • account_idчий це був телефон
  • device_keyключ, який украли
  • revoked_at
  • recordпідписаний доказ, щоб телефон міг перевірити його, не питаючи нас
Украдений телефон, і різниця між цим і виведенням з обігу. Виведення означає, що він більше не може діяти з цієї миті, і це правильна відповідь для телефона, який хтось замінив. Це означає не довіряти всьому, що він колись підписав, і інші телефони мусять мати змогу це перевірити, не питаючи нас. Рядок отримує лише крадіжка.
retirements зберігається
  • account_idобліковий запис, якого більше немає
  • retired_at
  • recordпідписаний доказ, зроблений власним ключем того облікового запису, перш ніж ключ знищили
Список відсутностей, і добра новина тут у другому стовпці. Коли хтось іде з Rilo, телефони його друзів отримують підпис, зроблений власним ключем цієї людини. Сім’я довідується, що когось немає, з доказу, а не з наших слів. Ніщо, сказане нами самими, не може забрати обличчя з екрана твоєї дитини. Це єдина таблиця тут без посилання назад на обліковий запис, бо вона мусить пережити той, про який вона.
entitlements зберігається
  • account_id
  • storeapple, google або від нас
  • store_refнепрозорий ідентифікатор від магазину. Це єдине, що дозволяє нам пізніше поставити йому питання
  • store_ref_hashвідбиток ідентифікатора вище. Саме він не дає одному чеку покрити два облікові записи
  • environment
  • stateactive, grace, lapsed, lapsed_trial або disabled
  • seatsскільком дітям це покриває. Число, ніколи їхні імена
  • expires_at
  • acts_at
  • updated_at
Тут ніщо не називає людину. Ні облікового запису в магазині, ні Apple ID, ні акаунта Google, ні імені, ні пошти, ні картки. Яких дітей покриває платіж, обчислюють із таблиці опікунів у той момент, коли питають, і ніколи не записують.
roster_notices прибирається
  • device_keyтелефон, якому ще не сказали
  • change_idпро яку саме зміну йдеться
  • whatзапропоновано, оспорено або вирішено
  • account_idчий список телефонів змінився. Коли дорослому кажуть про його дитину, тут стоїть дитина
  • subject_keyтелефон, який додали або прибрали
  • effective_at
  • created_at
  • expires_at
  • delivered_atколи нам удалося це сказати, або порожньо, бо не вдалося
Черга, і причина, чому додавання чи прибирання телефона відбувається вголос. Про це чує кожен телефон у сім’ї, а для дитини ще й кожен дорослий, який за неї відповідає, разом із телефоном, який щойно прибрали. Саме так той, у кого його бути не повинно, довідується, що ти це бачиш. Повідомлення не несе нічого, чого не несе сама зміна, і стирається, коли згасає, незалежно від того, чи вдалося нам його доставити.
reports зберігається
  • id
  • about_keyпро кого скарга. Читабельно, щоб хтось міг за нею щось зробити
  • reasonяка з шести речей сталася, так, як це обрав дорослий, що скаржиться
  • csa
  • sealedщо було написано, запечатане на телефоні, який це надіслав, ключем, що лежить на папері у засновника. Жоден сервер не може це прочитати
  • sealed_digestвідбиток запечатаної частини, щоб те, що відкриють згодом, доказово було тим, що прийшло
  • received_at
  • edge_snapshotкому було дозволено когось досягати і який дорослий це дозволив, як воно стояло в ту мить. Це відтворити пізніше неможливо
  • reporter_keyхто надіслав, або нічого взагалі, коли надіслали без імені
  • source
  • sealed_key
  • receiptномер, який лишається у відправника. Це єдиний спосіб, яким ми можемо комусь відповісти про скаргу, якої не можемо відкрити
  • acknowledged_at
  • decided_at
  • actioned_at
  • escalated_at
  • decided_by
  • outcome
Єдина таблиця тут, яка існує, щоб щось сталося. Більша частина скарги запечатана на телефоні, який її надіслав, і ми не можемо її відкрити. Читабельним лишається те, про кого вона, бо скарга, за якою ніхто не може нічого зробити, це не захист. Вона переживає обліковий запис, про який вона. Інакше будь-хто міг би видалити себе з неї. Записів жодного дзвінка в ній немає, ні тут, ні деінде, бо їх ніколи не роблять.

Стовпці, яких не існує

  • пошта
  • ім’я
  • вік
  • фото
  • текст повідомлення
  • вміст дзвінка
  • історія дзвінків
  • список контактів
  • місце
  • модель пристрою

Твою пошту перевіряють один раз під час реєстрації і в тому самому подиху запечатують ключем, який лежить на папері у засновника і ніколи не торкався сервера. Закон просить, щоб ми могли довести, що дорослий погодився. Прочитати її назад ми й самі не змогли б. Імена й фото ставлять на кожному телефоні і передають прямо іншому телефону. До нас вони не доходять, і саме тому ти можеш назвати дідуся “Дідою”, а ми ніколи про це не дізнаємось. Ми не змогли б нічого з цього комусь показати, навіть якби хотіли, і нас не можна до цього змусити.

Цей розділ згенеровано зі власної схеми сервера, з accounts.ts і roster-changes.ts, двох файлів, які створюють базу даних. Якщо вони колись розійдуться, праві файли, а ця сторінка помиляється.

Контакти

Як насправді виникає контакт.

Чийсь телефон робить одноразове запрошення. Інший телефон його читає, відсканувавши з екрана в тій самій кімнаті або відкривши з посилання, яке надіслав дорослий. Це читання починає знайомство і в ту саму мить записує відкритий ключ іншого телефона, тож обидва телефони відтоді точно знають, який ключ комусь належить. Саме ця частина робить неможливим вклинитися посередині пізнішого дзвінка, і для нас теж. Ніщо з цього не з’єднано, поки дорослий з іншого боку не скаже «так».

Це ще не контакт. Відповідальний дорослий мусить підписати контакт на власному телефоні, після біометричної перевірки, зробленої в ту саму мить. Він підписує все цілком. Хто, з ким і в якому напрямку. Саме цей підпис сервер перевіряє, перш ніж щось передати. Сервер не тримає жодного ключа, яким можна підписати, тож він не може створити контакт, і той, хто вкраде базу даних, теж не може.

Коли обидва телефони мають завершений контакт, рядок стирають, і контакт існує лише на двох пристроях. Розрив пізніше пише підписане відкликання, і це єдиний запис, який лишається безстроково. Він зберігається як хеш пари, тож він каже, що контакт скінчився, не кажучи, хто когось знав.

Хто ще причетний

Дзвінка торкаються три компанії. Жодна з них не може його почути.

Сервісу торкаються ще шість компаній, і всі вони перелічені в політиці приватності. Три з них потрібні, щоб дзвінок задзвонив. Ось що кожна з цих трьох бачить і чого не бачить.

  • Apple & Google

    Пуш-повідомлення, щоб дзвінок задзвонив на телефоні, який спить. На обох платформах цього не обійти. Вони несуть непрозорий ідентифікатор і більше нічого. Ні імені, ні номера, ні вмісту.

  • Cloudflare

    Приблизно один дзвінок із шести не може з’єднати два телефони напряму, у готельному Wi-Fi або в деяких мобільних мережах, і його відбивають через ретрансляцію. Ретрансляція передає зашифровані пакети, яких не може відкрити, але вона таки бачить, що два анонімні ідентифікатори були з’єднані, коли, як довго і з яких адрес. Це метадані, яких ми не зберігаємо, і які лежать у когось іншого.

  • Ми

    Таблиця вище. Один невеликий сервер у Нідерландах, який знайомить два телефони і потім відходить убік.