Resumen

  • draft-ietf-jmap-object-history-00 añade versiones anteriores y destruidas a Foo/get, aunque permite agrupar ediciones rápidas, eliminar versiones en cualquier orden y descartar el historial sin aviso cuando falta almacenamiento.
  • Una copia devuelta sirve para recuperar datos. No identifica al actor, la solicitud, la decisión de autorización ni el efecto posterior; tampoco hasMoreHistory: false certifica una historia completa.

Un usuario puede recuperar un contacto anterior a la eliminación de su teléfono o un correo tal como estaba justo antes de borrarlo. Ese uso es valioso: una modificación accidental deja de ser definitiva y cada cliente no necesita construir su propio archivo paralelo.

Pero los mismos datos pueden parecer más concluyentes de lo que son. Un identificador constante, un número de versión y una fecha de reemplazo se dibujan fácilmente como una cronología impecable. Ninguno revela quién actuó, qué cliente envió la operación, qué regla la permitió o si una consecuencia posterior llegó a producirse.

Esa es la frontera operativa de JMAP Object History, presentado con el nombre del grupo de trabajo el 15 de septiembre. La propuesta hace consultable un estado antiguo. No convierte el almacén de recuperación en un libro de auditoría.

El protocolo entrega estados retenidos, no acontecimientos

El núcleo de JMAP define una familia de métodos por tipo: Foo/get, Foo/changes, Foo/set, Foo/query y Foo/queryChanges. La sincronización del estado actual ya forma parte del diseño. Foo/changes enumera qué identificadores cambiaron entre estados, pero no aporta sus valores anteriores.

La capacidad urn:ietf:params:jmap:object-history amplía Foo/get. Con includeReplaced: true aparecen representaciones sustituidas junto al objeto vivo. includeDestroyed: true permite pedir objetos que ya no existen. Al activar ambos, pueden volver todas las versiones que el servidor aún conserve de un objeto destruido.

Cada versión mantiene el mismo identificador. objectHistory.version establece el orden dentro de la respuesta y objectHistory.replaced registra cuándo esa representación fue reemplazada por una actualización o destrucción; un valor nulo corresponde al objeto vivo.

Son coordenadas de estado, no procedencia del evento. La marca de reemplazo no dice cuándo nació la versión. El número no nombra una transacción. Los campos no identifican actor, sesión, dispositivo, cuerpo de petición, política de autorización, aprobación, motivo, notificación o consecuencia externa.

Dos imágenes contiguas permiten calcular qué valores difieren. No prueban que una sola acción atómica produjera la diferencia.

La etapa ausente quizá nunca se almacenó

El borrador permite expresamente condensar varias actualizaciones rápidas en una única versión. Si un teléfono se corrige tres veces en pocos minutos, la historia puede conservar solo el valor anterior y el posterior al intervalo. No queda una fila por cada gesto.

Además, el servidor puede podar versiones antiguas en cualquier orden. Son válidos los huecos y el cliente no debe suponer que la secuencia es continua ni exhaustiva. Una ausencia admite por lo menos dos explicaciones: nunca hubo una versión separada o existió y luego fue eliminada.

El número de versión tampoco ofrece una identidad estable. Se recomienda coherencia, pero los clientes no deben depender de ella entre solicitudes. Su función principal es ordenar las variantes del mismo objeto en una respuesta. Usarlo como identificador duradero de un evento exigiría algo que la especificación se niega a prometer.

La flexibilidad permite controlar costes. También significa que “todo lo que el servidor conserva” y “todo lo que ocurrió” son conjuntos distintos.

hasMoreHistory orienta la lectura, no certifica integridad

historyLimit limita el total de entradas y hace que el servidor entregue las más recientes. Si alcanza el límite para algún identificador, hasMoreHistory: true señala que en ese momento hay elementos antiguos disponibles. Cuando se pidieron varios objetos, no dice cuál tiene más; hay que consultarlos uno por uno.

El valor verdadero caduca. La propuesta advierte que esas entradas pueden ser purgadas antes de la siguiente llamada. Informa de una disponibilidad instantánea, no reserva el material.

El valor falso dice todavía menos. Normalmente indica que se devolvió todo lo retenido para esos identificadores, pero el servidor puede responder falso pese a existir más material cuando averiguarlo con eficiencia resulte difícil. No garantiza que cada cambio se capturara, que nada fuera podado ni que se localizaran todas las representaciones que aún existen.

Una interfaz que traduzca falso como “auditoría completa” estaría invirtiendo el contrato. La leyenda correcta es más estrecha: en esta respuesta no se informó de más historial retenido.

Duración nula no equivale a retención perpetua

Las cuentas que anuncian la capacidad exponen maxHistoryDuration. Un número expresa la edad máxima, en segundos desde el reemplazo, a partir de la cual una versión puede haber desaparecido. Nulo significa que no hay un límite basado en el tiempo.

No significa “para siempre”. La sección de seguridad reconoce la presión de almacenamiento y permite eliminar historial silenciosamente al alcanzar límites de capacidad. El tiempo es un eje de retención y el espacio es otro. Incluso un tipo puede admitir la interfaz sin conservar ninguna versión previa: el servidor devuelve el objeto vivo con objectHistory y puede numerar cada objeto actual como versión 1.

La capacidad confirma que hay una forma de preguntar, no que exista un archivo probatorio mínimo. Compras, cumplimiento y gobierno deben definir retención mínima, aviso de eliminación, exportación, integridad y conservación legal por separado.

Recuperación y rendición de cuentas requieren comprobantes distintos

Para recuperar, la superficie está bien planteada. Un cliente puede consultar Email/changes para localizar identificadores destruidos, pasarlos a Email/get, solicitar el contenido borrado y mostrar el último estado previo. Así ayuda al usuario sin fingir que los objetos siguen activos.

Para atribuir responsabilidad, faltan datos. Un recibo serio necesita actor autenticado y sesión, cliente y dispositivo de origen, cuerpo exacto de la mutación, token de estado condicional, versión y decisión de la política, transacción aceptada, huellas anterior y posterior, identificador durable del evento, entrega de notificación y resultado observado.

Object History puede aportar una representación anterior o posterior. No completa el resto. Inferir el actor a partir del propietario es inseguro. Inferir autorización porque el estado cambió omite la política aplicada. Inferir un efecto externo a partir de un valor guardado confunde aceptación en el plano de control con ejecución y observación.

Cuando haga falta rendir cuentas, los estados de recuperación deben enlazarse con un registro de eventos de solo adición. No conviene imponer a un único almacén dos obligaciones incompatibles.

El acceso histórico no es igual al acceso de hoy

El historial introduce otra pregunta: quién puede verlo. La propuesta prohíbe leer las versiones de un objeto cuyo estado actual no se puede leer. Si los permisos cambiaron, solo deben entregarse las versiones que ese solicitante habría tenido derecho a leer en su momento.

La regla evita que una persona autorizada recientemente descubra datos de una época anterior. También obliga al servidor a recordar suficiente contexto de autorización. Una lista de control vigente puede no bastar para reconstruir la decisión histórica.

JMAP Sharing ya diferencia principales, permisos pretendidos y acceso efectivo. Object History añade la dimensión temporal: el derecho a una versión antigua depende de un derecho antiguo, no solo del mapa shareWith actual. La entrega de una versión demuestra la decisión de revelar adoptada por el servidor, pero no constituye una explicación completa de esa decisión si no se registró aparte.

A veces el historial correcto debe estar incompleto

Una copia antigua puede mantener información que alguien quiso retirar. El teléfono borrado de un contacto puede seguir visible en su historia. Por eso el borrador recomienda una función administrativa para purgar el historial de objetos concretos por privacidad o cumplimiento.

No es una contradicción que deba ocultarse. Recuperación, responsabilidad, derecho de supresión y economía de almacenamiento tienen incentivos diferentes. Cada clase de datos necesita definir cuál prevalece, quién autoriza una purga, qué comprobante resistente a alteraciones sobrevive y qué hechos independientes deben mantenerse una vez eliminado el contenido.

Ofrecer “historial inmutable” mediante una interfaz que permite borrar sería engañoso. Renunciar a toda recuperación porque no es un registro forense tampoco tiene sentido. Hay que separar los almacenes y explicar sus límites.

La adopción del grupo no es un despliegue

El documento de septiembre es casi idéntico al borrador individual de marzo. El cuerpo técnico no añade nueva evidencia; el cambio importante es que ahora lleva el nombre del grupo de trabajo JMAP.

Datatracker lo marca como documento del grupo en I-D Exists. No constan director de área responsable, shepherd ni teleconferencia. El resumen deja vacío el estado previsto mientras la cabecera dice Standards Track. Sigue siendo un Internet-Draft, no un RFC, y las fuentes congeladas no demuestran una implementación en producción ni una política de retención de ningún servicio identificado.

La madurez importa. Una entrada de registro puede coordinar implementaciones. Solo la evidencia en ejecución permite saber qué versiones se capturaron o fusionaron, qué se podó, qué autorización se evaluó y si una restauración terminó bien.

Fuentes