+6
deno.lock
+6
deno.lock
···
7
7
"jsr:@noble/hashes@2.0": "2.0.1",
8
8
"jsr:@noble/hashes@^2.0.1": "2.0.1",
9
9
"jsr:@noble/post-quantum@~0.5.2": "0.5.2",
10
+
"jsr:@puregarlic/randimal@^1.0.1": "1.0.1",
10
11
"jsr:@std/assert@^1.0.14": "1.0.14",
11
12
"jsr:@std/expect@^1.0.17": "1.0.17",
12
13
"jsr:@std/internal@^1.0.10": "1.0.10",
···
35
36
"jsr:@noble/curves",
36
37
"jsr:@noble/hashes@2.0"
37
38
]
39
+
},
40
+
"@puregarlic/randimal@1.0.1": {
41
+
"integrity": "101da9e89561f4b8038426f47f6cb258484a11887e1f4d8a2b07cd5ebf487a5d"
38
42
},
39
43
"@std/assert@1.0.14": {
40
44
"integrity": "68d0d4a43b365abc927f45a9b85c639ea18a9fab96ad92281e493e4ed84abaa4",
···
169
173
"members": {
170
174
"packages/consumer": {
171
175
"dependencies": [
176
+
"jsr:@puregarlic/randimal@^1.0.1",
177
+
"npm:@atcute/atproto@^3.1.9",
172
178
"npm:@atcute/client@^4.0.5",
173
179
"npm:@atcute/lexicons@^1.2.2"
174
180
]
+3
-1
packages/consumer/deno.jsonc
+3
-1
packages/consumer/deno.jsonc
···
4
4
".": "./mod.ts"
5
5
},
6
6
"imports": {
7
+
"@atcute/atproto": "npm:@atcute/atproto@^3.1.9",
7
8
"@atcute/client": "npm:@atcute/client@^4.0.5",
8
-
"@atcute/lexicons": "npm:@atcute/lexicons@^1.2.2"
9
+
"@atcute/lexicons": "npm:@atcute/lexicons@^1.2.2",
10
+
"@puregarlic/randimal": "jsr:@puregarlic/randimal@^1.0.1"
9
11
}
10
12
}
+50
-6
packages/consumer/mod.ts
+50
-6
packages/consumer/mod.ts
···
1
-
import type { Client, CredentialManager } from "@atcute/client";
2
1
import { produceRequirements } from "@cistern/shared";
2
+
import { generateKeys } from "@cistern/crypto";
3
+
import { generateRandomName } from "@puregarlic/randimal";
3
4
import type { Did } from "@atcute/lexicons/syntax";
5
+
import type { Client, CredentialManager } from "@atcute/client";
6
+
import type { AppCisternLexiconPubkey } from "@cistern/lexicon";
4
7
import type { ConsumerOptions, ConsumerParams, LocalKeyPair } from "./types.ts";
8
+
9
+
import type {} from "@atcute/atproto";
5
10
6
11
export async function createConsumer(
7
12
options: ConsumerOptions,
···
22
27
23
28
constructor(params: ConsumerParams) {
24
29
this.did = params.miniDoc.did;
25
-
this.keypair = params.options.keypair;
30
+
this.keypair = params.options.keypair
31
+
? {
32
+
privateKey: Uint8Array.fromBase64(params.options.keypair.privateKey),
33
+
publicKey: params.options.keypair.publicKey,
34
+
}
35
+
: undefined;
26
36
this.rpc = params.rpc;
27
37
this.manager = params.manager;
28
38
}
29
39
30
40
/**
31
41
* Generates a key pair, uploading the public key to PDS and returning the pair.
32
-
* @todo Generate key pair
33
-
* @todo Store private key in local registry
34
-
* @todo Upload public key to PDS
35
42
*/
36
-
async generateKeyPair() {}
43
+
async generateKeyPair(): Promise<LocalKeyPair> {
44
+
if (this.keypair) {
45
+
throw new Error("client already has a key pair");
46
+
}
47
+
48
+
const keys = generateKeys();
49
+
const name = await generateRandomName();
50
+
51
+
const record: AppCisternLexiconPubkey.Main = {
52
+
$type: "app.cistern.lexicon.pubkey",
53
+
name,
54
+
algorithm: "x_wing",
55
+
content: keys.publicKey.toBase64(),
56
+
createdAt: new Date().toISOString(),
57
+
};
58
+
const res = await this.rpc.post("com.atproto.repo.createRecord", {
59
+
input: {
60
+
collection: "app.cistern.lexicon.pubkey",
61
+
repo: this.did,
62
+
record,
63
+
},
64
+
});
65
+
66
+
if (!res.ok) {
67
+
throw new Error(
68
+
`failed to save public key: ${res.status} ${res.data.error}`,
69
+
);
70
+
}
71
+
72
+
const keypair = {
73
+
privateKey: keys.secretKey,
74
+
publicKey: res.data.uri,
75
+
};
76
+
77
+
this.keypair = keypair;
78
+
79
+
return keypair;
80
+
}
37
81
38
82
/**
39
83
* Returns an async iterator that returns pages of the user's items from their PDS.
+9
-4
packages/consumer/types.ts
+9
-4
packages/consumer/types.ts
···
1
1
import type { BaseClientOptions, ClientRequirements } from "@cistern/shared";
2
-
import type { AppCisternLexiconPubkey } from "@cistern/lexicon";
2
+
import type { ResourceUri } from "@atcute/lexicons";
3
+
4
+
export interface InputLocalKeyPair {
5
+
privateKey: string;
6
+
publicKey: ResourceUri;
7
+
}
3
8
4
9
export interface LocalKeyPair {
5
-
privateKey: string;
6
-
publicKey: AppCisternLexiconPubkey.Main;
10
+
privateKey: Uint8Array;
11
+
publicKey: ResourceUri;
7
12
}
8
13
9
14
export interface ConsumerOptions extends BaseClientOptions {
10
-
keypair?: LocalKeyPair;
15
+
keypair?: InputLocalKeyPair;
11
16
}
12
17
13
18
export type ConsumerParams = ClientRequirements<ConsumerOptions>;