@synonymdev/pubky 0.6.0-rc.6 → 0.6.0

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.
package/README.md CHANGED
@@ -17,7 +17,7 @@ Module system + TS types: ESM and CommonJS both supported; TypeScript typings ge
17
17
  ## Getting Started
18
18
 
19
19
  ```js
20
- import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
20
+ import { Pubky, PublicKey, Keypair, AuthFlowKind } from "@synonymdev/pubky";
21
21
 
22
22
  // Initiate a Pubky SDK facade wired for default mainnet Pkarr relays.
23
23
  const pubky = new Pubky(); // or: const pubky = Pubky.testnet(); for localhost testnet.
@@ -28,7 +28,7 @@ const signer = pubky.signer(keypair);
28
28
 
29
29
  // 2) Sign up at a homeserver (optionally with an invite)
30
30
  const homeserver = PublicKey.from(
31
- "8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo",
31
+ "8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo"
32
32
  );
33
33
  const signupToken = "<your-invite-code-or-null>";
34
34
  const session = await signer.signup(homeserver, signupToken);
@@ -38,24 +38,41 @@ const path = "/pub/example.com/hello.json";
38
38
  await session.storage.putJson(path, { hello: "world" });
39
39
 
40
40
  // 4) Read it publicly (no auth needed)
41
- const userPk = session.info.publicKey.z32();
42
- const addr = `pubky${userPk}/pub/example.com/hello.json`;
41
+ const userPk = session.info.publicKey.toString();
42
+ const addr = `${userPk}/pub/example.com/hello.json`;
43
43
  const json = await pubky.publicStorage.getJson(addr); // -> { hello: "world" }
44
44
 
45
45
  // 5) Authenticate on a 3rd-party app
46
- const authFlow = pubky.startAuthFlow("/pub/my.app/:rw"); // require permissions to read and write into `my.app`
46
+ const authFlow = pubky.startAuthFlow("/pub/my-cool-app/:rw", AuthFlowKind::signin()); // require permissions to read and write into `my.app`
47
47
  renderQr(authFlow.authorizationUrl); // show to user
48
48
  const session = await authFlow.awaitApproval();
49
49
  ```
50
50
 
51
51
  Find here [**ready-to-run examples**](https://github.com/pubky/pubky-core/tree/main/examples).
52
52
 
53
+ ### Key formats (display vs transport)
54
+
55
+ `PublicKey` has two string forms:
56
+
57
+ - **Display format**: `pubky<z32>` (for logs/UI/human-facing identifiers).
58
+ - **Transport/storage format**: raw `z32` (for hostnames, headers, query params, serde/JSON, DB storage).
59
+
60
+ Use `publicKey.z32()` for transport/storage. Use `publicKey.toString()` for display.
61
+
62
+ ### Initialization & events
63
+
64
+ The npm package bundles the WebAssembly module and **initializes it before exposing any APIs**. This avoids the common wasm-pack pitfall where events fire before the module finishes instantiating. Long-polling flows such as `authFlow.awaitApproval()` or `authFlow.tryPollOnce()` only start their relay calls after the underlying module is ready, so you won't miss approvals while the bundle is loading.
65
+
66
+ ### Reuse a single facade across your app
67
+
68
+ Use a shared `Pubky` (e.g, via context or prop drilling) instead of constructing one per request. This avoids reinitializing transports and keeps the same client available for repeated usage.
69
+
53
70
  ## API Overview
54
71
 
55
72
  Use `new Pubky()` to quickly get any flow started:
56
73
 
57
74
  ```js
58
- import { Pubky, Keypair } from "@synonymdev/pubky";
75
+ import { Pubky, Keypair, AuthFlowKind } from "@synonymdev/pubky";
59
76
 
60
77
  // Mainnet (default relays)
61
78
  const pubky = new Pubky();
@@ -68,7 +85,7 @@ const pubkyLocal = Pubky.testnet("localhost");
68
85
  const signer = pubky.signer(Keypair.random());
69
86
 
70
87
  // Pubky Auth flow (with capabilities)
71
- const authFlow = pubky.startAuthFlow("/pub/my.app/:rw");
88
+ const authFlow = pubky.startAuthFlow("/pub/my-cool-app/:rw", AuthFlowKind::signin());
72
89
 
73
90
  // Public storage (read-only)
74
91
  const publicStorage = pubky.publicStorage;
@@ -83,12 +100,13 @@ const client = pubky.client;
83
100
  ### Client (HTTP bridge)
84
101
 
85
102
  ```js
86
- import { Client, resolvePubky } from "@synonymdev/pubky";
103
+ import { Client, PublicKey, resolvePubky } from "@synonymdev/pubky";
87
104
 
88
105
  const client = new Client(); // or: pubky.client.fetch(); instead of constructing a client manually
89
106
 
90
107
  // Convert the identifier into a transport URL before fetching.
91
- const url = resolvePubky("pubky<pubky>/pub/example.com/file.txt");
108
+ const userId = PublicKey.from("pubky<z32>").toString();
109
+ const url = resolvePubky(`${userId}/pub/example.com/file.txt`);
92
110
  const res = await client.fetch(url);
93
111
  ```
94
112
 
@@ -104,6 +122,7 @@ const pubkey = keypair.publicKey;
104
122
 
105
123
  // z-base-32 roundtrip
106
124
  const parsed = PublicKey.from(pubkey.z32());
125
+ const displayId = pubkey.toString(); // pubky<z32> (display only)
107
126
  ```
108
127
 
109
128
  #### Recovery file (encrypt/decrypt root secret)
@@ -147,12 +166,26 @@ await session.signout(); // invalidates server session
147
166
  **Session details**
148
167
 
149
168
  ```js
150
- const userPk = session.info.publicKey.z32(); // -> PublicKey as z32 string
169
+ const userPk = session.info.publicKey.toString(); // -> pubky<z32> identifier
151
170
  const caps = session.info.capabilities; // -> string[] permissions and paths
152
171
 
153
172
  const storage = session.storage; // -> This User's storage API (absolute paths)
154
173
  ```
155
174
 
175
+ **Persist a session across tab refreshes (browser)**
176
+
177
+ ```js
178
+ // Save the session snapshot (no secrets inside; relies on the HTTP-only cookie).
179
+ const snapshot = session.export();
180
+ localStorage.setItem("pubky-session", snapshot);
181
+
182
+ // Later (after a reload), rehydrate using the browser's stored cookie.
183
+ const restored = await pubky.restoreSession(localStorage.getItem("pubky-session")!);
184
+ ```
185
+
186
+ > The exported string contains only public session metadata. The browser must keep the
187
+ > HTTP-only cookie alive for the restored session to remain authenticated.
188
+
156
189
  **Approve a pubkyauth request URL**
157
190
 
158
191
  ```js
@@ -166,17 +199,17 @@ await signer.approveAuthRequest("pubkyauth:///?caps=...&secret=...&relay=...");
166
199
  End-to-end auth (3rd-party app asks a user to approve via QR/deeplink, E.g. Pubky Ring).
167
200
 
168
201
  ```js
169
- import { Pubky } from "@synonymdev/pubky";
202
+ import { Pubky, AuthFlowKind } from "@synonymdev/pubky";
170
203
  const pubky = new Pubky();
171
204
 
172
205
  // Comma-separated capabilities string
173
- const caps = "/pub/my.app/:rw,/pub/another.app/folder/:w";
206
+ const caps = "/pub/my-cool-app/:rw,/pub/another-app/folder/:w";
174
207
 
175
208
  // Optional relay; defaults to Synonym-hosted relay if omitted
176
209
  const relay = "https://httprelay.pubky.app/link/"; // optional (defaults to this)
177
210
 
178
211
  // Start the auth polling
179
- const flow = pubky.startAuthFlow(caps, relay);
212
+ const flow = pubky.startAuthFlow(caps, AuthFlowKind::signin(), relay);
180
213
 
181
214
  renderQr(flow.authorizationUrl); // show to user
182
215
 
@@ -192,7 +225,7 @@ normalized string (ordering actions like `:rw`) and throws a structured error
192
225
  when the input is malformed.
193
226
 
194
227
  ```js
195
- import { Pubky, validateCapabilities } from "@synonymdev/pubky";
228
+ import { Pubky, validateCapabilities, AuthFlowKind } from "@synonymdev/pubky";
196
229
 
197
230
  const pubky = new Pubky();
198
231
 
@@ -200,7 +233,7 @@ const rawCaps = formData.get("caps");
200
233
 
201
234
  try {
202
235
  const caps = validateCapabilities(rawCaps ?? "");
203
- const flow = pubky.startAuthFlow(caps);
236
+ const flow = pubky.startAuthFlow(caps, AuthFlowKind::signin());
204
237
  renderQr(flow.authorizationUrl);
205
238
  const session = await flow.awaitApproval();
206
239
  // ...
@@ -233,19 +266,30 @@ surface precise feedback to the user.
233
266
  const pub = pubky.publicStorage;
234
267
 
235
268
  // Reads
236
- await pub.getJson(`pubky${userPk.z32()}/pub/example.com/data.json`);
237
- await pub.getText(`pubky${userPk.z32()}/pub/example.com/readme.txt`);
238
- await pub.getBytes(`pubky${userPk.z32()}/pub/example.com/icon.png`); // Uint8Array
269
+ const response = await pub.get(
270
+ `${userPk}/pub/example.com/data.json`
271
+ ); // -> Response (stream it)
272
+ await pub.getJson(`${userPk}/pub/example.com/data.json`);
273
+ await pub.getText(`${userPk}/pub/example.com/readme.txt`);
274
+ await pub.getBytes(`${userPk}/pub/example.com/icon.png`); // Uint8Array
239
275
 
240
276
  // Metadata
241
- await pub.exists(`pubky${userPk.z32()}/pub/example.com/foo`); // boolean
242
- await pub.stats(`pubky${userPk.z32()}/pub/example.com/foo`); // { content_length, content_type, etag, last_modified } | null
277
+ await pub.exists(`${userPk}/pub/example.com/foo`); // boolean
278
+ await pub.stats(`${userPk}/pub/example.com/foo`); // { content_length, content_type, etag, last_modified } | null
243
279
 
244
280
  // List directory (addressed path "<pubky>/pub/.../") must include trailing `/`.
245
281
  // list(addr, cursor=null|suffix|fullUrl, reverse=false, limit?, shallow=false)
246
- await pub.list(`pubky${userPk.z32()}/pub/example.com/`, null, false, 100, false);
282
+ await pub.list(
283
+ `${userPk}/pub/example.com/`,
284
+ null,
285
+ false,
286
+ 100,
287
+ false
288
+ );
247
289
  ```
248
290
 
291
+ Use `get()` when you need the raw `Response` for streaming or custom parsing.
292
+
249
293
  #### SessionStorage (read/write; uses cookies)
250
294
 
251
295
  ```js
@@ -257,6 +301,7 @@ await s.putText("/pub/example.com/note.txt", "hello");
257
301
  await s.putBytes("/pub/example.com/img.bin", new Uint8Array([1, 2, 3]));
258
302
 
259
303
  // Reads
304
+ const response = await s.get("/pub/example.com/data.json"); // -> Response (stream it)
260
305
  await s.getJson("/pub/example.com/data.json");
261
306
  await s.getText("/pub/example.com/note.txt");
262
307
  await s.getBytes("/pub/example.com/img.bin");
@@ -272,12 +317,14 @@ await s.list("/pub/example.com/", null, false, 100, false);
272
317
  await s.delete("/pub/example.com/data.json");
273
318
  ```
274
319
 
320
+ `get()` exposes the underlying `Response`, which is handy for streaming bodies or inspecting headers before consuming content.
321
+
275
322
  Path rules:
276
323
 
277
324
  - Session storage uses **absolute** paths like `"/pub/app/file.txt"`.
278
325
  - Public storage uses **addressed** form `pubky<user>/pub/app/file.txt` (preferred) or `pubky://<user>/...`.
279
326
 
280
- **Convention:** put your app’s public data under a domain-like folder in `/pub`, e.g. `/pub/mycoolnew.app/`.
327
+ **Convention:** put your app’s public data under a domain-like folder in `/pub`, e.g. `/pub/my-new-app/`.
281
328
 
282
329
  ---
283
330
 
@@ -291,7 +338,7 @@ import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
291
338
  const pubky = new Pubky();
292
339
 
293
340
  // Read-only resolver
294
- const homeserver = await pubky.getHomeserverOf(PublicKey.from("<user-z32>")); // string | undefined
341
+ const homeserver = await pubky.getHomeserverOf(PublicKey.from("pubky<z32>")); // string | undefined
295
342
 
296
343
  // With keys (signer-bound)
297
344
  const signer = pubky.signer(Keypair.random());
@@ -323,7 +370,8 @@ Use `resolvePubky()` when you need to feed an addressed resource into a raw HTTP
323
370
  ```js
324
371
  import { resolvePubky } from "@synonymdev/pubky";
325
372
 
326
- const identifier = "pubkyoperrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rdo/pub/pubky.app/posts/0033X02JAN0SG";
373
+ const identifier =
374
+ "pubkyoperrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rdo/pub/pubky.app/posts/0033X02JAN0SG";
327
375
  const url = resolvePubky(identifier);
328
376
  // -> "https://_pubky.operrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rdo/pub/pubky.app/posts/0033X02JAN0SG"
329
377
  ```
@@ -332,6 +380,12 @@ Both `pubky<pk>/…` (preferred) and `pubky://<pk>/…` resolve to the same HTTP
332
380
 
333
381
  ---
334
382
 
383
+ ## WASM memory (`free()` helpers)
384
+
385
+ `wasm-bindgen` generates `free()` methods on exported classes (for example `Pubky`, `AuthFlow` `PublicKey`). JavaScript's GC eventually releases the underlying Rust structs on its own, but calling `free()` lets you drop them **immediately** if you are creating many short-lived instances (e.g. in a long-running worker). It is safe to skip manual frees in typical browser or Node apps.
386
+
387
+ ---
388
+
335
389
  ## Errors
336
390
 
337
391
  All async methods throw a structured `PubkyError`:
@@ -369,6 +423,12 @@ try {
369
423
  }
370
424
  ```
371
425
 
426
+ ## Browser environment notes
427
+
428
+ - Keep the Pubky client UI and the homeserver on the **same origin family** (both local or both remote). Browsers partition cookies by scheme/host, and cross-site requests (e.g., http://localhost calling https://staging…​) can silently drop or cache `SameSite`/`Secure` session cookies.
429
+ - If you must mix environments, use a reverse proxy so the browser always talks to one consistent origin (or disable caching via devtools and clear cookies between switches).
430
+ - When troubleshooting auth/session caching: open a fresh incognito window, clear site data for the target origin, and verify the request includes credentials.
431
+
372
432
  ---
373
433
 
374
434
  ## Local Test & Development