Attachments and Storage
NodeWarden stores attachment metadata separately from file bodies.
Storage locations
| Data | Location |
|---|---|
Attachment ID, cipher_id, encrypted file name, size, encrypted key | D1 attachments table |
| Attachment binary body | R2 ATTACHMENTS or KV ATTACHMENTS_KV |
| Send file body | R2/KV with the sends/ key prefix |
Attachment object keys use:
<cipherId>/<attachmentId>Send file object keys use:
sends/<sendId>/<fileId>R2 first
src/services/blob-store.ts chooses storage in this order:
- If
ATTACHMENTSis bound, use R2. - Otherwise, if
ATTACHMENTS_KVis bound, use KV. - If neither is bound, uploads fail with “Attachment storage is not configured”.
Size limits
NodeWarden's default attachment limit is 100 MB, but KV mode is capped at 25 MiB because of Cloudflare KV object limits.
For stable attachment and Send file support, use R2.
Upload flow
Official clients usually upload attachments in two steps:
POST /api/ciphers/{cipherId}/attachment/v2creates attachment metadata.- The client receives an upload URL with a short token, then uploads the binary body.
The upload token is a short-lived JWT containing:
userIdcipherIdattachmentIdexp
The server verifies the token, cipher ownership, and attachment ownership before writing the file to R2 or KV.
Download flow
Downloads also use short-lived tokens:
- An authenticated user requests attachment information.
- The server generates a download token valid for 5 minutes.
- The client opens
/api/attachments/{cipherId}/{attachmentId}?token=.... - The server verifies the token, checks that the attachment exists, and reads R2 or KV.
Download tokens include jti. The server records consumed jti values in used_attachment_download_tokens, so the same download token can only be used once.
Delete flow
When deleting a cipher or attachment, the server deletes the R2/KV object first, then deletes D1 metadata. Bulk attachment deletion uses small concurrency so one Worker request does not overload storage operations.