@synonymdev/pubky 0.5.2 → 0.6.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (6) hide show
  1. package/README.md +193 -159
  2. package/index.cjs +1582 -401
  3. package/index.js +1680 -468
  4. package/package.json +3 -2
  5. package/pubky.d.ts +821 -0
  6. package/pubky_bg.wasm +0 -0
package/README.md CHANGED
@@ -1,13 +1,8 @@
1
1
  # Pubky
2
2
 
3
- JavaScript implementation of [Pubky](https://github.com/pubky/pubky-core) client.
3
+ JS/WASM SDK for [Pubky](https://github.com/pubky/pubky-core).
4
4
 
5
- ## Table of Contents
6
-
7
- - [Install](#install)
8
- - [Getting Started](#getting-started)
9
- - [API](#api)
10
- - [Test and Development](#test-and-development)
5
+ Works in browsers and Node 20+.
11
6
 
12
7
  ## Install
13
8
 
@@ -15,277 +10,316 @@ JavaScript implementation of [Pubky](https://github.com/pubky/pubky-core) client
15
10
  npm install @synonymdev/pubky
16
11
  ```
17
12
 
18
- ### Prerequisites
13
+ > **Node**: requires Node v20+ (undici fetch, WebCrypto).
19
14
 
20
- For Nodejs, you need Node v20 or later.
15
+ Module system + TS types: ESM and CommonJS both supported; TypeScript typings generated via tsify are included. Use `import { Pubky } from "@synonymdev/pubky"` (ESM) or `const { Pubky } = require("@synonymdev/pubky")` (CJS).
21
16
 
22
- ## Getting started
17
+ ## Getting Started
23
18
 
24
19
  ```js
25
- import { Client, Keypair, PublicKey } from "../index.js";
20
+ import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
26
21
 
27
- // Initialize Client with Pkarr relay(s).
28
- let client = new Client();
22
+ // Initiate a Pubky SDK facade wired for default mainnet Pkarr relays.
23
+ const pubky = new Pubky(); // or: const pubky = Pubky.testnet(); for localhost testnet.
29
24
 
30
- // Generate a keypair
31
- let keypair = Keypair.random();
25
+ // 1) Create random user keys and bind to a new Signer.
26
+ const keypair = Keypair.random();
27
+ const signer = pubky.signer(keypair);
32
28
 
33
- // Create a new account
34
- let homeserver = PublicKey.from(
29
+ // 2) Sign up at a homeserver (optionally with an invite)
30
+ const homeserver = PublicKey.from(
35
31
  "8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"
36
32
  );
33
+ const signupToken = "<your-invite-code-or-null>";
34
+ const session = await signer.signup(homeserver, signupToken);
37
35
 
38
- await client.signup(keypair, homeserver, signup_token);
36
+ // 3) Write a public JSON file (session-scoped storage uses cookies automatically)
37
+ const path = "/pub/example.com/hello.json";
38
+ await session.storage().putJson(path, { hello: "world" });
39
39
 
40
- const publicKey = keypair.publicKey();
40
+ // 4) Read it publicly (no auth needed)
41
+ const userPk = session.info().publicKey();
42
+ const addr = `${userPk.z32()}/pub/example.com/hello.json`;
43
+ const json = await pubky.publicStorage().getJson(addr); // -> { hello: "world" }
44
+ ```
41
45
 
42
- // Pubky URL
43
- let url = `pubky://${publicKey.z32()}/pub/example.com/arbitrary`;
46
+ Find here [**ready-to-run examples**](https://github.com/pubky/pubky-core/tree/main/examples).
44
47
 
45
- // Verify that you are signed in.
46
- const session = await client.session(publicKey);
48
+ ## API Overview
47
49
 
48
- // PUT public data, by authorized client
49
- await client.fetch(url, {
50
- method: "PUT",
51
- body: JSON.stringify({ foo: "bar" }),
52
- credentials: "include",
53
- });
50
+ Use `new Pubky()` to quickly get any flow started:
54
51
 
55
- // GET public data without signup or signin
56
- {
57
- const client = new Client();
52
+ ```js
53
+ import { Pubky, Keypair } from "@synonymdev/pubky";
58
54
 
59
- let response = await client.fetch(url);
60
- }
55
+ // Mainnet (default relays)
56
+ const pubky = new Pubky();
61
57
 
62
- // Delete public data, by authorized client
63
- await client.fetch(url, { method: "DELETE", credentials: "include " });
64
- ```
58
+ // Local testnet wiring (Pkarr + HTTP mapping).
59
+ // Omit the argument for "localhost".
60
+ const pubkyLocal = Pubky.testnet("localhost");
65
61
 
66
- ## API
62
+ // Signer (bind your keypair to a new Signer actor)
63
+ const signer = pubky.signer(Keypair.random());
67
64
 
68
- ### Client
65
+ // Public storage (read-only)
66
+ const publicStorage = pubky.publicStorage();
69
67
 
70
- #### constructor
68
+ // PKDNS resolver (read-only)
69
+ const pkdns = pubky.pkdns();
71
70
 
72
- ```js
73
- let client = new Client();
71
+ // Optional: raw HTTP client for advanced use
72
+ const client = pubky.client();
74
73
  ```
75
74
 
76
- #### fetch
75
+ ### Client (HTTP bridge)
77
76
 
78
77
  ```js
79
- let response = await client.fetch(url, opts);
78
+ import { Client } from "@synonymdev/pubky";
79
+
80
+ const client = new Client(); // or: pubky.client(); instead of constructing a client manually
81
+
82
+ // Works with both pubky:// and http(s)://
83
+ const res = await client.fetch("pubky://<pubky>/pub/example.com/file.txt");
80
84
  ```
81
85
 
82
- Just like normal Fetch API, but it can handle `pubky://` urls and `http(s)://` urls with Pkarr domains.
86
+ ---
83
87
 
84
- #### signup
88
+ ### Keys
85
89
 
86
90
  ```js
87
- await client.signup(keypair, homeserver, signup_token);
88
- ```
89
-
90
- - keypair: An instance of [Keypair](#keypair).
91
- - homeserver: An instance of [PublicKey](#publickey) representing the homeserver.
92
- - signup_token: A homeserver could optionally ask for a valid signup token (aka, invitation code).
91
+ import { Keypair, PublicKey } from "@synonymdev/pubky";
93
92
 
94
- Returns:
93
+ const keypair = Keypair.random();
94
+ const pubkey = keypair.publicKey();
95
95
 
96
- - session: An instance of [Session](#session).
96
+ // z-base-32 roundtrip
97
+ const parsed = PublicKey.from(pubkey.z32());
98
+ ```
97
99
 
98
- #### signin
100
+ #### Recovery file (encrypt/decrypt root secret)
99
101
 
100
102
  ```js
101
- let session = await client.signin(keypair);
103
+ // Encrypt to recovery file (Uint8Array)
104
+ const recoveryFile = keypair.createRecoveryFile("strong passphrase");
105
+
106
+ // Decrypt back into a Keypair
107
+ const restored = Keypair.fromRecoveryFile(recoveryFile, "strong passphrase");
108
+
109
+ // Build a Signer from a recovered key
110
+ const signer = pubky.signer(restored);
102
111
  ```
103
112
 
104
113
  - keypair: An instance of [Keypair](#keypair).
114
+ - passphrase: A utf-8 string [passphrase](https://www.useapassphrase.com/).
115
+ - Returns: A recovery file with a spec line and an encrypted secret key.
105
116
 
106
- Returns:
107
-
108
- - An instance of [Session](#session).
117
+ ---
109
118
 
110
- #### signout
119
+ ### Signer & Session
111
120
 
112
121
  ```js
113
- await client.signout(publicKey);
114
- ```
122
+ import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
115
123
 
116
- - publicKey: An instance of [PublicKey](#publicKey).
124
+ const pubky = new Pubky();
117
125
 
118
- #### authRequest
126
+ const keypair = Keypair.random();
127
+ const signer = pubky.signer(keypair);
119
128
 
120
- ```js
121
- let pubkyAuthRequest = client.authRequest(relay, capabilities);
122
-
123
- let pubkyauthUrl = pubkyAuthRequest.url();
129
+ const homeserver = PublicKey.from("8pinxxgq…");
130
+ const session = await signer.signup(homeserver, /* invite */ null);
124
131
 
125
- showQr(pubkyauthUrl);
132
+ const session2 = await signer.signin(); // fast, prefer this; publishes PKDNS in background
133
+ const session3 = await signer.signinBlocking(); // slower but safer; waits for PKDNS publish
126
134
 
127
- let pubky = await pubkyAuthRequest.response();
135
+ await session.signout(); // invalidates server session
128
136
  ```
129
137
 
130
- Sign in to a user's Homeserver, without access to their [Keypair](#keypair), nor even [PublicKey](#publickey),
131
- instead request permissions (showing the user pubkyauthUrl), and await a Session after the user consenting to that request.
138
+ **Session details**
132
139
 
133
- - relay: A URL to an [HTTP relay](https://httprelay.io/features/link/) endpoint.
134
- - capabilities: A list of capabilities required for the app for example `/pub/pubky.app/:rw,/pub/example.com/:r`.
140
+ ```js
141
+ const info = session.info();
142
+ const userPk = info.publicKey(); // -> PublicKey
143
+ const caps = info.capabilities(); // -> string[]
135
144
 
136
- #### sendAuthToken
145
+ const storage = session.storage(); // -> SessionStorage (absolute paths)
146
+ ```
147
+
148
+ **Approve a pubkyauth request URL**
137
149
 
138
150
  ```js
139
- await client.sendAuthToken(keypair, pubkyauthUrl);
151
+ await signer.approveAuthRequest("pubkyauth:///?caps=...&secret=...&relay=...");
140
152
  ```
141
153
 
142
- Consenting to authentication or authorization according to the required capabilities in the `pubkyauthUrl` , and sign and send an auth token to the requester.
154
+ ---
143
155
 
144
- - keypair: An instance of [KeyPair](#keypair)
145
- - pubkyauthUrl: A string `pubkyauth://` url
156
+ ### AuthFlow (pubkyauth)
146
157
 
147
- #### session {#session-method}
158
+ End-to-end auth (3rd-party app asks a user to approve via QR/deeplink, E.g. Pubky Ring).
148
159
 
149
160
  ```js
150
- let session = await client.session(publicKey);
151
- ```
161
+ import { Pubky } from "@synonymdev/pubky";
162
+ const pubky = new Pubky();
152
163
 
153
- - publicKey: An instance of [PublicKey](#publickey).
154
- - Returns: A [Session](#session) object if signed in, or undefined if not.
164
+ // Comma-separated capabilities string
165
+ const caps = "/pub/my.app/:rw,/pub/another.app/folder/:w";
155
166
 
156
- ### list
167
+ // Optional relay; defaults to Synonym-hosted relay if omitted
168
+ const relay = "https://httprelay.pubky.app/link/"; // optional (defaults to this)
157
169
 
158
- ```js
159
- let response = await client.list(url, cursor, reverse, limit);
160
- ```
170
+ // Start the auth polling
171
+ const flow = pubky.startAuthFlow(caps, relay);
172
+
173
+ renderQr(flow.authorizationUrl()); // show to user
161
174
 
162
- - url: A string representing the Pubky URL. The path in that url is the prefix that you want to list files within.
163
- - cursor: Usually the last URL from previous calls. List urls after/before (depending on `reverse`) the cursor.
164
- - reverse: Whether or not return urls in reverse order.
165
- - limit: Number of urls to return.
166
- - Returns: A list of URLs of the files in the `url` you passed.
175
+ // Blocks until the signer approves; returns a ready Session
176
+ const session = await flow.awaitApproval();
177
+ ```
167
178
 
168
- ### Keypair
179
+ #### Http Relay & reliability
169
180
 
170
- #### random
181
+ - If you don’t specify a relay, `PubkyAuthFlow` defaults to a Synonym-hosted relay. If that relay is down, logins won’t complete.
182
+ - For production and larger apps, run **your own http relay** (MIT, Docker): [https://httprelay.io](https://httprelay.io).
183
+ The channel is derived as `base64url(hash(secret))`; the token is end-to-end encrypted with the `secret` and cannot be decrypted by the relay.
171
184
 
172
- ```js
173
- let keypair = Keypair.random();
174
- ```
185
+ ---
175
186
 
176
- - Returns: A new random Keypair.
187
+ ### Storage
177
188
 
178
- #### fromSecretKey
189
+ #### PublicStorage (read-only)
179
190
 
180
191
  ```js
181
- let keypair = Keypair.fromSecretKey(secretKey);
182
- ```
192
+ const pub = pubky.publicStorage();
183
193
 
184
- - secretKey: A 32 bytes Uint8array.
185
- - Returns: A new Keypair.
194
+ // Reads
195
+ await pub.getJson(`${userPk.z32()}/pub/example.com/data.json`);
196
+ await pub.getText(`${userPk.z32()}/pub/example.com/readme.txt`);
197
+ await pub.getBytes(`${userPk.z32()}/pub/example.com/icon.png`); // Uint8Array
186
198
 
187
- #### publicKey {#publickey-method}
199
+ // Metadata
200
+ await pub.exists(`${userPk.z32()}/pub/example.com/foo`); // boolean
201
+ await pub.stats(`${userPk.z32()}/pub/example.com/foo`); // { content_length, content_type, etag, last_modified } | null
188
202
 
189
- ```js
190
- let publicKey = keypair.publicKey();
203
+ // List directory (addressed path "<pubky>/pub/.../") must include trailing `/`.
204
+ // list(addr, cursor=null|suffix|fullUrl, reverse=false, limit?, shallow=false)
205
+ await pub.list(`${userPk.z32()}/pub/example.com/`, null, false, 100, false);
191
206
  ```
192
207
 
193
- - Returns: The [PublicKey](#publickey) associated with the Keypair.
194
-
195
- #### secretKey
208
+ #### SessionStorage (read/write; uses cookies)
196
209
 
197
210
  ```js
198
- let secretKey = keypair.secretKey();
199
- ```
211
+ const s = session.storage();
200
212
 
201
- - Returns: The Uint8array secret key associated with the Keypair.
213
+ // Writes
214
+ await s.putJson("/pub/example.com/data.json", { ok: true });
215
+ await s.putText("/pub/example.com/note.txt", "hello");
216
+ await s.putBytes("/pub/example.com/img.bin", new Uint8Array([1, 2, 3]));
202
217
 
203
- ### PublicKey
218
+ // Reads
219
+ await s.getJson("/pub/example.com/data.json");
220
+ await s.getText("/pub/example.com/note.txt");
221
+ await s.getBytes("/pub/example.com/img.bin");
204
222
 
205
- #### from
223
+ // Metadata
224
+ await s.exists("/pub/example.com/data.json");
225
+ await s.stats("/pub/example.com/data.json");
206
226
 
207
- ```js
208
- let publicKey = PublicKey.from(string);
227
+ // Listing (session-scoped absolute dir)
228
+ await s.list("/pub/example.com/", null, false, 100, false);
229
+
230
+ // Delete
231
+ await s.delete("/pub/example.com/data.json");
209
232
  ```
210
233
 
211
- - string: A string representing the public key.
212
- - Returns: A new PublicKey instance.
234
+ Path rules:
213
235
 
214
- #### z32
236
+ - Session storage uses **absolute** paths like `"/pub/app/file.txt"`.
237
+ - Public storage uses **addressed** form `<user>/pub/app/file.txt` (or `pubky://<user>/...`).
215
238
 
216
- ```js
217
- let pubky = publicKey.z32();
218
- ```
239
+ **Convention:** put your app’s public data under a domain-like folder in `/pub`, e.g. `/pub/mycoolnew.app/`.
219
240
 
220
- Returns: The z-base-32 encoded string representation of the PublicKey.
241
+ ---
221
242
 
222
- ### Session
243
+ ### PKDNS (Pkarr)
223
244
 
224
- #### pubky
245
+ Resolve or publish `_pubky` records.
225
246
 
226
247
  ```js
227
- let pubky = session.pubky();
228
- ```
248
+ import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
229
249
 
230
- Returns an instance of [PublicKey](#publickey)
250
+ const pubky = new Pubky();
231
251
 
232
- #### capabilities
252
+ // Read-only resolver
253
+ const resolver = pubky.pkdns();
254
+ const homeserver = await resolver.getHomeserverOf(PublicKey.from("<user-z32>")); // string | undefined
233
255
 
234
- ```js
235
- let capabilities = session.capabilities();
256
+ // With keys (signer-bound)
257
+ const signer = pubky.signer(Keypair.random());
258
+
259
+ // Republish if missing or stale (reuses current host unless overridden)
260
+ await signer.pkdns().publishHomeserverIfStale();
261
+ // Or force an override now:
262
+ await signer.pkdns().publishHomeserverForce(/* optional override homeserver*/);
236
263
  ```
237
264
 
238
- Returns an array of capabilities, for example `["/pub/pubky.app/:rw"]`
265
+ ---
239
266
 
240
- ### Helper functions
267
+ ## Errors
241
268
 
242
- #### createRecoveryFile
269
+ All async methods throw a structured `PubkyJsError`:
243
270
 
244
- ```js
245
- let recoveryFile = createRecoveryFile(keypair, passphrase);
271
+ ```ts
272
+ type PubkyJsError = {
273
+ name:
274
+ | "RequestError" // network/server/validation/JSON
275
+ | "InvalidInput"
276
+ | "AuthenticationError"
277
+ | "PkarrError"
278
+ | "InternalError";
279
+ message: string;
280
+ statusCode?: number; // present for HTTP server errors (4xx/5xx)
281
+ };
246
282
  ```
247
283
 
248
- - keypair: An instance of [Keypair](#keypair).
249
- - passphrase: A utf-8 string [passphrase](https://www.useapassphrase.com/).
250
- - Returns: A recovery file with a spec line and an encrypted secret key.
251
-
252
- #### createRecoveryFile
284
+ Example:
253
285
 
254
286
  ```js
255
- let keypair = decryptRecoveryfile(recoveryFile, passphrase);
287
+ try {
288
+ await publicStorage.getJson(`${pk}/pub/example.com/missing.json`);
289
+ } catch (e) {
290
+ if (e.name === "RequestError" && e.statusCode === 404) {
291
+ // handle not found
292
+ }
293
+ }
256
294
  ```
257
295
 
258
- - recoveryFile: An instance of Uint8Array containing the recovery file blob.
259
- - passphrase: A utf-8 string [passphrase](https://www.useapassphrase.com/).
260
- - Returns: An instance of [Keypair](#keypair).
296
+ ---
261
297
 
262
- ## Test and Development
298
+ ## Local Test & Development
263
299
 
264
300
  For test and development, you can run a local homeserver in a test network.
265
301
 
266
- If you don't have Cargo Installed, start by installing it:
302
+ 1. Install Rust (for wasm builds):
267
303
 
268
304
  ```bash
269
305
  curl https://sh.rustup.rs -sSf | sh
270
306
  ```
271
307
 
272
- Clone the Pubky repository:
273
-
274
- ```bash
275
- git clone https://github.com/pubky/pubky
276
- cd pubky-client/pkg
277
- ```
278
-
279
- Run the local testnet server
308
+ 2. Run the local testnet:
280
309
 
281
310
  ```bash
282
311
  npm run testnet
283
312
  ```
284
313
 
285
- Use the logged addresses as inputs to `Client`
314
+ 3. Point the SDK at testnet:
286
315
 
287
316
  ```js
288
- import { Client } from "../index.js";
317
+ import { Pubky } from "@synonymdev/pubky";
289
318
 
290
- const client = Client().testnet();
319
+ const pubky = Pubky.testnet(); // defaults to localhost
320
+ // or: const pubky = Pubky.testnet("testnet-host"); // custom host (e.g. Docker bridge)
291
321
  ```
322
+
323
+ ---
324
+
325
+ MIT © Synonym