@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 +84 -24
- package/index.cjs +935 -443
- package/index.js +1049 -555
- package/package.json +3 -3
- package/pubky.d.ts +201 -68
- package/pubky_bg.wasm +0 -0
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.
|
|
42
|
-
const addr =
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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.
|
|
237
|
-
|
|
238
|
-
|
|
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(
|
|
242
|
-
await pub.stats(
|
|
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(
|
|
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/
|
|
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("<
|
|
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 =
|
|
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
|