Encrypted, ephemeral, private memos on atproto

style: fix fmt, lint, and types

graham.systems 698fdf1f 59be5ff0

verified
Changed files
+28 -22
packages
+8 -8
README.md
··· 1 1 # Cistern 2 2 3 3 Cistern is an attempt at implementing a private, personal quick-capture system 4 - on AT Protocol. Cistern "items" are encrypted, so that they are only readable 5 - by the holder of the correct secret key—stored off-protocol. These items are 4 + on AT Protocol. Cistern "items" are encrypted, so that they are only readable by 5 + the holder of the correct secret key—stored off-protocol. These items are 6 6 intended to be ephemeral, and to be deleted after they've been read. 7 7 8 8 The intention is for Cistern to bridge the gap between where ideas are had and 9 9 where they can be stored long-term. For example, let's say you have an idea 10 - while at a restaurant. You create an item using your phone, and once you're 11 - back at your desk and you open Obsidian, that item is automatically pulled from 12 - your PDS, decrypted, and deleted from your PDS. If your notebook was open at 13 - the time you created your item, the Cistern Obsidian plugin would have been 14 - notified of the new item via the Jetstream, and so you would find your memo 15 - waiting for you once you got home. 10 + while at a restaurant. You create an item using your phone, and once you're back 11 + at your desk and you open Obsidian, that item is automatically pulled from your 12 + PDS, decrypted, and deleted from your PDS. If your notebook was open at the time 13 + you created your item, the Cistern Obsidian plugin would have been notified of 14 + the new item via the Jetstream, and so you would find your memo waiting for you 15 + once you got home.
+1 -1
packages/crypto/deno.jsonc
··· 1 1 { 2 2 "name": "@cistern/crypto", 3 3 "exports": { 4 - ".": "mod.ts" 4 + ".": "./mod.ts" 5 5 }, 6 6 "imports": { 7 7 "@noble/ciphers": "jsr:@noble/ciphers@^2.0.1",
+4 -3
packages/crypto/src/keys.ts
··· 1 1 import { XWing } from "@noble/post-quantum/hybrid.js"; 2 + import type { KeyPair } from "./types.ts"; 2 3 3 - export function generateKeys() { 4 + export function generateKeys(): KeyPair { 4 5 return XWing.keygen(); 5 6 } 6 7 7 - export function serializeKey(key: Uint8Array) { 8 + export function serializeKey(key: Uint8Array): string { 8 9 return key.toBase64(); 9 10 } 10 11 11 - export function deserializeKey(key: string) { 12 + export function deserializeKey(key: string): Uint8Array { 12 13 return Uint8Array.fromBase64(key); 13 14 }
+5
packages/crypto/src/types.ts
··· 1 + export type KeyPair = { 2 + secretKey: Uint8Array; 3 + publicKey: Uint8Array; 4 + }; 5 + 1 6 export interface EncryptedPayload { 2 7 cipherText: string; 3 8 content: string;
+1 -1
packages/lexicon/deno.jsonc
··· 1 1 { 2 2 "name": "@cistern/lexicon", 3 3 "exports": { 4 - ".": "mod.ts" 4 + ".": "./mod.ts" 5 5 }, 6 6 "imports": { 7 7 "@atproto/lexicon": "npm:@atproto/lexicon@^0.5.1"
+4 -4
packages/lexicon/mod.ts
··· 1 1 import item from "./schemas/item.json" with { type: "json" }; 2 2 import pubkey from "./schemas/pubkey.json" with { type: "json" }; 3 3 4 - import { Lexicons } from "@atproto/lexicon"; 4 + import { type LexiconDoc, Lexicons } from "@atproto/lexicon"; 5 5 6 - const lexicons = new Lexicons(); 6 + const lexicons: Lexicons = new Lexicons(); 7 7 8 - lexicons.add(item); 9 - lexicons.add(pubkey); 8 + lexicons.add(item as LexiconDoc); 9 + lexicons.add(pubkey as LexiconDoc); 10 10 11 11 export { item, lexicons, pubkey };
+4 -4
packages/lexicon/schemas/item.json
··· 1 1 { 2 - "version": 1, 2 + "lexicon": 1, 3 3 "id": "app.cistern.lexicon.item", 4 4 "description": "An encrypted memo intended to be accessed and deleted later.", 5 5 "defs": { ··· 23 23 "type": "string", 24 24 "description": "TID representing when this item was created", 25 25 "format": "tid" 26 - } 26 + }, 27 27 "ciphertext": { 28 28 "type": "string", 29 29 "description": "Encapsulated shared ciphertext", ··· 32 32 "nonce": { 33 33 "type": "string", 34 34 "description": "Base64-encoded nonce used for content encryption", 35 - "maxLength": 32, 35 + "maxLength": 32 36 36 }, 37 37 "algorithm": { 38 38 "type": "string", ··· 50 50 }, 51 51 "contentLength": { 52 52 "type": "integer", 53 - "description": "Original content length in bytes", 53 + "description": "Original content length in bytes" 54 54 }, 55 55 "contentHash": { 56 56 "type": "string",
+1 -1
packages/lexicon/schemas/pubkey.json
··· 1 1 { 2 - "version": 1, 2 + "lexicon": 1, 3 3 "id": "app.cistern.lexicon.pubkey", 4 4 "description": "A public key used to encrypt Cistern items", 5 5 "defs": {