Encrypted, ephemeral, private memos on atproto

feat(producer): implement creating items

graham.systems ebcc4349 8195c329

verified
Changed files
+50 -6
packages
producer
+6 -1
deno.lock
··· 16 16 "npm:@atcute/jetstream@^1.1.2": "1.1.2", 17 17 "npm:@atcute/lex-cli@^2.3.1": "2.3.1", 18 18 "npm:@atcute/lexicons@^1.2.2": "1.2.2", 19 + "npm:@atcute/tid@^1.0.3": "1.0.3", 19 20 "npm:@atproto/lexicon@~0.5.1": "0.5.1" 20 21 }, 21 22 "jsr": { ··· 115 116 "@standard-schema/spec", 116 117 "esm-env" 117 118 ] 119 + }, 120 + "@atcute/tid@1.0.3": { 121 + "integrity": "sha512-wfMJx1IMdnu0CZgWl0uR4JO2s6PGT1YPhpytD4ZHzEYKKQVuqV6Eb/7vieaVo1eYNMp2FrY67FZObeR7utRl2w==" 118 122 }, 119 123 "@atproto/common-web@0.4.3": { 120 124 "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", ··· 236 240 "dependencies": [ 237 241 "npm:@atcute/atproto@^3.1.9", 238 242 "npm:@atcute/client@^4.0.5", 239 - "npm:@atcute/lexicons@^1.2.2" 243 + "npm:@atcute/lexicons@^1.2.2", 244 + "npm:@atcute/tid@^1.0.3" 240 245 ] 241 246 }, 242 247 "packages/shared": {
+2 -1
packages/producer/deno.jsonc
··· 6 6 "imports": { 7 7 "@atcute/atproto": "npm:@atcute/atproto@^3.1.9", 8 8 "@atcute/client": "npm:@atcute/client@^4.0.5", 9 - "@atcute/lexicons": "npm:@atcute/lexicons@^1.2.2" 9 + "@atcute/lexicons": "npm:@atcute/lexicons@^1.2.2", 10 + "@atcute/tid": "npm:@atcute/tid@^1.0.3" 10 11 } 11 12 }
+42 -4
packages/producer/mod.ts
··· 1 1 import { produceRequirements } from "@cistern/shared"; 2 + import { encryptText } from "@cistern/crypto"; 2 3 import type { 3 4 LocalPublicKey, 4 5 ProducerOptions, ··· 6 7 } from "./types.ts"; 7 8 import { type Did, parse, type ResourceUri } from "@atcute/lexicons"; 8 9 import type { Client, CredentialManager } from "@atcute/client"; 10 + import { now } from "@atcute/tid"; 9 11 import { 12 + type AppCisternLexiconItem, 10 13 AppCisternLexiconPubkey, 11 14 } from "@cistern/lexicon"; 12 15 ··· 60 63 61 64 /** 62 65 * Creates an item and saves it as a record in the user's PDS 63 - * @todo Error if there is no selected public key 64 - * @todo Construct valid item 65 - * @todo Save item to PDS 66 66 */ 67 - async createItem() {} 67 + async createItem(text: string): Promise<ResourceUri> { 68 + if (!this.publicKey) { 69 + throw new Error( 70 + "no public key set; select a public key before creating an item", 71 + ); 72 + } 73 + 74 + const payload = encryptText( 75 + Uint8Array.fromBase64(this.publicKey.record.content), 76 + text, 77 + ); 78 + const record: AppCisternLexiconItem.Main = { 79 + $type: "app.cistern.lexicon.item", 80 + tid: now(), 81 + algorithm: "x_wing-xchacha20_poly1305-sha3_512", 82 + ciphertext: payload.cipherText, 83 + contentHash: payload.hash, 84 + contentLength: payload.length, 85 + nonce: payload.nonce, 86 + payload: payload.content, 87 + pubkey: this.publicKey.uri, 88 + }; 89 + 90 + const res = await this.rpc.post("com.atproto.repo.createRecord", { 91 + input: { 92 + collection: "app.cistern.lexicon.item", 93 + repo: this.did, 94 + record, 95 + }, 96 + }); 97 + 98 + if (!res.ok) { 99 + throw new Error( 100 + `failed to create new item: ${res.status} ${res.data.error}`, 101 + ); 102 + } 103 + 104 + return res.data.uri; 105 + } 68 106 69 107 /** 70 108 * Lists public keys registered in the user's PDS