<?xml version="1.0" encoding="UTF-8"?>
  <?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
  <!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.32 (Ruby 3.3.0) -->


<!DOCTYPE rfc  [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">

]>


<rfc ipr="trust200902" docName="draft-gondwana-jmap-blobext-05" category="std" consensus="true" submissionType="IETF" updates="8620, 9404" tocInclude="true" sortRefs="true" symRefs="true">
  <front>
    <title abbrev="JMAP Blob Extended">JMAP Blob Extensions</title>

    <author initials="B." surname="Gondwana" fullname="Bron Gondwana">
      <organization>Fastmail</organization>
      <address>
        <email>brong@fastmailteam.com</email>
      </address>
    </author>

    <date year="2026" month="March" day="30"/>

    
    
    <keyword>Internet-Draft</keyword>

    <abstract>


<?line 30?>

<t>The JMAP base protocol (RFC8620) provides the ability to upload and download
arbitrary binary data.  This binary data is called a "blob", and can be used
in all other JMAP extensions.</t>

<t>The JMAP blob extension (RFC9404) added additional ways to create and access
blobs by making inline method calls within a standard JMAP request.</t>

<t>This extension adds more methods to work with blobs, including handling large
blobs by processing them in chunks (building on RFC9404's blob construction
support), and providing server-side blob conversion operations: image format
conversion, archive creation and extraction (zip, tar, cpio), compression
and decompression, and delta/patch operations.</t>



    </abstract>



  </front>

  <middle>


<?line 45?>

<section anchor="introduction"><name>Introduction</name>

<t>The JMAP Blob extension (<xref target="JMAP-BLOB"/> — JMAP Blob Management) offers additional
ways to create blobs, and query where they are used.</t>

<t>This extension builds on that work, offering ways to find more information about
the internal structure of the server's blob store in order to work efficiently
with it, and a new Blob/convert method for server-side blob transformations
including image format conversion, archive creation and extraction, compression
and decompression, and delta/patch operations.</t>

<section anchor="conventions-used-in-this-document"><name>Conventions Used in This Document</name>

<t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
appear in all capitals, as shown here.</t>

<?line -18?>

</section>
</section>
<section anchor="addition-to-the-capabilities-object"><name>Addition to the Capabilities Object</name>

<t>The capabilities object is returned as part of the JMAP Session
object; see <xref target="JMAP-CORE"/>, Section 2.</t>

<t>This document defines an additional capability URI.</t>

<section anchor="urnietfparamsjmapblobext"><name>urn:ietf:params:jmap:blobext</name>

<t>The capability <spanx style="verb">urn:ietf:params:jmap:blobext</spanx> being present in the
"accountCapabilities" property of an account represents support for
these extended properties on that account.  This capability depends
on <spanx style="verb">urn:ietf:params:jmap:blob</spanx> (<xref target="JMAP-BLOB"/>); both <bcp14>MUST</bcp14> be present
in the account's "accountCapabilities" and in the request's <spanx style="verb">using</spanx>
array.  If this capability is present in one or more
"accountCapabilities" properties then the server <bcp14>MUST</bcp14> also include
the key in the "capabilities" property.</t>

<t>The value of this property in the JMAP session "capabilities"
property <bcp14>MUST</bcp14> be an empty object.</t>

<t>The value of this property in an account's "accountCapabilities"
property is an object that <bcp14>MUST</bcp14> contain the following information
on server capabilities and permissions for that account:</t>

<t><list style="symbols">
  <t>resumableUploadUrl: "String|null"  <vspace blankLines='1'/>
If present, a <xref target="URI-TEMPLATE"/> which supports <xref target="HTTP-RESUMABLE-UPLOADS"/>.
  This <bcp14>MAY</bcp14> be the same as the uploadUrl, and has the same keys.</t>
  <t>chunkSize: "UnsignedInt|null"  <vspace blankLines='1'/>
A hint indicating the preferred chunk size in octets.  If a
  client uploads blobs with exactly this size except for the
  final chunk, and uses Blob/upload with DataSourceObjects
  referencing these chunks, the server can optimise storage of
  these chunks.  Servers <bcp14>MUST</bcp14> allow other sizes for the
  individual data blocks in Blob/upload though, and will then
  choose whether to store them as an array of blobs still, or
  to combine them.</t>
  <t>supportedImageTypes: "String[]"  <vspace blankLines='1'/>
The media types (<xref target="MEDIA-TYPES"/>) supported for ImageConvertRecipe.</t>
  <t>supportedArchiveTypes: "String[]"  <vspace blankLines='1'/>
The archive MIME types supported for creating archives via
  ArchiveRecipe.  Defined values are "application/zip",
  "application/x-tar", and "application/x-cpio".</t>
  <t>supportedExtractTypes: "String[]"  <vspace blankLines='1'/>
The archive MIME types supported for extracting archives via
  ExtractRecipe.  This <bcp14>MAY</bcp14> include types not in
  <spanx style="verb">supportedArchiveTypes</spanx> (e.g., "application/vnd.ms-tnef").</t>
  <t>supportedCompressTypes: "String[]"  <vspace blankLines='1'/>
The compression MIME types supported for compressing via
  CompressRecipe.  Defined values are "application/gzip",
  "application/x-bzip2", "application/x-xz", and
  "application/zstd".</t>
  <t>supportedDecompressTypes: "String[]"  <vspace blankLines='1'/>
The compression MIME types supported for decompressing via
  DecompressRecipe.  This <bcp14>MAY</bcp14> include types not in
  <spanx style="verb">supportedCompressTypes</spanx>.</t>
  <t>supportedDeltaTypes: "String[]"  <vspace blankLines='1'/>
The delta media types supported for computing deltas via
  DeltaRecipe.  Defined values are
  "application/x-rdiff-delta", "application/x-bsdiff", and
  "text/x-diff" (unified diff).</t>
  <t>supportedPatchTypes: "String[]"  <vspace blankLines='1'/>
The delta media types supported for applying patches via
  PatchRecipe.  This <bcp14>MAY</bcp14> include types not in
  <spanx style="verb">supportedDeltaTypes</spanx>.</t>
  <t>maxConvertSize: "UnsignedInt|null"  <vspace blankLines='1'/>
If supplied, the maximum size in octets of any single input
  blob to a Blob/convert operation.  Requests referencing a blob
  larger than this value <bcp14>MUST</bcp14> be rejected with a "tooLarge"
  SetError.  If null, the server does not advertise a specific
  limit but <bcp14>MAY</bcp14> still reject blobs that are too large.</t>
  <t>maxArchiveEntries: "UnsignedInt|null"  <vspace blankLines='1'/>
If supplied, the maximum number of entries allowed in an
  ArchiveRecipe.  Requests exceeding this limit <bcp14>MUST</bcp14> be rejected
  with a "tooLarge" SetError.  If null, the server does not
  advertise a specific limit but <bcp14>MAY</bcp14> still reject requests with
  too many entries.</t>
  <t>maxImageDimension: "UnsignedInt|null"  <vspace blankLines='1'/>
If supplied, the maximum value accepted for <spanx style="verb">width</spanx> or <spanx style="verb">height</spanx>
  in an ImageConvertRecipe, in pixels.  Requests exceeding this
  limit <bcp14>MUST</bcp14> be rejected with a "tooLarge" SetError.  If null,
  the server does not advertise a specific limit but <bcp14>MAY</bcp14> still
  reject requests with dimensions that are too large.</t>
</list></t>

<section anchor="capability-example"><name>Capability Example</name>

<figure><sourcecode type="json"><![CDATA[
{
  "urn:ietf:params:jmap:blobext": {
    "resumableUploadUrl": null,
    "chunkSize": 5242880,
    "supportedImageTypes": [
      "image/png",
      "image/jpeg",
      "image/gif"
    ],
    "supportedArchiveTypes": [
      "application/zip",
      "application/x-tar"
    ],
    "supportedExtractTypes": [
      "application/zip",
      "application/x-tar",
      "application/vnd.ms-tnef"
    ],
    "supportedCompressTypes": [
      "application/gzip",
      "application/zstd"
    ],
    "supportedDecompressTypes": [
      "application/gzip",
      "application/x-bzip2",
      "application/zstd"
    ],
    "supportedDeltaTypes": [
      "application/x-rdiff-delta",
      "text/x-diff"
    ],
    "supportedPatchTypes": [
      "application/x-rdiff-delta",
      "text/x-diff"
    ],
    "maxConvertSize": 104857600,
    "maxArchiveEntries": 10000,
    "maxImageDimension": 8192
  }
}
]]></sourcecode></figure>

</section>
</section>
<section anchor="additions-to-blobget"><name>Additions to Blob/get</name>

<t>When this capability is present, Blob/get accepts an additional
request argument:</t>

<t><list style="symbols">
  <t>dataSourceProperties: "String[]" (default: ["blobId", "size"])
If supplied, only the properties listed in the array are returned
for each DataSourceObject in the <spanx style="verb">chunks</spanx> array.  If omitted, the
default of <spanx style="verb">["blobId", "size"]</spanx> is used.  Available properties
include <spanx style="verb">blobId</spanx>, <spanx style="verb">size</spanx>, <spanx style="verb">offset</spanx>, <spanx style="verb">length</spanx>, <spanx style="verb">position</spanx>, and
<spanx style="verb">digest:*</spanx> values (e.g. <spanx style="verb">digest:sha-256</spanx>).</t>
</list></t>

<t>Blob/get also returns an additional response property (returned by
default when this capability is included in <spanx style="verb">using</spanx>):</t>

<t><list style="symbols">
  <t>chunks: "DataSourceObject[]"  <vspace blankLines='1'/>
An array of one or more data source objects (as defined in <xref target="JMAP-BLOB"/>,
  Section 4.2).  The blob is reconstructed by concatenating the data
  from each data source object in the listed order.  While it is
  expected that each data source object will reference the entire
  underlying chunk blob, the server <bcp14>MAY</bcp14> return offset and length values
  that select only a portion of a chunk's blob.  The client <bcp14>MUST</bcp14> use
  the offset and length to determine which octets to read from each
  chunk.</t>
</list></t>

<t>Blob/get also accepts the following additional property in
the <spanx style="verb">properties</spanx> array:</t>

<t><list style="symbols">
  <t>imageData: "ImageData|null" (server-set)  <vspace blankLines='1'/>
If the blob is an image or video type and the server can
  extract metadata, this is an ImageData object.  Otherwise
  null.  Only returned if explicitly requested in
  <spanx style="verb">properties</spanx>.  The server <bcp14>MAY</bcp14> need to read the blob content
  to compute this data, so clients <bcp14>SHOULD</bcp14> only request it
  when needed.  Since blobs are immutable, the result for a
  given blobId will never change, and the server <bcp14>SHOULD</bcp14> cache
  it.  <vspace blankLines='1'/>
An ImageData object has the following properties:  <list style="symbols">
      <t>width: "UnsignedInt|null"
Width of the image in pixels, if known.</t>
      <t>height: "UnsignedInt|null"
Height of the image in pixels, if known.</t>
      <t>orientation: "UnsignedInt|null"
EXIF orientation value (1-8), if present.</t>
      <t>date: "UTCDate|null"
Date the image or video was captured (from EXIF
DateTimeOriginal or equivalent), if present.</t>
      <t>gps: "ImageGPS|null"
GPS coordinates from EXIF, if present.  An ImageGPS
object has the following properties:
      <list style="symbols">
          <t>latitude: "Number" — Latitude in decimal degrees.</t>
          <t>longitude: "Number" — Longitude in decimal degrees.</t>
        </list></t>
      <t>duration: "UnsignedInt|null"
Duration in seconds for video content, if known.</t>
      <t>comment: "String|null"
Embedded EXIF comment or description, if present.</t>
    </list></t>
</list></t>

</section>
<section anchor="additions-to-datasourceobject"><name>Additions to DataSourceObject</name>

<t>When this capability is present, the DataSourceObject (as defined in
<xref target="JMAP-BLOB"/>, Section 4.2) is extended with the following additional
properties.  These apply both to the <spanx style="verb">chunks</spanx> returned by Blob/get
and to DataSourceObjects used in Blob/upload.  The <spanx style="verb">offset</spanx> and
<spanx style="verb">length</spanx> properties are already defined in <xref target="JMAP-BLOB"/>; they are
listed here to document their use in the <spanx style="verb">chunks</spanx> response context,
where they describe the range of each chunk's underlying data source
that contributes to the containing blob.</t>

<t><list style="symbols">
  <t>offset: "UnsignedInt|null"
The offset within the data source from which to start copying
octets (see <xref target="JMAP-BLOB"/>).  <bcp14>MUST</bcp14> fit within the data source
(i.e. offset <bcp14>MUST</bcp14> be less than or equal to the data source size).
If null, defaults to 0 (the start of the data source).</t>
  <t>length: "UnsignedInt|null"
The number of octets to copy from the data source (see
<xref target="JMAP-BLOB"/>).  <bcp14>MUST</bcp14> fit within the data source (i.e. offset +
length <bcp14>MUST</bcp14> be less than or equal to the data source size).  If
null, copy from <spanx style="verb">offset</spanx> to the end of the data source.</t>
  <t>size: "UnsignedInt|null"
The full size of the chunk's underlying data source in octets.</t>
  <t>position: "UnsignedInt|null"
The byte offset of the start of this chunk within the outer
(containing) blob.</t>
</list></t>

<t>If a <spanx style="verb">digest:*</spanx> property (e.g. <spanx style="verb">digest:sha</spanx>, <spanx style="verb">digest:sha-256</spanx>) is
included in <spanx style="verb">dataSourceProperties</spanx>, each DataSourceObject in the
<spanx style="verb">chunks</spanx> array will include the corresponding digest value computed
over the octets that this chunk contributes (i.e. after applying
<spanx style="verb">offset</spanx> and <spanx style="verb">length</spanx>).</t>

<t>When a server provides <spanx style="verb">size</spanx>, <spanx style="verb">position</spanx>, or <spanx style="verb">digest:*</spanx> values in
a Blob/get response, it <bcp14>MUST</bcp14> calculate them correctly.  When a
DataSourceObject containing <spanx style="verb">size</spanx>, <spanx style="verb">position</spanx>, or <spanx style="verb">digest:*</spanx> values
is used in Blob/upload, the server <bcp14>MUST</bcp14> reject the object if any
provided value does not match the actual data.</t>

</section>
<section anchor="additions-to-blobupload"><name>Additions to Blob/upload</name>

<t>When this capability is present, each object in the Blob/upload
(<xref target="JMAP-BLOB"/>, Section 4) <spanx style="verb">create</spanx> map <bcp14>MAY</bcp14> include the following
additional property:</t>

<t><list style="symbols">
  <t>noPersist: "Boolean" (default: false)
If true, the resulting blob is ephemeral: it may be referenced via
creation id backreferences within the same JMAP request, but the
server is not required to persist it beyond the lifetime of the
request.  The server <bcp14>MAY</bcp14> omit ephemeral blobs from the <spanx style="verb">created</spanx>
map of the response and from the <spanx style="verb">createdIds</spanx> of the final Response
object if it did not create a referenceable blob.  This allows
servers to optimise pipelines where intermediate blobs are never
needed after the request completes.</t>
</list></t>

<t>When this capability is present, Blob/upload also gains the
following additional request properties:</t>

<t><list style="symbols">
  <t>update: "Id[Object]" (default: null)
A map of blobId to an empty object.  No properties may be
set on a blob; the purpose of update is to "touch" the blob,
refreshing its lifetime on the server.  The response includes
an <spanx style="verb">updated</spanx> map of blobId to an object which <bcp14>MAY</bcp14> contain an
<spanx style="verb">expires</spanx> property with the new expiry time.  If the blobId
does not exist, it <bcp14>MUST</bcp14> be reported in a <spanx style="verb">notUpdated</spanx> map
with a <spanx style="verb">notFound</spanx> SetError.</t>
  <t>destroy: "Id[]" (default: null)
An array of blobIds to destroy.  The server <bcp14>MUST</bcp14> reject
any blob that is still referenced by another object with a
<spanx style="verb">blobHasReference</spanx> SetError.  If the blobId does not exist, the
server <bcp14>MUST</bcp14> return a <spanx style="verb">notFound</spanx> SetError.  Successfully
destroyed blobIds are returned in a <spanx style="verb">destroyed</spanx> array.
Failed destroys are returned in a <spanx style="verb">notDestroyed</spanx> map of
blobId to SetError.</t>
</list></t>

</section>
<section anchor="the-expires-response-property"><name>The "expires" response property</name>

<t>When this capability is present, the following blob creation
responses <bcp14>MAY</bcp14> include an additional property: the
resumableUploadUrl response, the Blob/upload response
(<xref target="JMAP-BLOB"/>, Section 4), and the Blob/convert response defined
below.</t>

<t><list style="symbols">
  <t>expires: "UTCDate|null"
A hint from the server indicating the likely availability of
the blob.  The blob is likely to remain available until this
time, and likely not to be available after it.  This is not a
guarantee in either direction: the server <bcp14>MAY</bcp14> garbage collect
the blob before this time if it is unreferenced, and <bcp14>MAY</bcp14>
retain it longer.  If null or absent, the server provides no
hint about the blob's lifetime.</t>
</list></t>

<t>Clients that need the blob to persist beyond the <spanx style="verb">expires</spanx> time
should reference it from a persistent object (e.g., a FileNode
or an Email) before it expires.</t>

</section>
<section anchor="new-method-blobconvert"><name>New method Blob/convert</name>

<t>Blob/convert is defined under the <spanx style="verb">urn:ietf:params:jmap:blobext</spanx>
capability and requires that capability in the request's <spanx style="verb">using</spanx>
array.</t>

<t>Blob/convert performs server-side transformations on blobs.  Like
Blob/upload (<xref target="JMAP-BLOB"/>, Section 4), it takes an <spanx style="verb">accountId</spanx> and a
<spanx style="verb">create</spanx> argument that maps creation ids to conversion request objects.</t>

<t>Each conversion request object <bcp14>MAY</bcp14> also include the following property:</t>

<t><list style="symbols">
  <t>noPersist: "Boolean" (default: false)
If true, the resulting blob is ephemeral: it may be referenced via
creation id backreferences within the same JMAP request, but the
server is not required to persist it beyond the lifetime of the
request.  The server <bcp14>MAY</bcp14> omit ephemeral blobs from the <spanx style="verb">created</spanx>
map of the response and from the <spanx style="verb">createdIds</spanx> of the final Response
object if it did not create a referenceable blob.</t>
</list></t>

<t>Each conversion request object <bcp14>MUST</bcp14> contain exactly one of the
following properties, which determines the type of conversion:</t>

<t><list style="symbols">
  <t>imageConvert: ImageConvertRecipe</t>
  <t>archive: ArchiveRecipe</t>
  <t>extract: ExtractRecipe</t>
  <t>compress: CompressRecipe</t>
  <t>decompress: DecompressRecipe</t>
  <t>delta: DeltaRecipe</t>
  <t>patch: PatchRecipe</t>
</list></t>

<t>The response has the same structure as Blob/upload (<xref target="JMAP-BLOB"/>,
Section 4): a <spanx style="verb">created</spanx> map of creation id to an object containing
<spanx style="verb">id</spanx>, <spanx style="verb">type</spanx>, and <spanx style="verb">size</spanx> for each successful conversion, and a
<spanx style="verb">notCreated</spanx> map of creation id to a SetError object for each failed
conversion.  The <spanx style="verb">id</spanx> is the blobId of the created blob.  The server
<bcp14>MAY</bcp14> also return an <spanx style="verb">expires</spanx> property (see "The expires response
property" above).  Creation id backreferences (using the <spanx style="verb">#</spanx> prefix)
resolve to this <spanx style="verb">id</spanx> and may be used in subsequent conversions within
the same Blob/convert call or in later method calls within the same
JMAP request.</t>

<t>A server <bcp14>MAY</bcp14> return a blobId for a conversion result without
immediately generating the output data.  In this case the server
<bcp14>MUST</bcp14> generate the data when the blob is later accessed (e.g., via
a download request or as input to another operation).  This allows
the server to respond quickly to Blob/convert requests while
deferring expensive work such as image resizing or archive
creation.  The returned blobId <bcp14>MUST</bcp14> be usable in all contexts
where a regular blobId is accepted.  If the deferred generation
later fails (e.g., the source blob has expired), the server
<bcp14>SHOULD</bcp14> return an appropriate HTTP error when the blob is
downloaded.  If the exact size of the output is not yet known, the
server <bcp14>MUST</bcp14> omit the <spanx style="verb">size</spanx> property from the response for that
creation.  If a client later requests the <spanx style="verb">size</spanx> property via
Blob/get for a deferred blob, the server <bcp14>MUST</bcp14> generate the blob
at that point and return the actual size.</t>

<t>The server <bcp14>MUST</bcp14> resolve the order of dependencies between entries
in the <spanx style="verb">create</spanx> map and process them in an order such that all
backreferences are satisfied.  If a dependency cycle is detected,
all members of the cycle <bcp14>MUST</bcp14> be rejected with an
"invalidProperties" error.</t>

<section anchor="imageconvertrecipe"><name>ImageConvertRecipe</name>

<t>An ImageConvertRecipe converts an image blob to a different format
or size.  It is an object with the following properties:</t>

<t><list style="symbols">
  <t>blobId: "BlobId"
The blobId of the source image.</t>
  <t>type: "String"
Media type (<xref target="MEDIA-TYPES"/>) of the image to create (e.g.
"image/png").  <bcp14>MUST</bcp14> be one of the values in the server's
<spanx style="verb">supportedImageTypes</spanx> capability.</t>
  <t>width: "UnsignedInt|null"
Maximum width in pixels of the image to create.  If null, the
server preserves the source width (or scales proportionally if
only <spanx style="verb">height</spanx> is given).</t>
  <t>height: "UnsignedInt|null"
Maximum height in pixels of the image to create.  If null, the
server preserves the source height (or scales proportionally if
only <spanx style="verb">width</spanx> is given).</t>
  <t>ignoreAspect: "Boolean|null"
If true, resize to exactly the given width and height, even if
the aspect ratio is changed.  If null or false, the image is
scaled to fit within the given dimensions while preserving the
aspect ratio.</t>
  <t>quality: "UnsignedInt|null"
Compression quality for lossy formats, as a value from 1 (lowest
quality, smallest file) to 100 (highest quality, largest file).
Only meaningful for formats that support lossy compression such as
image/jpeg and image/webp.  If null, the server selects a sensible
default.</t>
  <t>colorSpace: "String|null"
The color space for the output image.  Defined values are "sRGB"
and "grayscale".  If null, the server preserves the source image's
color space where possible.</t>
  <t>background: "String|null"
A fill color to use when the source image has transparency but the
target format does not support it (e.g. converting PNG to JPEG).
The value is a CSS-style hex color string (e.g. "#ffffff" for white).
If null, the server selects a sensible default (typically white).</t>
  <t>stripMetadata: "Boolean|null"
If true, strip image metadata such as EXIF, XMP, and IPTC data from
the output.  If null or false, the server preserves metadata where
the target format supports it.</t>
  <t>autoOrient: "Boolean|null"
If true, automatically rotate and flip the image according to its
EXIF orientation tag, then reset the tag.  If null or false, the
image data is not reoriented.</t>
</list></t>

<t>Errors:</t>

<t><list style="symbols">
  <t>"notFound" — the referenced blobId does not exist.</t>
  <t>"invalidProperties" — the type is not in <spanx style="verb">supportedImageTypes</spanx>, or
the source blob is not a supported image format.</t>
  <t>"tooLarge" — the source blob exceeds <spanx style="verb">maxConvertSize</spanx>, or the
requested dimensions exceed <spanx style="verb">maxImageDimension</spanx>.</t>
</list></t>

</section>
<section anchor="archiverecipe"><name>ArchiveRecipe</name>

<t>An ArchiveRecipe creates an archive blob from a list of entries.
It is an object with the following properties:</t>

<t><list style="symbols">
  <t>type: "String"
The MIME type of the archive to create.  <bcp14>MUST</bcp14> be one of the values
in the server's <spanx style="verb">supportedArchiveTypes</spanx> capability.  Defined values
are "application/zip", "application/x-tar", and "application/x-cpio".</t>
  <t>entries: "ArchiveEntry[]"
An array of ArchiveEntry objects describing the contents of the
archive.</t>
</list></t>

<t>Errors:</t>

<t><list style="symbols">
  <t>"notFound" — a referenced entry blobId does not exist.</t>
  <t>"invalidProperties" — the type is not in <spanx style="verb">supportedArchiveTypes</spanx>;
an entry has an unsupported entryType for the archive format; or
a required field (e.g. linkTarget for symlink entries) is missing.</t>
  <t>"tooLarge" — the number of entries exceeds <spanx style="verb">maxArchiveEntries</spanx>,
or a referenced blob exceeds <spanx style="verb">maxConvertSize</spanx>.</t>
</list></t>

<section anchor="archiveentry"><name>ArchiveEntry</name>

<t>An ArchiveEntry describes a single entry in an archive.  It is used
both as input (in ArchiveRecipe) and as output (in ExtractRecipe
results).  It is an object with the following properties:</t>

<t><list style="symbols">
  <t>name: "String"
The path of the entry within the archive.  Directory entries <bcp14>MUST</bcp14>
have a name ending with "/".</t>
  <t>blobId: "BlobId|null"
The blobId of the content for this entry.  <bcp14>MUST</bcp14> be non-null for
file entries.  <bcp14>MUST</bcp14> be null or absent for directory, symlink,
hardlink, fifo, and device entries.  Violating these constraints
is an "invalidProperties" error.</t>
  <t>entryType: "String|null"
The type of the entry.  If null, defaults to "file".  Defined values
are "file" (a regular file, the default), "directory", "symlink"
(a symbolic link), "hardlink" (a hard link), "fifo" (a named pipe),
"blockDevice" (a block device node), and "charDevice" (a character
device node).  The server <bcp14>MUST</bcp14> reject entries with unsupported
types for the chosen archive format with an "invalidProperties"
error.</t>
  <t>modified: "UTCDate|null"
The modification time of the entry as an RFC 3339 timestamp.
If null, defaults to the current server time.</t>
  <t>linkTarget: "String|null"
The target path for symlink and hardlink entries.  <bcp14>MUST</bcp14> be non-null
when entryType is "symlink" or "hardlink".  <bcp14>MUST</bcp14> be null for all
other entry types.</t>
  <t>mode: "String|null"
Unix file permissions as an octal string (e.g. "0755", "0644").
If null, the server chooses a reasonable default.  This is
represented as a string rather than an integer to avoid ambiguity
between octal and decimal interpretation.</t>
  <t>uid: "UnsignedInt|null"
The numeric user ID of the entry owner.</t>
  <t>gid: "UnsignedInt|null"
The numeric group ID of the entry owner.</t>
  <t>ownerName: "String|null"
The user name of the entry owner.</t>
  <t>groupName: "String|null"
The group name of the entry owner.</t>
  <t>devMajor: "UnsignedInt|null"
The major device number for "blockDevice" and "charDevice" entries.</t>
  <t>devMinor: "UnsignedInt|null"
The minor device number for "blockDevice" and "charDevice" entries.</t>
  <t>comment: "String|null"
A comment string for this entry.</t>
  <t>compressionMethod: "String|null"
The per-entry compression method.  Defined values are "store" (no
compression) and "deflate".  If null, the server chooses a
reasonable default.</t>
</list></t>

</section>
<section anchor="considerations-for-applicationzip"><name>Considerations for application/zip</name>

<t>The zip format only supports "file" and "directory" entry types.
The <spanx style="verb">comment</spanx> and <spanx style="verb">compressionMethod</spanx> properties are only meaningful
for zip archives.  The <spanx style="verb">mode</spanx>, <spanx style="verb">uid</spanx>, <spanx style="verb">gid</spanx>, <spanx style="verb">ownerName</spanx>,
<spanx style="verb">groupName</spanx>, <spanx style="verb">devMajor</spanx>, and <spanx style="verb">devMinor</spanx> properties are ignored.</t>

</section>
<section anchor="considerations-for-applicationx-tar"><name>Considerations for application/x-tar</name>

<t>The tar format supports all entry types.  The <spanx style="verb">mode</spanx>, <spanx style="verb">uid</spanx>, <spanx style="verb">gid</spanx>,
<spanx style="verb">ownerName</spanx>, <spanx style="verb">groupName</spanx>, <spanx style="verb">devMajor</spanx>, and <spanx style="verb">devMinor</spanx> properties are
meaningful for tar archives.  The <spanx style="verb">comment</spanx> and <spanx style="verb">compressionMethod</spanx>
properties are ignored.</t>

<t>Tar archives are not inherently compressed.  To create a compressed
tar archive (e.g. a .tar.gz file), first create the tar archive
using ArchiveRecipe, then compress the result using CompressRecipe.
The following example creates a .tar.gz containing three files in a
single Blob/convert call, using a backreference from the archive
creation to the compression step:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "create": {
    "t1": {
      "archive": {
        "type": "application/x-tar",
        "entries": [
          {
            "name": "site/index.html",
            "blobId": "Baaaa",
            "modified": "2026-03-01T12:00:00Z",
            "mode": "0644"
          },
          {
            "name": "site/logo.png",
            "blobId": "Bbbbb",
            "modified": "2026-02-15T09:30:00Z",
            "mode": "0644"
          },
          {
            "name": "site/style.css",
            "blobId": "Bcccc",
            "modified": "2026-03-01T12:00:00Z",
            "mode": "0644"
          }
        ]
      }
    },
    "t2": {
      "compress": {
        "blobId": "#t1",
        "type": "application/gzip"
      }
    }
  }
}, "0"]]
]]></sourcecode></figure>

</section>
<section anchor="considerations-for-applicationx-cpio"><name>Considerations for application/x-cpio</name>

<t>The cpio format supports all entry types except that <spanx style="verb">ownerName</spanx> and
<spanx style="verb">groupName</spanx> are not supported.  The <spanx style="verb">comment</spanx> and
<spanx style="verb">compressionMethod</spanx> properties are ignored.</t>

</section>
</section>
<section anchor="extractrecipe"><name>ExtractRecipe</name>

<t>An ExtractRecipe extracts the entry listing from an existing archive
blob.  It is an object with the following properties:</t>

<t><list style="symbols">
  <t>blobId: "BlobId"
The blobId of the archive to extract.</t>
  <t>type: "String|null"
The MIME type of the archive format.  If null, the server <bcp14>SHOULD</bcp14>
attempt to auto-detect the format from the blob content (e.g. by
inspecting magic bytes).  If auto-detection fails, the server <bcp14>MUST</bcp14>
return an "unknownFormat" error.</t>
</list></t>

<t>In addition to the standard creation response properties, a
successful ExtractRecipe result includes:</t>

<t><list style="symbols">
  <t>entries: "ArchiveEntry[]"
An array of ArchiveEntry objects describing the contents of the
archive.  Each file entry will have a blobId that can be used to
access the content of that entry.</t>
</list></t>

<t>Errors:</t>

<t><list style="symbols">
  <t>"notFound" — the referenced blobId does not exist.</t>
  <t>"unknownFormat" — the server could not determine or does not support
the archive format.</t>
  <t>"invalidProperties" — the type is not in <spanx style="verb">supportedExtractTypes</spanx>.</t>
  <t>"tooLarge" — the source blob exceeds <spanx style="verb">maxConvertSize</spanx>.</t>
</list></t>

</section>
<section anchor="compressrecipe"><name>CompressRecipe</name>

<t>A CompressRecipe compresses a blob using a specified compression
algorithm.  It is an object with the following properties:</t>

<t><list style="symbols">
  <t>blobId: "BlobId"
The blobId of the data to compress.</t>
  <t>type: "String"
The MIME type of the compression format to use.  <bcp14>MUST</bcp14> be one of the
values in the server's <spanx style="verb">supportedCompressTypes</spanx> capability.
Defined values are "application/gzip", "application/x-bzip2",
"application/x-xz", and "application/zstd".</t>
  <t>level: "UnsignedInt|null"
The compression level, where higher values produce smaller output
at the cost of more CPU time.  If null, the server uses the
format's default level.  The valid range depends on the format;
if the requested level is outside the valid range, the server
<bcp14>SHOULD</bcp14> use the nearest valid value.</t>
  <t>checksum: "Boolean|null"
If true, include an integrity checksum in the compressed output.
If null, the server uses the format's default behaviour.</t>
</list></t>

<t>Errors:</t>

<t><list style="symbols">
  <t>"notFound" — the referenced blobId does not exist.</t>
  <t>"invalidProperties" — the type is not in <spanx style="verb">supportedCompressTypes</spanx>.</t>
  <t>"tooLarge" — the source blob exceeds <spanx style="verb">maxConvertSize</spanx>.</t>
</list></t>

<section anchor="considerations-for-applicationgzip"><name>Considerations for application/gzip</name>

<t>Compression level ranges from 1 (fastest) to 9 (best compression).
The default is typically 6.  Gzip always includes a CRC-32 checksum;
the <spanx style="verb">checksum</spanx> property is ignored.</t>

</section>
<section anchor="considerations-for-applicationx-bzip2"><name>Considerations for application/x-bzip2</name>

<t>Compression level ranges from 1 (fastest, 100k block size) to 9
(best compression, 900k block size).  The default is typically 9.
Bzip2 always includes a CRC-32 checksum; the <spanx style="verb">checksum</spanx> property is
ignored.</t>

</section>
<section anchor="considerations-for-applicationx-xz"><name>Considerations for application/x-xz</name>

<t>Compression level ranges from 0 (fastest) to 9 (best compression).
The default is typically 6.  Xz always includes an integrity check;
if <spanx style="verb">checksum</spanx> is true the server <bcp14>SHOULD</bcp14> use SHA-256, otherwise CRC-64
is used by default.</t>

</section>
<section anchor="considerations-for-applicationzstd"><name>Considerations for application/zstd</name>

<t>Compression level ranges from 1 (fastest) to 22 (best compression).
The default is typically 3.  If <spanx style="verb">checksum</spanx> is true, an xxHash-64
checksum is included in the frame; the default is false.</t>

</section>
</section>
<section anchor="decompressrecipe"><name>DecompressRecipe</name>

<t>An DecompressRecipe decompresses a compressed blob.  It is an object
with the following properties:</t>

<t><list style="symbols">
  <t>blobId: "BlobId"
The blobId of the compressed data to decompress.</t>
  <t>type: "String|null"
The MIME type of the compression format.  If null, the server <bcp14>SHOULD</bcp14>
attempt to auto-detect the format from magic bytes.  If auto-detection
fails, the server <bcp14>MUST</bcp14> return an "unknownFormat" error.</t>
</list></t>

<t>Errors:</t>

<t><list style="symbols">
  <t>"notFound" — the referenced blobId does not exist.</t>
  <t>"unknownFormat" — the server could not determine or does not support
the compression format.</t>
  <t>"invalidProperties" — the type is not in <spanx style="verb">supportedDecompressTypes</spanx>.</t>
  <t>"tooLarge" — the source blob exceeds <spanx style="verb">maxConvertSize</spanx>.</t>
</list></t>

</section>
<section anchor="deltarecipe"><name>DeltaRecipe</name>

<t>A DeltaRecipe computes a delta between two blobs.  It is an object
with the following properties:</t>

<t><list style="symbols">
  <t>blobId: "BlobId"
The blobId of the original (base) blob.</t>
  <t>newBlobId: "BlobId"
The blobId of the new blob to compare against.</t>
  <t>type: "String"
The media type of the delta format to produce.  <bcp14>MUST</bcp14> be one of the
values in the server's <spanx style="verb">supportedDeltaTypes</spanx> capability.</t>
</list></t>

<t>The result blob is the computed delta, which can be applied to the
base blob using PatchRecipe to reconstruct the new blob.</t>

<t>Errors:</t>

<t><list style="symbols">
  <t>"notFound" — a referenced blobId does not exist.</t>
  <t>"invalidProperties" — the type is not in <spanx style="verb">supportedDeltaTypes</spanx>.</t>
  <t>"tooLarge" — a referenced blob exceeds <spanx style="verb">maxConvertSize</spanx>.</t>
</list></t>

<section anchor="considerations-for-applicationx-rdiff-delta"><name>Considerations for application/x-rdiff-delta</name>

<t>The server computes an rdiff signature of the base blob and then
generates a delta against the new blob.  The resulting delta blob
can only be applied to the exact base blob used to generate it.</t>

</section>
<section anchor="considerations-for-applicationx-bsdiff"><name>Considerations for application/x-bsdiff</name>

<t>The server produces a bsdiff-format patch.  Both blobs must fit in
memory; servers <bcp14>MAY</bcp14> reject very large blobs with a "tooLarge" error.</t>

</section>
<section anchor="considerations-for-textx-diff"><name>Considerations for text/x-diff</name>

<t>The server produces a unified diff.  Both blobs are interpreted as
text.  If either blob contains content that cannot be interpreted as
text, the server <bcp14>MUST</bcp14> return an "unknownFormat" error.</t>

</section>
</section>
<section anchor="patchrecipe"><name>PatchRecipe</name>

<t>A PatchRecipe applies a delta to a base blob to produce a new blob.
It is an object with the following properties:</t>

<t><list style="symbols">
  <t>blobId: "BlobId"
The blobId of the base blob to patch.</t>
  <t>deltaBlobId: "BlobId"
The blobId of the delta to apply.</t>
  <t>deltaType: "String"
The media type of the delta format.  <bcp14>MUST</bcp14> be one of the values in
the server's <spanx style="verb">supportedPatchTypes</spanx> capability.</t>
</list></t>

<t>The result blob is the patched output.</t>

<t>Errors:</t>

<t><list style="symbols">
  <t>"notFound" — a referenced blobId does not exist.</t>
  <t>"invalidProperties" — the deltaType is not in <spanx style="verb">supportedPatchTypes</spanx>.</t>
  <t>"unknownFormat" — the delta blob is not valid for the specified
format (e.g., corrupt or malformed delta data).</t>
  <t>"tooLarge" — a referenced blob exceeds <spanx style="verb">maxConvertSize</spanx>.</t>
</list></t>

</section>
</section>
</section>
<section anchor="examples"><name>Examples</name>

<section anchor="querying-blob-chunks"><name>Querying Blob Chunks</name>

<t>This example fetches a blob's chunk structure with offsets, sizes,
and SHA-256 digests:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/get", {
  "accountId": "abc",
  "ids": ["B1a2b3c"],
  "dataSourceProperties": [
    "blobId", "size", "offset", "length", "position",
    "digest:sha-256"
  ]
}, "0"]]
]]></sourcecode></figure>

<t>The response shows the blob is stored as two chunks:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/get", {
  "accountId": "abc",
  "list": [{
    "id": "B1a2b3c",
    "size": 10485760,
    "chunks": [
      {
        "blobId": "Bchunk1",
        "size": 5242880,
        "offset": 0,
        "length": 5242880,
        "position": 0,
        "digest:sha-256": "a1b2c3..."
      },
      {
        "blobId": "Bchunk2",
        "size": 5242880,
        "offset": 0,
        "length": 5242880,
        "position": 5242880,
        "digest:sha-256": "d4e5f6..."
      }
    ]
  }],
  "notFound": []
}, "0"]]
]]></sourcecode></figure>

<t>The <spanx style="verb">position</spanx> values show where each chunk fits in the assembled
blob, and the <spanx style="verb">digest:sha-256</spanx> values can be used to verify chunk
integrity.</t>

</section>
<section anchor="creating-a-zip-archive"><name>Creating a Zip Archive</name>

<t>This example creates a zip file containing an HTML document, a CSS
file, and a JPEG image:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "create": {
    "z1": {
      "archive": {
        "type": "application/zip",
        "entries": [
          {
            "name": "site/index.html",
            "blobId": "Baaaa"
          },
          {
            "name": "site/style.css",
            "blobId": "Bbbbb"
          },
          {
            "name": "site/photo.jpg",
            "blobId": "Bcccc"
          }
        ]
      }
    }
  }
}, "0"]]
]]></sourcecode></figure>

<t>The response includes the blobId, type, and size of the created
archive:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "created": {
    "z1": {
      "id": "B9f2a4e",
      "type": "application/zip",
      "size": 104857
    }
  },
  "notCreated": {}
}, "0"]]
]]></sourcecode></figure>

</section>
<section anchor="creating-a-compressed-tar-archive"><name>Creating a Compressed Tar Archive</name>

<t>This example creates a .tar.gz file from the same three files.
The intermediate tar blob uses <spanx style="verb">noPersist</spanx> since only the final
compressed result is needed:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "create": {
    "t1": {
      "noPersist": true,
      "archive": {
        "type": "application/x-tar",
        "entries": [
          {
            "name": "site/index.html",
            "blobId": "Baaaa",
            "modified": "2026-03-01T12:00:00Z",
            "mode": "0644"
          },
          {
            "name": "site/style.css",
            "blobId": "Bbbbb",
            "modified": "2026-03-01T12:00:00Z",
            "mode": "0644"
          },
          {
            "name": "site/photo.jpg",
            "blobId": "Bcccc",
            "modified": "2026-02-15T09:30:00Z",
            "mode": "0644"
          }
        ]
      }
    },
    "t2": {
      "compress": {
        "blobId": "#t1",
        "type": "application/gzip"
      }
    }
  }
}, "0"]]
]]></sourcecode></figure>

<t>Because "t1" was created with <spanx style="verb">noPersist</spanx>, the server may omit it
from the response:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "created": {
    "t2": {
      "id": "Bd81c7f",
      "type": "application/gzip",
      "size": 98304
    }
  },
  "notCreated": {}
}, "0"]]
]]></sourcecode></figure>

</section>
<section anchor="extracting-a-compressed-tar-archive"><name>Extracting a Compressed Tar Archive</name>

<t>This example decompresses and extracts a .tar.gz file to discover
its contents.  The intermediate decompressed tar blob uses
<spanx style="verb">noPersist</spanx>:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "create": {
    "u1": {
      "noPersist": true,
      "decompress": {
        "blobId": "Bd81c7f",
        "type": "application/gzip"
      }
    },
    "u2": {
      "extract": {
        "blobId": "#u1",
        "type": "application/x-tar"
      }
    }
  }
}, "0"]]
]]></sourcecode></figure>

<t>The response for the ExtractRecipe includes the standard creation
response properties plus an <spanx style="verb">entries</spanx> array listing the archive
contents, with a blobId for each file entry:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "created": {
    "u2": {
      "id": "Be3a901",
      "type": "application/x-tar",
      "size": 102400,
      "entries": [
        {
          "name": "site/index.html",
          "blobId": "Bdd001",
          "entryType": "file",
          "modified": "2026-03-01T12:00:00Z",
          "mode": "0644"
        },
        {
          "name": "site/style.css",
          "blobId": "Bdd002",
          "entryType": "file",
          "modified": "2026-03-01T12:00:00Z",
          "mode": "0644"
        },
        {
          "name": "site/photo.jpg",
          "blobId": "Bdd003",
          "entryType": "file",
          "modified": "2026-02-15T09:30:00Z",
          "mode": "0644"
        }
      ]
    }
  },
  "notCreated": {}
}, "0"]]
]]></sourcecode></figure>

<t>The returned blobIds ("Bdd001", "Bdd002", "Bdd003") can be used
to download individual files or as inputs to further Blob/convert
operations.</t>

</section>
<section anchor="computing-and-applying-a-delta"><name>Computing and Applying a Delta</name>

<t>This example computes a unified diff between two versions of a
text file:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "create": {
    "d1": {
      "delta": {
        "blobId": "BoldVersion",
        "newBlobId": "BnewVersion",
        "type": "text/x-diff"
      }
    }
  }
}, "0"]]
]]></sourcecode></figure>

<t>The response contains the delta blob:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "created": {
    "d1": {
      "id": "Bdelta789",
      "type": "text/x-diff",
      "size": 1234
    }
  },
  "notCreated": {}
}, "0"]]
]]></sourcecode></figure>

<t>The delta can later be applied to the original blob to reconstruct
the new version:</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "create": {
    "p1": {
      "patch": {
        "blobId": "BoldVersion",
        "deltaBlobId": "Bdelta789",
        "deltaType": "text/x-diff"
      }
    }
  }
}, "0"]]
]]></sourcecode></figure>

<t>The result is a blob identical to "BnewVersion":</t>

<figure><sourcecode type="json"><![CDATA[
[["Blob/convert", {
  "accountId": "abc",
  "created": {
    "p1": {
      "id": "Breconstructed",
      "type": "application/octet-stream",
      "size": 48576
    }
  },
  "notCreated": {}
}, "0"]]
]]></sourcecode></figure>

</section>
</section>
<section anchor="security-considerations"><name>Security Considerations</name>

<t>All security considerations from <xref target="JMAP-CORE"/> and <xref target="JMAP-BLOB"/> apply
to this document.</t>

<section anchor="resource-consumption"><name>Resource Consumption</name>

<t>Several operations defined in this document can consume significant
server resources.</t>

<t>Archive and compression operations may require substantial CPU and
memory.  Servers <bcp14>SHOULD</bcp14> impose reasonable limits on archive size,
number of entries, nesting depth of archives within archives,
compression ratios (to mitigate zip bomb attacks), and total
processing time.  Servers <bcp14>SHOULD</bcp14> reject requests that would exceed
these limits with a "tooLarge" or "serverFail" error as appropriate.</t>

<t>Image conversion can also consume significant resources, especially
for very large images or high output dimensions.</t>

<t>Delta and patch operations can also be resource-intensive,
particularly for large blobs.  Servers <bcp14>SHOULD</bcp14> impose limits on the
size of blobs that can be used as inputs to these operations.</t>

<t>Servers <bcp14>SHOULD</bcp14> advertise their limits via the <spanx style="verb">maxConvertSize</spanx>,
<spanx style="verb">maxArchiveEntries</spanx>, and <spanx style="verb">maxImageDimension</spanx> capability properties
so that clients can avoid making requests that will be rejected.
Even when these properties are not advertised, servers <bcp14>SHOULD</bcp14> set
sensible internal limits and reject requests that exceed them.</t>

</section>
<section anchor="archive-path-traversal"><name>Archive Path Traversal</name>

<t>Archive formats allow entry names containing path separators and
relative path components such as "../".  Malicious archives may use
names like "../../etc/passwd" to attempt directory traversal.  While
ExtractRecipe only returns entry metadata and blob references
(not files on the server filesystem), clients that extract archive
contents to a filesystem <bcp14>MUST</bcp14> validate entry names and reject or
sanitize paths containing ".." components or absolute paths.
Servers <bcp14>SHOULD</bcp14> reject ArchiveRecipe requests containing entry names
with ".." path components.</t>

</section>
<section anchor="content-smuggling"><name>Content Smuggling</name>

<t>The ability to split content into multiple blobs, recombine them via
Blob/upload, and apply delta patches may be used to bypass security
scanners that inspect blob content.  Servers that perform content
scanning <bcp14>SHOULD</bcp14> scan the output of Blob/convert operations as well
as the inputs.</t>

</section>
</section>
<section anchor="iana-considerations"><name>IANA Considerations</name>

<section anchor="jmap-capability-registration-for-urnietfparamsjmapblobext"><name>JMAP Capability Registration for urn:ietf:params:jmap:blobext</name>

<t>IANA is requested to register the "Blob Extended" Capability as follows:</t>

<t>Capability Name: urn:ietf:params:jmap:blobext</t>

<t>Intended use: common</t>

<t>Change Controller: IETF</t>

<t>Specification document: this document</t>

<t>Security and privacy considerations: this document, Security Considerations</t>

</section>
<section anchor="jmap-error-code-registrations"><name>JMAP Error Code Registrations</name>

<t>IANA is requested to register the following entries in the "JMAP
Error Codes" registry:</t>

<section anchor="unknownformat"><name>unknownFormat</name>

<t>JMAP Error Code: unknownFormat</t>

<t>Intended use: common</t>

<t>Change Controller: IETF</t>

<t>Description: The server could not determine the format of the blob,
or the detected format is not supported.  This error is returned
when auto-detection of archive or compression format fails, or when
the blob content does not match the specified format.</t>

<t>Reference: this document</t>

</section>
<section anchor="blobhasreference"><name>blobHasReference</name>

<t>JMAP Error Code: blobHasReference</t>

<t>Intended use: common</t>

<t>Change Controller: IETF</t>

<t>Description: The blob cannot be destroyed because it is still
referenced by one or more objects.  The client can use Blob/lookup
(<xref target="JMAP-BLOB"/>, Section 5) to discover which objects reference
the blob.</t>

<t>Reference: this document</t>

</section>
</section>
</section>
<section anchor="changes"><name>Changes</name>

<t>EDITOR: please remove this section before publication.</t>

<t>The source of this document exists on github at: https://github.com/brong/draft-gondwana-jmap-blobext/</t>

<t><strong>draft-gondwana-jmap-blobext-05</strong></t>

<t><list style="symbols">
  <t>Added imageData property to Blob/get for image/video metadata
extraction (dimensions, orientation, date, GPS, duration).</t>
  <t>Renamed UnArchiveRecipe to ExtractRecipe and UnCompressRecipe
to DecompressRecipe.</t>
  <t>Split capability lists into separate create/extract pairs:
supportedArchiveTypes/supportedExtractTypes,
supportedCompressTypes/supportedDecompressTypes,
supportedDeltaTypes/supportedPatchTypes.</t>
</list></t>

<t><strong>draft-gondwana-jmap-blobext-04</strong></t>

<t><list style="symbols">
  <t>Moved noPersist from top-level Blob/upload property to per-item
in the create map, for consistency with Blob/convert.</t>
</list></t>

<t><strong>draft-gondwana-jmap-blobext-03</strong></t>

<t><list style="symbols">
  <t>Added update (touch) and destroy operations to Blob/upload.</t>
</list></t>

<t><strong>draft-gondwana-jmap-blobext-02</strong></t>

<t><list style="symbols">
  <t>Replaced RdiffRecipe with generic DeltaRecipe and PatchRecipe using
media types (rdiff, bsdiff, unified diff).</t>
  <t>Replaced supportsRdiff with supportedPatchTypes capability.</t>
  <t>Clarified chunkSize as a hint, not a definitive statement.</t>
  <t>Added lazy generation text for Blob/convert (deferred output).</t>
  <t>Defined "expires" response property for blob creation responses.</t>
</list></t>

<t><strong>draft-gondwana-jmap-blobext-01</strong></t>

<t><list style="symbols">
  <t>Added Blob/convert method with recipes for image conversion, archiving,
compression, and rdiff.</t>
  <t>Added noPersist option to Blob/upload and Blob/convert for ephemeral
intermediate blobs in pipelines.</t>
  <t>Removed rdiffSignature and rdiffPatch from Blob/get and DataSourceObject.</t>
  <t>Fleshed out capability object with supported type lists.</t>
  <t>Added capability and method examples.</t>
  <t>Expanded Security Considerations.</t>
  <t>Updated IANA registrations with error codes and corrected capability URI.</t>
  <t>Added limit capability properties: maxConvertSize, maxArchiveEntries,
maxImageDimension, and supportsRdiff.</t>
  <t>Added dependency resolution requirement for Blob/convert create map
with cycle detection.</t>
  <t>Now updates both RFC 8620 and RFC 9404.</t>
</list></t>

<t><strong>draft-gondwana-jmap-blobext-00</strong></t>

<t><list style="symbols">
  <t>initial proposal</t>
</list></t>

</section>
<section anchor="acknowledgements"><name>Acknowledgements</name>

<t>TODO</t>

<t>{backmatter}</t>

</section>


  </middle>

  <back>



    <references title='Normative References' anchor="sec-normative-references">



<reference anchor="URI-TEMPLATE">
  <front>
    <title>URI Template</title>
    <author fullname="J. Gregorio" initials="J." surname="Gregorio"/>
    <author fullname="R. Fielding" initials="R." surname="Fielding"/>
    <author fullname="M. Hadley" initials="M." surname="Hadley"/>
    <author fullname="M. Nottingham" initials="M." surname="Nottingham"/>
    <author fullname="D. Orchard" initials="D." surname="Orchard"/>
    <date month="March" year="2012"/>
    <abstract>
      <t>A URI Template is a compact sequence of characters for describing a range of Uniform Resource Identifiers through variable expansion. This specification defines the URI Template syntax and the process for expanding a URI Template into a URI reference, along with guidelines for the use of URI Templates on the Internet. [STANDARDS-TRACK]</t>
    </abstract>
  </front>
  <seriesInfo name="RFC" value="6570"/>
  <seriesInfo name="DOI" value="10.17487/RFC6570"/>
</reference>

<reference anchor="JMAP-CORE">
  <front>
    <title>The JSON Meta Application Protocol (JMAP)</title>
    <author fullname="N. Jenkins" initials="N." surname="Jenkins"/>
    <author fullname="C. Newman" initials="C." surname="Newman"/>
    <date month="July" year="2019"/>
    <abstract>
      <t>This document specifies a protocol for clients to efficiently query, fetch, and modify JSON-based data objects, with support for push notification of changes and fast resynchronisation and for out-of- band binary data upload/download.</t>
    </abstract>
  </front>
  <seriesInfo name="RFC" value="8620"/>
  <seriesInfo name="DOI" value="10.17487/RFC8620"/>
</reference>

<reference anchor="JMAP-BLOB">
  <front>
    <title>JSON Meta Application Protocol (JMAP) Blob Management Extension</title>
    <author fullname="B. Gondwana" initials="B." role="editor" surname="Gondwana"/>
    <date month="August" year="2023"/>
    <abstract>
      <t>The JSON Meta Application Protocol (JMAP) base protocol (RFC 8620) provides the ability to upload and download arbitrary binary data via HTTP POST and GET on a defined endpoint. This binary data is called a "blob".</t>
      <t>This extension adds additional ways to create and access blobs by making inline method calls within a standard JMAP request.</t>
      <t>This extension also adds a reverse lookup mechanism to discover where blobs are referenced within other data types.</t>
    </abstract>
  </front>
  <seriesInfo name="RFC" value="9404"/>
  <seriesInfo name="DOI" value="10.17487/RFC9404"/>
</reference>

<reference anchor="MEDIA-TYPES">
  <front>
    <title>Media Type Specifications and Registration Procedures</title>
    <author fullname="N. Freed" initials="N." surname="Freed"/>
    <author fullname="J. Klensin" initials="J." surname="Klensin"/>
    <author fullname="T. Hansen" initials="T." surname="Hansen"/>
    <date month="January" year="2013"/>
    <abstract>
      <t>This document defines procedures for the specification and registration of media types for use in HTTP, MIME, and other Internet protocols. This memo documents an Internet Best Current Practice.</t>
    </abstract>
  </front>
  <seriesInfo name="BCP" value="13"/>
  <seriesInfo name="RFC" value="6838"/>
  <seriesInfo name="DOI" value="10.17487/RFC6838"/>
</reference>


<reference anchor="HTTP-RESUMABLE-UPLOADS">
   <front>
      <title>Resumable Uploads for HTTP</title>
      <author fullname="Marius Kleidl" initials="M." surname="Kleidl">
         <organization>Transloadit</organization>
      </author>
      <author fullname="Guoye Zhang" initials="G." surname="Zhang">
         <organization>Apple Inc.</organization>
      </author>
      <author fullname="Lucas Pardue" initials="L." surname="Pardue">
         <organization>Cloudflare</organization>
      </author>
      <date day="2" month="March" year="2026"/>
      <abstract>
	 <t>   HTTP data transfers can encounter interruption due to reasons such as
   canceled requests or dropped connections.  If the intended recipient
   can indicate how much of the data was received prior to interruption,
   a sender can resume data transfer at that point instead of attempting
   to transfer all of the data again.  HTTP range requests support this
   concept of resumable downloads from server to client.  This document
   describes a mechanism that supports resumable uploads from client to
   server using HTTP.

	 </t>
      </abstract>
   </front>
   <seriesInfo name="Internet-Draft" value="draft-ietf-httpbis-resumable-upload-11"/>
   
</reference>

<reference anchor="RFC2119">
  <front>
    <title>Key words for use in RFCs to Indicate Requirement Levels</title>
    <author fullname="S. Bradner" initials="S." surname="Bradner"/>
    <date month="March" year="1997"/>
    <abstract>
      <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
    </abstract>
  </front>
  <seriesInfo name="BCP" value="14"/>
  <seriesInfo name="RFC" value="2119"/>
  <seriesInfo name="DOI" value="10.17487/RFC2119"/>
</reference>

<reference anchor="RFC8174">
  <front>
    <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
    <author fullname="B. Leiba" initials="B." surname="Leiba"/>
    <date month="May" year="2017"/>
    <abstract>
      <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
    </abstract>
  </front>
  <seriesInfo name="BCP" value="14"/>
  <seriesInfo name="RFC" value="8174"/>
  <seriesInfo name="DOI" value="10.17487/RFC8174"/>
</reference>




    </references>





  </back>

<!-- ##markdown-source:
H4sIAAAAAAAAA+197XLbyHbgfzwFlv5xJYekJVn22HQqWVmSZ5SybEeSc++N
xxWCRJPCGAQQAJREz/rWPkQeIM+yj7JPkvPVXwBISf7ITWp3aqpMAY3u06dP
n+8+PRgMgjqpUzUK/+H04F34Ms0n4fFNrbIqybMqiCaTUl21XsYqDuJ8mkUL
+DAuo1k9mOdZfB1l0eC3RVQMJtBU3dSDnSdBtZwskgq7u1gV0Pzk+OJVMI1q
Nc/L1Sis6jhYFjH8XY3CZ0/3dvrh8/2d/SCfVHmq8GmQFOUorMtlVe/t7Dzf
2Qs+qdV1XsbQV1arMlP14AhhCKo6yuJ/idI8g3FWqgqKZBR+qPNpP6zysi7V
rIJfqwX++BgE0bK+zMtREA6CEP5LMoDg5TD8WWZCD3mKL8s885/n5XwUvoqq
ehElKT1R+GsUTqDp/H/O5E2tosVwmi+CIMvLRVQnV2pErd+fnQwujk/fvT64
OB6FZ68Onz75aYfeIKoHh2/P+DFixD5++frtS3pMKMLHp8dHJweDiz+/Oz7n
bp49fkYvfrm4eDc4Oz5/f3rw8vXx4P27128PjqDNyeBomKh6Nris62KSVINS
VctFNEnVYFmkeRQHQZLNLLDBYDAIo0lVl9G0DoKLS8XEMIkqFRZlDtjN03BL
QN3GR1dJrKqwhpbRJEmTehXWecidh7BAYZxfZzRSVE4S6LdchZMkw3+ADKJh
GF5cJpX7KIQ/p1GaKvg+7CFt9frU0zTKwokKlxXQY5KF0CTMYdySQVSGjocu
4EjF5hVBjtjcDqM4xgHiOKnhRZSG19GqQtCnpQLypAGj6VRVVYB9AISrcBF9
SrI5kE6aZCpcKCComECtwuukvkSYQqLKqIx5+FL961JVNUEE07KAwMBVuMhL
3Q0NDWT+iXoisIF6k2yaLmMc8xJ6TfFHGpVzZUGCBUAY8Q2gYgFfhNPLZfap
CrcmyySlb2E4mfYfKkbIFLAEW2yKU4ctWxSwX7YZybyi+FmlyitVDipYX/MV
PCDw80KVEX4NmyhZRHMVMhEFtg10V04vgaoYozRp6B9QgLRFi/E5KfphHZX9
cFokOQAAe6coFbGPgGhHOU8YvlildfSoiOrppQPFkCl3kcRxqoLgAfKKMo9l
hpYcXjbI4fffzU778iX8v//735xmp7D752qhsno7zGczmJVDLkGDXGTBEEJY
caDkayBMhWuyAjww0bapgJaowgWqL6Oa1r/PY+EC6CFmCfRKtGL2KiJzki/r
APddQnwRSJjXdAkN8xntSF5CvepVzX0AO4th12iCU7NZMk1gmukqIOJLap5I
FGbqmnDxiJe11jQPULTJA9Y1qwx8VWCJ16WQ8B4U8m0E8eBBeIhjZfQgfA8r
gJOnJTjKp0tcWSYNkDCICliJ3un78wtgN/Rv+OYt/T47/sf3J2fHR/j7/JeD
16/Nj0BanP/y9v3rI/vLfnn49vT0+M0RfwxPQ+9R0Ds9+LNwt97bdxcnb98c
vO4hlDVCGQuUREGwXBNZa0BAjcyrCoD1TstkwjN7efju//z77n74++//A/b7
3u7uc6Bp/uPZ7k/78AcQpWAtz9KV/Ik0GkRFoaIyFLY6jYqkjlIk6CqsLoGD
h0jOgNSHHxAzH0fh306mxe7+38kDnLD3UOPMe0g4az9pfcxI7HjUMYzBpve8
gWkf3oM/e39rvDsP//bvicMPdp/9/d8FyE4OZOPjKuDGOowKlnYJyL63k9+U
lpZT90VOL1CewYItQXXBNQuLCDaSbFDiNudC4Nz8BewsFQpjQs3gy5c+NGGW
uad5iCGNWAF7gLGizBVmBowV6h68GQCAEeoCIwAgWlQjVN1Goro1gF+F402t
x0CJuK9xIyIMRK8q6IG8zJdZ7SKnhwIFdiV0CVNGILkNYES+BgJjAYQMAvkZ
aBpK1E79MSFTmKR0oBUHB+ZYFfBVFUDL9eCPG0x/+0U4AS0iJCqeKD2ngOek
RwMW2j073EvSVGQ9NB0vUSCPQeMpoxUAejLj/ezACn852AMVFpgy8fjNWExY
1coc7s6Qw2bNRV1QJBSQpwlgvWnngoiadBWlSxEYBJSslnxLBFoxgTY6Ckxb
jTtYXrUocKmJkm8dwJLDOvzaMRIicdlRRAg0KkiTOhJYZ3ma5tesoRk5hNQg
iPL2Juk6qhRjpSKR5tIX6MIPQ6Mtvyd99n0JKn/vvEbp/Ov/ypZp2gtI/YYF
ltUEjgl719X3ie0mIJyEzCt4362uf/kypN6IroFLIUppncEqQcaBv5caEGbj
l/KYmsCSo9x7yBrgefIZbJnee9A05sB5QCHyQD4IQV9F4osTMM9Ef8RZgPJR
ws6jPsIKOiECndaqrpiU2SSapqgyCDysYLAWDJsXpDdIF1pu6kDdTFVRC4oV
fQ5MC9kUDsIzAQWpYl1DbAfq6wjsgfN8WU4VM9mKPiYgVTYVqIFhsM7bd7cF
Wgt5USewwopUH9RB8hl14H4EkzqnLyq9k4CIxLBA6CsPbsQX6MdLAJ5sFZj3
FJRtlL4O7KAkLeeXPLHrBAQq7llG22Wew9AgeWkAECeslZHyHjEbR66B+4Vx
WtXQAWiFJUOeo040QemEn9ByC2HBGqOehXZ3Zcj0w0dZb9yJCxUnUVhjA+SC
jjUJfNB2QxOmvg5Z7TtT06RQ/lgHrLptGE0rd6cnp8cyqD8Ea32whtKyCq8S
Ji7pXMYNwyMScjGzkorUoR5oLCmRbp49AkMClCn80nt8MwDjQqtX/gs0OHr+
jI5Z7/yWGWnVtWtO0r2Zk9nlwrWlwyzHTUlfjDuRDQJMDefDvj+jqyweLqpB
nalZb9uf16FozBsm5ijVG5ZLN4LZ6Unpvu+8UvO1SzWBN3u9fvPxzWdewPYn
n6s6bizhkTEPvnWyjqHhTNf2/1XL6K3EuAk6mDEboCYzx9vB7dVZEuVRy8qB
Gf7csD5da1HGyWw2oI7aKzKp8K27KjXQPbygx+HWMktmCQyBfzZI8R1aad8w
SQRkRZonduRsLur4q9bE4p0XZBHdCNe7RYCCJMROQAzGLHfgy2SxXDRkJqu9
qxDpKMXnsEr0PdvMOegLnoVtTFiYxxlrlJUn8EjmTKgL8gWR2iIGI6taWh0r
FYpMJaI0gnXK89f4SY++Plf1cVnmJUt1nJknP+NcUBbFCBhK0SisCsDxLJny
8CBb63CyrAnXJKdkTBFdrE+R7ZozsBrFwtCOM6ABIoZ7YzlbLiYAJWBXcScs
uNkSjrJOMWLwiQqJill9ALTxRJpooy5aqLsr2ujrLtRtQlupAcRhRd7nMGWg
H5mlRiCJ56NkwY6kr0Eg0wq6OAu9vcbXSVxfjtEUGV+qZH5Zj0XpQb2krRGg
gzIskhuVVuux65DK7YTZhV2tsd2JMLuwKzpjG8PAogSBa2j1AfqQrNF2fBMt
CvQv/uUvf/mtAtvid+i6t8lS7o3C35lHtk0JeGfn1zM6Ozx+sre/9+zZjrzp
UO+gzQd6Ca/JufaoyOYiVs2j3wrVejZPZrz5PzY7d1UMt/tuNatT0eru2FWr
vrLjzleuxtM9sidu1w09Xzs26RfdPTf0jPv3bdSdew+spdW6MRvCWzdxRXR3
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

-->

</rfc>

