From ee9625c513e3f6e886defa16b8fcb024681df016 Mon Sep 17 00:00:00 2001 From: Graham Barber Date: Sun, 9 Nov 2025 14:38:01 -0800 Subject: [PATCH] fix(consumer): return decrypted items --- packages/consumer/mod.ts | 43 ++++++++++++++++++++++++++++++++------ packages/consumer/types.ts | 7 ++++++- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/consumer/mod.ts b/packages/consumer/mod.ts index 4ff9269..2c19242 100644 --- a/packages/consumer/mod.ts +++ b/packages/consumer/mod.ts @@ -1,5 +1,5 @@ import { produceRequirements } from "@cistern/shared"; -import { generateKeys } from "@cistern/crypto"; +import { decryptText, generateKeys } from "@cistern/crypto"; import { generateRandomName } from "@puregarlic/randimal"; import { is, parse, type RecordKey } from "@atcute/lexicons"; import { JetstreamSubscription } from "@atcute/jetstream"; @@ -9,7 +9,12 @@ import { AppCisternLexiconItem, type AppCisternLexiconPubkey, } from "@cistern/lexicon"; -import type { ConsumerOptions, ConsumerParams, LocalKeyPair } from "./types.ts"; +import type { + ConsumerOptions, + ConsumerParams, + DecryptedItem, + LocalKeyPair, +} from "./types.ts"; import type {} from "@atcute/atproto"; @@ -88,10 +93,14 @@ export class Consumer { * Asynchronously iterate through items in the user's PDS */ async *listItems(): AsyncIterator< - AppCisternLexiconItem.Main, + DecryptedItem, void, undefined > { + if (!this.keypair) { + throw new Error("no key pair set; generate a key before listing items"); + } + let cursor: string | undefined; while (true) { @@ -112,7 +121,19 @@ export class Consumer { if (res.data.cursor) cursor = res.data.cursor; for (const record of res.data.records) { - yield parse(AppCisternLexiconItem.mainSchema, record.value); + const item = parse(AppCisternLexiconItem.mainSchema, record.value); + const decrypted = decryptText(this.keypair.privateKey, { + nonce: item.nonce, + cipherText: item.ciphertext, + content: item.payload, + hash: item.contentHash, + length: item.contentLength, + }); + + yield { + tid: item.tid, + text: decrypted, + }; } if (!cursor) return; @@ -124,7 +145,7 @@ export class Consumer { * @todo Allow specifying Jetstream endpoint */ async *subscribeToItems(): AsyncIterator< - AppCisternLexiconItem.Main, + DecryptedItem, void, "stop" | undefined > { @@ -150,7 +171,17 @@ export class Consumer { continue; } - if ((yield record) === "stop") return; + const decrypted = decryptText(this.keypair.privateKey, { + nonce: record.nonce, + cipherText: record.ciphertext, + content: record.payload, + hash: record.contentHash, + length: record.contentLength, + }); + + const command = yield { tid: record.tid, text: decrypted }; + + if (command === "stop") return; } } } diff --git a/packages/consumer/types.ts b/packages/consumer/types.ts index d97f070..9433e44 100644 --- a/packages/consumer/types.ts +++ b/packages/consumer/types.ts @@ -1,5 +1,5 @@ import type { BaseClientOptions, ClientRequirements } from "@cistern/shared"; -import type { ResourceUri } from "@atcute/lexicons"; +import type { ResourceUri, Tid } from "@atcute/lexicons"; export interface InputLocalKeyPair { privateKey: string; @@ -16,3 +16,8 @@ export interface ConsumerOptions extends BaseClientOptions { } export type ConsumerParams = ClientRequirements; + +export interface DecryptedItem { + tid: Tid; + text: string; +} -- 2.51.2