@synonymdev/pubky 0.10.0-alpha.0 → 0.11.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
@@ -1,6 +1,6 @@
1
1
  # Pubky
2
2
 
3
- JS/WASM SDK for [Pubky](https://github.com/pubky/pubky-core).
3
+ JS/WASM SDK for [Pubky](https://github.com/pubky/pubky-homeserver).
4
4
 
5
5
  Works in browsers and Node 20+.
6
6
 
@@ -33,18 +33,18 @@ const homeserver = PublicKey.from(
33
33
  const signupToken = "<your-invite-code-or-null>";
34
34
  await signer.signup(homeserver, signupToken);
35
35
 
36
- // 3a) Signin with the signer directly
37
- const session = await signer.signin("example.com");
36
+ // 3a) Signin with the signer directly.
37
+ let session = await signer.signin("example.com");
38
38
 
39
- // 3b) Alternatively if you do not have the keypair available, authenticate on a 3rd-party app
39
+ // 3b) Or, if you do not have the keypair available, authenticate on a 3rd-party app
40
40
  // by delegating the authentication to a signer with a QR code.
41
- const authFlow = pubky.startGrantAuthFlow(
41
+ const authFlow = await pubky.startGrantAuthFlow(
42
42
  "/pub/my-cool-app/:rw",
43
43
  AuthFlowKind.signin(),
44
44
  { clientId: "my-cool-app.example" },
45
45
  );
46
46
  renderQr(authFlow.authorizationUrl); // Show to user the signin deeplink via a QR code
47
- const session = await authFlow.awaitApproval();
47
+ session = await authFlow.awaitApproval();
48
48
 
49
49
  // 4) Write a public JSON file
50
50
  const path = "/pub/my-cool-app/hello.json";
@@ -56,7 +56,7 @@ const addr = `${userPk}/pub/my-cool-app/hello.json`;
56
56
  const json = await pubky.publicStorage.getJson(addr); // -> { hello: "world" }
57
57
  ```
58
58
 
59
- Find here [**ready-to-run examples**](https://github.com/pubky/pubky-core/tree/main/examples).
59
+ Find here [**ready-to-run examples**](https://github.com/pubky/pubky-homeserver/tree/main/examples).
60
60
 
61
61
  ### Key formats (display vs transport)
62
62
 
@@ -93,7 +93,7 @@ const pubkyLocal = Pubky.testnet("localhost");
93
93
  const signer = pubky.signer(Keypair.random());
94
94
 
95
95
  // Grant Pubky Auth flow (with capabilities)
96
- const authFlow = pubky.startGrantAuthFlow(
96
+ const authFlow = await pubky.startGrantAuthFlow(
97
97
  "/pub/my-cool-app/:rw",
98
98
  AuthFlowKind.signin(),
99
99
  { clientId: "my-cool-app.example" },
@@ -187,16 +187,37 @@ const storage = session.storage; // -> This User's storage API (absolute paths)
187
187
  **Persist a grant session**
188
188
 
189
189
  ```js
190
- // Save the session secret. Treat this string like a bearer token.
191
- const secret = await session.exportSecret();
192
- localStorage.setItem("pubky-session", secret);
190
+ // Persist explicitly when your app wants reload/returning-user restore.
191
+ // In browsers, this uses IndexedDB and supports multiple accounts.
192
+ const stored = await pubky.browserSessionStore.save(session);
193
193
 
194
- // Later, restore by minting a fresh short-lived bearer.
195
- const restored = await pubky.restoreSession(localStorage.getItem("pubky-session")!);
194
+ const accounts = await pubky.browserSessionStore.list();
195
+ const restored = await pubky.browserSessionStore.restore(stored.id);
196
196
  ```
197
197
 
198
+ `pubky.startGrantAuthFlow(...)` chooses delegated browser PoP keys
199
+ when the runtime supports them, then falls back to local PoP keys. Delegated
200
+ sessions keep the private PoP key non-extractable in browser storage; local
201
+ sessions store bearer-equivalent secret material in IndexedDB.
202
+
203
+ ```js
204
+ const flow = await pubky.startGrantAuthFlow(
205
+ "/pub/example.com/:rw",
206
+ AuthFlowKind.signin(),
207
+ { clientId: "example.com" },
208
+ );
209
+ const session = await flow.awaitApproval();
210
+ await pubky.browserSessionStore.save(session);
211
+ ```
212
+
213
+ For advanced/manual storage, local grant sessions still expose
214
+ `exportLocalSecret()` and `pubky.restoreSession(secret)`. Treat that string
215
+ like a bearer token. Delegated browser grant sessions should use
216
+ `browserSessionStore` because their PoP keys are non-extractable.
217
+
198
218
  > `session.export()` is still available for legacy cookie sessions, but new
199
- > applications should use grant auth plus `exportSecret()`.
219
+ > applications should use grant auth plus `browserSessionStore` or
220
+ > `exportLocalSecret()`.
200
221
 
201
222
  **Approve a pubkyauth request URL**
202
223
 
@@ -204,6 +225,16 @@ const restored = await pubky.restoreSession(localStorage.getItem("pubky-session"
204
225
  await signer.approveAuthRequest("pubkyauth://signin?caps=...&secret=...&relay=...");
205
226
  ```
206
227
 
228
+ **Handle a pubkyauth deep link**
229
+
230
+ General entry point for any supported `pubkyauth://` link. Auth request links
231
+ are approved through their relay (like `approveAuthRequest`), while a
232
+ `direct_signup` link creates the account directly on its target homeserver:
233
+
234
+ ```js
235
+ await signer.handleDeepLink("pubkyauth://direct_signup?hs=...");
236
+ ```
237
+
207
238
  ---
208
239
 
209
240
  ### GrantAuthFlow (pubkyauth)
@@ -221,10 +252,19 @@ const caps = "/pub/my-cool-app/:rw,/pub/another-app/folder/:w";
221
252
  const relay = "https://httprelay.pubky.app/inbox/"; // optional (defaults to this)
222
253
 
223
254
  // Start grant auth polling
224
- const flow = pubky.startGrantAuthFlow(
255
+ const flow = await pubky.startGrantAuthFlow(
225
256
  caps,
226
257
  AuthFlowKind.signin(),
227
- { clientId: "my-cool-app.example", relay },
258
+ {
259
+ clientId: "my-cool-app.example",
260
+ relay,
261
+ xCallback: {
262
+ xSource: "My Cool App",
263
+ xSuccess: "my-cool-app://auth/success?nonce=unique",
264
+ xError: "my-cool-app://auth/error?nonce=unique",
265
+ xCancel: "my-cool-app://auth/cancel?nonce=unique",
266
+ },
267
+ },
228
268
  );
229
269
 
230
270
  renderQr(flow.authorizationUrl); // show to user
@@ -233,6 +273,36 @@ renderQr(flow.authorizationUrl); // show to user
233
273
  const session = await flow.awaitApproval();
234
274
  ```
235
275
 
276
+ The same callback object can be passed as the fourth argument to legacy cookie
277
+ auth: `startCookieAuthFlow(caps, kind, relay, xCallback)`. The SDK emits
278
+ `x-source`, `x-success`, `x-error`, and `x-cancel` using the encoding expected
279
+ by Pubky Ring. Parsed deep-link objects expose them through `xCallback`; the
280
+ legacy `callback` parameter is accepted as an `xSuccess` fallback.
281
+
282
+ #### Resume an auth flow after page refresh
283
+
284
+ Grant auth flows are resumable by saving the pending flow state before refresh and restoring it afterwards:
285
+
286
+ ```js
287
+ const flow = pubky.startGrantAuthFlow(caps, AuthFlowKind.signin(), {
288
+ clientId: "my-cool-app.example",
289
+ relay,
290
+ });
291
+ sessionStorage.setItem("pubky-grant-auth", flow.save());
292
+
293
+ const saved = sessionStorage.getItem("pubky-grant-auth");
294
+ if (saved) {
295
+ try {
296
+ const resumed = pubky.resumeGrantAuthFlow(saved);
297
+ const session = await resumed.awaitApproval();
298
+ } finally {
299
+ sessionStorage.removeItem("pubky-grant-auth");
300
+ }
301
+ }
302
+ ```
303
+
304
+ Legacy cookie auth flows can be resumed with `pubky.resumeCookieAuthFlow(authorizationUrl)`. Store pending auth state in `sessionStorage`, not `localStorage`, and delete it once the flow completes or is abandoned.
305
+
236
306
  #### Validate and normalize capabilities
237
307
 
238
308
  If you accept capability strings from user input (forms, CLI arguments, etc.),
@@ -249,7 +319,7 @@ const rawCaps = formData.get("caps");
249
319
 
250
320
  try {
251
321
  const caps = validateCapabilities(rawCaps ?? "");
252
- const flow = pubky.startGrantAuthFlow(caps, AuthFlowKind.signin(), {
322
+ const flow = await pubky.startGrantAuthFlow(caps, AuthFlowKind.signin(), {
253
323
  clientId: "my-cool-app.example",
254
324
  });
255
325
  renderQr(flow.authorizationUrl);
@@ -264,9 +334,13 @@ try {
264
334
  }
265
335
  ```
266
336
 
267
- On invalid input, `validateCapabilities` throws a `PubkyError` with
268
- `{ name: "InvalidInput", message: "Invalid capability entries: …" }`, so you can
269
- surface precise feedback to the user.
337
+ On invalid input, `validateCapabilities` throws a `PubkyError` identifying the
338
+ first malformed entry and its position. Its `data.invalidEntries` array contains
339
+ that entry so applications can surface precise feedback to the user.
340
+
341
+ Capability scopes must be canonical absolute paths. Repeated separators and
342
+ `.` or `..` segments are rejected rather than normalized. Percent sequences are
343
+ literal scope characters; URL encoding is handled by the enclosing deep link.
270
344
 
271
345
  #### Http Relay & reliability
272
346
 
@@ -338,11 +412,13 @@ await s.delete("/pub/example.com/data.json");
338
412
 
339
413
  Path rules:
340
414
 
341
- - Session storage uses **absolute** paths like `"/pub/app/file.txt"`.
415
+ - Session storage uses **absolute** paths under `/pub/` or `/priv/`.
342
416
  - Public storage uses **addressed** form `pubky<user>/pub/app/file.txt` (preferred) or `pubky://<user>/...`.
343
417
 
344
418
  **Convention:** put your app’s public data under a domain-like folder in `/pub`, e.g. `/pub/my-new-app/`.
345
419
 
420
+ See [Private Storage](https://github.com/pubky/pubky-homeserver/blob/main/docs/PRIVATE_STORAGE.md) for `/priv/` access and events.
421
+
346
422
  ---
347
423
 
348
424
  ### PKDNS (Pkarr)
@@ -355,7 +431,8 @@ import { Pubky, PublicKey, Keypair } from "@synonymdev/pubky";
355
431
  const pubky = new Pubky();
356
432
 
357
433
  // Read-only resolver
358
- const homeserver = await pubky.getHomeserverOf(PublicKey.from("pubky<z32>")); // string | undefined
434
+ const homeserver = await pubky.getHomeserverOf(PublicKey.from("pubky<z32>")); // PublicKey | undefined
435
+ // Rejects if PKARR resolution fails or the `_pubky` target is malformed.
359
436
 
360
437
  // With keys (signer-bound)
361
438
  const signer = pubky.signer(Keypair.random());