JMAP download returns 404 for directly-shared FileNode blobs despite mayRead = true (works when shared via parent folder)

Issue Description

When a FileNode is shared directly with another user, the recipient can see the file via FileNode/get (with myRights.mayRead = true), but downloading the blob through /jmap/download/... returns 404 Not Found. The owner can download the same blob fine, and the recipient can also download it if the parent folder is shared instead of the file itself. This points to a difference in the download-path ACL evaluation between directly-shared files and files accessed through a shared folder.

The file under test:

  • id = fileId123
  • blobId = blobId123
  • name = file.txt
  • type = text/plain
  • located in sharedAccount

Expected Behavior

When a recipient has read access to a FileNode (confirmed by FileNode/get returning the file with myRights.mayRead = true), the associated blob should be downloadable via /jmap/download/{accountId}/{blobId}/{name}, returning 200 OK — regardless of whether access was granted by sharing the file directly or by sharing a parent folder.

Actual Behavior

Case A — file shared directly:

  • FileNode/get (recipient): file is visible, myRights.mayRead = true
  • GET /jmap/download/sharedAccount/blobId123/file.txt?accept=text/plain404 Not Found
  • Same request as the owner → 200 OK (so the blob exists)
    Case A — file shared directly:
    Case B — file inside a shared folder:
  • GET /jmap/download/sharedAccount/blobId123/file.txt?accept=text/plain (recipient) → 200 OK

Using an arbitrary file name in the URL still returns 404 in Case A, so the failure is not related to the {name} segment.

Reproduction Steps

  1. As [email protected] (account ownerAccount), create a FileNode in sharedAccount with id = fileId123, blobId = blobId123, name = file.txt, type = text/plain.
  2. Share that file directly with [email protected] (account recipientAccount).
  3. As the recipient, call FileNode/get for the file → file is returned, myRights.mayRead = true.
  4. As the recipient: GET /jmap/download/sharedAccount/blobId123/file.txt?accept=text/plain404 Not Found.
  5. As the owner, issue the same download request → 200 OK.
  6. Remove the direct share. Place the file inside a folder (folderId123) and share the folder with the recipient.
  7. As the recipient, issue the same download request → 200 OK.

Stalwart Version

v0.16.x

Installation Method

Binary (Linux)

Database Backend

PostgreSQL

Blob Storage

PostgreSQL

Search Engine

Meilisearch

Directory Backend

Internal

Additional Context

Optional hypothesis (not confirmed): the blob-access check on the download path for FileNode-linked blobs may resolve items reachable through a shared folder/hierarchy but not items shared individually. Workaround: share the parent folder instead of the file directly, which makes the JMAP download succeed (200 OK).

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

Let me look into the blob download ACL logic in the Stalwart server source code to understand how download access is checked for FileNode blobs.

So, what did you get? @helpbot

This has been fixed. The fix will be included in v0.16.7.