lib/fileEncryption.ts has correct, standalone AES-256-GCM encrypt/decrypt/upload/download logic, but nothing calls it — there is no file-attach input anywhere in the live conversation UI. This depends on the session/envelope fixes, since the file's symmetric key needs to travel inside a working per-device envelope.
Acceptance criteria:
app/app/conversations/[id]/page.tsx gets a file-attach control that calls encryptFile/requestPresignedUpload/uploadCiphertextToS3/sendEncryptedFile
- The file's decryption key travels inside the per-device envelope ciphertext, not as a separate plaintext field
- Receiving a file message calls
fetchPresignedDownload/downloadAndDecryptFile to retrieve and decrypt it client-side
- Coordinated with the backend's file-message envelope migration — client and server land together
- A test exercises the encrypt→upload→download→decrypt round trip against the now-real upload endpoints
lib/fileEncryption.tshas correct, standalone AES-256-GCM encrypt/decrypt/upload/download logic, but nothing calls it — there is no file-attach input anywhere in the live conversation UI. This depends on the session/envelope fixes, since the file's symmetric key needs to travel inside a working per-device envelope.Acceptance criteria:
app/app/conversations/[id]/page.tsxgets a file-attach control that callsencryptFile/requestPresignedUpload/uploadCiphertextToS3/sendEncryptedFilefetchPresignedDownload/downloadAndDecryptFileto retrieve and decrypt it client-side