VFLIN
ВойтиСкачать

docs · docs/30-core/PROFILE_STORAGE_SYNC.md · перевод от 2026-09-12 · с английской ревизии 4482d7eeaee0

Профили и синхронизация

подписанные зашифрованные блобы; жизненный цикл; векторы версий; чего запущенный профиль никогда не делает

Назначение: определить жизненный цикл профиля, его формы на диске и при хранении и протокол синхронизации. Это модель, проверенная в поле отгруженными антидетект-продуктами (<uuid>.zip + <uuid>.sig, частичная локальная материализация) и формализованная здесь (ADR-0004).

1. Две формы профиля

  • Рабочая форма (runtime form): живой user-data-dir, с которым может работать Chromium (cookies, localStorage, IndexedDB…).
  • Форма для хранения и синхронизации: Profile Blob = manifest + payload:
    • payload = user-data-dir → детерминированный архив (tar/zip) → сжатиешифрование (AEAD).
    • <uuid>.blob (шифртекст) + <uuid>.sig (подпись над шифртекстом и манифестом).
    • манифест (открытый, с защитой целостности): uuid, вектор версий, размер, версия ядра, открывавшего профиль последним, id отпечатка, createdAt, deviceId, contentHash.

Мосты между двумя формами: materialize (блоб → папка) и pack (папка → блоб). Это единственные разрешённые преобразования.

2. Конечный автомат жизненного цикла

            create / import
                  │
            ┌─────▼──────┐  pull(blob)     ┌──────────────┐ materialize ┌───────────────┐
            │ CLOUD_ONLY │────────────────►│ LOCAL_CACHED │────────────►│ MATERIALIZED  │
            └─────▲──────┘                 └──────▲───────┘             └──────┬────────┘
                  │ evict                          │ pack                       │ start
                  │                                │                            ▼
          ┌───────┴────────┐  upload        ┌──────┴───────┐   stop      ┌──────────────┐
          │ SYNCING_UPLOAD │◄───────────────│    DIRTY     │◄────────────│   RUNNING    │
          └────────────────┘                └──────────────┘             └──────────────┘
   remote newer │                                  ▲
                ▼                                   │ resolve
        ┌────────────────┐                   ┌──────┴──────┐      lock held elsewhere
        │ SYNCING_DOWNLOAD│                  │  CONFLICT   │◄──────  LOCKED_REMOTE
        └────────────────┘                   └─────────────┘

Определения состояний:

  • CLOUD_ONLY — блоб существует только в облаке; локально ничего нет.
  • LOCAL_CACHED — блоб есть локально, не распакован.
  • MATERIALIZED — живая папка существует, профиль не запущен.
  • RUNNING — ядро работает с папкой (монопольно; синхронизация не может её трогать).
  • DIRTY — остановлен с локальными изменениями, ещё не загруженными.
  • SYNCING_UPLOAD/DOWNLOAD — идёт передача.
  • CONFLICT — локальная и удалённая версии разошлись (обе изменились после общей базы).
  • LOCKED_REMOTE — блокировку запуска/правки держит другое устройство.

3. Шифрование и подпись (zero-knowledge, несколько KEK — ADR-0008)

  • Иерархия конвертов: полезная нагрузка каждого профиля шифруется AEAD случайным ключом данных профиля (DEK). DEK независимо оборачивается несколькими KEK — любой из них его разворачивает. Сервер хранит только шифртекст и копии обёрнутого DEK → сервер не может расшифровать ни на одном пути.
  • Факторы KEK: пароль (Argon2id) + обязательный Recovery Key (офлайн-набор на экстренный случай); необязательные passkey (WebAuthn PRF) и связка ключей ОС; командные профили добавляют KEK ключа организации (обёртка открытым ключом) для восстановления с помощью администратора — по-прежнему zero-knowledge для сервера.
  • AEAD (AES-256-GCM или XChaCha20-Poly1305) для полезной нагрузки; подпись (Ed25519) над манифестом и шифртекстом — чтобы обнаружить подмену и повреждение (.sig).
  • Восстановление: потеряли один фактор → восстановите через другой. Потеряли все факторы → потеряли профили (честный предел сквозного шифрования). Никакого депонирования на сервере. Подробности: KEY_RECOVERY_COMPARISON.md, SECURITY_POLICY §4.

4. Протокол синхронизации

  1. Блокировка: sync.lock.acquire(id) перед загрузкой или правкой. Блокировки выдаются в аренду (TTL) и помечены владельцем.
  2. Векторы версий: каждый блоб несёт {deviceId: counter}; сравнение показывает, перемотка это вперёд или расхождение.
  3. Загрузка: pack → шифрование → подпись → multipart-загрузка в объектное хранилище → фиксация манифеста в базе метаданных.
  4. Скачивание: получить блоб → проверить подпись → расшифровать → материализовать по требованию.
  5. Конфликт: если удалённая версия ушла дальше локальной базы, пока локальная в DIRTY → CONFLICT. Разрешение по умолчанию: побеждает последний записавший, под защитой блокировки; CONFLICT сохраняет оба блоба (a/b) для ручного выбора.
  6. Вытеснение: LOCAL_CACHED/MATERIALIZED могут вытесняться в CLOUD_ONLY при нехватке места на диске (LRU), но никогда — в DIRTY/RUNNING.

5. Гарантия local-first

Без облачного аккаунта работает всё: профили живут как локальные блобы и папки, жизненный цикл — без состояний SYNCING/LOCKED_REMOTE. Облако — добавочный слой и никогда не зависимость (ARCHITECTURE.md §1.3).

6. Инварианты (их соблюдает Profile Lifecycle, в обход — никогда)

  • Профиль в RUNNING никогда не упаковывается, не загружается и не вытесняется.
  • Синхронизация никогда не правит живой user-data-dir (DO_NOT_DO.md).
  • Materialize/pack — единственные преобразования папка↔блоб.
  • Каждый переход состояния порождает событие profile.state_changed и (для опасных) запись аудита.
  • Materialized = единственное окно открытого текста. Пока профиль в MATERIALIZED/RUNNING, user-data-dir лежит на диске в открытом виде (он нужен Chromium). При pack/вытеснении папка надёжно стирается; окно держится минимальным. (Остаточный риск при компрометации машины: THREAT_MODEL §4.)
  • materialize никогда не отдаёт сырой путь удалённым клиентам. Путь получает только локальный интерфейс / контекст владельца; клиенты CLI/MCP/скриптов получают непрозрачный дескриптор, а любая выдача сырого пути — это power mode + аудит: иначе путь обошёл бы единую границу управления.

Открытые вопросы

Детерминизм архивов между ОС, частичная/дельта-синхронизация против полного блоба, TTL блокировки и политика перехвата, UX конфликтов, варианты восстановления ключей (социальное/депонирование?) → OPEN_QUESTIONS.md.