An encrypted personal cloud built on the AT Protocol.
at main 162 lines 4.9 kB view raw view rendered
1# Document Operations 2 3## Upload 4 5Encrypts a file and uploads it as an opaque blob with a metadata record. 6 7```mermaid 8sequenceDiagram 9 participant User 10 participant CLI 11 participant Crypto 12 participant PDS 13 14 User->>CLI: opake upload photo.jpg 15 16 CLI->>CLI: Read file from disk, detect MIME type 17 CLI->>Crypto: generate_content_key() 18 Crypto-->>CLI: random AES-256-GCM key K 19 20 CLI->>Crypto: encrypt_blob(K, plaintext) 21 Crypto-->>CLI: { ciphertext, nonce } 22 23 CLI->>PDS: com.atproto.repo.uploadBlob (ciphertext) 24 PDS-->>CLI: blob ref { $link, size } 25 26 CLI->>Crypto: wrap_key(K, owner_pubkey, owner_did) 27 Crypto-->>CLI: wrappedKey (x25519-hkdf-a256kw) 28 29 CLI->>Crypto: encrypt_metadata(K, {name, mimeType, size, tags, ...}) 30 Crypto-->>CLI: encryptedMetadata { ciphertext, nonce } 31 32 CLI->>PDS: com.atproto.repo.createRecord (document) 33 PDS-->>CLI: { uri, cid } 34 35 CLI->>User: Uploaded: at://did/app.opake.document/<tid> 36``` 37 38## Download (Own Files) 39 40Fetches a document you own, unwraps the content key, and decrypts. 41 42```mermaid 43sequenceDiagram 44 participant User 45 participant CLI 46 participant PDS 47 participant Crypto 48 49 User->>CLI: opake download photo.jpg 50 51 CLI->>CLI: Resolve filename → AT-URI (via listRecords if needed) 52 53 CLI->>PDS: com.atproto.repo.getRecord (document) 54 PDS-->>CLI: Document record (envelope, blob ref) 55 56 CLI->>CLI: Find wrappedKey matching own DID 57 CLI->>Crypto: unwrap_key(wrappedKey, private_key) 58 Crypto-->>CLI: content key K 59 60 CLI->>PDS: com.atproto.sync.getBlob (did, cid) 61 PDS-->>CLI: ciphertext bytes 62 63 CLI->>Crypto: decrypt_blob(K, nonce, ciphertext) 64 Crypto-->>CLI: plaintext 65 66 CLI->>CLI: Write plaintext to disk 67 CLI->>User: Saved to ./photo.jpg 68``` 69 70## Download (Shared Files — Cross-PDS) 71 72Downloads a file shared with you by another user. Requires the grant URI (auto-discovery via `inbox` is not yet implemented). 73 74```mermaid 75sequenceDiagram 76 participant User 77 participant CLI 78 participant PLC as PLC Directory 79 participant OwnerPDS as Owner's PDS 80 participant Crypto 81 82 User->>CLI: opake download --grant at://did:plc:owner/.../grant-tid 83 84 CLI->>CLI: Parse grant URI, extract owner DID 85 86 CLI->>PLC: GET /did:plc:owner (DID document) 87 PLC-->>CLI: { service: [{ #atproto_pds: owner-pds-url }] } 88 89 CLI->>OwnerPDS: com.atproto.repo.getRecord (grant) 90 OwnerPDS-->>CLI: Grant record { document, wrappedKey } 91 92 CLI->>Crypto: unwrap_key(grant.wrappedKey, private_key) 93 Crypto-->>CLI: content key K 94 95 CLI->>OwnerPDS: com.atproto.repo.getRecord (document) 96 OwnerPDS-->>CLI: Document record { blob, encryption.nonce } 97 98 CLI->>OwnerPDS: com.atproto.sync.getBlob (did, cid) 99 OwnerPDS-->>CLI: ciphertext bytes 100 101 CLI->>Crypto: decrypt_blob(K, nonce, ciphertext) 102 Crypto-->>CLI: plaintext 103 104 CLI->>CLI: Write to disk 105 CLI->>User: Saved to ./shared-file.txt 106``` 107 108Data never leaves the owner's PDS. The recipient fetches everything directly from the source. 109 110## List 111 112Lists document records on your PDS with optional tag filtering. 113 114```mermaid 115sequenceDiagram 116 participant User 117 participant CLI 118 participant PDS 119 120 User->>CLI: opake ls --tag vacation --long 121 122 loop Paginate until no cursor 123 CLI->>PDS: com.atproto.repo.listRecords (collection, cursor) 124 PDS-->>CLI: { records: [...], cursor? } 125 end 126 127 CLI->>CLI: For each document, unwrap content key 128 CLI->>Crypto: decrypt_metadata(K, encryptedMetadata) 129 Crypto-->>CLI: { name, mimeType, size, tags, description } 130 131 CLI->>CLI: Filter by tag, format output 132 CLI->>User: Display table (name, size, tags, URI) 133``` 134 135## Delete 136 137Deletes a document record. The blob becomes orphaned and is eventually garbage-collected by the PDS. If the document is tracked in a directory, the parent's entry list is updated. 138 139For path-based deletion (`Photos/beach.jpg`), recursive directory deletion, and directory-related flows, see [directories.md](directories.md). 140 141```mermaid 142sequenceDiagram 143 participant User 144 participant CLI 145 participant PDS 146 147 User->>CLI: opake rm photo.jpg 148 149 Note over CLI: Bare name → fast path (document-only resolution) 150 CLI->>PDS: listRecords (document collection, paginated) 151 PDS-->>CLI: match found → AT-URI 152 153 CLI->>User: delete photo.jpg? [y/N] 154 User-->>CLI: y 155 156 CLI->>PDS: com.atproto.repo.deleteRecord (collection, rkey) 157 PDS-->>CLI: 200 OK 158 159 CLI->>User: deleted at://did/.../document/<rkey> 160``` 161 162The fast path resolves bare document names with a single paginated `listRecords` call, the same cost as the pre-directory implementation. AT-URIs skip resolution entirely. Only path references (`dir/file`) and directory targets trigger a full tree load — see [directories.md](directories.md#path-resolution) for details.