JSON Meta Application Protocol (JMAP) Blob Management Extension
updates
- rfc-8620 — The JSON Meta Application Protocol (JMAP)
Extracted elements (29)
Embedding blobs inline in JMAP method calls is analogous to IMAP literals extended by RFC 7888, reducing round trips. Base64 encoding (RFC 4648) is used for both creating and fetching blob data because raw blobs may contain arbitrary binary data that cannot be directly embedded in JSON.
The definition of 'reference' in Blob/lookup is intentionally loose: it means 'you could discover this blobId by looking at this object or at other objects recursively contained within it'. For example, a Thread references a blobId if any Email in that Thread does, but an Email referencing a Mailbox does not inherit all blobs referenced by other Emails in that Mailbox.
This document updates RFC 8620. Blob/copy defined in RFC 8620 is unchanged and remains selected by urn:ietf:params:jmap:core; the new Blob/upload, Blob/get, and Blob/lookup methods are selected only by urn:ietf:params:jmap:blob.
Clients MUST NOT attempt to create blobs larger than maxSizeBlobSet. If maxSizeBlobSet is null, clients are not required to limit blob size, though servers may still reject any creation.
Clients SHOULD use the upload mechanism defined by RFC 8620 for blobs larger than one megabyte, since Blob/upload via inline method calls is restricted by the server's maxUploadSize limit for overall JMAP requests.
Digest algorithm names in supportedDigestAlgorithms MUST be present in the HTTP Digest Algorithm Values registry (RFC 3230) but MUST be lowercased in JMAP (e.g., 'sha-256' not 'SHA-256'). Clients SHOULD prefer algorithms listed earlier in the array.
If a blob is not visible to a user or does not exist at all, the server MUST still return an empty array for each type in a Blob/lookup response, so as not to leak information about whether the blob exists but is inaccessible to the requester.
If a blobId DataSourceObject's range cannot be fully satisfied (begins or extends past the end of the blob), the DataSourceObject is invalid and MUST result in a notCreated response. Servers MUST add a createdIds entry for each successful Blob/upload so the blobId is available via back-reference in subsequent method calls.
If requested octets are not valid UTF-8 and data or data:asText was requested, isEncodingProblem MUST be set to true and data:asText MUST be null. When data was requested and data is not valid UTF-8, data:asBase64 MUST be returned instead.
If the data properties have any invalid references or invalid data (including invalid base64 in data:asBase64 or invalid UTF-8 in data:asText), the server MUST NOT guess the user's intent and MUST reject the creation, returning a notCreated response for that creation id.
If the requested offset+length extends beyond the blob, the server returns available octets and MUST set isTruncated to true. If offset is past the end, an empty string is returned with isTruncated true. Any digest is calculated on the octets that would be returned for a data field.
Servers MUST allow at least 64 DataSourceObjects per Blob/upload creation. This minimum is stated in both the capability description and the Blob/upload method definition.
Servers SHOULD store blob sizes in a format efficient to read, and clients SHOULD limit requests to just the size parameter when that is all they need, since fetching blob content may be significantly more expensive and slower.
Servers that include urn:ietf:params:jmap:blob in one or more accountCapabilities MUST also include it in the top-level session capabilities property. The session-level value MUST be an empty object.
The size value in a Blob/get response MUST always be the number of octets in the underlying blob, regardless of the offset and length parameters used in the request.
Blob/get is a standard JMAP get extended with two optional parameters: offset (UnsignedInt|null, default 0) to start reading into the blob, and length (UnsignedInt|null) to limit how many octets are returned. Request properties include data:asText, data:asBase64, data, digest:<algorithm>, and size.
Blob/lookup takes accountId, typeNames (String[]), and ids (Id[]) and performs a reverse lookup to find which objects of each named data type reference each blobId. The response contains a list of BlobInfo objects each with id and matchedIds (a map from type name to Id[]).
Blob/upload accepts accountId and a create map of Id to UploadObject. It behaves like Foo/set but permits only creation (no update or delete); the response has no state field. Created objects include id (blobId), type (String|null), and size (UnsignedInt).
The blob capability object in accountCapabilities contains four fields: maxSizeBlobSet (UnsignedInt|null, max blob octets), maxDataSources (UnsignedInt, max DataSourceObjects per create), supportedTypeNames (String[], types available for Blob/lookup), and supportedDigestAlgorithms (String[], algorithms available for Blob/get digest).
The urn:ietf:params:jmap:blob capability, when present in accountCapabilities, signals support for Blob/upload, Blob/get, and Blob/lookup methods. Its value in the session-level capabilities MUST be an empty object; in account capabilities it carries server limits and supported features.
RFC 9404 creates the 'JMAP Data Types' IANA registry with registration policy 'Specification Required'. Initial entries include Core, PushSubscription, Mailbox, Thread, Email, EmailDelivery, SearchSnippet, Identity, EmailSubmission, VacationResponse, and MDN, each with 'Can Ref Blobs', 'Can Use for State Change', and associated capability columns.
RFC 9404 registers the JMAP capability urn:ietf:params:jmap:blob with IANA under intended use 'common', with security and privacy considerations in Section 5.
RFC 9404 registers the JMAP error code 'unknownDataType' with IANA. This error is returned when a server does not recognise the data type specified in a Blob/lookup typeNames list, or the capability enabling that type is absent from the current Request Object.
Blob/upload with carefully chosen data sources can be used to reassemble dangerous content on the far side of security scanners (anti-virus, exfiltration scanners) watching the upload endpoint. Server implementations SHOULD provide a hook for security scanners to inspect the fully concatenated resulting blob.
Digest algorithms can be computationally expensive. Servers that share resources among multiple users should track per-client resource usage and rate-limit expensive digest operations to prevent resource starvation.
JSON parsers are not consistent in handling non-UTF-8 data. Because JMAP requires all JSON to be UTF-8, servers MUST return null for data:asText and set isEncodingProblem: true rather than attempting to return non-UTF-8 octets directly in a JSON string.
Servers MUST apply access controls so that a Blob/lookup cannot reveal blobIds from objects (e.g., emails in inaccessible mailboxes) that the authenticated user would not be able to discover through normal queries. Access-controlled IDs MUST NOT appear in lookup responses.
The server MUST NOT trust that data given to Blob/upload is a well-formed instance of the specified media type. If the server parses the blob, it MUST use only hardened parsers designed for arbitrary untrusted data. The server SHOULD NOT reject data solely because it is not a valid specimen of the stated media type.
UploadObject has a data field (DataSourceObject[]) and an optional type field (String|null). DataSourceObject contains exactly one of: data:asText (UTF-8 string), data:asBase64 (base64 string), or a blobId source with optional offset (UnsignedInt|null) and length (UnsignedInt|null) fields.