<?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-04" 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="28"/>

    
    
    <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 ArchiveRecipe and UnArchiveRecipe.
  Defined values are "application/zip", "application/x-tar", and
  "application/x-cpio".</t>
  <t>supportedCompressionTypes: "String[]"  <vspace blankLines='1'/>
The compression MIME types supported for CompressRecipe and
  UnCompressRecipe.  Defined values are "application/gzip",
  "application/x-bzip2", "application/x-xz", and "application/zstd".</t>
  <t>supportedPatchTypes: "String[]"  <vspace blankLines='1'/>
The delta/patch media types supported for DeltaRecipe and
  PatchRecipe.  Defined values are "application/x-rdiff-delta",
  "application/x-bsdiff", and "text/x-diff" (unified diff).
  An empty array means no delta/patch operations are supported.</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"
    ],
    "supportedCompressionTypes": [
      "application/gzip",
      "application/x-bzip2",
      "application/zstd"
    ],
    "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>

</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>unArchive: UnArchiveRecipe</t>
  <t>compress: CompressRecipe</t>
  <t>unCompress: UnCompressRecipe</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 UnArchiveRecipe
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="unarchiverecipe"><name>UnArchiveRecipe</name>

<t>An UnArchiveRecipe 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 UnArchiveRecipe 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">supportedArchiveTypes</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">supportedCompressionTypes</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">supportedCompressionTypes</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="uncompressrecipe"><name>UnCompressRecipe</name>

<t>An UnCompressRecipe 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">supportedCompressionTypes</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">supportedPatchTypes</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">supportedPatchTypes</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,
      "unCompress": {
        "blobId": "Bd81c7f",
        "type": "application/gzip"
      }
    },
    "u2": {
      "unArchive": {
        "blobId": "#u1",
        "type": "application/x-tar"
      }
    }
  }
}, "0"]]
]]></sourcecode></figure>

<t>The response for the UnArchiveRecipe 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
UnArchiveRecipe 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-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:
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

-->

</rfc>

