feat: Sprint 5 — encrypted file transfer with chunked upload/download

- Add uploadBlob (@21) and downloadBlob (@22) RPCs to Cap'n Proto
  schema with SHA-256 content addressing and chunked transfer
- Server blob handler: 256KB chunks, SHA-256 verification on finalize,
  .meta JSON sidecar, 50MB size limit, content-addressable storage
- Add FileRef (0x08) AppMessage variant with blob_id, filename,
  file_size, mime_type
- /send-file command: read file, compute hash, upload in chunks with
  progress display, send FileRef via MLS, MIME auto-detection
- /download command: fetch blob in chunks with progress, verify hash,
  save to disk with collision avoidance
- 2 new E2E tests: upload/download round-trip with partial reads,
  hash mismatch rejection (14 E2E tests total)
- New error codes: E024-E027 for blob operations
This commit is contained in:
2026-03-04 00:27:18 +01:00
parent 81d5e2e590
commit 3350d765e5
12 changed files with 1086 additions and 8 deletions

View File

@@ -100,6 +100,22 @@ interface NodeService {
# Reverse lookup: resolve an Ed25519 identity key to the registered username.
# Returns empty Text if the identity key is not associated with any user.
resolveIdentity @20 (identityKey :Data, auth :Auth) -> (username :Text);
# Upload a blob chunk. The server reassembles chunks and verifies SHA-256 on completion.
# blobHash : expected SHA-256 hash (32 bytes) of the complete blob.
# chunk : raw bytes for this segment.
# offset : byte offset within the blob where this chunk starts.
# totalSize : total size of the complete blob in bytes.
# mimeType : MIME type of the blob (e.g. "image/png").
# Returns blobId = blobHash once the blob is fully uploaded and verified.
uploadBlob @21 (auth :Auth, blobHash :Data, chunk :Data, offset :UInt64, totalSize :UInt64, mimeType :Text) -> (blobId :Data);
# Download a blob chunk. Returns up to `length` bytes starting at `offset`.
# blobId : the blob identifier (SHA-256 hash returned by uploadBlob).
# offset : byte offset within the blob to start reading from.
# length : maximum number of bytes to return (capped at 256 KB).
# Returns the requested chunk, the total blob size, and its MIME type.
downloadBlob @22 (auth :Auth, blobId :Data, offset :UInt64, length :UInt32) -> (chunk :Data, totalSize :UInt64, mimeType :Text);
}
struct Auth {