@synonymdev/pubky 0.9.3 → 0.10.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.
Files changed (6) hide show
  1. package/README.md +134 -46
  2. package/index.cjs +3336 -1129
  3. package/index.js +3337 -1130
  4. package/package.json +7 -2
  5. package/pubky.d.ts +790 -36
  6. package/pubky_bg.wasm +0 -0
package/pubky.d.ts CHANGED
@@ -21,6 +21,50 @@ type ReadableStreamType = "bytes";
21
21
  */
22
22
  export type PubkyErrorName = "RequestError" | "InvalidInput" | "AuthenticationError" | "PkarrError" | "ClientStateError" | "InternalError";
23
23
 
24
+ /**
25
+ * Optional x-callback-url metadata carried by a Pubky deep link.
26
+ *
27
+ * Callback destinations are untrusted navigation hints. Authentication still
28
+ * completes through the encrypted relay.
29
+ */
30
+ export interface XCallbackParams {
31
+ /**
32
+ * Human-readable name of the requesting application.
33
+ */
34
+ xSource?: string | null;
35
+ /**
36
+ * Destination to open after successful completion.
37
+ */
38
+ xSuccess?: string | null;
39
+ /**
40
+ * Destination to open after an error.
41
+ */
42
+ xError?: string | null;
43
+ /**
44
+ * Destination to open after user cancellation or timeout.
45
+ */
46
+ xCancel?: string | null;
47
+ }
48
+
49
+ /**
50
+ * Options for starting a grant-backed pubkyauth flow.
51
+ */
52
+ export interface GrantAuthFlowOptions {
53
+ /**
54
+ * App identifier shown in the user\'s grant/session list, typically a domain.
55
+ */
56
+ clientId: string;
57
+ /**
58
+ * Optional HTTP relay base, e.g. `\"https://demo.httprelay.io/inbox/\"`.
59
+ * Defaults to the default Synonym-hosted relay when omitted.
60
+ */
61
+ relay?: string | null;
62
+ /**
63
+ * Optional return destinations for success, error, and cancellation.
64
+ */
65
+ xCallback?: XCallbackParams | null;
66
+ }
67
+
24
68
  /**
25
69
  * Pkarr Config
26
70
  */
@@ -160,16 +204,18 @@ export type CapabilityEntry = `${CapabilityScope}:${CapabilityAction}`;
160
204
  type CapabilitiesTail = `,${CapabilityEntry}${string}`;
161
205
  export type Capabilities = "" | CapabilityEntry | `${CapabilityEntry}${CapabilitiesTail}`;
162
206
 
163
- export type Path = `/pub/${string}`;
207
+ export type Path = `/pub/${string}` | `/priv/${string}`;
164
208
 
165
209
 
166
210
  /**
167
211
  * Start and control a pubkyauth authorization flow.
168
212
  *
169
213
  * Typical flow:
170
- * 1) `AuthFlow.start(...)` or `pubky.startAuthFlow(...)`
214
+ * 1) `AuthFlow.start(...)` or `pubky.startCookieAuthFlow(...)`
171
215
  * 2) Show `authorizationUrl()` as QR/deeplink to the user’s signing device
172
216
  * 3) `awaitApproval()` to receive a ready `Session`
217
+ *
218
+ * @deprecated Use `GrantAuthFlow` instead.
173
219
  */
174
220
  export class AuthFlow {
175
221
  private constructor();
@@ -198,10 +244,10 @@ export class AuthFlow {
198
244
  awaitToken(): Promise<AuthToken>;
199
245
  /**
200
246
  * Resume a previously started auth flow from its saved `authorizationUrl` (standalone).
201
- * Prefer `pubky.resumeAuthFlow()` to reuse a facade client; this creates a default (mainnet) client.
247
+ * Prefer `pubky.resumeCookieAuthFlow()` to reuse a facade client; this creates a default (mainnet) client.
202
248
  *
203
249
  * Relay messages expire after ~5 minutes; resume is only viable in that window.
204
- * See `Pubky.resumeAuthFlow()` / `Pubky.startAuthFlow()` for full guidance.
250
+ * See `Pubky.resumeCookieAuthFlow()` / `Pubky.startCookieAuthFlow()` for full guidance.
205
251
  *
206
252
  * **Security:** `authorizationUrl` contains the `client_secret`.
207
253
  * Delete it from storage as soon as resume completes or is abandoned.
@@ -214,7 +260,7 @@ export class AuthFlow {
214
260
  static resume(authorization_url: string): AuthFlow;
215
261
  /**
216
262
  * Start a flow (standalone).
217
- * Prefer `pubky.startAuthFlow()` to reuse a facade client.
263
+ * Prefer `pubky.startCookieAuthFlow()` to reuse a facade client.
218
264
  *
219
265
  * @param {string} capabilities
220
266
  * Comma-separated capabilities, e.g. `"/pub/app/:rw,/priv/foo.txt:r"`.
@@ -234,6 +280,9 @@ export class AuthFlow {
234
280
  * Optional HTTP relay base, e.g. `"https://demo.httprelay.io/inbox/"`.
235
281
  * Defaults to the default Synonym-hosted relay when omitted.
236
282
  *
283
+ * @param {XCallbackParams} [xCallback]
284
+ * Optional return destinations for success, error, and cancellation.
285
+ *
237
286
  * @returns {AuthFlow}
238
287
  * A running auth flow. Call `authorizationUrl()` to show the deep link,
239
288
  * then `awaitApproval()` to receive a `Session`.
@@ -244,8 +293,10 @@ export class AuthFlow {
244
293
  * const flow = AuthFlow.start("/pub/my-cool-app/:rw,/pub/pubky.app/:w");
245
294
  * renderQRCode(flow.authorizationUrl());
246
295
  * const session = await flow.awaitApproval();
296
+ *
297
+ * @deprecated Use `GrantAuthFlow.start(...)` instead.
247
298
  */
248
- static start(capabilities: Capabilities, kind: AuthFlowKind, relay?: string | null): AuthFlow;
299
+ static start(capabilities: Capabilities, kind: AuthFlowKind, relay?: string | null, x_callback?: XCallbackParams | null): AuthFlow;
249
300
  /**
250
301
  * Non-blocking single poll step (advanced UIs).
251
302
  *
@@ -257,7 +308,7 @@ export class AuthFlow {
257
308
  *
258
309
  * **Security:** This URL contains the `client_secret` in plaintext.
259
310
  * Treat it as a short-lived secret and delete it after the flow completes.
260
- * See `Pubky.startAuthFlow()` docs for storage guidance.
311
+ * See `Pubky.startCookieAuthFlow()` docs for storage guidance.
261
312
  *
262
313
  * @returns {string} A `pubkyauth://…` or `https://…` URL with channel info.
263
314
  *
@@ -305,7 +356,7 @@ export class AuthFlowKind {
305
356
  * ### Typical usage
306
357
  * ```js
307
358
  * // Start an auth-only flow (no capabilities)
308
- * const flow = pubky.startAuthFlow("", relay);
359
+ * const flow = pubky.startCookieAuthFlow("", relay);
309
360
  *
310
361
  * // Wait for the signer to approve; returns an AuthToken
311
362
  * const token = await flow.awaitToken();
@@ -367,7 +418,7 @@ export class AuthToken {
367
418
  /**
368
419
  * Returns the **capabilities** requested by the flow at the time this token was signed.
369
420
  *
370
- * Most auth-only flows pass an empty string to `startAuthFlow("", relay)`, so this will
421
+ * Most auth-only flows pass an empty string to `startCookieAuthFlow("", relay)`, so this will
371
422
  * commonly be an empty array.
372
423
  *
373
424
  * Returns: `string[]`, where each item is the canonical entry `"<scope>:<actions>"`.
@@ -387,6 +438,51 @@ export class AuthToken {
387
438
  readonly publicKey: PublicKey;
388
439
  }
389
440
 
441
+ /**
442
+ * Browser-backed durable store for completed grant sessions.
443
+ */
444
+ export class BrowserSessionStore {
445
+ private constructor();
446
+ free(): void;
447
+ [Symbol.dispose](): void;
448
+ /**
449
+ * Clear all local stored session records for this origin.
450
+ *
451
+ * Delegated keys referenced by those stored session records are removed.
452
+ * Delegated keys that only belong to pending grant flows are preserved.
453
+ */
454
+ clear(): Promise<void>;
455
+ /**
456
+ * Clear all browser auth persistence owned by this SDK origin.
457
+ *
458
+ * This removes all stored session records and all browser-held delegated
459
+ * grant keys, including keys for pending delegated grant flows. Saved
460
+ * delegated flow state becomes unrestorable. This does not revoke remote
461
+ * grants or immediately invalidate already-live in-memory sessions.
462
+ */
463
+ clearAll(): Promise<void>;
464
+ /**
465
+ * Whether IndexedDB is available for durable session persistence.
466
+ */
467
+ isAvailable(): Promise<boolean>;
468
+ /**
469
+ * List all locally stored sessions for this origin.
470
+ */
471
+ list(): Promise<StoredSessionInfo[]>;
472
+ /**
473
+ * Remove local stored session metadata and any SDK-owned delegated key for that record.
474
+ */
475
+ remove(id: string): Promise<void>;
476
+ /**
477
+ * Restore a specific stored session by id.
478
+ */
479
+ restore(id: string): Promise<Session>;
480
+ /**
481
+ * Persist a completed grant session in IndexedDB.
482
+ */
483
+ save(session: Session): Promise<StoredSessionInfo>;
484
+ }
485
+
390
486
  /**
391
487
  * Low-level HTTP bridge used by the Pubky facade and actors.
392
488
  *
@@ -450,6 +546,62 @@ export class Client {
450
546
  static testnet(host?: string | null): Client;
451
547
  }
452
548
 
549
+ /**
550
+ * Cookie-only view over a cookie-backed `Session`.
551
+ *
552
+ * Grant-backed sessions do not expose this view; use `session.cookie` and
553
+ * check for `undefined` before calling cookie-specific methods.
554
+ */
555
+ export class CookieSession {
556
+ private constructor();
557
+ free(): void;
558
+ [Symbol.dispose](): void;
559
+ /**
560
+ * Export session metadata for legacy cookie restore.
561
+ *
562
+ * The returned string contains no secrets; the browser cookie jar must
563
+ * still hold the HTTP-only cookie.
564
+ *
565
+ * @returns {string}
566
+ */
567
+ export(): string;
568
+ /**
569
+ * Export the cookie secret needed to restore this cookie session.
570
+ *
571
+ * This is available when the SDK captured the cookie secret, such as in
572
+ * Node.js. Browser sessions cannot read HTTP-only Set-Cookie values.
573
+ *
574
+ * @returns {Promise<string>}
575
+ */
576
+ exportSecret(): Promise<string>;
577
+ }
578
+
579
+ /**
580
+ * Parsed direct signup deeplink.
581
+ */
582
+ export class DirectSignupDeepLink {
583
+ private constructor();
584
+ free(): void;
585
+ [Symbol.dispose](): void;
586
+ /**
587
+ * Parse a direct signup deeplink URL.
588
+ */
589
+ static parse(url: string): DirectSignupDeepLink;
590
+ toString(): string;
591
+ /**
592
+ * Homeserver public key requested for account creation.
593
+ */
594
+ readonly homeserver: PublicKey;
595
+ /**
596
+ * Optional signup token included in the request.
597
+ */
598
+ readonly signupToken: string | undefined;
599
+ /**
600
+ * Optional x-callback-url metadata carried by this deep link.
601
+ */
602
+ readonly xCallback: XCallbackParams;
603
+ }
604
+
453
605
  /**
454
606
  * A single event from the event stream.
455
607
  *
@@ -510,6 +662,15 @@ export class Event {
510
662
  * console.log(event.eventType, event.resource.path);
511
663
  * }
512
664
  * ```
665
+ *
666
+ * @example
667
+ * ```typescript
668
+ * // Private events: attach a session and request a `/priv/...` path.
669
+ * const stream = await pubky.eventStreamForUser(userPubkey, null)
670
+ * .session(session)
671
+ * .path("/priv/app/")
672
+ * .subscribe();
673
+ * ```
513
674
  */
514
675
  export class EventStreamBuilder {
515
676
  private constructor();
@@ -579,12 +740,17 @@ export class EventStreamBuilder {
579
740
  */
580
741
  live(): EventStreamBuilder;
581
742
  /**
582
- * Filter events by path prefix.
743
+ * Filter events by path. Call once per path to receive the
744
+ * union of several scopes (e.g. `/pub/` plus a private `/priv/app/`).
745
+ *
746
+ * Format: a path WITHOUT the `pubky://` scheme or user pubkey. A trailing
747
+ * slash matches a directory and all its descendants (`/pub/files/`); no
748
+ * trailing slash matches an exact file (`/pub/notes.txt`).
583
749
  *
584
- * Format: Path WITHOUT `pubky://` scheme or user pubkey (e.g., "/pub/files/" or "/pub/").
585
- * Only events whose path starts with this prefix are returned.
750
+ * Private (`/priv/...`) paths require a session attached via `session()`;
751
+ * without one the homeserver rejects the subscription with 401.
586
752
  *
587
- * @param {string} path - Path prefix to filter by
753
+ * @param {string} path - Path filter (repeatable)
588
754
  * @returns {EventStreamBuilder} - Builder for chaining
589
755
  */
590
756
  path(path: string): EventStreamBuilder;
@@ -600,6 +766,19 @@ export class EventStreamBuilder {
600
766
  * @returns {EventStreamBuilder} - Builder for chaining
601
767
  */
602
768
  reverse(): EventStreamBuilder;
769
+ /**
770
+ * Authenticate the subscription with a user `Session`.
771
+ *
772
+ * Required to receive private (`/priv/...`) events: the session credential
773
+ * (grant or cookie) is attached so the homeserver can authorize each private
774
+ * `path()` against the session's read capabilities. Public subscriptions
775
+ * don't need this.
776
+ *
777
+ *
778
+ * @param {Session} session - The authenticated session
779
+ * @returns {EventStreamBuilder} - Builder for chaining
780
+ */
781
+ session(session: Session): EventStreamBuilder;
603
782
  /**
604
783
  * Subscribe to the event stream.
605
784
  *
@@ -627,6 +806,244 @@ export class EventStreamBuilder {
627
806
  subscribe(): Promise<ReadableStream>;
628
807
  }
629
808
 
809
+ /**
810
+ * Start and control a grant-backed pubkyauth authorization flow.
811
+ *
812
+ * Typical flow:
813
+ * 1) `GrantAuthFlow.start(...)` or `pubky.startGrantAuthFlow(...)`
814
+ * 2) Show `authorizationUrl()` as QR/deeplink to the user's signing device
815
+ * 3) `awaitApproval()` to receive a grant-backed, self-refreshing `Session`
816
+ */
817
+ export class GrantAuthFlow {
818
+ private constructor();
819
+ free(): void;
820
+ [Symbol.dispose](): void;
821
+ /**
822
+ * Block until the user approves on their signer device; returns a grant-backed `Session`.
823
+ */
824
+ awaitApproval(): Promise<Session>;
825
+ /**
826
+ * Resume a previously saved pending grant auth flow (standalone).
827
+ * Prefer `pubky.resumeGrantAuthFlow()` to reuse a facade client.
828
+ *
829
+ * **Security:** `savedState` contains the relay secret and PoP client private key.
830
+ * Store it only temporarily and delete it once the flow completes or is abandoned.
831
+ *
832
+ * @param {string} savedState A string produced by `grantFlow.saveLocal()`.
833
+ * @returns {GrantAuthFlow} A flow reconnected to the original relay channel.
834
+ */
835
+ static resume(saved_state: string): GrantAuthFlow;
836
+ /**
837
+ * Resume a previously saved pending delegated grant auth flow.
838
+ *
839
+ * Runtime: delegated grant keys require a secure browser context with
840
+ * WebCrypto `crypto.subtle` and IndexedDB. The saved `keyId` must still
841
+ * exist in IndexedDB for the same origin. Unsupported runtimes reject with
842
+ * `ClientStateError`.
843
+ */
844
+ static resumeDelegated(saved_state: string): Promise<GrantAuthFlow>;
845
+ /**
846
+ * Save sensitive state required to resume this delegated pending grant flow.
847
+ *
848
+ * This does not export the delegated private key, but it includes the relay
849
+ * secret in the authorization URL. Store it only temporarily and delete it
850
+ * once the flow completes or is abandoned.
851
+ */
852
+ saveDelegated(): string;
853
+ /**
854
+ * Save sensitive state required to resume this pending local grant flow.
855
+ *
856
+ * @returns {string} Opaque state for `GrantAuthFlow.resume()` or
857
+ * `pubky.resumeGrantAuthFlow()`.
858
+ */
859
+ saveLocal(): string;
860
+ /**
861
+ * Start a grant-backed flow with a new DHT client.
862
+ * Prefer `pubky.startGrantAuthFlow()` to reuse a facade DHT client.
863
+ *
864
+ * @param {string} capabilities
865
+ * Comma-separated capabilities, e.g. `"/pub/app/:rw,/priv/foo.txt:r"`.
866
+ * Empty string is allowed (no scopes).
867
+ *
868
+ * @param {AuthFlowKind} kind
869
+ * The kind of authentication flow to perform.
870
+ *
871
+ * @param {GrantAuthFlowOptions} options
872
+ * Options for the grant flow: `{ clientId, relay?, xCallback? }`.
873
+ *
874
+ * @returns {GrantAuthFlow}
875
+ * A running grant auth flow. Call `authorizationUrl()` to show the deep link,
876
+ * then `awaitApproval()` to receive a grant-backed `Session`.
877
+ */
878
+ static start(capabilities: Capabilities, kind: AuthFlowKind, options: GrantAuthFlowOptions): GrantAuthFlow;
879
+ /**
880
+ * Start a browser delegated grant-backed flow.
881
+ *
882
+ * The SDK creates a fresh non-extractable WebCrypto Ed25519 key in
883
+ * IndexedDB and uses that key for grant Proof-of-Possession signing.
884
+ *
885
+ * Runtime: delegated grant keys require a secure browser context with
886
+ * WebCrypto `crypto.subtle` and IndexedDB. Unsupported runtimes reject
887
+ * with `ClientStateError`.
888
+ */
889
+ static startDelegated(capabilities: Capabilities, kind: AuthFlowKind, options: GrantAuthFlowOptions): Promise<GrantAuthFlow>;
890
+ /**
891
+ * Non-blocking single poll step (advanced UIs).
892
+ *
893
+ * @returns {Promise<Session|undefined>} A session if the approval arrived, otherwise `undefined`.
894
+ */
895
+ tryPollOnce(): Promise<Session | undefined>;
896
+ /**
897
+ * Return the authorization deep link (URL) to show as QR or open on the signer device.
898
+ */
899
+ readonly authorizationUrl: string;
900
+ /**
901
+ * Whether delegated grant auth is available in the current JS runtime.
902
+ *
903
+ * This checks for a secure browser context with WebCrypto `crypto.subtle`
904
+ * and IndexedDB. It is a coarse synchronous feature-detection helper only;
905
+ * some runtimes expose those primitives without Ed25519 support.
906
+ */
907
+ static readonly isDelegationAvailable: boolean;
908
+ }
909
+
910
+ /**
911
+ * Summary of an active grant returned by `GrantManager.list()`.
912
+ */
913
+ export class GrantInfo {
914
+ private constructor();
915
+ free(): void;
916
+ [Symbol.dispose](): void;
917
+ /**
918
+ * Comma-separated capabilities authorized by the grant.
919
+ */
920
+ readonly capabilities: string;
921
+ /**
922
+ * Application identifier.
923
+ */
924
+ readonly clientId: string;
925
+ /**
926
+ * Expiry timestamp, in Unix seconds.
927
+ */
928
+ readonly expiresAt: number;
929
+ /**
930
+ * Grant identifier used for revocation.
931
+ */
932
+ readonly grantId: string;
933
+ /**
934
+ * Issued-at timestamp, in Unix seconds.
935
+ */
936
+ readonly issuedAt: number;
937
+ }
938
+
939
+ /**
940
+ * Account-level grant management for a signed-in user.
941
+ */
942
+ export class GrantManager {
943
+ free(): void;
944
+ [Symbol.dispose](): void;
945
+ /**
946
+ * List all active grants for this user.
947
+ *
948
+ * Requires a root-capability session. Non-root sessions surface the
949
+ * homeserver `403` as the standard request error.
950
+ *
951
+ * @returns {Promise<GrantInfo[]>}
952
+ */
953
+ list(): Promise<GrantInfo[]>;
954
+ /**
955
+ * Create a grant manager authenticated by a session.
956
+ *
957
+ * @param {Session} session
958
+ * @returns {GrantManager}
959
+ */
960
+ constructor(session: Session);
961
+ /**
962
+ * Revoke a specific grant by id.
963
+ *
964
+ * Requires a root-capability session. Malformed ids throw
965
+ * `InvalidInput`.
966
+ *
967
+ * @param {string} grantId
968
+ * @returns {Promise<void>}
969
+ */
970
+ revoke(grant_id: string): Promise<void>;
971
+ }
972
+
973
+ /**
974
+ * Grant-only view over a grant-backed `Session`.
975
+ *
976
+ * Cookie-backed sessions do not expose this view; use `session.grant` and
977
+ * check for `undefined` before calling grant-session methods.
978
+ */
979
+ export class GrantSession {
980
+ private constructor();
981
+ free(): void;
982
+ [Symbol.dispose](): void;
983
+ /**
984
+ * Export the portable local secret material needed to restore this grant session.
985
+ *
986
+ * Treat the returned string as bearer-equivalent secret material until the
987
+ * grant expires or is revoked.
988
+ *
989
+ * @returns {Promise<string>}
990
+ */
991
+ exportLocalSecret(): Promise<string>;
992
+ /**
993
+ * Current grant id (`jti`) backing this session.
994
+ *
995
+ * @returns {Promise<string>}
996
+ */
997
+ grantId(): Promise<string>;
998
+ /**
999
+ * Full grant session metadata.
1000
+ *
1001
+ * @returns {Promise<GrantSessionInfo>}
1002
+ */
1003
+ sessionInfo(): Promise<GrantSessionInfo>;
1004
+ }
1005
+
1006
+ /**
1007
+ * Grant-specific session metadata returned by `grant.sessionInfo()`.
1008
+ */
1009
+ export class GrantSessionInfo {
1010
+ private constructor();
1011
+ free(): void;
1012
+ [Symbol.dispose](): void;
1013
+ /**
1014
+ * Authorized capabilities for this session.
1015
+ */
1016
+ readonly capabilities: string[];
1017
+ /**
1018
+ * Application identifier.
1019
+ */
1020
+ readonly clientId: string;
1021
+ /**
1022
+ * Session creation timestamp, in Unix seconds.
1023
+ */
1024
+ readonly createdAt: number;
1025
+ /**
1026
+ * Underlying grant expiry timestamp, in Unix seconds.
1027
+ */
1028
+ readonly grantExpiresAt: number;
1029
+ /**
1030
+ * Grant id this session was minted from.
1031
+ */
1032
+ readonly grantId: string;
1033
+ /**
1034
+ * Homeserver that issued this session.
1035
+ */
1036
+ readonly homeserver: PublicKey;
1037
+ /**
1038
+ * User public key for this session.
1039
+ */
1040
+ readonly publicKey: PublicKey;
1041
+ /**
1042
+ * Bearer token expiry timestamp, in Unix seconds.
1043
+ */
1044
+ readonly tokenExpiresAt: number;
1045
+ }
1046
+
630
1047
  export class IntoUnderlyingByteSource {
631
1048
  private constructor();
632
1049
  free(): void;
@@ -706,13 +1123,17 @@ export class Pkdns {
706
1123
  * Resolve the homeserver for **this** user (requires keypair).
707
1124
  *
708
1125
  * @returns {Promise<PublicKey|undefined>} Homeserver public key or `undefined` if not found.
1126
+ * @throws When no keypair is attached, Pkarr resolution fails, or a resolved `_pubky` target
1127
+ * is malformed.
709
1128
  */
710
1129
  getHomeserver(): Promise<PublicKey | undefined>;
711
1130
  /**
712
1131
  * Resolve the homeserver for a given public key (read-only).
713
1132
  *
714
1133
  * @param {PublicKey} user
715
- * @returns {Promise<PublicKey|undefined>} Homeserver public key or `undefined` if not found.
1134
+ * @returns {Promise<PublicKey|undefined>} Homeserver public key, or `undefined` when the
1135
+ * user has no resolvable homeserver record.
1136
+ * @throws When Pkarr resolution fails or a resolved `_pubky` target is malformed.
716
1137
  */
717
1138
  getHomeserverOf(pubky: PublicKey): Promise<PublicKey | undefined>;
718
1139
  /**
@@ -797,7 +1218,9 @@ export class Pubky {
797
1218
  * Uses an internal read-only Pkdns actor.
798
1219
  *
799
1220
  * @param {PublicKey} user
800
- * @returns {Promise<PublicKey|undefined>} Homeserver public key or `undefined` if not found.
1221
+ * @returns {Promise<PublicKey|undefined>} Homeserver public key, or `undefined` when the
1222
+ * user has no resolvable homeserver record.
1223
+ * @throws When Pkarr resolution fails or a resolved `_pubky` target is malformed.
801
1224
  */
802
1225
  getHomeserverOf(user_public_key: PublicKey): Promise<PublicKey | undefined>;
803
1226
  /**
@@ -816,12 +1239,13 @@ export class Pubky {
816
1239
  */
817
1240
  constructor();
818
1241
  /**
819
- * Restore a session from a previously exported snapshot, using this instance's client.
1242
+ * Restore a session from a previously exported token or snapshot, using this instance's client.
820
1243
  *
821
- * This does **not** read or write any secrets. It revalidates the session metadata with
822
- * the server using the browser-managed HTTP-only cookie that must still be present.
1244
+ * Accepts grant secret tokens from `session.exportLocalSecret()` and legacy cookie
1245
+ * secret tokens. Also accepts legacy cookie metadata snapshots from `session.export()`.
1246
+ * Grant restore mints a fresh short-lived bearer.
823
1247
  *
824
- * @param {string} exported A string produced by `session.export()`.
1248
+ * @param {string} exported A string produced by `session.exportLocalSecret()` or legacy `session.export()`.
825
1249
  * @returns {Promise<Session>}
826
1250
  * A rehydrated session bound to this SDK's HTTP client.
827
1251
  *
@@ -832,20 +1256,62 @@ export class Pubky {
832
1256
  /**
833
1257
  * Resume a previously started **pubkyauth** flow from its saved `authorizationUrl`.
834
1258
  *
1259
+ * If the user refreshes or navigates away mid-flow, WASM memory is lost and
1260
+ * the original `AuthFlow` object is gone. You can reconnect to the same relay
1261
+ * channel by saving the `authorizationUrl` beforehand and calling this method
1262
+ * after reload.
1263
+ *
835
1264
  * The relay inbox retains messages for **~5 minutes**. Resume is only
836
1265
  * viable within that window; afterwards start a fresh flow.
837
1266
  *
838
1267
  * **Security:** The URL contains the `client_secret` in plaintext.
839
- * Delete it from storage as soon as the resumed flow completes.
840
- * See `startAuthFlow()` docs for full storage guidance.
1268
+ * Store it in `sessionStorage` (scoped to the tab), **not** `localStorage`,
1269
+ * and delete it as soon as the resumed flow completes or is abandoned.
1270
+ * See `startCookieAuthFlow()` docs for full storage guidance.
841
1271
  *
842
1272
  * @param {string} authorizationUrl The `pubkyauth://…` URL from a previous flow.
843
1273
  * @returns {AuthFlow} A flow reconnected to the original relay channel.
844
1274
  * @throws {PubkyError}
845
1275
  * - `{ name: "AuthenticationError" }` if the URL is invalid or not a signin/signup link
846
1276
  * - `{ name: "RequestError" }` on network/relay failure
1277
+ *
1278
+ * @example
1279
+ * // 1) Before a potential refresh, persist the URL.
1280
+ * const flow = pubky.startCookieAuthFlow("/pub/my-cool-app/:rw", AuthFlowKind.signin());
1281
+ * sessionStorage.setItem("pubky-auth-url", flow.authorizationUrl);
1282
+ * renderQr(flow.authorizationUrl);
1283
+ *
1284
+ * // 2) After reload, resume from the saved URL.
1285
+ * const savedUrl = sessionStorage.getItem("pubky-auth-url");
1286
+ * if (savedUrl) {
1287
+ * try {
1288
+ * const resumed = pubky.resumeCookieAuthFlow(savedUrl);
1289
+ * const session = await resumed.awaitApproval();
1290
+ * } finally {
1291
+ * sessionStorage.removeItem("pubky-auth-url");
1292
+ * }
1293
+ * }
1294
+ */
1295
+ resumeCookieAuthFlow(authorization_url: string): AuthFlow;
1296
+ /**
1297
+ * Resume a previously saved pending delegated grant auth flow.
1298
+ *
1299
+ * Runtime: delegated grant keys require a secure browser context with
1300
+ * WebCrypto `crypto.subtle` and IndexedDB. The saved `keyId` must still
1301
+ * exist in IndexedDB for the same origin. Unsupported runtimes reject with
1302
+ * `ClientStateError`.
1303
+ */
1304
+ resumeDelegatedGrantAuthFlow(saved_state: string): Promise<GrantAuthFlow>;
1305
+ /**
1306
+ * Resume a previously saved pending grant auth flow.
1307
+ *
1308
+ * **Security:** `savedState` contains the relay secret and PoP client private key.
1309
+ * Delete it from storage as soon as the resumed flow completes.
1310
+ *
1311
+ * @param {string} savedState A string produced by `grantFlow.saveLocal()`.
1312
+ * @returns {GrantAuthFlow} A flow reconnected to the original relay channel.
847
1313
  */
848
- resumeAuthFlow(authorization_url: string): AuthFlow;
1314
+ resumeGrantAuthFlow(saved_state: string): GrantAuthFlow;
849
1315
  /**
850
1316
  * Create a `Signer` from an existing `Keypair`.
851
1317
  *
@@ -854,7 +1320,7 @@ export class Pubky {
854
1320
  *
855
1321
  * @example
856
1322
  * const signer = pubky.signer(Keypair.random());
857
- * const session = await signer.signup(homeserverPk, null);
1323
+ * await signer.signup(homeserverPk);
858
1324
  */
859
1325
  signer(keypair: Keypair): Signer;
860
1326
  /**
@@ -877,6 +1343,7 @@ export class Pubky {
877
1343
  * - `AuthFlowKind.signin()` - Sign in to an existing account.
878
1344
  * - `AuthFlowKind.signup(homeserverPublicKey, signupToken)` - Sign up for a new account.
879
1345
  * @param {string=} relay Optional HTTP relay base (e.g. `"https://…/inbox/"`).
1346
+ * @param {XCallbackParams=} xCallback Optional app return destinations.
880
1347
  * @returns {AuthFlow}
881
1348
  * A running auth flow. Show `authorizationUrl` as QR/deeplink,
882
1349
  * then `awaitApproval()` to obtain a `Session`.
@@ -886,11 +1353,33 @@ export class Pubky {
886
1353
  * - `{ name: "RequestError" }` if the flow cannot be started (network/relay)
887
1354
  *
888
1355
  * @example
889
- * const flow = pubky.startAuthFlow("/pub/my-cool-app/:rw");
1356
+ * const flow = pubky.startCookieAuthFlow("/pub/my-cool-app/:rw");
890
1357
  * renderQr(flow.authorizationUrl);
891
1358
  * const session = await flow.awaitApproval();
892
1359
  */
893
- startAuthFlow(capabilities: Capabilities, kind: AuthFlowKind, relay?: string | null): AuthFlow;
1360
+ startCookieAuthFlow(capabilities: Capabilities, kind: AuthFlowKind, relay?: string | null, x_callback?: XCallbackParams | null): AuthFlow;
1361
+ /**
1362
+ * Start a grant-backed **pubkyauth** flow.
1363
+ *
1364
+ * Grant auth uses a user-signed grant JWS plus Proof-of-Possession and
1365
+ * returns a self-refreshing session.
1366
+ *
1367
+ * @param {string} capabilities Comma-separated caps, e.g. `"/pub/app/:rw,/pub/foo/file:r"`.
1368
+ * @param {AuthFlowKind} kind The kind of authentication flow to perform.
1369
+ * @param {GrantAuthFlowOptions} options Options for the grant flow:
1370
+ * `{ clientId, relay?, xCallback? }`.
1371
+ * @returns {Promise<GrantAuthFlow>}
1372
+ * A running grant auth flow. Show `authorizationUrl` as QR/deeplink,
1373
+ * then `awaitApproval()` to obtain a grant-backed `Session`.
1374
+ *
1375
+ * @example
1376
+ * const flow = await pubky.startGrantAuthFlow(
1377
+ * "/pub/my-cool-app/:rw",
1378
+ * AuthFlowKind.signin(),
1379
+ * { clientId: "my-cool-app.example" },
1380
+ * );
1381
+ */
1382
+ startGrantAuthFlow(capabilities: Capabilities, kind: AuthFlowKind, options: GrantAuthFlowOptions): Promise<GrantAuthFlow>;
894
1383
  /**
895
1384
  * Create a Pubky facade preconfigured for a **local testnet**.
896
1385
  *
@@ -916,6 +1405,14 @@ export class Pubky {
916
1405
  * const pubky = Pubky.withClient(client);
917
1406
  */
918
1407
  static withClient(client: Client): Pubky;
1408
+ /**
1409
+ * Browser-backed store for explicitly persisted completed grant sessions.
1410
+ *
1411
+ * Use `browserSessionStore.save(session)` after a successful grant auth flow to make
1412
+ * the session restorable after reload. The store supports multiple accounts
1413
+ * and multiple grants per account.
1414
+ */
1415
+ readonly browserSessionStore: BrowserSessionStore;
919
1416
  /**
920
1417
  * Access the underlying HTTP client (advanced).
921
1418
  *
@@ -1084,6 +1581,10 @@ export class SeedExportDeepLink {
1084
1581
  static parse(url: string): SeedExportDeepLink;
1085
1582
  toString(): string;
1086
1583
  readonly secret: Uint8Array;
1584
+ /**
1585
+ * Optional x-callback-url metadata carried by this deep link.
1586
+ */
1587
+ readonly xCallback: XCallbackParams;
1087
1588
  }
1088
1589
 
1089
1590
  /**
@@ -1103,8 +1604,25 @@ export class Session {
1103
1604
  *
1104
1605
  * @returns {string}
1105
1606
  * A base64 string to store (e.g. in `localStorage`).
1607
+ *
1608
+ * @deprecated Prefer `exportLocalSecret()` for grant sessions.
1106
1609
  */
1107
1610
  export(): string;
1611
+ /**
1612
+ * Export the local secret material needed to restore this session.
1613
+ *
1614
+ * For local grant sessions this exports the grant JWS and PoP client secret;
1615
+ * restoring mints a fresh bearer. For legacy cookie sessions this exports the
1616
+ * cookie secret when the SDK owns it. Delegated browser grant sessions cannot
1617
+ * export raw secret material; use `BrowserSessionStore`.
1618
+ *
1619
+ * Treat the returned string as a bearer-equivalent secret until the grant or
1620
+ * cookie session expires or is revoked.
1621
+ *
1622
+ * @returns {Promise<string>}
1623
+ * A secret token that can be passed to `pubky.restoreSession()`.
1624
+ */
1625
+ exportLocalSecret(): Promise<string>;
1108
1626
  /**
1109
1627
  * Restore a session from an `export()` string.
1110
1628
  *
@@ -1116,6 +1634,8 @@ export class Session {
1116
1634
  * @param {Client=} client
1117
1635
  * Optional client to reuse transport configuration.
1118
1636
  * @returns {Promise<Session>}
1637
+ *
1638
+ * @deprecated Prefer `Pubky.restoreSession(...)`.
1119
1639
  */
1120
1640
  static restore(exported: string, client?: Client | null): Promise<Session>;
1121
1641
  /**
@@ -1125,6 +1645,22 @@ export class Session {
1125
1645
  * @returns {Promise<void>}
1126
1646
  */
1127
1647
  signout(): Promise<void>;
1648
+ /**
1649
+ * Cookie-only view for cookie-backed sessions.
1650
+ *
1651
+ * Grant-backed sessions return `undefined`.
1652
+ *
1653
+ * @returns {CookieSession|undefined}
1654
+ */
1655
+ readonly cookie: CookieSession | undefined;
1656
+ /**
1657
+ * Grant-only management view for grant-backed sessions.
1658
+ *
1659
+ * Cookie-backed sessions return `undefined`.
1660
+ *
1661
+ * @returns {GrantSession|undefined}
1662
+ */
1663
+ readonly grant: GrantSession | undefined;
1128
1664
  /**
1129
1665
  * Retrieve immutable info about this session (user & capabilities).
1130
1666
  *
@@ -1167,7 +1703,7 @@ export class SessionInfo {
1167
1703
  }
1168
1704
 
1169
1705
  /**
1170
- * Read/write storage scoped to **your** session (absolute paths: `/pub/...`).
1706
+ * Read/write storage scoped to **your** session (absolute paths: `/pub/...` or `/priv/...`).
1171
1707
  */
1172
1708
  export class SessionStorage {
1173
1709
  private constructor();
@@ -1282,38 +1818,67 @@ export class Signer {
1282
1818
  * @returns {Signer}
1283
1819
  */
1284
1820
  static fromKeypair(keypair: Keypair): Signer;
1821
+ /**
1822
+ * Handle a `pubkyauth://` deep link.
1823
+ *
1824
+ * Auth requests are approved through their relay. A `direct_signup` link
1825
+ * creates an account on its target homeserver.
1826
+ */
1827
+ handleDeepLink(pubkyauth_url: string): Promise<void>;
1285
1828
  /**
1286
1829
  * Fast sign-in for a returning user. Publishes PKDNS in the background.
1287
1830
  *
1288
- * Creates a valid homeserver Session with root capabilities
1831
+ * Creates a valid grant-backed homeserver Session with root capabilities.
1832
+ * `clientId` is shown in the user's grant/session list.
1833
+ * @param {string} clientId App identifier, typically a domain.
1289
1834
  *
1290
1835
  * @returns {Promise<Session>}
1291
1836
  *
1292
1837
  * @throws {PubkyError}
1293
1838
  */
1294
- signin(): Promise<Session>;
1839
+ signin(client_id: string): Promise<Session>;
1295
1840
  /**
1296
1841
  * Blocking sign-in. Waits for PKDNS publish to complete (slower; safer).
1297
1842
  *
1298
- * Creates a valid homeserver Session with root capabilities
1843
+ * Creates a valid grant-backed homeserver Session with root capabilities.
1844
+ * `clientId` is shown in the user's grant/session list.
1845
+ * @param {string} clientId App identifier, typically a domain.
1299
1846
  *
1300
1847
  * @returns {Promise<Session>}
1301
1848
  */
1302
- signinBlocking(): Promise<Session>;
1849
+ signinBlocking(client_id: string): Promise<Session>;
1850
+ /**
1851
+ * Legacy cookie signin. Prefer `signin(clientId)`.
1852
+ *
1853
+ * @deprecated Prefer `signin(clientId)`.
1854
+ */
1855
+ signinCookie(): Promise<Session>;
1856
+ /**
1857
+ * Legacy cookie blocking signin. Prefer `signinBlocking(clientId)`.
1858
+ *
1859
+ * @deprecated Prefer `signinBlocking(clientId)`.
1860
+ */
1861
+ signinCookieBlocking(): Promise<Session>;
1303
1862
  /**
1304
- * Sign up at a homeserver. Returns a ready `Session`.
1863
+ * Sign up at a homeserver.
1305
1864
  *
1306
- * Creates a valid homeserver Session with root capabilities
1865
+ * Creates the account and publishes PKDNS. Call `signin(clientId)` to create a session.
1307
1866
  *
1308
1867
  * @param {PublicKey} homeserver The homeserver’s public key.
1309
1868
  * @param {string|null} signupToken Invite/registration token or `null`.
1310
- * @returns {Promise<Session>}
1869
+ * @returns {Promise<void>}
1311
1870
  *
1312
1871
  * @throws {PubkyError}
1313
1872
  * - `AuthenticationError` (bad/expired token)
1314
1873
  * - `RequestError` (network/server)
1315
1874
  */
1316
- signup(homeserver: PublicKey, signup_token?: string | null): Promise<Session>;
1875
+ signup(homeserver: PublicKey, signup_token?: string | null): Promise<void>;
1876
+ /**
1877
+ * Legacy cookie signup. Prefer `signup()` plus `signin(clientId)`.
1878
+ *
1879
+ * @deprecated Prefer `signup()` followed by `signin(clientId)`.
1880
+ */
1881
+ signupCookie(homeserver: PublicKey, signup_token?: string | null): Promise<Session>;
1317
1882
  /**
1318
1883
  * Get a PKDNS actor bound to this signer's client & keypair (publishing enabled).
1319
1884
  *
@@ -1340,6 +1905,72 @@ export class SigninDeepLink {
1340
1905
  readonly baseRelayUrl: string;
1341
1906
  readonly capabilities: string;
1342
1907
  readonly secret: Uint8Array;
1908
+ /**
1909
+ * Optional x-callback-url metadata carried by this deep link.
1910
+ */
1911
+ readonly xCallback: XCallbackParams;
1912
+ }
1913
+
1914
+ /**
1915
+ * Parsed grant-based signin deeplink.
1916
+ *
1917
+ * This is useful for tools, tests, and signer UIs that need to inspect a
1918
+ * `pubky://signin-grant` authorization URL before approving it.
1919
+ */
1920
+ export class SigninGrantDeepLink {
1921
+ private constructor();
1922
+ free(): void;
1923
+ [Symbol.dispose](): void;
1924
+ /**
1925
+ * Parse a grant signin deeplink URL.
1926
+ *
1927
+ * @param {string} url
1928
+ * @returns {SigninGrantDeepLink}
1929
+ * @throws {PubkyError} `InvalidInput` when the URL is malformed or not a grant signin link.
1930
+ */
1931
+ static parse(url: string): SigninGrantDeepLink;
1932
+ /**
1933
+ * Serialize this parsed deeplink back to its URL form.
1934
+ *
1935
+ * @returns {string}
1936
+ */
1937
+ toString(): string;
1938
+ /**
1939
+ * Base HTTP relay inbox URL used by this auth request.
1940
+ *
1941
+ * @returns {string}
1942
+ */
1943
+ readonly baseRelayUrl: string;
1944
+ /**
1945
+ * Capabilities requested by the application.
1946
+ *
1947
+ * @returns {string}
1948
+ */
1949
+ readonly capabilities: string;
1950
+ /**
1951
+ * Application identifier shown in the user's grant/session list.
1952
+ *
1953
+ * @returns {string}
1954
+ */
1955
+ readonly clientId: string;
1956
+ /**
1957
+ * Public key for the Proof-of-Possession client created by the application.
1958
+ *
1959
+ * @returns {PublicKey}
1960
+ */
1961
+ readonly clientPublicKey: PublicKey;
1962
+ /**
1963
+ * Relay channel secret embedded in the deeplink.
1964
+ *
1965
+ * Treat this as sensitive temporary auth-flow material.
1966
+ *
1967
+ * @returns {Uint8Array}
1968
+ */
1969
+ readonly secret: Uint8Array;
1970
+ /**
1971
+ * Optional x-callback-url metadata carried by this deep link.
1972
+ */
1973
+ readonly xCallback: XCallbackParams;
1343
1974
  }
1344
1975
 
1345
1976
  export class SignupDeepLink {
@@ -1353,6 +1984,129 @@ export class SignupDeepLink {
1353
1984
  readonly homeserver: PublicKey;
1354
1985
  readonly secret: Uint8Array;
1355
1986
  readonly signupToken: string | undefined;
1987
+ /**
1988
+ * Optional x-callback-url metadata carried by this deep link.
1989
+ */
1990
+ readonly xCallback: XCallbackParams;
1991
+ }
1992
+
1993
+ /**
1994
+ * Parsed grant-based signup deeplink.
1995
+ *
1996
+ * This is useful for tools, tests, and signer UIs that need to inspect a
1997
+ * `pubky://signup-grant` authorization URL before approving it.
1998
+ */
1999
+ export class SignupGrantDeepLink {
2000
+ private constructor();
2001
+ free(): void;
2002
+ [Symbol.dispose](): void;
2003
+ /**
2004
+ * Parse a grant signup deeplink URL.
2005
+ *
2006
+ * @param {string} url
2007
+ * @returns {SignupGrantDeepLink}
2008
+ * @throws {PubkyError} `InvalidInput` when the URL is malformed or not a grant signup link.
2009
+ */
2010
+ static parse(url: string): SignupGrantDeepLink;
2011
+ /**
2012
+ * Serialize this parsed deeplink back to its URL form.
2013
+ *
2014
+ * @returns {string}
2015
+ */
2016
+ toString(): string;
2017
+ /**
2018
+ * Base HTTP relay inbox URL used by this auth request.
2019
+ *
2020
+ * @returns {string}
2021
+ */
2022
+ readonly baseRelayUrl: string;
2023
+ /**
2024
+ * Capabilities requested by the application.
2025
+ *
2026
+ * @returns {string}
2027
+ */
2028
+ readonly capabilities: string;
2029
+ /**
2030
+ * Application identifier shown in the user's grant/session list.
2031
+ *
2032
+ * @returns {string}
2033
+ */
2034
+ readonly clientId: string;
2035
+ /**
2036
+ * Public key for the Proof-of-Possession client created by the application.
2037
+ *
2038
+ * @returns {PublicKey}
2039
+ */
2040
+ readonly clientPublicKey: PublicKey;
2041
+ /**
2042
+ * Homeserver public key requested for account creation.
2043
+ *
2044
+ * @returns {PublicKey}
2045
+ */
2046
+ readonly homeserver: PublicKey;
2047
+ /**
2048
+ * Relay channel secret embedded in the deeplink.
2049
+ *
2050
+ * Treat this as sensitive temporary auth-flow material.
2051
+ *
2052
+ * @returns {Uint8Array}
2053
+ */
2054
+ readonly secret: Uint8Array;
2055
+ /**
2056
+ * Optional signup token included in the request.
2057
+ *
2058
+ * @returns {string|undefined}
2059
+ */
2060
+ readonly signupToken: string | undefined;
2061
+ /**
2062
+ * Optional x-callback-url metadata carried by this deep link.
2063
+ */
2064
+ readonly xCallback: XCallbackParams;
2065
+ }
2066
+
2067
+ /**
2068
+ * Metadata for a session saved in the browser session store.
2069
+ */
2070
+ export class StoredSessionInfo {
2071
+ private constructor();
2072
+ free(): void;
2073
+ [Symbol.dispose](): void;
2074
+ /**
2075
+ * Authorized capabilities.
2076
+ */
2077
+ readonly capabilities: string[];
2078
+ /**
2079
+ * Application/client identifier.
2080
+ */
2081
+ readonly clientId: string;
2082
+ /**
2083
+ * Local save timestamp, in Unix milliseconds.
2084
+ */
2085
+ readonly createdAt: number;
2086
+ /**
2087
+ * Underlying grant expiry timestamp, in Unix seconds.
2088
+ */
2089
+ readonly grantExpiresAt: number;
2090
+ /**
2091
+ * Grant identifier backing this stored session.
2092
+ */
2093
+ readonly grantId: string;
2094
+ /**
2095
+ * Homeserver public key as z32.
2096
+ */
2097
+ readonly homeserver: string;
2098
+ /**
2099
+ * Stable local identifier for this stored session.
2100
+ */
2101
+ readonly id: string;
2102
+ /**
2103
+ * User public key as z32.
2104
+ */
2105
+ readonly publicKey: string;
2106
+ /**
2107
+ * `delegated` for origin-bound WebCrypto sessions, `localSecret` for raw local PoP secret storage.
2108
+ */
2109
+ readonly storageMode: string;
1356
2110
  }
1357
2111
 
1358
2112
  /**
@@ -1390,11 +2144,11 @@ export function setLogLevel(level: Level): void;
1390
2144
  * Validate and normalize a capabilities string.
1391
2145
  *
1392
2146
  * - Normalizes action order (`wr` -> `rw`)
1393
- * - Throws `InvalidInput` listing malformed entries.
2147
+ * - Throws `InvalidInput` identifying the first malformed entry.
1394
2148
  *
1395
2149
  * @param {string} input
1396
2150
  * @returns {string} Normalized string (same shape as input).
1397
2151
  * @throws {PubkyError} `{ name: "InvalidInput" }` with a helpful message.
1398
- * The error's `data` field is `{ invalidEntries: string[] }` listing malformed tokens.
2152
+ * The error's `data` field is `{ invalidEntries: string[] }` containing the malformed token.
1399
2153
  */
1400
2154
  export function validateCapabilities(input: string): string;