Internet-Draft JMAP FileNode March 2026
Gondwana Expires 15 September 2026 [Page]
Workgroup:
Network Working Group
Internet-Draft:
draft-ietf-jmap-filenode-07
Updates:
8620 (if approved)
Published:
Intended Status:
Standards Track
Expires:
Author:
B. Gondwana
Fastmail

JMAP File Storage extension

Abstract

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.

This extension adds a method 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 15 September 2026.

Table of Contents

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 [JMAP-CORE], Section 2.

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 datatype. 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 (i.e., 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.

  • 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 recognise (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. (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.

  • 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.

2.1.1. Capability Example

{
  "urn:ietf:params:jmap:filenode": {
    "maxFileNodeDepth": 50,
    "maxSizeFileNodeName": 255,
    "fileNodeQuerySortOptions": [
      "name", "type", "size", "created", "modified",
      "hasType", "tree"
    ],
    "mayCreateTopLevelFileNode": false,
    "webTrashUrl": "https://files.example.com/trash",
    "webUrlTemplate": "https://files.example.com/view/{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 FileNode can refer to either a collection or a resource.

The following JMAP Methods are selected by the urn:ietf:params:jmap:filenode capability.

3.1. FileNode objects

The filenode object has the following keys:

  • 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.

  • blobId: "Id|null"

    The blobId for the content of this node, or null if this node is a collection. MUST be non-null for file nodes (including zero byte files). Creating a file node without a blobId is an "invalidProperties" error. The blobId is immutable after creation. A blob referenced by a FileNode MUST NOT be expired or garbage collected by the server while the FileNode exists.

  • size: "UnsignedInt|null" (server-set)

    The size in bytes of the associated blob data. This MUST be null if, and only if, the blobId is null. Size is optional on create, but if provided it MUST match the size of the provided blobId, or be null if the node is a directory. The size is immutable after creation.

  • 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. Servers MAY reject names that violate server policy (e.g., names containing control characters). Further:

    • The name MUST NOT be "." or ".."

    • The name MUST NOT contain a "/"

    Servers MAY reject names containing additional characters that are problematic on common platforms (e.g. "\", ":", "*", "?", """, "<", ">", "|") with an "invalidProperties" error.

    A server MUST order creation and deletion operations within a single FileNode/set such that the sibling constraint is retained at the end of the transaction, but replacing an existing file can be done atomically.

  • type: "String|null"

    The media type of the FileNode. This MUST be null if, and only if, the node does not have a blobId. Valid values are found in the IANA media-types registry. Servers MUST NOT reject media types that are not recognised. Servers MUST reject media types if the value does not conform to the ABNF of [MEDIATYPE] Section 4.2. The type is immutable after creation.

  • 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; clients SHOULD provide an updated value when modifying the node. This gives clients control over the timestamp, for example when preserving original modification times during file synchronization. If the client does not include modified in an update, the server MUST leave it unchanged. If the client sets modified to null, the server MUST set it to the current server time.

  • 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.

  • executable: "Boolean" (default: false)

    If true, the node should be treated as an executable by operating systems that support this flag.

  • isSubscribed: "Boolean" (default: true)

    This property is stored per user, and if true, the node should be displayed to the current user. Some servers may not allow this field to be changed for some or all nodes, e.g. only for directories, or only for nodes on which the shareWith ACL is actually set for this user, not nodes which inherit their ACL from a parent.

  • 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: Boolean The user may read the contents of this node.

    • mayWrite: Boolean The user may modify the properties of this node, including renaming or deleting children.

    • mayShare: Boolean The 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. The owner of the node MUST NOT be in this set. This is null if the user requesting the object does not have myRights.mayShare, or if the node is not shared with anyone.

  • role: "String|null"

    An indication that this directory has a special role. The role MUST be null for files. Values are registered in the "JMAP FileNode Roles" registry (see IANA Considerations). The initial values are:

    • "root" - the base of a filesystem.

    • "home" - a user's home directory.

    • "temp" - a temporary space; contents may be cleaned up automatically by the server.

    • "trash" - a place where deleted data is moved to; contents may be deleted either automatically or manually.

3.2. FileNode Methods

3.2.1. 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 an error, and an invalidProperties error will be returned.

There are these additional top level arguments:

  • onDestroyRemoveChildren: "Boolean" (default: false)

    If false, an attempt to destroy a FileNode which is the parentId of another FileNode will be rejected with a nodeHasChildren error. NOTE: if all the child nodes are being destroyed in the same operation, then the server MUST NOT return this error. Servers MUST either sort the destroys children before parents, or only check this constraint on the final state, remembering that JMAP set operations must be atomic.

    If true, then all child nodes will also be destroyed when a node is destroyed. When deleting child nodes, the server MUST include the ids of all deleted nodes in the method response.

  • onExists: "String|null" (default: null)

    If null, an attempt to create or update a FileNode which would cause a name collision will be rejected by the server with an "alreadyExists" error.

    If "replace", the existing item will be destroyed. In this case, the server MUST include the id of the replaced item in the destroyed response list. NOTE: if the replaced item is a directory which has children, then the server MUST respond with a nodeHasChildren error to this action unless onDestroyRemoveChildren is true.

    If "rename", the server will change the "name" field to not clash, using an algorithm of its choice. If the server changes the name, it MUST include the new "name" value in the created or updated response field for this id.

Errors (in addition to standard SetError codes from [JMAP-CORE]):

  • "alreadyExists" — a create or update would cause a name collision with an existing sibling FileNode, and onExists is null. The SetError object MUST include an existingId property with the id of the existing FileNode.

  • "invalidProperties" — an attempt was made to move a node to a descendant of itself, creating a cycle in the tree.

  • "nodeHasChildren" — a destroy was attempted on a FileNode that has children, and onDestroyRemoveChildren is false.

3.2.2. FileNode/copy

This is a standard Foo/copy function with the same additional top-level arguments as FileNode/set, onDestroyRemoveChildren and onExists, with the same behaviour.

Errors: the same additional errors as FileNode/set apply.

3.2.3. FileNode/get

This is a standard Foo/get method.

3.2.4. FileNode/changes

This is a standard Foo/changes method.

3.2.5. FileNode/query

This is a standard Foo/query method except for the following:

There's one more property to the query:

  • depth: "UnsignedInt|null"

    The number of levels of subdirectories to recurse into. If absent, null, or zero, do not recurse.

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.

  • hasType: "Boolean"

    If true, the FileNode must be a file/resource, not a directory/collection.

  • hasRole: "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"

    Does a glob match of the specified name against the name property of the node.

  • type: "String"

    A FileNode must have exactly the same octets in its type property to match the condition.

  • typeMatch: "String"

    Does a glob match of the specified type against the type property of the node.

  • 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"

    Is equivalent to body OR nameMatch OR typeMatch.

It also supports the following additional sort properties:

  • tree:

    Sort by tree; which means by name, but any directory/collection node is immediately followed by the recursive application of the same sort to its child nodes. This is similar to the output of the find command on a filesystem with the depth parameter provided above.

  • hasType:

    Sort directories before files (false sorts before true)

  • type:

    Sorts directories first, and sorts by media type for files

3.2.6. FileNode/queryChanges

This is a standard Foo/queryChanges method.

4. Integration with JMAP Blob Extensions

When a server advertises both urn:ietf:params:jmap:filenode and urn:ietf:params:jmap:blobext, the ArchiveEntry object type (defined in [JMAP-BLOBEXT]) is extended with two additional properties:

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.

4.1. Examples

To archive an entire directory tree rooted at FileNode "dirabc" into a zip file:

json [["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:

json [["Blob/convert", { "accountId": "account1", "create": { "selective": { "archive": { "type": "application/zip", "entries": [ { "nodeId": "filenode1", "name": "README.txt" }, { "nodeId": "dirnode2", "name": "docs/", "recurse": true } ] } } } }, "R1"]]

5. Access Control

Since nodes create a tree, ACLs created by shareWith automatically apply to children as well, so if mayRead is set on a node, all its child nodes are also readable. The myRights property on every node reflects the derived rights for the current user, taking inheritance into account.

When a shareWith change on a node causes the derived myRights to change for any descendant, the server MUST report those descendants as changed in FileNode/changes responses. The server SHOULD NOT report descendants whose derived myRights did not actually change, but MAY do so if it cannot efficiently determine whether the derived values differ.

If a server does not support changing access on non-directory nodes, it can set mayShare to false on those nodes, even if the parent directory has true.

Nodes which are not "discoverable" MUST return notFound errors if fetched with FileNode/get and MUST NOT be returned in response to FileNode/query. Nodes are discoverable in one of two ways:

  1. If the node or an ancestor of the node has mayRead true.

  2. If the node is an ancestor of a node which has mayRead true.

In the second case, the node itself will have mayRead false, and appears only to give the full path to the visible nodes. 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.

This leads to a sharee seeing the full path to anything that they have access to, but nothing else.

E.g. in a unix homedirectory environment where user Alice had shared the "forBob" folder, one might see, when logged in as Bob:

/home
  /alice
    /shared
      /forBob
        file1.jpg
        file2.txt
        ...
  /bob
    bobfile.txt
    ...

While Alice would see many more files and folders at the home directory level.

This does lead to potentially large changes in the visible Node set, so 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. Security considerations

All security considerations from [JMAP-CORE] apply to this document.

6.1. Path Traversal

FileNode names MUST NOT contain "/" or be "." or "..". Servers MUST enforce these constraints. Clients that reconstruct filesystem paths from FileNode hierarchies MUST validate the resulting paths and reject any that would escape the intended directory tree.

6.2. 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.

6.3. 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.

Changes to the shareWith property on a node affect all descendants. Removing sharing from a parent node will make all descendants undiscoverable to the affected users, which may be surprising if those users had been actively working with the shared files.

6.4. Content Security

Servers that perform content scanning or malware detection SHOULD scan blobs referenced by FileNode objects. The type property is client-asserted and MUST NOT be trusted for security decisions; servers SHOULD independently verify content types where this matters.

7. IANA considerations

7.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

7.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: The node being destroyed is still referenced by other nodes which have not been destroyed.

Reference: this document

7.3. 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

7.4. 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]).

Table 1
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

8. TODO

9. 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-07

draft-ietf-jmap-filenode-06

draft-ietf-jmap-filenode-05

draft-ietf-jmap-filenode-04

draft-ietf-jmap-filenode-03

draft-ietf-jmap-filenode-02

draft-ietf-jmap-filenode-01

draft-ietf-jmap-filenode-00

draft-gondwana-jmap-filenode-01

draft-gondwana-jmap-filenode-00

10. Acknowledgements

Neil Jenkins and the JMAP working group at the IETF.

{backmatter}

11. References

11.1. Normative References

[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 Extensions", Work in Progress, Internet-Draft, draft-gondwana-jmap-blobext-00, , <https://datatracker.ietf.org/doc/html/draft-gondwana-jmap-blobext-00>.
[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-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>.
[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>.

11.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-26, , <https://datatracker.ietf.org/doc/html/draft-ietf-jmap-calendars-26>.
[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>.
[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>.
[UNICODE]
Klensin, J. and M. Padlipsky, "Unicode Format for Network Interchange", RFC 5198, DOI 10.17487/RFC5198, , <https://www.rfc-editor.org/rfc/rfc5198>.
[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>.

Author's Address

Bron Gondwana
Fastmail