Кратко

  • draft-ietf-jmap-object-history-00 добавляет к Foo/get прежние и уничтоженные версии, но разрешает серверу объединять частые правки, удалять версии в любом порядке и без уведомления сбрасывать историю при нехватке хранилища.
  • Полученный снимок полезен для восстановления. Он не называет исполнителя, запрос, решение об авторизации или последующий эффект, а даже hasMoreHistory: false не удостоверяет полноту истории.

Адресная книга может вернуть телефон, удалённый вчера. Почтовый ящик может показать письмо в виде непосредственно перед уничтожением. Это полезная функция: ошибка становится обратимой, а каждому клиенту не приходится строить собственный параллельный архив.

Однако те же поля легко выглядят убедительнее, чем обещает протокол. Одинаковый ID, номер версии и время замены превращаются на экране в аккуратную хронологию. Ни одно из них не сообщает, кто действовал, какой клиент отправил изменение, какое правило его разрешило и наступил ли внешний результат.

Именно здесь проходит важная граница JMAP Object History, поданного от имени рабочей группы 15 сентября. Проект делает старое состояние доступным по запросу. Он не превращает хранилище восстановления в аудиторский реестр.

Протокол возвращает сохранённые состояния, а не события

Ядро JMAP задаёт семейство методов для типа объекта: Foo/get, Foo/changes, Foo/set, Foo/query и Foo/queryChanges. Синхронизация текущего состояния уже встроена в архитектуру. Foo/changes может указать ID объектов, изменившихся между состояниями, но не передаёт прежние значения.

Capability urn:ietf:params:jmap:object-history расширяет Foo/get. С includeReplaced: true рядом с живым объектом могут прийти заменённые представления. С includeDestroyed: true можно запросить объекты, которых уже нет. Вместе параметры позволяют вернуть все ещё сохранённые версии уничтоженного объекта.

Все версии носят один ID объекта. objectHistory.version упорядочивает их в ответе, а objectHistory.replaced отмечает время, когда представление было заменено обновлением или уничтожением; null означает живую версию.

Это координаты состояния, не происхождение события. Время замены не говорит, когда старая версия была создана. Номер не является идентификатором транзакции. Поля не называют исполнителя, сессию, устройство, тело запроса, политику авторизации, одобрение, мотив, уведомление или внешний эффект.

Два соседних снимка позволяют вычислить разницу значений. Они не доказывают, что разницу вызвало одно атомарное действие.

Пропущенная середина могла никогда не сохраняться

Проект прямо разрешает сворачивать несколько быстрых обновлений в одну версию. Если телефон исправляли три раза подряд, история может оставить лишь значение до этой серии и после неё. Отдельная запись для каждого изменения не обязательна.

Сервер вправе удалять старые версии в любом порядке. Пробелы в последовательности допустимы; клиент не должен считать историю непрерывной или полной. У отсутствия есть как минимум два объяснения: отдельная версия не создавалась или была создана, а затем удалена.

Даже номер намеренно ненадёжен между запросами. Серверу рекомендуется сохранять последовательность, но клиент не должен от неё зависеть. Число прежде всего сортирует версии одного объекта внутри ответа. Превратить его в постоянный ID аудиторского события — значит приписать спецификации свойство, которого она не гарантирует.

Такая свобода честно учитывает стоимость хранения. Она же делает запрос «покажите всё, что произошло» невозможным без отдельной событийной системы.

hasMoreHistory помогает продолжить выборку, но не удостоверяет полноту

historyLimit ограничивает общее количество записей и просит выдавать самые свежие. Если предел достигнут хотя бы для одного запрошенного ID, hasMoreHistory: true сообщает, что в этот момент есть более старые сохранённые элементы. В многообъектном ответе не указано, какой ID имеет продолжение; их нужно спрашивать отдельно.

True носит временный характер. Проект предупреждает, что старые элементы могут быть удалены до следующего запроса. Флаг описывает доступность в конкретный момент, но ничего не резервирует.

False ещё слабее. Обычно он означает, что сервер отдал всю сохранённую историю указанных ID. Но сервер может вернуть false при наличии дополнительных данных, если точно выяснить это слишком дорого. Значение не говорит, что каждое изменение было записано, ничего не удалялось или были найдены все ещё существующие представления.

Интерфейс, переводящий false как «полный аудит», переворачивает смысл контракта. Корректная подпись уже: в этом ответе не сообщалось о дополнительной сохранённой истории.

Null вместо срока не означает вечное хранение

Учётная запись с этой capability публикует maxHistoryDuration. Число задаёт максимальный возраст в секундах после замены, по истечении которого версия могла быть удалена. Null означает отсутствие лимита, основанного на времени.

Это не обещание постоянства. Раздел безопасности учитывает давление на хранилище и допускает молчаливое удаление истории при достижении лимитов. Время — один рычаг хранения, общий объём — другой. Тип объекта может поддерживать интерфейс, не сохраняя ни одной прежней версии. Сервер всё равно вернёт текущий объект с objectHistory и может обозначить все живые объекты версией 1.

Следовательно, capability доказывает наличие интерфейса, а не минимального доказательного архива. Закупка и комплаенс должны отдельно задать минимальный срок, уведомление об удалении, экспорт, целостность и legal hold.

Восстановлению и ответственности нужны разные квитанции

Для восстановления поверхность подходит хорошо. Клиент может спросить Email/changes, какие ID уничтожены, передать их в Email/get, запросить удалённые объекты и показать последние значения до уничтожения. Пользователь восстанавливает содержимое, а протокол не притворяется, будто оно всё ещё активно.

Для установления ответственности ответа недостаточно. Надёжная квитанция должна связывать аутентифицированного исполнителя и сессию, исходный клиент и устройство, точное тело изменения, условный токен состояния, версию политики и решение об авторизации, принятую сервером транзакцию, хеши до и после, устойчивый ID события, доставку уведомления и наблюдаемый результат.

Object History способен предоставить представление до или после. Остальные столбцы он не заполняет. Выводить исполнителя из владельца небезопасно. Выводить полномочие из видимого перехода значит игнорировать применённую политику. Выводить внешний эффект из сохранённого значения значит путать приём в плоскости управления с исполнением и наблюдением.

Там, где требуется ответственность, снимки восстановления нужно соединять с независимым журналом только для добавления. Одно хранилище не должно изображать два несовместимых контракта.

Исторический доступ не равен нынешнему доступу

История создаёт сложный вопрос авторизации. Пользователь без права читать текущий объект не должен читать его историю. Если разрешения менялись, сервер должен вернуть только версии, на чтение которых этот заявитель имел бы право в соответствующее время.

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

JMAP Sharing уже разделяет principals, предполагаемое разрешение и эффективный доступ. Object History добавляет время: право просматривать старую версию зависит от прежнего права, а не только от нынешней карты shareWith. Выданная версия подтверждает, что сервер решил раскрыть её, но без отдельной записи не объясняет это решение полностью.

Иногда правильная история намеренно неполна

Старое представление может сохранить именно те сведения, которые пользователь или администратор хотел удалить. Телефон, убранный из контакта, способен остаться в истории. Поэтому проект рекомендует административную возможность стирать историю отдельных объектов ради приватности или соблюдения требований.

Это не дефект, который следует скрыть. Восстановление, ответственность, право на удаление и экономика хранения тянут в разные стороны. Для каждого класса данных нужно назвать приоритет, полномочие на очистку, сохраняемую защищённую квитанцию и независимые аудиторские факты, которые останутся после удаления содержимого.

Обещание «неизменяемой истории» поверх интерфейса, рассчитанного на удаление, опасно. Отказываться от восстановления, потому что оно не является судебным журналом, столь же ошибочно. Правильная архитектура разделяет хранилища и публикует их границы.

Принятие рабочей группой ещё не внедрение

Сентябрьский документ почти дословно совпадает с индивидуальным проектом марта. В теле протокола не появилось нового эксплуатационного доказательства; существенное изменение — имя рабочей группы JMAP.

Datatracker отмечает его как WG Document на стадии I-D Exists. Ответственный area director, shepherd и telechat не указаны. В сводке поле intended status пусто, хотя заголовок проекта говорит Standards Track. Это по-прежнему Internet-Draft, не RFC, а зафиксированные источники не доказывают работающую реализацию или политику хранения какого-либо названного сервиса.

Граница зрелости подчёркивает операционный урок. Запись в реестре может согласовать словарь реализаций. Только свидетельства работающей системы покажут, какие версии захватили, объединили или удалили, кто был авторизован и удалось ли восстановление.

Источники