Summary
draft-ietf-jmap-object-history-00adds retained previous and destroyed object versions toFoo/get, but explicitly permits servers to collapse changes, prune versions in any order and silently discard history under storage pressure.- A returned snapshot can support recovery. It does not identify the actor, mutation request, authorization decision or downstream outcome, and even
hasMoreHistory: falsecannot certify that no additional history exists.
The server can return a contact before its phone number was removed, or an email as it stood immediately before destruction. That is useful. It can make an accidental edit reversible and give a user a “recently deleted” view without requiring a second protocol.
It can also look more authoritative than it is. The response contains the same object ID, an ordered version number and the time at which a version was replaced. A screen can render those fields as a clean timeline. Nothing in that timeline says who changed the object, which client sent the mutation, what authorization rule allowed it or whether a later action succeeded.
That boundary is central to JMAP Object History, uploaded under its Working Group name on 15 September. The proposal makes old state retrievable. It does not turn a recovery store into an audit ledger.
The protocol returns retained states, not events
JMAP core provides a family of methods for an object type: Foo/get, Foo/changes, Foo/set, Foo/query and Foo/queryChanges. Current-state synchronization is already part of the architecture. Foo/changes can tell a client which object IDs changed between states, but it does not supply the previous values.
The new capability, urn:ietf:params:jmap:object-history, extends Foo/get. With includeReplaced: true, the response can include older versions alongside the live object. With includeDestroyed: true, it can also include objects that no longer exist. If both flags are used, all versions the server has retained for a destroyed object may be returned.
Each version keeps the same object ID. objectHistory.version orders versions of one object within the response. objectHistory.replaced records when that representation was superseded by an update or destruction; null means the object is live.
Those are state coordinates. They are not event provenance. The replacement timestamp does not say when the old version was created. The version number does not name a transaction. Neither field identifies an actor, session, device, request, authorization policy, approval, motive, notification or external effect.
Two adjacent snapshots let an observer calculate a difference in values. They do not establish that one atomic act caused that difference.
The missing middle may never have been stored
The draft explicitly allows a server to collapse rapid successive updates into one version. It need not create a historical record for every individual change. If a phone number is edited three times in a short interval, the history surface may preserve only the value before and the value after that interval.
The server may also prune old versions in any order. Gaps in the version sequence are valid. Clients must not assume the available history is complete or contiguous. A missing version therefore has at least two explanations: no separate version was ever created, or a version once existed and was later removed.
Even the number is intentionally weak across requests. Servers should return consistent version numbers, but clients must not rely on that. The number exists primarily to order versions of the same object inside one response. Treating it as a durable audit-event ID would depend on behaviour the specification declines to promise.
This makes the proposal honest about storage. It also makes “show me everything that happened” an invalid request unless a separate event system supplies the missing contract.
hasMoreHistory is a retrieval hint, not a completeness certificate
historyLimit caps the total number of entries and directs the server to return the most recent versions. If the limit is reached for any requested ID, hasMoreHistory: true tells the client that older retained entries are available. A multi-object response does not say which ID has more; the client must query them separately.
True is temporary. The draft warns that the older entries may be purged before the next request. The flag describes what the server believes is retrievable at one moment, not a reservation.
False is weaker still. It normally means the server returned all retained history for the requested IDs. Yet the server may return false even when more history exists if determining that efficiently is difficult. It does not mean every change was captured. It does not mean nothing was pruned. It does not even guarantee that every still-existing older representation was discovered.
A dashboard that converts false into “complete audit history” reverses the protocol's meaning. The defensible label is narrower: no additional retained history was reported in this response.
A null duration is not infinite retention
Accounts that advertise the capability expose maxHistoryDuration. A number is the maximum age, in seconds after replacement, beyond which a version may have been discarded. Null means the server imposes no time-based limit.
Null does not mean permanent. The security section recognizes storage pressure and allows servers to silently discard history when storage limits are reached. Duration is one retention control; total storage is another. An object type may support the history interface while keeping no prior versions at all. In that case, the server still returns the live object with objectHistory and may label every current object version 1.
The capability therefore proves that an interface exists, not that a minimum evidentiary archive exists behind it. Procurement and compliance controls need their own minimum retention, deletion notification, export, integrity and legal-hold requirements.
Recovery and accountability need different receipts
For recovery, the proposed surface is well shaped. A client can ask Email/changes which IDs were destroyed, pass those IDs into Email/get, request destroyed objects and display the last pre-destruction values. A user may restore content without the protocol pretending that the deleted objects are still live.
For accountability, the same response is insufficient. A serious change receipt needs the authenticated actor and session, originating client and device, exact mutation body, conditional state token, effective authorization-policy version and decision, accepted server transaction, before-and-after hashes, durable event ID, notification delivery and observed result.
Object history can contribute the before or after representation. It cannot fill the other columns. Inferring the actor from the object owner is unsafe. Inferring authority from a successful state transition ignores which policy was applied. Inferring downstream effect from a stored value confuses control-plane acceptance with execution and observation.
The architecture should join recovery snapshots to an append-only audit surface where accountability is required. It should not force one store to perform both jobs under incompatible assumptions.
Historical access is not simply present access
History also creates a difficult authorization question. The draft says a user without permission to read the current object must not read its history. When permissions changed over time, the server must return only versions for which that requester would have had read access.
That rule protects against a newly authorized reader seeing values from a period in which the reader had no rights. It also means the server needs enough historical authorization context to evaluate the request. A current access-control list alone may be unable to answer the question.
JMAP Sharing already separates principals, intended permission state and effective access. Object History adds a temporal dimension: the right to inspect an old version depends on a historical right, not merely the current shareWith map. The history response is evidence that the server chose to disclose a version under its evaluation; it is not a complete record of the permission decision unless that decision is separately logged.
Sometimes the correct history is deliberately incomplete
The proposal acknowledges that an old object can reveal information a user or administrator intended to remove. A deleted phone number can remain visible in a contact's history. It therefore recommends an administrative ability to purge history for specific objects when privacy or compliance requires it.
That is not a flaw to conceal. Recovery, accountability, privacy erasure and storage economy pull in different directions. A useful product needs to state which objective prevails for each class of data, who can authorize a purge, whether a tamper-evident purge receipt survives, and which independent audit facts must remain after content is erased.
The dangerous promise is “immutable history” on top of an interface designed to allow deletion. The equally dangerous reaction is to disable recovery because it is not a forensic ledger. The correct design separates the stores and publishes their boundaries.
Working Group adoption is not deployment
The September document is almost textually identical to the individual draft published in March. Its protocol body did not acquire new evidence. The meaningful delta is that the proposal now carries the JMAP Working Group name.
Datatracker marks it as a WG Document at I-D Exists. The page has no responsible area director, shepherd or telechat. Its summary leaves intended status blank while the draft header says Standards Track. It remains an Internet-Draft, not an RFC, and the frozen sources do not establish a running implementation or retention policy at any named service.
That maturity boundary reinforces the operating lesson. A registry column saying a data type supports history will coordinate implementations. Only running evidence can show which versions were captured, which were collapsed, what was pruned, who was authorized and whether a restore worked.
Sources
- JMAP Object History Datatracker record
- JMAP Object History revision history
- JMAP Object History, Working Group revision 00
- JMAP Object History, individual revision 00
- RFC 8620: The JSON Meta Application Protocol
- RFC 8621: JMAP for Mail
- RFC 9610: JMAP for Contacts
- RFC 9670: JMAP Sharing
- IANA JMAP Parameters
- RFC 2119: Key Words for Requirement Levels
- RFC 8174: Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words
- Minimum Initial Specification, Localized Future Decision, and Voluntary Adoption
- Running-Code Primacy
Member Briefing
Deeper Profile Context
Sign in with the right membership level to unlock the full briefing and source notes.
Only for Strategic Circle
Strategic Circle
Open to all readers. Unlock profile briefings after joining and signing in.
Join Strategic CircleOnly for Leadership Alliance
Leadership Alliance
For qualified IP-asset owners and management; sign in to unlock alliance briefings.
Join Leadership Alliance

