| Internet-Draft | JMAP FileNode | October 2026 |
| Gondwana | Expires 1 May 2027 | [Page] |
- Workgroup:
- Network Working Group
- Internet-Draft:
- draft-ietf-jmap-filenode-19
- Updates:
- 8620 (if approved)
- Published:
- Intended Status:
- Standards Track
- Expires:
JMAP File Storage extension
Abstract
The JMAP base protocol (RFC 8620) 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.¶
This extension adds a FileNode data type and methods to expose blobs as a filesystem, along with the types of metadata that are provided by other remote filesystem protocols.¶
Status of This Memo
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 1 May 2027.¶
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
1. Introduction
JMAP ([JMAP-CORE] — JSON Meta Application Protocol) is a generic protocol for synchronizing data between a client and a server. It is optimized for mobile and web environments, and aims to provide a consistent interface to different data types.¶
In the same way that JMAP Calendars ([JMAP-CALENDARS]) replaces CalDAV ([CALDAV]) and JMAP Contacts ([JMAP-CONTACTS]) replaces CardDAV ([CARDDAV]), this document replaces the use of WebDAV ([WEBDAV]) for remote filesystem access.¶
1.1. Conventions Used in This Document
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
2. Addition to the Capabilities Object
The capabilities object is returned as part of the JMAP Session object; see Section 2 of [JMAP-CORE].¶
This document defines an additional capability URI.¶
2.1. urn:ietf:params:jmap:filenode
The capability urn:ietf:params:jmap:filenode being present in the
"accountCapabilities" property of an account represents support
for the FileNode data type. Servers that include the capability
in one or more "accountCapabilities" properties MUST also include
the property in the "capabilities" property.¶
The value of this property in the JMAP session "capabilities" property MUST be an empty object.¶
The value of this property in an account's "accountCapabilities" property is an object that MUST contain the following information on server capabilities and permissions for that account:¶
-
maxFileNodeDepth: "UnsignedInt|null"¶
The maximum depth of the FileNode hierarchy (that is, one more than the maximum number of ancestors a FileNode may have), or null for no limit.¶
-
maxSizeFileNodeName: "UnsignedInt"¶
The maximum length, in (UTF-8) octets, allowed for the name of a FileNode. This MUST be at least 100, although it is recommended servers allow more.¶
-
forbiddenNameChars: "String|null"¶
A string where each character is forbidden in FileNode names. If null, the server does not restrict name characters beyond the requirement that names be non-empty Net-Unicode strings. The server MUST reject names containing any of these characters with an "invalidProperties" error.¶
-
supportedNodeTypes: "String[]"¶
The
@typevalues that the server allows clients to create in this account (see "FileNode Objects" below). This MUST include "Directory" and "File". It MAY include "SymLink" and any other value registered in the "JMAP FileNode Types" registry. Clients MUST ignore values they do not recognize.¶ -
fileNodeQuerySortOptions: "String[]"¶
A list of all the values the server supports for the "property" field of the Comparator object in a "FileNode/query" sort (see "FileNode/query" below). This MAY include properties the client does not recognize (for example, custom properties specified in a vendor extension). Clients MUST ignore any unknown properties in the list.¶
-
mayCreateTopLevelFileNode: "Boolean"¶
If true, the user may create a FileNode (see "FileNode/set" below) in this account with a null parentId. If false, creating a node with a null parentId, or moving one there, fails with "forbidden": the request is valid, but not allowed. (Permission for creating a child of an existing FileNode is given by the "myRights" property on that FileNode.)¶
-
webTrashUrl: "String|null"¶
The URL at which the folder with the "trash" role can be viewed on the web. If null, there is no web URL available.¶
-
caseInsensitiveNames: "Boolean"¶
If true, the server treats file names as case-insensitive for all name collision checks, including the sibling uniqueness constraint and
onExistshandling. The server preserves the original case as provided by the client.¶ -
webUrlTemplate: "String|null"¶
A template by which any node can be viewed on the web, in URI Template (level 1) format [URI-TEMPLATE]. The available variable is
{id}, which is the FileNode id. If null, there is no web URL available.¶ -
webWriteUrlTemplate: "String|null"¶
A URI Template (level 1) [URI-TEMPLATE] for writing content to a FileNode with
@type"File". The available variable is{id}, which is the FileNode id. If null, direct HTTP writes are not supported and clients must use FileNode/set to update content. See "Direct HTTP Write" below for the HTTP semantics.¶
2.1.1. Capability Example
{
"urn:ietf:params:jmap:filenode": {
"maxFileNodeDepth": 50,
"maxSizeFileNodeName": 255,
"fileNodeQuerySortOptions": [
"name", "mediaType", "size", "created", "modified",
"nodeType", "tree"
],
"supportedNodeTypes": ["Directory", "File", "SymLink"],
"forbiddenNameChars": "/<>:\"\\|?*",
"caseInsensitiveNames": false,
"mayCreateTopLevelFileNode": false,
"webTrashUrl": "https://files.example.com/trash",
"webUrlTemplate": "https://files.example.com/view/{id}",
"webWriteUrlTemplate": "https://files.example.com/write/{id}"
}
}
¶
3. FileNode Data Type
A FileNode is a set of metadata which behaves similarly to an inode in a filesystem. In [WEBDAV] terminology a Directory corresponds to a collection and a File to a resource.¶
The urn:ietf:params:jmap:filenode capability selects the
FileNode data type and the methods defined below.¶
3.1. FileNode Objects
Every FileNode has a type, given by its @type property. This
document defines three types: "File", "Directory", and "SymLink".
Each type has its own set of properties in addition to the
properties common to all FileNodes.¶
Properties that this document defines for one type MUST NOT
appear on a FileNode of another type; they are omitted rather
than set to null. An attempt to create or update a FileNode
that sets a property not defined for its @type to a non-null
value is an "invalidProperties" error. The server MUST ignore a
null value for such a property, both on create and on update.
This lets a client send the same set of properties for nodes of
any type.¶
As in Section 5.3 of [JMAP-CORE], setting a property that has a default value to null, on create or update, sets it to that default, unless the property's description says otherwise.¶
FileNode/get has additional rules for these properties (see "FileNode/get" below). In particular, for a node the user can discover but not read, it returns null for most properties, even those whose type does not otherwise allow null (see "Access Control").¶
3.1.1. Common Properties
The following properties are defined for every FileNode, regardless of its type:¶
-
@type: "String" (immutable)¶
The type of the node. Values are registered in the "JMAP FileNode Types" registry (see "IANA Considerations"). This document defines the values "File", "Directory", and "SymLink". This MUST be provided on creation. Attempting to change
@typeafter creation is an "invalidProperties" error. A server MUST reject the creation of a node with an@typethat is not in thesupportedNodeTypescapability with an "invalidProperties" error. Clients MUST be prepared to receive nodes with@typevalues they do not recognize, and SHOULD NOT interpret any of their properties other than the common properties.¶ -
id: "Id" (immutable; server-set)¶
The Id of the FileNode.¶
-
parentId: "Id|null"¶
The Id of the parent node, or null if this is a top-level node. The parent MUST be a node whose
@typepermits children; of the types defined in this document, only "Directory" does. Setting parentId to any other node is an "invalidProperties" error.¶ -
name: "String"¶
User-visible name for the FileNode. This MUST be a Net-Unicode string [UNICODE] of at least 1 character in length, subject to the maximum size given in the capability object. There MUST NOT be two sibling FileNodes with both the same parent and the same name. The server MUST reject names containing any character listed in
forbiddenNameCharswith an "invalidProperties" error. It MAY also reject other names it cannot store, such as "." and "..", in the same way. See "Implementation Considerations" for guidance on which characters and names to forbid.¶ -
created: "UTCDate" (default: current server time)¶
The date the node was created.¶
-
modified: "UTCDate|null" (default: current server time)¶
The date the node was last updated. The server does not automatically update this value, except on a Direct HTTP Write (see "Direct HTTP Write"). If the client does not include
modifiedin an update, the server MUST leave it unchanged. If the client setsmodifiedto null, the server MUST set it to the current server time. See "Implementation Considerations" for usage guidance.¶ -
accessed: "UTCDate|null" (default: current server time)¶
The date the node was last accessed. As with
modified, the server does not automatically update this value; clients SHOULD provide an updated value when appropriate. If not included in an update, the server MUST leave it unchanged. If set to null, the server MUST set it to the current server time.¶ -
changed: "UTCDate" (server-set)¶
The date the server last recorded a change to any property of this node. The server MUST automatically update this value whenever any property of the node is modified, including
blobId,name,parentId,shareWith,executable, and all other mutable properties. This is not settable by clients.¶
3.1.2. Container Properties
The following properties are defined for every FileNode whose
@type permits children; of the types defined in this document,
only "Directory" does. They do not appear on other nodes, whose
rights come from their parent (see "Access Control").¶
-
isSubscribed: "Boolean" (default: true)¶
This property is stored per user, and if true, the node should be displayed to the current user. Setting it changes only the current user's value, and requires no rights beyond being able to discover the node.¶
-
myRights: "FilesRights" (server-set)¶
The set of rights (ACLs) the user has in relation to this node. A FilesRights object has the following properties:¶
-
mayRead:
BooleanThe user may read all properties and the blob content of this node. Without it, some properties are still visible (see "Access Control").¶ -
mayAddChildren:
BooleanThe user may create child nodes in this directory, or move nodes into it.¶ -
mayRename:
BooleanThe user may rename or move this node.¶ -
mayDelete:
BooleanThe user may destroy this node.¶ -
mayModifyContent:
BooleanThe user may update the content-related properties of this node (blobId,mediaType,target,modified,accessed,executable).¶ -
mayShare:
BooleanThe user may change the sharing of this node (see [JMAP-SHARING]).¶
-
-
shareWith: "Id[FilesRights]|null"¶
A map of userId to rights for users this node is shared with. This is the access control list set on this node only; rights inherited from ancestors do not appear in it (see "Access Control"). The owner of the node MUST NOT be in this set. This is
nullif the user requesting the object does not havemyRights.mayShare, or if the node is not shared with anyone.¶
3.1.3. File Properties
A FileNode with @type "File" is a regular file with blob
content. In addition to the common properties, it has the
following properties:¶
-
blobId: "Id"¶
The blobId for the content of this file. This MUST be provided on creation, including for zero byte files. Setting blobId to null is an "invalidProperties" error. Updating the blobId replaces the file's content; the server MUST update
sizeto match the new blob. A blob referenced by a FileNode MUST NOT be expired or garbage collected by the server while the FileNode exists.¶ -
size: "UnsignedInt" (server-set)¶
The size in bytes of the associated blob data. Size is optional on create and update, but if provided it MUST match the size of the provided blobId. If the client updates
blobIdwithout providingsize, the server setssizefrom the new blob and returns the size in the "updated" response.¶ -
mediaType: "String" (default: "application/octet-stream")¶
The media type of the file. If not provided on creation, or if set to null, the server sets it to "application/octet-stream". Valid values are found in the IANA media-types registry. Servers MUST NOT reject media types that are not recognized. Servers MUST reject media types if the value does not conform to the ABNF of Section 4.2 of [MEDIATYPE].¶
-
executable: "Boolean" (default: false)¶
If true, the file should be treated as an executable by operating systems that support this flag.¶
3.1.4. Directory Properties
A FileNode with @type "Directory" is a collection that may
contain child nodes. In addition to the common properties, it
has the following property:¶
3.1.5. SymLink Properties
A FileNode with @type "SymLink" is a symbolic link to another
path in the tree. In addition to the common properties, it has
the following property:¶
-
target: "String[]"¶
The target path of the symlink, as an array of path elements. Each element is a node name in the path. An empty string as the first element indicates an absolute path from the root of the tree; otherwise the path is relative to the symlink's parent. An element of ".." refers to the parent directory. The target is not required to resolve to an existing node (symlinks may dangle). The target is mutable; updating it changes where the symlink points. This MUST be provided on creation. Setting target to null is an "invalidProperties" error.¶
3.1.6. FileNode Examples
A directory, a file inside it, and a symlink pointing to the
file. Common properties other than @type, id, parentId,
and name are omitted for brevity:¶
[
{
"@type": "Directory",
"id": "n1",
"parentId": null,
"name": "Documents",
"role": "documents"
},
{
"@type": "File",
"id": "n3",
"parentId": "n1",
"name": "report.pdf",
"blobId": "Bfa61c0",
"size": 48576,
"mediaType": "application/pdf",
"executable": false
},
{
"@type": "SymLink",
"id": "n7",
"parentId": null,
"name": "latest-report",
"target": ["Documents", "report.pdf"]
}
]
¶
3.2. FileNode Methods
3.2.1. FileNode/get
This is a standard Foo/get method, with the following differences:¶
-
The
@typeproperty is always returned, even if it is not included in thepropertiesargument.¶ -
If the
propertiesargument includes a property that is defined for some FileNode type but not for a node's@type, the server omits that property from that node in the response. It does not return the property with a null value.¶ -
For a node the user can discover but not read, the server returns null for most properties (see "Access Control").¶
There is one additional request argument:¶
-
fetchParents: "Boolean" (default: false)¶
If true, the server returns all ancestor nodes of the requested ids in addition to the requested nodes themselves. This allows a client to reconstruct the full path to each requested node in a single call. Ancestor nodes that have already been included (either because they were explicitly requested or because they are ancestors of multiple requested nodes) are not duplicated. The ancestors are returned as additional entries in the
listarray.¶
3.2.2. FileNode/changes
This is a standard Foo/changes method.¶
3.2.3. FileNode/set
This is a standard Foo/set method, with the following differences:¶
Since parentId creates a tree structure, an attempt to move a node to a parent for which this node is also an ancestor is rejected with an "invalidProperties" error.¶
Within a single FileNode/set, the server MUST check the uniqueness of sibling names against the result of all its creates, updates and destroys together, so that a client can destroy a node and create its replacement in the same method.¶
There are these additional top-level arguments:¶
-
onDestroyRemoveChildren: "Boolean" (default: false)¶
If false, an attempt to destroy a FileNode that has children is rejected with a "nodeHasChildren" error. If all the children are destroyed in the same method, the server MUST NOT return this error: it either destroys children before their parents, or checks this constraint only against the final state.¶
If true, destroying a node also destroys all its descendants, and the server MUST include the ids of all of them in the "destroyed" list of the response.¶
-
onExists: "String|null" (default: null)¶
Controls what happens when a create or update would give a FileNode the same name as an existing sibling.¶
If null, the server rejects the create or update with an "alreadyExists" error.¶
If "replace", the server destroys the existing node, and MUST include its id in the "destroyed" list of the response. A node of any
@typemay replace a node of any other@type.¶If "rename", the server changes the "name" property so that it does not clash, using an algorithm of its choice, and MUST include the new name in the "created" or "updated" entry for that id.¶
Any other value is rejected with an "invalidArguments" error.¶
"replace" destroys whichever node holds the name when the method runs. To replace only the node it expects, a client instead destroys that node and creates the new one in the same FileNode/set, with an "ifUnchangedBy" precondition ([JMAP-CONDITIONAL]) on the node it destroys.¶
For "replace", the server makes these checks in order, and stops at the first that fails:¶
-
If the existing node is not discoverable by the user (see "Access Control"), the operation fails with "forbidden", and the server MUST NOT destroy that node or reveal its id. Servers may also return "forbidden" for other reasons, such as a file name policy, so the error does not confirm that the name is taken.¶
-
If the user does not have
mayDeleteon the existing node, the operation fails with "forbidden".¶ -
If the existing node has children, the operation fails with "nodeHasChildren" if onDestroyRemoveChildren is false, or with "forbidden" if replacing it would remove nodes the user cannot discover (see "Access Control").¶
"rename" applies whether or not the user can discover the existing node.¶
-
-
compareCaseInsensitively: "Boolean" (default: false)¶
If true, name collision checks (including
onExistshandling) treat names as case-insensitive for this request. This has no effect if the server already hascaseInsensitiveNamesset to true in its capability. This allows clients syncing to case-insensitive platforms to prevent name collisions that would be problematic on those platforms.¶
Errors (in addition to standard SetError codes from [JMAP-CORE]):¶
-
"alreadyExists" — a create or update would cause a name collision with a discoverable sibling FileNode, and onExists did not resolve it. The SetError object MUST include an
existingIdproperty with the id of the existing FileNode.¶ -
"forbidden" — among other cases: a name collision with a sibling the user cannot discover; an onExists replacement without the required rights on the existing node (see onExists above); a destroy that would remove nodes the user cannot discover; a rename, move or destroy without the rights it needs, or of a Directory with a role; or a create or move to the top level without
mayCreateTopLevelFileNode(see "Access Control").¶ -
"invalidProperties" — among other cases: an attempt to move a node to a descendant of itself, creating a cycle in the tree, to make a node the child of a node whose
@typedoes not permit children, to change the@typeof a node, or to set a property that is not defined for the node's@typeto a non-null value.¶ -
"nodeHasChildren" — a destroy or onExists replacement of a FileNode that has children, with onDestroyRemoveChildren false.¶
3.2.4. FileNode/copy
This is a standard Foo/copy method with the same additional top-level arguments as FileNode/set (onDestroyRemoveChildren, onExists, and compareCaseInsensitively), with the same behavior.¶
The response has one additional property:¶
-
destroyed: "Id[]|null"¶
The ids of the FileNodes in the destination account that were destroyed because onExists replaced them, including any descendants removed with onDestroyRemoveChildren. This is null if no FileNodes were destroyed.¶
Errors: the same additional errors as FileNode/set apply.¶
3.2.5. FileNode/query
This is a standard Foo/query method except for the following:¶
There is one additional request argument:¶
-
depth: "UnsignedInt|null" (default: null)¶
The maximum depth in the tree, counted from the top level, at which a node can match: nodes with a null parentId are at depth 0, their children at depth 1, and so on. This applies whatever the filter; conditions such as
parentIdandancestorIdonly select among the nodes within it, and depth is never counted from them. SoancestorIdwithoutdepthmatches the whole subtree. If null, there is no limit.¶
The following filter criteria are defined:¶
-
isTopLevel: "Boolean"¶
If true, the node must have a null parentId to match the condition.¶
-
parentId: "Id"¶
A FileNode id. A node must have a parentId equal to this to match the condition.¶
-
ancestorId: "Id"¶
A FileNode id. A node must have an ancestor (parent, parent of parent, etc.) with an id equal to this to match the condition.¶
-
descendantId: "Id"¶
A FileNode id. A node must be an ancestor (parent, parent of parent, etc.) of the node with this id to match the condition. This is the inverse of
ancestorId.¶ -
nodeType: "String"¶
A node type value. Only nodes whose
@typeis precisely this value match this condition. This filter, and thenodeTypesort property below, are namednodeTyperather than@type; they operate on the@typeproperty.¶ -
role: "String"¶
A role name. Only nodes with precisely this role match this condition.¶
-
hasAnyRole: "Boolean"¶
If true, any node with a defined role matches this condition. If false, any node which has a role does not match this condition.¶
-
blobId: "Id"¶
A FileNode must have a blobId equal to this to match the condition.¶
-
isExecutable: "Boolean"¶
If true, the FileNode must have a true executable value.¶
-
createdBefore: "UTCDate"¶
The creation date of the node must be before this date to match the condition.¶
-
createdAfter: "UTCDate"¶
The creation date of the node must be on or after this date to match the condition.¶
-
modifiedBefore: "UTCDate"¶
The modified date of the node must be before this date to match the condition.¶
-
modifiedAfter: "UTCDate"¶
The modified date of the node must be on or after this date to match the condition.¶
-
accessedBefore: "UTCDate"¶
The accessed date of the node must be before this date to match the condition.¶
-
accessedAfter: "UTCDate"¶
The accessed date of the node must be on or after this date to match the condition.¶
-
minSize: "UnsignedInt"¶
The size of the node in bytes must be equal to or greater than this number to match the condition.¶
-
maxSize: "UnsignedInt"¶
The size of the node in bytes must be less than this number to match the condition.¶
-
name: "String"¶
A FileNode must have exactly the same octets in its name property to match the condition.¶
-
nameMatch: "String"¶
Matches the name property of the node using glob syntax. The following special patterns are defined:
*matches any sequence of characters,?matches any single character, and a pair of brackets matches any single character in the bracketed set (for example,[abc]matches "a", "b", or "c";[a-z]matches any letter from "a" to "z";[!abc]or[^abc]matches any character not in the set). All other characters match literally. The match is case-insensitive.¶ -
mediaType: "String"¶
A FileNode must have exactly the same octets in its mediaType property to match the condition.¶
-
mediaTypeMatch: "String"¶
Matches the mediaType property of the node using the same glob syntax as nameMatch.¶
-
body: "String"¶
Match the content of the referenced blob, see the definition of body in Section 4.4.1 of [JMAP-MAIL]. The server may use any technology to extract meaningful text from the blob for searching, or interpret the string in any way, to choose the nodes that it believes the user wants to see.¶
-
text: "String"¶
Matches a node if the body condition with this value matches, or if its name or mediaType contains this value, compared case-insensitively.¶
The blobId, isExecutable, minSize, maxSize, mediaType,
mediaTypeMatch, and body conditions match only nodes whose @type is "File". The
role and hasAnyRole conditions match only nodes whose @type is
"Directory". This applies to both true and false values. For
example, a hasAnyRole condition of false matches only Directories
without a role, and an isExecutable condition of false matches
only Files that are not executable.¶
For a node the user can discover but not read, a condition on a property that FileNode/get returns as null for that node (see "Access Control") does not match the node, whatever its value. The body condition, and the body part of the text condition, never match such a node.¶
It also supports the following additional sort properties:¶
-
tree:¶
Sort by tree; which means by name, but any Directory node is immediately followed by the recursive application of the same sort to its child nodes. This is similar to the output of the
findcommand on a filesystem.¶ -
nodeType:¶
Sort by
@type. Directories sort first, then symlinks, then files, then nodes of any other@typeordered by the@typevalue.¶
When sorting by a property that is not defined for a node's
@type (for example size on a Directory), or that FileNode/get
returns as null because the user cannot read the node (see
"Access Control"), nodes without the property sort before
nodes that have it when isAscending is true. Nodes that lack the
property are ordered among themselves as if that Comparator were
absent from the sort.¶
3.2.6. FileNode/queryChanges
This is a standard Foo/queryChanges method, which also takes the
depth argument of FileNode/query, with the same meaning.¶
4. Direct HTTP Write
When the server advertises a webWriteUrlTemplate, clients can
update the content of a File directly via HTTP without using
FileNode/set. The URL is constructed by expanding the template
with the FileNode id. This is only valid for nodes with
@type "File"; requests targeting any other type of node MUST
be rejected with 400 (Bad Request). A request for a node that does
not exist, or that the user cannot discover, MUST be rejected with
404 (Not Found).¶
The user MUST have mayModifyContent on the node, which for a File
is in its parent's myRights (see "Access Control"). Requests
without it MUST be rejected with 403 (Forbidden). Authentication uses the same mechanism as other
JMAP HTTP endpoints.¶
4.1. PUT
A PUT request replaces the entire content of the node. The
request body is the new content. The type and subtype of the
Content-Type header field set the mediaType of the node; the
server does not store any parameters (such as "charset"). If the
request has no Content-Type header field, the server sets
mediaType to "application/octet-stream".¶
On success, the server MUST update the node's blobId, size,
and mediaType properties, and set modified to the current
server time (a PUT cannot carry a client timestamp; a client that
wants a different one sets it with FileNode/set afterwards). It
returns 200 (OK) with a JSON body:¶
{
"blobId": "Bnewblob123",
"size": 48576,
"mediaType": "text/plain"
}
¶
4.2. PATCH
A PATCH request applies a partial modification to the node's
current content. The Content-Type of the request indicates the
patch format. The supported patch formats are those listed in
the supportedPatchTypes capability of [JMAP-BLOBEXT] (for example,
application/x-rdiff-delta, application/x-bsdiff,
text/x-diff). The server applies the delta to the node's
current blob to produce the new content.¶
On success, the server MUST update the node's blobId and
size properties, and set modified to the current server time,
as for PUT. The server MUST NOT change the node's mediaType.
A client that needs to change it uses PUT, or updates mediaType
with FileNode/set. The response is 200 (OK) with the same JSON
body as PUT:¶
{
"blobId": "Bpatched456",
"size": 49152,
"mediaType": "text/plain"
}
¶
Servers that do not support a given patch format MUST respond with 415 (Unsupported Media Type).¶
4.3. Integrity
A PUT or PATCH request MAY carry Repr-Digest or Content-Digest
header fields [DIGEST-FIELDS] for the data it sends: for PUT, the
new content of the node; for PATCH, the patch. The server MUST
verify each digest whose algorithm it supports. If any does not
match, the server MUST respond with 400 (Bad Request) and MUST NOT
change the node. As [DIGEST-FIELDS] describes, a digest whose algorithm
the server does not support is ignored. The algorithms a server
supports are those in the supportedDigestAlgorithms capability of
[JMAP-BLOBEXT], which lists them in the server's order of
preference; clients SHOULD use the first one they support.¶
A client can ask for a digest of the node's new content by sending
Want-Repr-Digest [DIGEST-FIELDS]. On success, for each
algorithm it lists that the server supports, the server SHOULD
include a member named "digest:" followed by the algorithm name
(for example "digest:sha-256") in the JSON response body, holding
the base64-encoded digest of the node's new content, as Blob/get
does in [JMAP-BLOBEXT]. The digest is in the body because any
digest header fields on the response describe the response body
itself, not the node. This lets a client confirm the result of a
PATCH, or of a PUT whose digest it did not send, without a further
request.¶
{
"blobId": "Bpatched456",
"size": 49152,
"mediaType": "text/plain",
"digest:sha-256": "lu2dEm1cr1Hg2GqVDXMTGP3sgQfFYyp8XUd2ZTgp5Qc="
}
¶
5. Access Control
Access control lists are set with shareWith, which only nodes
whose @type permits children can have. Rights are inherited
down the tree: a node's myRights is the union of the rights
granted to the user by the shareWith entries on the node and on
all its ancestors. Inherited rights are a floor. A shareWith
entry on a descendant can grant a user more rights on that
subtree, but it cannot take away rights granted higher up.¶
A node whose @type does not permit children has no myRights.
The user's rights on it are those in its parent's myRights;
mayAddChildren and mayShare do not apply to it. Such a node
with a null parentId is discoverable only by the account owner,
who has all rights on it.¶
To rename or move a node the server checks mayRename, and to
destroy it mayDelete, in the rights of the node itself; for a
node with no myRights, these are its parent's rights. Moving a
node also requires mayAddChildren on the new parent, or
mayCreateTopLevelFileNode to move it to the top level. Without
these rights, the operation fails with "forbidden".¶
A Directory with a role cannot be destroyed, renamed or moved; the
server rejects any attempt with "forbidden". This does not change
the rights it reports in myRights, which its children with no
myRights of their own take as theirs: files in a directory with
the "trash" role, for example, can still be destroyed.¶
A change to shareWith on a node is a change to every node in
its subtree: the server MUST report all of them as changed in
FileNode/changes, including nodes that have no myRights of their
own and take their rights from their parent. A client therefore
learns the new rights of everything below a node whose sharing
changed from FileNode/changes alone, without working out which
nodes are affected itself.¶
Nodes which are not "discoverable" MUST be reported in notFound
if fetched with FileNode/get and MUST NOT be returned in response to
FileNode/query. Nodes are discoverable in one of two ways:¶
-
If the node or an ancestor of the node has mayRead true.¶
-
If the node or a descendant of the node has a
shareWithentry that grants the user any right.¶
When a node stops being discoverable by the user, for example
because a shareWith entry was removed, the server reports it as
destroyed in FileNode/changes.¶
A node that is discoverable only through the second rule has mayRead false. It is visible so that the user can use the rights granted on it, and can see the full path to the nodes they have access to. This means that a query with that node as the parent will only return those of its children which are otherwise discoverable through the second discoverability rule.¶
For a node that is discoverable but does not have mayRead,
FileNode/get returns the real values of id, @type, name,
parentId, myRights, isSubscribed, and shareWith (which
follows its own rule for mayShare). It returns null for every
other property defined for the node's @type, including
created, changed, and role, even where that property's type
does not otherwise allow null. A change to a property the user
sees as null is not visible to the user, so the server SHOULD NOT
report the node as updated in FileNode/changes because of it, but
MAY do so if it cannot efficiently determine whether any property
visible to the user changed.¶
Such a node can have descendants the user cannot discover. A destroy that would remove any of them, either through onDestroyRemoveChildren or as part of a node replaced through onExists, MUST be rejected with "forbidden", and the server MUST NOT destroy any node in that subtree. Without onDestroyRemoveChildren, destroying such a node while it has children fails with "nodeHasChildren" whether or not the user can discover those children.¶
This leads to a sharee seeing the full path to anything that they have access to, but nothing else.¶
For example, in a Unix home directory layout where Alice has shared her "forBob" directory, Bob might see:¶
/home
/alice
/shared
/forBob
file1.jpg
file2.txt
...
/bob
bobfile.txt
...
¶
Alice sees many more files and directories under "/home".¶
Sharing can make large changes to the set of nodes a user can see: when Alice first shared that directory, Bob would have been told of a large number of new nodes in response to a FileNode/changes query. The server might report these as "created" or "updated" — it is not always possible for the server to know whether a node was previously visible to the user.¶
6. Quotas
Servers that support JMAP Quotas ([JMAP-QUOTAS]) SHOULD
provide Quota objects with "FileNode" in the types array.
Both the octets resource type (total storage used by FileNode
blobs) and the count resource type (number of FileNode
objects) are applicable.¶
7. Integration with JMAP Blob Extensions
When a server advertises both urn:ietf:params:jmap:filenode and
urn:ietf:params:jmap:blob2, the ArchiveEntry object type
(defined in [JMAP-BLOBEXT]) is extended with two additional
properties:¶
-
nodeId: "Id|null"¶
A FileNode id. When present, the server populates the archive entry from the referenced FileNode's properties. The
blobIdandnameare taken from the FileNode. The FileNode'screatedandmodifiedtimestamps map to the archive entry's time metadata. The user's rights on the FileNode (specifically themayModifyContentright) and, for a File, theexecutableproperty inform themodefield in the archive entry (for example, permissions and executable bit). If the ArchiveEntry also provides explicit values for any of these properties, the explicit values take precedence. Mutually exclusive with providingblobIddirectly when the FileNode's@typeis "File".¶ -
recurse: "Boolean" (default: false)¶
Only valid when the referenced FileNode's
@typeis "Directory". When true, the server recursively includes all children of the directory in the archive. Each child entry'snameis its full path relative to the directory. Directory names MUST include a trailing/.¶
A FileNode with @type "SymLink", whether referenced directly by
nodeId or reached by recurse, is added to the archive as an
entry with entryType "symlink". The server never follows the
symlink. The entry's linkTarget is the elements of the
SymLink's target joined with "/". An absolute target, whose
first element is the empty string, therefore produces a
linkTarget that starts with "/".¶
Some archive formats cannot hold every node type. For example, "application/zip" has no symlink entries. The ArchiveRecipe (defined in [JMAP-BLOBEXT]) is extended with one additional property that controls how the server handles such nodes:¶
-
onUnsupportedEntry: "String" (default: "error")¶
This applies to any node, referenced by
nodeIdor reached byrecurse, that the archive format cannot represent. If "error", the server rejects the Blob/convert creation with an "unsupportedEntry" SetError, and creates no archive. If "skip", the server leaves such nodes out of the archive. Any other value is an "invalidProperties" error.¶
This property applies only to entries that the server builds
from FileNodes. An entry that the client gives in full, with an
unsupported entryType, is still an "invalidProperties" error as
[JMAP-BLOBEXT] requires.¶
If nodeId references a FileNode that the user does not have
permission to read, or that does not exist, the server MUST
reject the Blob/convert creation with a "notFound" SetError.¶
7.1. Examples
To archive an entire directory tree rooted at FileNode "dirabc" into a zip file:¶
[["Blob/convert", {
"accountId": "account1",
"create": {
"myarchive": {
"archive": {
"type": "application/zip",
"entries": [{
"nodeId": "dirabc",
"name": "/",
"recurse": true
}]
}
}
}
}, "R1"]]
¶
To archive specific files from different locations along with a full subdirectory:¶
[["Blob/convert", {
"accountId": "account1",
"create": {
"selective": {
"archive": {
"type": "application/zip",
"entries": [
{
"nodeId": "filenode1",
"name": "README.txt"
},
{
"nodeId": "dirnode2",
"name": "docs/",
"recurse": true
}
]
}
}
}
}, "R1"]]
¶
7.2. Extracting Archives into FileNodes
When both capabilities are present, the ExtractRecipe (defined in [JMAP-BLOBEXT]) is extended with the following additional properties:¶
-
parentNodeId: "Id|null"¶
The id of a FileNode with
@type"Directory". When present, the server extracts the archive contents into this directory, creating FileNode objects for each entry. Subdirectories in the archive are created as FileNodes with@type"Directory". File entries become FileNodes with@type"File" and theirblobIdset to the extracted content. Thecreatedandmodifiedtimestamps are taken from the archive entry metadata. If the archive entry includes amodefield, the server SHOULD use it to set theexecutableproperty on the created FileNode.¶Symlink entries become FileNodes with
@type"SymLink" if "SymLink" is in thesupportedNodeTypescapability. The server splits the entry'slinkTargeton "/" to produce thetargetpath elements. AlinkTargetthat starts with "/" produces an empty string as the first element, which marks an absolute path. The server drops any other empty elements.¶An entry is unsupported if the server cannot create a FileNode for it. These are symlink entries when "SymLink" is not in
supportedNodeTypes, and entries of any type that has no FileNode equivalent ("hardlink", "fifo", "blockDevice", and "charDevice"). TheonUnsupportedEntryproperty controls how the server handles them.¶ -
onUnsupportedEntry: "String" (default: "error")¶
If "error", the server rejects the Blob/convert creation with an "unsupportedEntry" SetError, and creates no FileNodes. If "skip", the server does not create a FileNode for an unsupported entry, and extracts the other entries as usual. A skipped entry still appears in the response
entriesarray, with a nullnodeId. Any other value is an "invalidProperties" error.¶ -
onExists: "String|null" (default: null)¶
Controls the behavior when an extracted entry would collide with an existing FileNode name under the same parent. The values and semantics are the same as for FileNode/set (see above): null rejects with an "alreadyExists" SetError, "replace" destroys the existing node, and "rename" gives the new node a server-chosen name.¶
When parentNodeId is set, each ArchiveEntry in the response
entries array includes an additional nodeId property
containing the id of the FileNode that was created for that
entry, or null if the server skipped the entry.¶
If parentNodeId references a FileNode that does not exist,
is not a Directory, or for which the user does not have
mayAddChildren permission, the server MUST reject the Blob/convert
creation with an appropriate SetError ("notFound",
"invalidProperties", or "forbidden").¶
7.2.1. Example
Extracting a zip file into a directory:¶
[["Blob/convert", {
"accountId": "account1",
"create": {
"e1": {
"extract": {
"blobId": "Barchive123",
"type": "application/zip",
"parentNodeId": "dir456"
}
}
}
}, "R1"]]
¶
The response includes a nodeId for each created entry:¶
[["Blob/convert", {
"accountId": "account1",
"created": {
"e1": {
"id": "Barchive123",
"type": "application/zip",
"size": 104857,
"entries": [
{
"name": "docs/",
"entryType": "directory",
"nodeId": "node001"
},
{
"name": "docs/readme.txt",
"blobId": "Bdd001",
"entryType": "file",
"modified": "2026-03-01T12:00:00Z",
"mode": "0644",
"nodeId": "node002"
},
{
"name": "docs/logo.png",
"blobId": "Bdd002",
"entryType": "file",
"modified": "2026-02-15T09:30:00Z",
"mode": "0644",
"nodeId": "node003"
}
]
}
},
"notCreated": {}
}, "R1"]]
¶
7.3. The unsupportedEntry Error
An "unsupportedEntry" SetError means that an archive or
extraction with onUnsupportedEntry set to "error" found one or
more entries that it could not handle. The SetError object MUST
include an entryNames property: a "String[]" that lists the
name of each such entry. For an archive, this is the name that
the entry would have had in the archive.¶
8. Extended Attributes and Named Streams
Operating systems associate named side-channel data with files: POSIX extended
attributes (getxattr/setxattr), macOS extended attributes (including Finder
tags and the quarantine flag) and resource forks, NTFS and SMB2 alternate data
streams, and NFSv4 named attributes. A FileNode carries these using the JMAP
Object Metadata extension ([JMAP-METADATA]). A server that supports named
attributes on FileNodes lists "FileNode" in the "dataTypes" of its
urn:ietf:params:jmap:metadata capability. Clients then read and write the
attributes through the "metadata" property, and through the "privateMetadata"
property where the server sets supportsPrivate. That extension adds both
properties to the FileNode object. They are common properties, available on
FileNodes of every @type, because platforms also attach named attributes to
directories and symbolic links.¶
8.1. The "xattr" Namespace
This document defines the "xattr" namespace for the "metadata" and "privateMetadata" properties. Its value is an object mapping an attribute name to the attribute's value. The attribute name is the platform-native name of the attribute or stream, stored verbatim (for example "user.comment", "com.apple.metadata:_kMDItemUserTags", or "Zone.Identifier"). Servers and clients MUST NOT normalize attribute names across platforms. The server keeps an attribute that one platform writes, even where another platform has no equivalent for it.¶
An attribute value whose bytes are valid UTF-8 MAY be a String holding that text. Any other value MUST be a Binary value as defined in [JMAP-METADATA] (for example "com.apple.FinderInfo", a binary plist, or a resource fork). The server returns a value in the form the client stored it.¶
Attribute values are ordinary metadata: they count against the size limits and quota of [JMAP-METADATA], and are copied, shared and protected by the rules there like any other namespace value. A server rejects an attribute too large for its limits with the errors [JMAP-METADATA] defines, so a large named stream such as a resource fork can only be stored where the server's limits allow it.¶
Attributes shared with every reader of the node (for example a quarantine flag) are stored under "metadata". Attributes private to the current user (for example that user's own tags) are stored under "privateMetadata" where the server supports it.¶
Example — a textual attribute and a binary one:¶
{
"metadata": {
"xattr": {
"user.comment": "draft copy",
"com.apple.FinderInfo": {
"@type": "Binary",
"base64": "VEVYVHR0eHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
}
}
}
}
¶
To set or remove a single attribute without disturbing the others, a client patches the path directly. RFC 6901 escaping applies to any "/" or "~" in the attribute name ("/" as "~1", "~" as "~0"):¶
[["FileNode/set", {
"accountId": "u1",
"update": {
"f42": {
"metadata/xattr/Zone.Identifier":
"[ZoneTransfer]\r\nZoneId=3\r\n"
}
}
}, "0"]]
¶
Setting an attribute's path to null removes it, following the "metadata" patch semantics of [JMAP-METADATA].¶
9. Implementation Considerations
9.1. Modification Timestamps
Clients SHOULD provide an updated modified value when changing
the content of a node. This gives clients control over the
timestamp, for example when preserving original modification times
during file synchronization. Clients that do not care about
preserving timestamps can set modified to null to have the server
use the current time.¶
Renaming or moving a node does not change its modified time, as
on common local filesystems. The server MUST NOT change modified
for an update that does not include it, and a client SHOULD NOT
include modified in an update that only renames or moves a node
unless it intends to change it (for example by setting it to null).¶
Clients may also choose to update the modified timestamp on a
parent directory when moving a child node in or out, to reflect
that the directory's contents have changed.¶
9.2. Timestamp Precision
The UTCDate type (Section 1.4 of [JMAP-CORE]) supports
fractional seconds.
Servers SHOULD include sub-second precision in created,
modified, accessed, and changed timestamps where their
storage backend supports it. Clients MUST preserve fractional
seconds when they send timestamps back to the server.¶
A server MAY store timestamps with less precision than a client sends, down to whole seconds. It then truncates the value it stores, and returns the stored value in FileNode/get, so a client can learn the precision in use. Clients comparing a local time with a server timestamp (for example to recognize their own changes) need to allow for this. Comparisons the server makes between timestamps use the stored precision.¶
9.3. Filename Character Restrictions
Servers should set forbiddenNameChars to include characters
that are problematic for their storage backend and for the
broadest set of clients, and may reject names that are
problematic (see "name" in "Common Properties").¶
The following characters are problematic on common platforms:¶
-
Windows:
<,>,:,",\,|,?,*, and control characters (U+0000-U+001F). Windows also forbids trailing dots and spaces in names.¶ -
macOS:
:(HFS+ legacy; displayed as/in Finder).¶ -
Linux: the null byte (U+0000).¶
The following names are problematic:¶
-
All platforms:
.and..(path traversal).¶ -
Windows: reserved device names CON, PRN, AUX, NUL, COM0-COM9, and LPT0-LPT9. These are reserved regardless of file extension (for example, "CON.txt" is also forbidden).¶
A server targeting broad client compatibility should include
at least /<>:"\|?* in forbiddenNameChars, and reject the
names . and ... Clients that sync to platforms with further
restrictions, such as the Windows reserved names, need to handle
those names locally, since other clients may create them.¶
9.4. Case Sensitivity
Windows and macOS filesystems are typically case-insensitive
(treating "Foo" and "foo" as the same name), while Linux
filesystems are typically case-sensitive. Clients syncing to
case-insensitive platforms should use compareCaseInsensitively
on FileNode/set requests to prevent creating sibling nodes
whose names differ only in case, as these cannot coexist on
the target platform.¶
9.5. Access Time Updates
Updating accessed on every file read generates significant
API traffic for little value. Clients are encouraged to use a
"relatime" strategy similar to modern POSIX filesystems: only
update accessed if the current value is older than modified,
or if more than 24 hours have elapsed since the last accessed
update. Clients should batch access time updates with their
next JMAP request rather than making a dedicated call.¶
10. Security Considerations
All security considerations from [JMAP-CORE] apply to this document.¶
10.1. Path Traversal
Clients that reconstruct filesystem paths from FileNode hierarchies
MUST validate the resulting paths and reject any that would escape
the intended directory tree. Servers SHOULD include / in
forbiddenNameChars and reject the names . and .. to
prevent path traversal attacks via node names.¶
10.2. Symlink Targets
Symlink targets are not validated by the server to point to
existing nodes. Clients that resolve symlinks MUST guard
against symlink loops (a symlink pointing to an ancestor of
itself) and MUST NOT follow symlinks outside the FileNode tree.
Servers SHOULD impose a limit on the length of target values
and MAY reject targets containing ".." path components.¶
10.3. Denial of Service
Deep nesting of FileNodes or very large numbers of children under a
single parent can consume significant server resources. Servers
SHOULD enforce reasonable limits via maxFileNodeDepth and MAY
impose additional limits on the number of children per node.
Recursive operations such as destroying a subtree with
onDestroyRemoveChildren can be expensive; servers SHOULD impose
limits on the size of subtrees that can be destroyed in a single
operation.¶
10.4. Access Control
The discoverability rules defined in this document mean that ancestor
nodes of shared content are visible (with mayRead false) to users
who have access to descendants. While these ancestor nodes do not
expose file content, their names and structure may reveal information
about the file hierarchy. Server administrators and users sharing
content should be aware of this.¶
A user who can destroy a node, but cannot discover all of its descendants, can still learn that the node has children: the destroy fails with "nodeHasChildren", or with "forbidden" when onDestroyRemoveChildren is true. Any failure reveals this. The alternatives would be to reveal the ids of the hidden nodes, or to destroy data the user cannot see, and both are worse.¶
Changes to the shareWith property on a node affect all descendants.
Removing sharing from a parent node will make its descendants
undiscoverable to the affected users, unless a descendant is
also shared with them directly, which may be surprising if
those users had been actively working with the shared files.¶
10.5. Extended Attributes
Some extended attributes carry security meaning on the platform that reads them. On Linux, "security.capability" grants capabilities to an executable, "security.selinux" sets its security label, and attributes under "trusted." and "system." (such as "system.posix_acl_access") change privileges or access control. Anyone with write access to a node can set these values, so a client that restores them lets that user change privileges on the client's machine. Clients MUST NOT apply attributes whose names begin with "security.", "trusted." or "system." to local files unless explicitly configured to by the local administrator.¶
Other attributes mark files as downloaded from an untrusted source, such as "com.apple.quarantine" on macOS and the "Zone.Identifier" stream on Windows, and the platform warns before such a file is opened. A client that drops these markers when it syncs a file lets the file bypass that warning, so clients SHOULD preserve them. A client SHOULD NOT treat the absence of a marker as evidence that a file is safe: any user with write access to the node can remove it.¶
10.6. Content Security
Servers that perform content scanning or malware detection SHOULD
scan blobs referenced by FileNode objects. The mediaType property is
client-asserted and MUST NOT be trusted for security decisions;
servers SHOULD independently verify content types where this matters.¶
11. IANA Considerations
11.1. JMAP Capability Registration for "filenode"
IANA is requested to register the "filenode" JMAP Capability as follows:¶
Capability Name: urn:ietf:params:jmap:filenode¶
Specification document: this document¶
Intended use: common¶
Change Controller: IETF¶
Security and privacy considerations: this document, Security Considerations¶
11.2. JMAP Error Codes Registration for "nodeHasChildren"
IANA is requested to register the "nodeHasChildren" JMAP Error Code as follows:¶
JMAP Error Code: nodeHasChildren¶
Intended use: common¶
Change Controller: IETF¶
Description: A FileNode could not be destroyed or replaced because it has children, and the client did not ask for them to be destroyed too.¶
Reference: this document¶
11.3. JMAP Error Codes Registration for "unsupportedEntry"
IANA is requested to register the "unsupportedEntry" JMAP Error Code as follows:¶
JMAP Error Code: unsupportedEntry¶
Intended use: common¶
Change Controller: IETF¶
Description: An archive or extraction found entries that the archive format or the server cannot represent, and the client asked for an error rather than for the entries to be skipped.¶
Reference: this document¶
11.4. JMAP Data Types Registration for "FileNode"
IANA is requested to register the "FileNode" JMAP Data Type as follows:¶
Type Name: FileNode¶
Can Reference Blobs: Yes¶
Can Use For State Change: Yes¶
Capability: urn:ietf:params:jmap:filenode¶
Reference: this document¶
11.5. JMAP FileNode Types Registry
IANA is requested to create a new "JMAP FileNode Types" registry with the following initial values. New registrations are subject to Specification Required ([IANA-GUIDELINES]). A registration MUST reference a specification that defines the properties specific to the type and whether nodes of the type may have children.¶
| Type | Description | Children | Reference |
|---|---|---|---|
| File | A regular file with blob content | No | this document |
| Directory | A collection that may contain child nodes | Yes | this document |
| SymLink | A symbolic link to another path in the tree | No | this document |
11.6. JMAP FileNode Roles Registry
IANA is requested to create a new "JMAP FileNode Roles" registry with the following initial values. New registrations are subject to Expert Review ([IANA-GUIDELINES]). The Designated Expert checks that a new role is not already covered by an existing one, and that its description says which directory a client should give it.¶
| Role | Description | Reference |
|---|---|---|
| root | The base of a filesystem | this document |
| home | A user's home directory | this document |
| temp | Temporary space; may be cleaned up automatically | this document |
| trash | Deleted data; may be removed automatically or manually | this document |
| documents | Document storage | this document |
| downloads | Downloaded files | this document |
| music | Audio files | this document |
| pictures | Photos and images | this document |
| videos | Video files | this document |
11.7. JMAP Metadata Namespace Registration for "xattr"
IANA is requested to register the "xattr" namespace in the "JMAP Metadata Namespaces" registry established by [JMAP-METADATA]:¶
Namespace Identifier: xattr¶
Value Type: String[String|Binary] — an object mapping an attribute name to
its value, as defined in "Extended Attributes and Named Streams" of this
document.¶
Applicable Properties: metadata, privateMetadata¶
Applicable Data Types: FileNode¶
Reference: this document¶
Intended Usage: common¶
Change Controller: IETF¶
12. Changes
EDITOR: please remove this section before publication.¶
The source of this document exists on github at: https://github.com/brong/draft-gondwana-jmap-filenode/¶
draft-ietf-jmap-filenode-19¶
-
The query
depthargument is counted from the top level of the account (top-level nodes are at depth 0), whatever the filter, and absent or null now means no limit rather than "do not recurse". FileNode/queryChanges takes it too.¶ -
Renaming, moving or destroying a node checks
mayRenameormayDeleteon the node itself, which for a node withoutmyRightsare its parent's rights. Moving also needsmayAddChildrenon the new parent. A Directory with a role cannot be destroyed, renamed or moved, without changing the rights it reports.¶ -
A node that stops being discoverable is reported as destroyed in FileNode/changes.¶
-
isSubscribedis per user, and setting it needs no rights beyond discovering the node.¶ -
A
shareWithchange MUST be reported as a change to every node in the subtree, including nodes withoutmyRights, so clients need not work out which nodes it affects.¶ -
Removed the
forbiddenNodeNamescapability: it could not express the names that matter (such as "CON.txt"), clients did not use it, and a server can reject names it cannot store regardless.¶ -
Removed the "newest" and "unchanged" values of
onExists. "newest" compared client clocks and was easily mistaken for a content precondition. "unchanged" duplicated, and conflicted with, [JMAP-CONDITIONAL]: to replace a node only if it is the one expected, a client destroys it and creates the replacement in one FileNode/set, with an "ifUnchangedBy" precondition on the destroy. [JMAP-CONDITIONAL] is now an informative reference.¶ -
Creating or moving a node to the top level without
mayCreateTopLevelFileNodefails with "forbidden".¶ -
Direct HTTP Write PUT and PATCH set
modifiedto the current server time.¶ -
Direct HTTP Write accepts
Repr-DigestandContent-Digest[DIGEST-FIELDS] on PUT and PATCH, rejecting a mismatch, and returns digests of the new content requested withWant-Repr-Digest.¶ -
Fixed the blob capability name in the ArchiveEntry text to
urn:ietf:params:jmap:blob2, matching [JMAP-BLOBEXT].¶ -
Renaming or moving a node does not change its
modifiedtime; the guidance to updatemodifiednow applies to content changes.¶ -
A server may store timestamps with less precision than clients send, down to whole seconds, truncating and returning the stored value.¶
-
Direct HTTP Write digests use the algorithms listed in the
supportedDigestAlgorithmscapability of [JMAP-BLOBEXT], and clients use the first one they support.¶ -
The "xattr" namespace maps an attribute name directly to its value: a String for UTF-8 text, or a [JMAP-METADATA] Binary value. Removed XattrValue and its "blobId", so attribute values are ordinary metadata and the metadata quota, copy and access rules apply to them. Removed the
maxDepthrequirement, which was wrong under [JMAP-METADATA]'s depth rules.¶ -
Added security considerations for extended attributes that grant privileges and for download markers.¶
-
Direct HTTP Write returns 404 for a node that does not exist or is not discoverable, and
mayModifyContentfor a File is its parent's. Themodifieddefinition notes that Direct HTTP Write sets it.¶ -
Direct HTTP Write PUT sets
mediaTypefrom the type and subtype of Content-Type only, without parameters.¶ -
The
textquery condition matches name and mediaType by substring rather than as a glob.¶ -
Added a children column to the "JMAP FileNode Types" registry, and guidance for the Designated Expert of the "JMAP FileNode Roles" registry.¶
-
[JMAP-BLOBEXT] and [JMAP-CONDITIONAL] now reference the working group drafts. [JMAP-MAIL], [JMAP-QUOTAS] and [UNICODE] are normative references.¶
-
Removed the TODO section, and made editorial fixes throughout: American spelling, error codes and section references written consistently, heading case, and list formatting.¶
draft-ietf-jmap-filenode-18¶
-
isSubscribed,myRights, andshareWithare defined only for nodes whose@typepermits children. Other nodes take their rights from their parent, so a sharing change no longer marks every descendant File as changed.¶ -
shareWithis the access control list on that node only;myRightsis the union of the lists on the node and its ancestors, so a descendant cannot narrow inherited rights.¶ -
A node with a
shareWithentry for the user, or with such a descendant, is discoverable.¶ -
Defined which properties FileNode/get returns for a node that is discoverable but not readable, and how FileNode/query filters and sorts treat it; body never matches it.¶
-
A destroy that would remove undiscoverable nodes fails with "forbidden".¶
-
onExists checks rights before comparing against the existing node; "newest" and "unchanged" also require
mayRead.¶ -
FileNode/copy returns a
destroyedlist for nodes replaced through onExists.¶
draft-ietf-jmap-filenode-17¶
-
Setting a property with a default to null resets it to that default (as in RFC 8620), for example
createdto the current server time andisSubscribedto true.¶ -
Setting
mediaTypeto null now resets it to "application/octet-stream" rather than being an error. (Thanks to Mark Atwood.)¶
draft-ietf-jmap-filenode-16¶
-
A name collision with an undiscoverable node returns "forbidden" without
existingId; only onExists "rename" proceeds. (Thanks to Mehdi Kouhen.)¶ -
onExists replacement requires
mayDeleteon the existing node.¶
draft-ietf-jmap-filenode-15¶
-
Replaced
nodeTypewith an@typeproperty (File, Directory, SymLink). Each type defines only the properties relevant to it; properties of other types are omitted rather than null.@typeis now required on creation instead of being inferred.¶ -
Moved
blobId,size,type, andexecutableto File,roleto Directory, andtargetto SymLink.¶ -
Renamed the File
typeproperty tomediaType, with a default of "application/octet-stream", so that it is not confused with@type. Renamed thetypeandtypeMatchfilters tomediaTypeandmediaTypeMatch, and thetypesort property tomediaType.¶ -
Added the
supportedNodeTypescapability.¶ -
Removed the
X-FileNode-Typeheader field. A Direct HTTP Write PATCH no longer changesmediaType; clients use PUT or FileNode/set.¶ -
Defined how SymLinks are archived (as symlink entries, never followed) and extracted (created as SymLinks if supported).¶
-
Added
onUnsupportedEntry("error", the default, or "skip") to the archive and extract recipes, for entries the archive format or the server cannot represent, and the "unsupportedEntry" SetError.¶ -
Defined the order among nodes that lack a sort property.¶
-
Breaking change: the node type values changed from "file", "directory", and "symlink" to "File", "Directory", and "SymLink". Clients MUST now provide
@typeon creation.¶ -
FileNode/get always returns
@type, and omits requested properties that are not defined for a node's@type.¶ -
Query filters on type-specific properties now match only nodes of that type, for both true and false values. For example,
hasAnyRole: falseno longer matches Files or SymLinks, andisExecutable: falseno longer matches Directories or SymLinks.¶ -
Added "unchanged" value for
onExists, replacing a colliding node only when a precondition on that node, supplied via "ifUnchangedBy" ([JMAP-CONDITIONAL]), is satisfied. The precondition binds to the current id of the node that occupies the contested name, so a client can only replace the one node it expected.¶ -
Added Extended Attributes and Named Streams: file nodes carry POSIX xattrs, macOS extended attributes, NTFS/SMB alternate data streams, and NFSv4 named attributes via the JMAP Object Metadata extension ([JMAP-METADATA]), defining the "xattr" namespace (attribute name to an XattrValue holding either inline "data" or a "blobId" for large/binary values). Registered the "xattr" namespace with IANA.¶
-
Escaped bracket characters in "String[]" type signatures.¶
-
Editorial: applied a subset of ASD-STE100 Simplified Technical English — removed "e.g."/"i.e.", split over-long sentences (notably
onExists"unchanged"), and reworded noun clusters and idioms. No normative change.¶
draft-ietf-jmap-filenode-14¶
-
Added "newest" value for
onExists.¶
draft-ietf-jmap-filenode-13¶
-
Reordered methods to get/changes/set/copy/query/queryChanges per RFC 8620 convention.¶
-
Promoted Direct HTTP Write to top-level section.¶
-
Moved Access Control before Quotas and integrations.¶
-
Added caseInsensitiveNames capability and compareCaseInsensitively parameter on FileNode/set.¶
draft-ietf-jmap-filenode-12¶
-
Added symlink support: nodeType property (file, directory, symlink) with IANA registry, and target property (array of path elements).¶
-
Replaced isFile/isDirectory query filters and sort with nodeType.¶
-
Expanded myRights: replaced mayWrite with mayAddChildren, mayRename, mayDelete, mayModifyContent.¶
-
Added forbiddenNameChars and forbiddenNodeNames capabilities; removed hardcoded name restrictions from spec.¶
-
Added changed (server-set) timestamp.¶
-
Added Implementation Considerations section with guidance on timestamps, access times, and filename restrictions.¶
draft-ietf-jmap-filenode-11¶
-
Updated for blobext recipe renames (ExtractRecipe).¶
draft-ietf-jmap-filenode-10¶
-
Added ExtractRecipe parentNodeId extension for extracting archives directly into FileNode trees.¶
draft-ietf-jmap-filenode-09¶
-
Renamed hasType filter to isFile and isDirectory (separate filters).¶
-
Renamed hasRole filter to role for consistency.¶
-
Defined glob matching syntax for nameMatch/typeMatch.¶
-
Added fetchParents argument to FileNode/get.¶
-
Added descendantId filter to FileNode/query.¶
draft-ietf-jmap-filenode-08¶
-
Removed immutability restriction on blobId, size, and type — nodes can now have their content updated via FileNode/set.¶
-
Added webWriteUrlTemplate capability for direct HTTP PUT/PATCH writes to file nodes.¶
-
Added documents, downloads, music, pictures, and videos roles.¶
-
Added Quotas section referencing RFC 9425.¶
draft-ietf-jmap-filenode-07¶
-
added webTrashUrl and webUrlTemplate to the capabilities object¶
-
added capability example JSON¶
-
added explicit Errors sections to FileNode/set and FileNode/copy¶
-
fixed typos and spelling errors¶
-
added BCP 14 boilerplate¶
-
fleshed out Security Considerations¶
-
replaced role TODO with IANA registry¶
-
documented webUrlTemplate variables¶
-
clarified modified/accessed semantics: client-managed with null to reset to server time¶
-
clarified blobId constraints and blob lifetime¶
-
documented myRights inheritance and FileNode/changes behaviour when shareWith changes¶
-
wrapped long lines throughout¶
-
added Integration with JMAP Blob Extensions section (ArchiveEntry extension with nodeId and recurse)¶
draft-ietf-jmap-filenode-06¶
-
Documented FileNode/copy and detailed that it has the same new top-level keys¶
draft-ietf-jmap-filenode-05¶
-
Renamed onDuplicate to onExists for name alignment¶
-
added "role" for directories¶
-
added a "TODO" for putting more restrictions on node names¶
-
changed hasParentId to isTopLevel with reversed boolean meaning¶
draft-ietf-jmap-filenode-04¶
-
Documented that blobId, size, and type are immutable after creation.¶
-
Documented that creating a file and deleting the old copy of the file within a transaction is legal.¶
-
Added onDuplicate top-level option for FileNode/set, giving both "rename" and "replace" options.¶
-
Updated the definition of shareWith to say that the keys of the hash are Ids, not arbitrary strings¶
draft-ietf-jmap-filenode-03¶
-
Added 'text' and 'body' searches (added JMAP-MAIL reference as additional information for body search)¶
-
Updated JMAP-CONTACTS and JMAP-SHARING references to published RFC numbers rather than draft names¶
-
Noted that the server MUST included the nodeids of deleted child nodes.¶
-
Added isSubscribed¶
-
Renamed mayAdmin to mayShare to align with other specs¶
-
Described the inheritance of ACLs¶
draft-ietf-jmap-filenode-02¶
-
Convert to Kramdown-RFC format (no intentional changes)¶
draft-ietf-jmap-filenode-01¶
-
Refreshing draft only¶
draft-ietf-jmap-filenode-00¶
-
upload as a working group document¶
draft-gondwana-jmap-filenode-01¶
-
require a blobId for the zero-byte file¶
-
make size also null for collections¶
-
add more to the TODO section¶
-
bikeshed; FileNode¶
-
correct UTCDate, UnsignedInt, and normalised UTF-8.¶
-
add some fields to the capabilities object¶
draft-gondwana-jmap-filenode-00¶
-
initial proposal¶
13. Acknowledgements
Mark Atwood, Ben Bucksch, Mauro De Gennaro, Seph Gentle, Arnt Gulbradson, Neil Jenkins, Mehdi Kouhen, Ricardo Signes, and members of the JMAP Working Group at IETF.¶
{backmatter}¶
14. References
14.1. Normative References
- [DIGEST-FIELDS]
- Polli, R. and L. Pardue, "Digest Fields", RFC 9530, DOI 10.17487/RFC9530, , <https://www.rfc-editor.org/rfc/rfc9530>.
- [IANA-GUIDELINES]
- Cotton, M., Leiba, B., and T. Narten, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 8126, DOI 10.17487/RFC8126, , <https://www.rfc-editor.org/rfc/rfc8126>.
- [JMAP-BLOBEXT]
- Gondwana, B., "JMAP Blob Management", Work in Progress, Internet-Draft, draft-ietf-jmap-blobext-02, , <https://datatracker.ietf.org/doc/html/draft-ietf-jmap-blobext-02>.
- [JMAP-CORE]
- Jenkins, N. and C. Newman, "The JSON Meta Application Protocol (JMAP)", RFC 8620, DOI 10.17487/RFC8620, , <https://www.rfc-editor.org/rfc/rfc8620>.
- [JMAP-MAIL]
- Jenkins, N. and C. Newman, "The JSON Meta Application Protocol (JMAP) for Mail", RFC 8621, DOI 10.17487/RFC8621, , <https://www.rfc-editor.org/rfc/rfc8621>.
- [JMAP-METADATA]
- De Gennaro, M., "JMAP Object Metadata", Work in Progress, Internet-Draft, draft-ietf-jmap-metadata-04, , <https://datatracker.ietf.org/doc/html/draft-ietf-jmap-metadata-04>.
- [JMAP-QUOTAS]
- Cordier, R., Ed., "JSON Meta Application Protocol (JMAP) for Quotas", RFC 9425, DOI 10.17487/RFC9425, , <https://www.rfc-editor.org/rfc/rfc9425>.
- [JMAP-SHARING]
- Jenkins, N., Ed., "JSON Meta Application Protocol (JMAP) Sharing", RFC 9670, DOI 10.17487/RFC9670, , <https://www.rfc-editor.org/rfc/rfc9670>.
- [MEDIATYPE]
- Freed, N., Klensin, J., and T. Hansen, "Media Type Specifications and Registration Procedures", BCP 13, RFC 6838, DOI 10.17487/RFC6838, , <https://www.rfc-editor.org/rfc/rfc6838>.
- [RFC2119]
- Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/rfc/rfc2119>.
- [RFC8174]
- Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/rfc/rfc8174>.
- [UNICODE]
- Klensin, J. and M. Padlipsky, "Unicode Format for Network Interchange", RFC 5198, DOI 10.17487/RFC5198, , <https://www.rfc-editor.org/rfc/rfc5198>.
- [URI-TEMPLATE]
- Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., and D. Orchard, "URI Template", RFC 6570, DOI 10.17487/RFC6570, , <https://www.rfc-editor.org/rfc/rfc6570>.
14.2. Informative References
- [CALDAV]
- Daboo, C., Desruisseaux, B., and L. Dusseault, "Calendaring Extensions to WebDAV (CalDAV)", RFC 4791, DOI 10.17487/RFC4791, , <https://www.rfc-editor.org/rfc/rfc4791>.
- [CARDDAV]
- Daboo, C., "CardDAV: vCard Extensions to Web Distributed Authoring and Versioning (WebDAV)", RFC 6352, DOI 10.17487/RFC6352, , <https://www.rfc-editor.org/rfc/rfc6352>.
- [JMAP-CALENDARS]
- Jenkins, N. and M. Douglass, "JSON Meta Application Protocol (JMAP) for Calendars", Work in Progress, Internet-Draft, draft-ietf-jmap-calendars-33, , <https://datatracker.ietf.org/doc/html/draft-ietf-jmap-calendars-33>.
- [JMAP-CONDITIONAL]
- Gondwana, B., "JMAP Conditional Set", Work in Progress, Internet-Draft, draft-ietf-jmap-conditional-02, , <https://datatracker.ietf.org/doc/html/draft-ietf-jmap-conditional-02>.
- [JMAP-CONTACTS]
- Jenkins, N., Ed., "JSON Meta Application Protocol (JMAP) for Contacts", RFC 9610, DOI 10.17487/RFC9610, , <https://www.rfc-editor.org/rfc/rfc9610>.
- [WEBDAV]
- Dusseault, L., Ed., "HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)", RFC 4918, DOI 10.17487/RFC4918, , <https://www.rfc-editor.org/rfc/rfc4918>.