import { CryptoService } from '../crypto/crypto-service'; import { WireAttachmentRef } from '../transport/protobuf-codec'; interface DeviceKeyInfo { deviceId: string; publicKey: string; } import { AttachmentRef, CreateUploadSessionOptions, DownloadAttachmentOptions, DownloadedAttachment, PrepareAttachmentOptions, UploadSession } from './attachment-types'; interface AttachmentClientDeps { httpUrl: string; fetchFn: typeof fetch; getValidDropOnAirJwt: () => Promise; fetchDeviceKeys: (userId: string) => Promise; fetchMyOtherDeviceKeys: (myDeviceId: string) => Promise; getCurrentUserId: () => string; getCurrentDeviceId: () => Promise; getKeyStorage: () => { get(k: string): Promise; }; cryptoService: CryptoService; log: (stage: string, data?: Record) => void; logError: (stage: string, data?: Record) => void; } /** * SDK-side coordinator for attachments. Handles: * - createUploadSession: reserves an attachmentId + presigned PUT URL. * - uploadBytes: PUTs bytes directly to the customer's bucket. * - finalize: confirms upload + commits sha256. * - prepareAttachmentAndUpload: convenience wrapper that does upload session, * optional E2EE encryption + per-device key wrap, PUT, finalize, and returns * a fully-populated AttachmentRef ready to drop into sendMessage. * - downloadAttachment: fetches presigned GET, downloads, decrypts if E2EE. * * DropOnAir never holds file bytes - the SDK transfers bytes directly between * the client and the customer's storage bucket. */ export declare class AttachmentClient { private readonly deps; constructor(deps: AttachmentClientDeps); createUploadSession(opts: CreateUploadSessionOptions): Promise; finalize(attachmentId: string, sha256: string): Promise; /** * Convenience: encrypts bytes if E2EE, uploads to the customer's bucket, * finalizes, and returns a fully-formed AttachmentRef for sendMessage. */ prepareAttachmentAndUpload(input: Uint8Array, opts: PrepareAttachmentOptions): Promise; /** * Revoke an attachment you sent. After revoke the platform * refuses to issue further download URLs and recipients with a live * connection get an ATTACHMENT_REVOKED event. Bytes a recipient already * downloaded cannot be recalled. */ revokeAttachment(attachmentId: string): Promise; getDownloadUrl(attachmentId: string): Promise<{ url: string; method: string; headers: Record; mimeType: string; sizeBytes: number; sha256: string; }>; /** * Downloads + decrypts an attachment. For E2EE, finds the wrappedKey for the * current device, unwraps it, then decrypts the stored bytes. */ downloadAttachment(ref: AttachmentRef, opts?: DownloadAttachmentOptions): Promise; /** Best effort: a missed confirmation only means the copy waits for its own expiry. */ private confirmReceived; /** Convert public AttachmentRef into the wire-format type for proto encoding. */ toWire(ref: AttachmentRef): WireAttachmentRef; /** Convert a wire AttachmentRef (from a received message) into the public type. */ static fromWire(wire: WireAttachmentRef): AttachmentRef; /** * Sends a file a part at a time, skipping the parts the storage already has. * * Each part is asked for a link of its own, sent, and its tag kept; when they are * all there the storage puts them together. An upload that broke leaves its parts * behind, so trying again sends only what is missing. */ private uploadInParts; /** What the storage already holds for this upload. */ private partsAlreadyStored; private partUrl; private finishParts; private uploadBytes; private wrapKeyForRecipients; private unwrapKey; /** * HKDF for the attachment file-key wrap. Sender and recipient both compute * the same X25519 shared secret (by symmetry of curve25519) and derive the * same AES-GCM key via this HKDF. Distinct info/salt from the message-payload * HKDF in CryptoService to avoid key reuse across protocols. */ private deriveAttachmentWrapKey; } export {};