@derec-alliance/nodejs 0.0.5 → 0.0.6

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/index.d.ts CHANGED
@@ -9,15 +9,15 @@ export interface SecretStore {
9
9
  ): Promise<Uint8Array | null | undefined>;
10
10
  /**
11
11
  * Load secrets of the same `kind` for several channels in one call,
12
- * scoped to `secretId`. Must return an array with one entry per input
13
- * id, in the same order, using `null` (or `undefined`) for channels
14
- * with no stored secret of `kind`.
12
+ * scoped to `secretId`. Must return an array with exactly one entry per
13
+ * input id, in the same order, using `null` (or `undefined`) for channels
14
+ * with no stored secret of `kind`. Whether a missing entry is an error is
15
+ * decided by the library.
15
16
  */
16
17
  loadMany(
17
18
  secretId: string,
18
19
  channelIds: string[],
19
20
  kind: 0 | 1 | 2,
20
- missingPolicy: "skip" | "fail",
21
21
  ): Promise<Array<Uint8Array | null | undefined>>;
22
22
  save(
23
23
  secretId: string,
@@ -123,22 +123,15 @@ export declare function channelFilterMatches(
123
123
  /**
124
124
  * The endpoints a peer-supplied message advertises, in the peer's own order.
125
125
  *
126
- * Yields `supported_transports` when it is non-empty, and otherwise the
127
- * singular `transport_protocol` — which is how every implementation predating
128
- * the offer list advertises, and the reason this is a function rather than a
129
- * field read. Reading `transport_protocol` directly is a bug: its meaning
130
- * narrowed to "one entry of a list, and possibly absent", so a peer that has
131
- * moved past it looks unreachable to a reader that was correct before 0.0.3.
132
- *
133
126
  * Reports what was advertised, not what is acceptable — nothing here is
134
127
  * validated, and the protocol still applies its own transport policy to
135
128
  * whatever it records.
136
129
  */
137
130
  export declare function advertisedEndpoints(
138
131
  message:
139
- | Pick<ContactMessage, "transport_protocol" | "supported_transports">
140
- | Pick<PairRequestMessage, "transport_protocol" | "supported_transports">
141
- | Pick<PrePairRequestMessage, "transport_protocol" | "supported_transports">
132
+ | Pick<ContactMessage, "supported_transports">
133
+ | Pick<PairRequestMessage, "supported_transports">
134
+ | Pick<PrePairRequestMessage, "supported_transports">
142
135
  | null
143
136
  | undefined,
144
137
  ): TransportProtocol[];
@@ -228,11 +221,7 @@ export interface ChannelStore {
228
221
  secretId: string,
229
222
  filter: ReplicaFilter,
230
223
  ): Promise<Uint8Array | null | undefined>;
231
- linkChannel(
232
- secretId: string,
233
- channelId: string,
234
- linkedChannelId: string,
235
- ): Promise<void>;
224
+ linkChannel(secretId: string, a: string, b: string): Promise<void>;
236
225
  linkedChannels(secretId: string, channelId: string): Promise<string[]>;
237
226
  }
238
227
 
@@ -276,6 +265,10 @@ export interface UserSecrets {
276
265
  version: number;
277
266
  secrets: UserSecretEntry[];
278
267
  description?: string;
268
+ /** Decimal `replica_id` of the member that published `version`. Absent
269
+ * when none is recorded. Store and return it unchanged: replica members
270
+ * compare it to detect a conflicting copy of the same version. */
271
+ author_replica_id?: string;
279
272
  }
280
273
 
281
274
  /**
@@ -319,6 +312,18 @@ export interface StateStore {
319
312
  loadAll(secretId: string, kind: 0 | 1 | 2 | 3 | 4): Promise<Uint8Array[]>;
320
313
  }
321
314
 
315
+ /** A transport protocol, by name. */
316
+ export type TransportProtocolName = "https" | "grpc";
317
+
318
+ /**
319
+ * One endpoint a node serves or a peer advertised, as every app-facing call
320
+ * and event carries it.
321
+ */
322
+ export interface Endpoint {
323
+ uri: string;
324
+ protocol: TransportProtocolName;
325
+ }
326
+
322
327
  /**
323
328
  * Outbound message delivery.
324
329
  *
@@ -364,7 +369,7 @@ export interface Transport {
364
369
  * with {@link singleEndpointTransport} rather than by indexing.
365
370
  */
366
371
  send(
367
- endpoints: ReadonlyArray<{ protocol: string; uri: string }>,
372
+ endpoints: ReadonlyArray<Endpoint>,
368
373
  message: Uint8Array,
369
374
  ): Promise<void>;
370
375
  }
@@ -384,10 +389,25 @@ export interface Transport {
384
389
  * a round trip; skipping one that would have worked costs the delivery.
385
390
  */
386
391
  export type SendOne = (
387
- endpoint: { protocol: string; uri: string },
392
+ endpoint: Endpoint,
388
393
  message: Uint8Array,
389
394
  ) => Promise<void>;
390
395
 
396
+ /**
397
+ * The DeRec protocol version this build speaks — the `protocolVersionMajor` /
398
+ * `protocolVersionMinor` it writes into every envelope it produces. Not the
399
+ * package version.
400
+ */
401
+ export declare function protocol_version(): { major: number; minor: number };
402
+
403
+ /**
404
+ * A fresh replica identity, generated by the core — never `0`. Persist it
405
+ * once per device and pass the same value to `withReplicaId` on every
406
+ * protocol init: a device whose id changes cannot be re-identified by its
407
+ * group.
408
+ */
409
+ export declare function generate_replica_id(): bigint;
410
+
391
411
  /**
392
412
  * Builds a {@link Transport} that tries each endpoint in the order the peer
393
413
  * offered it and stops at the first success.
@@ -432,7 +452,7 @@ export enum SenderKind {
432
452
  * the scanner must fetch the actual keys over the wire via the `PrePair`
433
453
  * round-trip and verify them against the commitment before pairing.
434
454
  * - `NoKeys`: no key material and no commitment. The contact carries only
435
- * `channel_id`, `nonce`, and `transport_protocol` — small enough to be
455
+ * `channel_id`, `nonce`, and `supported_transports` — small enough to be
436
456
  * hand-typed or dictated. Keys are generated on the fly by the contact
437
457
  * creator when the `PrePairRequest` arrives; the scanner accepts them
438
458
  * without cryptographic verification. Trust rests entirely on the OOB
@@ -474,20 +494,33 @@ export enum FlowKind {
474
494
  UnpairReplica = 8,
475
495
  }
476
496
 
497
+ /** Result status carried in every protocol response (`result.proto`). Passed
498
+ * to `reject()` to say why an inbound request was refused. */
499
+ export enum StatusEnum {
500
+ Ok = 0,
501
+ Partial = 1,
502
+ Fail = 2,
503
+ SizeLimitExceeded = 3,
504
+ TooFrequent = 4,
505
+ UnknownSecretId = 5,
506
+ UnknownShareVersion = 6,
507
+ DecryptionFailed = 7,
508
+ VerificationFailed = 8,
509
+ FormatError = 9,
510
+ Rejected = 10,
511
+ IncompatibleParameterRange = 11,
512
+ UnsupportedTransportProtocol = 12,
513
+ VersionConflict = 13,
514
+ ReplicaIdConflict = 14,
515
+ RequestToClose = 99,
516
+ }
517
+
477
518
  export type UnpairAck = "required" | "not_required";
478
519
 
479
520
  export interface ContactMessage {
480
521
  channel_id: bigint;
481
522
  /** `ContactMode` numeric value (0 = INLINE_KEYS, 1 = HASHED_KEYS, 2 = NO_KEYS). */
482
523
  contact_mode: number;
483
- /**
484
- * @deprecated Reading this field directly is incorrect: its meaning narrowed
485
- * to "one entry of a list, and possibly absent", so a peer advertising only
486
- * `supported_transports` looks unreachable to a reader that was correct
487
- * before 0.0.3. Call {@link advertisedEndpoints}, which resolves both
488
- * spellings. Removed at 0.0.5.
489
- */
490
- transport_protocol?: TransportProtocol;
491
524
  nonce: bigint;
492
525
  /** Present only when `contact_mode === ContactMode.InlineKeys`. */
493
526
  mlkem_encapsulation_key?: Uint8Array;
@@ -497,8 +530,7 @@ export interface ContactMessage {
497
530
  contact_binding_hash?: Uint8Array;
498
531
  timestamp?: Timestamp;
499
532
  /** Every transport endpoint the creator of this contact can be reached
500
- * on, in its own preference order. Empty means "only
501
- * `transport_protocol` is offered". */
533
+ * on, in its own preference order. */
502
534
  supported_transports: TransportProtocol[];
503
535
  }
504
536
 
@@ -546,20 +578,11 @@ export interface UpdateChannelInfoParams {
546
578
  * map untouched; pass an empty object to clear it. */
547
579
  communication_info?: Record<string, string>;
548
580
 
549
- /**
550
- * New transport endpoint. Absent leaves it untouched.
551
- *
552
- * @deprecated Use `own_transports`, which carries every endpoint this node
553
- * now serves; its first entry also fills this field for peers predating the
554
- * list. Removed at 0.0.5.
555
- */
556
- transport_protocol?: { uri: string; protocol: number };
557
581
  /**
558
582
  * Every endpoint this node now serves, in its own preference order.
559
- * Omitted leaves the target(s)' stored set untouched. Takes precedence
560
- * over `transport_protocol`, whose first entry it also fills.
583
+ * Omitted leaves the target(s)' stored set untouched.
561
584
  */
562
- own_transports?: TransportProtocol[];
585
+ own_transports?: Endpoint[];
563
586
  }
564
587
 
565
588
  /**
@@ -618,18 +641,34 @@ export type DeRecEvent =
618
641
  action: Uint8Array;
619
642
 
620
643
  action_kind: PendingActionKind;
644
+ /** Correlation token of the inbound request, decimal-encoded. */
645
+ trace_id: string;
646
+ /** Pairing only. */
621
647
  peer_communication_info?: Record<string, string>;
622
-
648
+ /** `sender_kind` of the inbound pair request (Pairing only). */
623
649
  sender_kind?: SenderKind;
624
-
650
+ /** Share version (StoreShare / VerifyShare / GetShare). */
625
651
  version?: number;
652
+ /** Description of the secret version (StoreShare only). */
626
653
  share_description?: string;
627
-
654
+ /** Secret identifier, decimal-encoded (StoreShare / VerifyShare /
655
+ * GetShare). On GetShare, with `version`, names the requested share. */
628
656
  share_secret_id?: string;
657
+ /** Length in bytes of the share the helper would store (StoreShare
658
+ * only) — what a size or quota decision is made on. */
659
+ share_size?: number;
660
+ /** The peer's memo (Unpair only). */
661
+ unpair_memo?: string;
662
+ /** Communication info the peer replaces its stored map with
663
+ * (UpdateChannelInfo only). Absent: unchanged. Empty: cleared. */
664
+ updated_communication_info?: Record<string, string>;
665
+ /** Endpoints the peer is moving to (UpdateChannelInfo only). Absent:
666
+ * unchanged. */
667
+ updated_transports?: Endpoint[];
629
668
  }
630
669
  | { type: "ShareStored"; channel_id: string; version: number }
631
670
  | { type: "ShareConfirmed"; channel_id: string; version: number }
632
- | { type: "ShareRejected"; channel_id: string; version: number; status: number; memo: string }
671
+ | { type: "ShareRejected"; channel_id: string; version: number; status: StatusEnum; memo: string }
633
672
  /** A publishing round finished — every targeted helper confirmed,
634
673
  * rejected, or timed out.
635
674
  *
@@ -654,7 +693,7 @@ export type DeRecEvent =
654
693
  replica_id: string;
655
694
  secret_id: string;
656
695
  version: number;
657
- status: number;
696
+ status: StatusEnum;
658
697
  memo: string;
659
698
  }
660
699
  /** A secret sync could not be delivered to a member at all — distinct from
@@ -707,9 +746,9 @@ export type DeRecEvent =
707
746
  helpers: Array<{
708
747
  channel_id: string;
709
748
  /** Every endpoint this peer advertised, in the order it offered them. */
710
- transports: Array<{ uri: string; protocol: number }>;
749
+ transports: Endpoint[];
711
750
  shared_key: Uint8Array;
712
- communication_info: Record<string, string>;
751
+ communication_info?: Record<string, string>;
713
752
  }>;
714
753
  secrets: Array<{
715
754
  id: Uint8Array;
@@ -729,9 +768,9 @@ export type DeRecEvent =
729
768
  members: Array<{
730
769
  replica_id: string;
731
770
  /** Every endpoint this peer advertised, in the order it offered them. */
732
- transports: Array<{ uri: string; protocol: number }>;
771
+ transports: Endpoint[];
733
772
  role: "Source" | "Destination";
734
- communication_info: Record<string, string>;
773
+ communication_info?: Record<string, string>;
735
774
  }>;
736
775
  shared_key: Uint8Array;
737
776
  };
@@ -740,12 +779,12 @@ export type DeRecEvent =
740
779
 
741
780
  | { type: "Unpaired"; channel_id: string }
742
781
 
743
- | { type: "UnpairRejected"; channel_id: string; status: number; memo: string }
782
+ | { type: "UnpairRejected"; channel_id: string; status: StatusEnum; memo: string }
744
783
 
745
784
  /** Contact creator answered the scanner's `PrePairRequest` with a
746
785
  * non-Ok status (HashedKeys flow). Distinct from a cryptographic
747
786
  * hash mismatch, which surfaces as a thrown error from `process()`. */
748
- | { type: "PrePairRejected"; channel_id: string; status: number; memo: string }
787
+ | { type: "PrePairRejected"; channel_id: string; status: StatusEnum; memo: string }
749
788
 
750
789
  /** Fires alongside `PairingCompleted` on replica-mode pair handshakes.
751
790
  * `peer_replica_id` is the peer's `u64` as a **decimal** string,
@@ -763,22 +802,28 @@ export type DeRecEvent =
763
802
  /** A `ReplicaSource` peer pushed a secret sync on a
764
803
  * `ReplicaDestination` channel. The library decoded the
765
804
  * `ReplicaSecretPayload`; the app installs `secret.secrets` and
766
- * optionally uses `shares` for recovery. `from_replica_id` and the
767
- * `replica_id` fields inside `secret` are `u64` as **decimal**
768
- * strings. */
805
+ * optionally uses `shares` for recovery. `from_replica_id`,
806
+ * `author_replica_id` and the `replica_id` fields inside `secret` are
807
+ * `u64` as **decimal** strings.
808
+ *
809
+ * `from_replica_id` is the member this copy came from: the publisher on
810
+ * a push, the serving member on a catch-up. `author_replica_id` is the
811
+ * member that published `version`, or `null` when the serving member's
812
+ * snapshot records none. */
769
813
  | {
770
814
  type: "ReplicaSecretReceived";
771
815
  channel_id: string;
772
816
  from_replica_id: string;
817
+ author_replica_id: string | null;
773
818
  secret_id: string;
774
819
  version: number;
775
820
  secret: {
776
821
  helpers: Array<{
777
822
  channel_id: string;
778
823
  /** Every endpoint this peer advertised, in the order it offered them. */
779
- transports: Array<{ uri: string; protocol: number }>;
824
+ transports: Endpoint[];
780
825
  shared_key: Uint8Array;
781
- communication_info: Record<string, string>;
826
+ communication_info?: Record<string, string>;
782
827
  }>;
783
828
  secrets: Array<{
784
829
  id: Uint8Array;
@@ -796,9 +841,9 @@ export type DeRecEvent =
796
841
  members: Array<{
797
842
  replica_id: string;
798
843
  /** Every endpoint this peer advertised, in the order it offered them. */
799
- transports: Array<{ uri: string; protocol: number }>;
844
+ transports: Endpoint[];
800
845
  role: "Source" | "Destination";
801
- communication_info: Record<string, string>;
846
+ communication_info?: Record<string, string>;
802
847
  }>;
803
848
  shared_key: Uint8Array;
804
849
  };
@@ -821,15 +866,16 @@ export type DeRecEvent =
821
866
  type: "ReplicaSecretInstalled";
822
867
  channel_id: string;
823
868
  from_replica_id: string;
869
+ author_replica_id: string | null;
824
870
  secret_id: string;
825
871
  version: number;
826
872
  secret: {
827
873
  helpers: Array<{
828
874
  channel_id: string;
829
875
  /** Every endpoint this peer advertised, in the order it offered them. */
830
- transports: Array<{ uri: string; protocol: number }>;
876
+ transports: Endpoint[];
831
877
  shared_key: Uint8Array;
832
- communication_info: Record<string, string>;
878
+ communication_info?: Record<string, string>;
833
879
  }>;
834
880
  secrets: Array<{
835
881
  id: Uint8Array;
@@ -847,9 +893,9 @@ export type DeRecEvent =
847
893
  members: Array<{
848
894
  replica_id: string;
849
895
  /** Every endpoint this peer advertised, in the order it offered them. */
850
- transports: Array<{ uri: string; protocol: number }>;
896
+ transports: Endpoint[];
851
897
  role: "Source" | "Destination";
852
- communication_info: Record<string, string>;
898
+ communication_info?: Record<string, string>;
853
899
  }>;
854
900
  shared_key: Uint8Array;
855
901
  };
@@ -859,6 +905,56 @@ export type DeRecEvent =
859
905
  committed_share: Uint8Array;
860
906
  }>;
861
907
  }
908
+ /** A member offered a different copy of the version this device holds.
909
+ * Nothing was written: this device keeps its own copy and refuses the
910
+ * incoming one with `VERSION_CONFLICT`, so the publisher sees
911
+ * `ReplicaSyncRejected`. Both copies are complete states — the held one
912
+ * is in the local stores, the incoming one is `secret`. Resolve by
913
+ * publishing the chosen state with `start(FlowKind.ProtectSecret)`; the
914
+ * next version supersedes both on every member and helper.
915
+ *
916
+ * `held_author_replica_id` / `incoming_author_replica_id` are the
917
+ * decimal `replica_id` of each copy's publisher, or `null` when that
918
+ * copy records none. */
919
+ | {
920
+ type: "ReplicaVersionConflict";
921
+ channel_id: string;
922
+ from_replica_id: string;
923
+ secret_id: string;
924
+ version: number;
925
+ held_author_replica_id: string | null;
926
+ incoming_author_replica_id: string | null;
927
+ secret: {
928
+ helpers: Array<{
929
+ channel_id: string;
930
+ /** Every endpoint this peer advertised, in the order it offered them. */
931
+ transports: Endpoint[];
932
+ shared_key: Uint8Array;
933
+ communication_info?: Record<string, string>;
934
+ }>;
935
+ secrets: Array<{
936
+ id: Uint8Array;
937
+ name: string;
938
+ data: Uint8Array;
939
+ }>;
940
+ /** The same shape as `SecretRecovered.secret.replicas`. */
941
+ replicas?: {
942
+ /** The one channel every member is addressed on. */
943
+ channel_id: string;
944
+ /** Every member of the group, including the writer. Exactly one
945
+ * carries `role: "Source"` — that member is where the secret
946
+ * originated, which is why no separate owner field is needed. */
947
+ members: Array<{
948
+ replica_id: string;
949
+ /** Every endpoint this peer advertised, in the order it offered them. */
950
+ transports: Endpoint[];
951
+ role: "Source" | "Destination";
952
+ communication_info?: Record<string, string>;
953
+ }>;
954
+ shared_key: Uint8Array;
955
+ };
956
+ };
957
+ }
862
958
  /** Peer's ack of a secret sync we sent. `status` is the `StatusEnum`
863
959
  * integer (0 = Ok), `memo` is the peer's explanation. */
864
960
  | {
@@ -867,7 +963,7 @@ export type DeRecEvent =
867
963
  from_replica_id: string;
868
964
  secret_id: string;
869
965
  version: number;
870
- status: number;
966
+ status: StatusEnum;
871
967
  memo: string;
872
968
  }
873
969
  /** A peer announced an updated `communication_info` map and/or
@@ -884,7 +980,7 @@ export type DeRecEvent =
884
980
  | {
885
981
  type: "ChannelInfoUpdateRejected";
886
982
  channel_id: string;
887
- status: number;
983
+ status: StatusEnum;
888
984
  memo: string;
889
985
  }
890
986
  /** Emitted by `process()` in place of `ActionRequired` when the
@@ -896,6 +992,38 @@ export type DeRecEvent =
896
992
  * (`"Pairing"`, `"StoreShare"`, …). */
897
993
  | { type: "AutoAccepted"; channel_id: string; action_kind: PendingActionKind }
898
994
  | { type: "NoOp" }
995
+ /** An inbound message was dropped untouched: no store was written and
996
+ * nothing was sent back. `reason` says why.
997
+ *
998
+ * `"PendingVerification"` means the peer sent something before this
999
+ * device confirmed the channel's fingerprint — typically a replica source
1000
+ * pushing its first copy while this destination still shows the code.
1001
+ * Confirming does not replay it: after `verifyFingerprint` succeeds, a
1002
+ * replica destination calls `start(FlowKind.ReplicaDiscovery)` to pull
1003
+ * the copy itself. `"Expired"` means the message was older than the
1004
+ * inbound timeout. `trace_id` matches the peer's `*Started` event for
1005
+ * the same round (`"0"` when the sender set none). */
1006
+ | {
1007
+ type: "MessageIgnored";
1008
+ channel_id: string;
1009
+ reason: IgnoreReason;
1010
+ trace_id: string;
1011
+ }
1012
+ /** Returned by `restore`: a roster entry got no channel, so this device
1013
+ * cannot reach that peer. Every other entry and the user-secret snapshot
1014
+ * were restored; `reason` says why this one was not. `"NoTransports"`
1015
+ * means the recovered roster names no endpoint for it.
1016
+ *
1017
+ * For a helper, `channel_id` is its channel and `replica_id` is absent.
1018
+ * For a replica group member, `channel_id` is the group's channel and
1019
+ * `replica_id` names the member. The peer itself is untouched — a helper
1020
+ * still holds its share — and pairing with it again makes it reachable. */
1021
+ | {
1022
+ type: "PeerNotRestored";
1023
+ channel_id: string;
1024
+ replica_id?: string;
1025
+ reason: NotRestoredReason;
1026
+ }
899
1027
  /** A pairing handshake was dispatched successfully. `kind` is the
900
1028
  * local party's role — same value the subsequent `PairingCompleted`
901
1029
  * will carry. Emitted by `start(Pairing)`. */
@@ -964,8 +1092,8 @@ export type DeRecEvent =
964
1092
  * inbound shares. The protocol enforces no size, quota or rate limit
965
1093
  * of its own, and `maxShareSize` is checked for range overlap at
966
1094
  * pairing time only, never against an actual share. While this is
967
- * `false`, `ActionRequired` carries the decoded request, so the
968
- * application can inspect the share and call `reject()` with
1095
+ * `false`, `ActionRequired` carries `share_size`, so the application
1096
+ * can compare it to its quota and call `reject()` with
969
1097
  * `StatusEnum.SizeLimitExceeded`. Setting it `true` removes that
970
1098
  * opportunity entirely: every share from every paired Owner is stored
971
1099
  * unconditionally, at whatever size it arrives. Keep off in any
@@ -993,10 +1121,23 @@ export interface AutoAcceptPolicy {
993
1121
  * between them without reaching for reference docs.
994
1122
  *
995
1123
  * Required setters: `withChannelStore`, `withShareStore`,
996
- * `withSecretStore`, `withTransport`, and either `withOwnTransport` or
997
- * `withOwnTransports`. Calling `build()` without all five throws.
1124
+ * `withSecretStore`, `withUserSecretStore`, `withStateStore`,
1125
+ * `withTransport`, and `withOwnTransports`. Calling `build()` without all
1126
+ * seven throws.
1127
+ *
1128
+ * An optional setter that is never called leaves the library's default in
1129
+ * force.
998
1130
  */
999
1131
  export declare class DeRecProtocolBuilder {
1132
+ /**
1133
+ * Release the builder's memory in the library. `build()` already releases
1134
+ * it; call this only for a builder that is discarded unbuilt. Safe to call
1135
+ * more than once.
1136
+ */
1137
+ free(): void;
1138
+ /** Same as {@link DeRecProtocolBuilder.free}, for `using`. */
1139
+ [Symbol.dispose](): void;
1140
+
1000
1141
  /**
1001
1142
  * Construct a builder bound to a specific secret. `secretId`
1002
1143
  * identifies the single secret this protocol instance manages.
@@ -1011,32 +1152,22 @@ export declare class DeRecProtocolBuilder {
1011
1152
  withUserSecretStore(store: UserSecretStore): DeRecProtocolBuilder;
1012
1153
  withStateStore(store: StateStore): DeRecProtocolBuilder;
1013
1154
  withTransport(transport: Transport): DeRecProtocolBuilder;
1014
- /**
1015
- * @deprecated Use {@link withOwnTransports}, which takes the whole
1016
- * preference list — `withOwnTransports([endpoint])` is the direct
1017
- * replacement. Removed at 0.0.5.
1018
- */
1019
- withOwnTransport(endpoint: { uri: string; protocol: string }): DeRecProtocolBuilder;
1020
1155
  /**
1021
1156
  * Set every transport endpoint this application serves, in preference
1022
1157
  * order. `protocol` is `"https"` or `"grpc"` (case-insensitive) per
1023
- * entry, same as {@link withOwnTransport}.
1158
+ * entry.
1024
1159
  *
1025
1160
  * The order is this application's own preference and decides which of
1026
1161
  * a peer's offered endpoints is used; it is not sorted, deduplicated,
1027
1162
  * or reordered. Every listed transport must actually be served,
1028
1163
  * because delivery is push-only — listing an endpoint this application
1029
1164
  * does not serve makes pairing succeed and replies vanish.
1030
- *
1031
- * Supersedes {@link withOwnTransport} for applications serving more
1032
- * than one transport; the single-endpoint setter remains fully
1033
- * supported.
1034
1165
  */
1035
- withOwnTransports(transports: { uri: string; protocol: string }[]): DeRecProtocolBuilder;
1166
+ withOwnTransports(transports: Endpoint[]): DeRecProtocolBuilder;
1036
1167
 
1037
- /** Default: 3. */
1168
+ /** Minimum number of shares required to reconstruct the secret. Default: 3. */
1038
1169
  withThreshold(threshold: number): DeRecProtocolBuilder;
1039
- /** Default: 3. */
1170
+ /** Number of recent versions each helper must retain. Default: 3. */
1040
1171
  withKeepVersionsCount(count: number): DeRecProtocolBuilder;
1041
1172
  /**
1042
1173
  * Configure how long the protocol waits on each thing that can keep it
@@ -1055,8 +1186,8 @@ export declare class DeRecProtocolBuilder {
1055
1186
  withTimeouts(timeouts: Timeouts): DeRecProtocolBuilder;
1056
1187
 
1057
1188
  /**
1058
- * Accept plaintext `http://` transport endpoints. **Development only.**
1059
- * Default: `false`.
1189
+ * Accept plaintext `http://` and `grpc://` transport endpoints.
1190
+ * **Development only.** Default: `false`.
1060
1191
  *
1061
1192
  * With `false`, plaintext is accepted in exactly one situation: an endpoint
1062
1193
  * this device configured for **itself** that names loopback (`localhost`,
@@ -1072,33 +1203,20 @@ export declare class DeRecProtocolBuilder {
1072
1203
  * delivery is your `Transport`. Nothing here stops an application sending
1073
1204
  * plaintext — it governs which endpoints the protocol will record,
1074
1205
  * propagate to peers, and reply to.
1075
- *
1076
- * @deprecated Use {@link withUnsafeConnection}, which names both gated
1077
- * schemes. Removed at 0.0.5.
1078
- */
1079
- withUnsafeHttp(allow: boolean): DeRecProtocolBuilder;
1080
- /**
1081
- * Accept plaintext `http://` and `grpc://` transport endpoints.
1082
- * **Development only.** Default: `false`. Supersedes
1083
- * {@link withUnsafeHttp}, which names only the HTTP scheme.
1084
- *
1085
- * Either flag alone is honored. Setting both to disagreeing values fails
1086
- * construction with the error code `CONFLICTING_PLAINTEXT_OPT_IN` rather
1087
- * than resolving silently, because precedence would hand the decision to
1088
- * the flag being removed.
1089
1206
  */
1090
1207
  withUnsafeConnection(allow: boolean): DeRecProtocolBuilder;
1091
1208
  /** Default: empty. */
1092
1209
  withCommunicationInfo(info: Record<string, string>): DeRecProtocolBuilder;
1093
1210
  /** Default: false. */
1094
1211
  withAutoRespondOnFailure(enabled: boolean): DeRecProtocolBuilder;
1095
- /** Default: "required". */
1212
+ /** Exactly `"required"` or `"not_required"`; any other string throws
1213
+ * `invalid_unpair_ack`. Default: `"required"`. */
1096
1214
  withUnpairAck(ack: UnpairAck): DeRecProtocolBuilder;
1097
1215
  /**
1098
1216
  * When `true`, every outbound channel-mode request stamps
1099
- * `request.replyTo = ownTransport` so the responder routes its reply
1100
- * back here even if the channel's stored peer endpoint points
1101
- * elsewhere. Default: false.
1217
+ * `request.reply_to` with this node's own transports so the responder
1218
+ * routes its reply back here even if the channel's stored peer endpoint
1219
+ * points elsewhere. Default: false.
1102
1220
  */
1103
1221
  withAutoReplyTo(enabled: boolean): DeRecProtocolBuilder;
1104
1222
  /**
@@ -1113,7 +1231,8 @@ export declare class DeRecProtocolBuilder {
1113
1231
  /**
1114
1232
  * Stable per-device replica id. Required to participate in any
1115
1233
  * `ReplicaSource` / `ReplicaDestination` pairing. The id must be
1116
- * stable across restarts. Default: unset.
1234
+ * stable across restarts: mint it once with {@link generate_replica_id}
1235
+ * and persist it. Default: unset.
1117
1236
  */
1118
1237
  withReplicaId(id: bigint | number): DeRecProtocolBuilder;
1119
1238
  /**
@@ -1136,10 +1255,33 @@ export declare class DeRecProtocolBuilder {
1136
1255
  build(): DeRecProtocol;
1137
1256
  }
1138
1257
 
1258
+ /**
1259
+ * Every method that touches protocol state returns a `Promise` and runs under
1260
+ * one lock per instance, so overlapping calls on the same instance — a `tick`
1261
+ * timer firing while `process` handles an inbound message — queue and run one
1262
+ * at a time, in the order they were made. Distinct instances do not share the
1263
+ * lock: two instances bound to the same `secretId` and the same stores must
1264
+ * still be serialized by the caller.
1265
+ *
1266
+ * A store or transport callback must not await a call on the instance that
1267
+ * invoked it: that call queues behind the one waiting on the callback, and
1268
+ * neither settles. Calling `free()` while a call is in flight throws.
1269
+ */
1139
1270
  export declare class DeRecProtocol {
1140
1271
  /** Use {@link DeRecProtocolBuilder} to construct instances. */
1141
1272
  private constructor();
1142
1273
 
1274
+ /**
1275
+ * Release this protocol's memory in the library, and its references to the
1276
+ * stores and transport. The library's memory is not reclaimed by the
1277
+ * JavaScript garbage collector in time to rely on, so call this when done.
1278
+ * Safe to call more than once; any other method called afterwards throws,
1279
+ * as does calling this while a call is in flight.
1280
+ */
1281
+ free(): void;
1282
+ /** Same as {@link DeRecProtocol.free}, for `using`. */
1283
+ [Symbol.dispose](): void;
1284
+
1143
1285
  /** The secret identifier this protocol instance is bound to. */
1144
1286
  secretId(): bigint;
1145
1287
 
@@ -1152,8 +1294,8 @@ export declare class DeRecProtocol {
1152
1294
  * directly in the contact. `ContactMode.HashedKeys`
1153
1295
  * embeds only a SHA-384 binding hash (keys are
1154
1296
  * fetched later via the `PrePair` round-trip).
1155
- * `HashedKeys` requires `ownTransportUri` to be
1156
- * ephemeral.
1297
+ * `HashedKeys` requires the advertised own
1298
+ * transports to be ephemeral.
1157
1299
  */
1158
1300
  /**
1159
1301
  * Single entry point for all three `ContactMode` variants.
@@ -1197,29 +1339,11 @@ export declare class DeRecProtocol {
1197
1339
  * contact peers — follow up with
1198
1340
  * <c>start(FlowKind.UpdateChannelInfo, ...)</c> to propagate.
1199
1341
  */
1200
- setCommunicationInfo(info: Record<string, string>): void;
1201
-
1202
- /**
1203
- * Replace this node's endpoint for one protocol, leaving the others
1204
- * alone. A node serves at most one endpoint per protocol, so the
1205
- * `(uri, protocol)` pair identifies the entry it replaces; an entry for
1206
- * a protocol not yet served is appended, and a replaced one keeps its
1207
- * position in the preference order.
1208
- *
1209
- * IMPORTANT: keep the old endpoint operational during the changeover
1210
- * (see the Rust docs on the matching setter for the discipline).
1211
- *
1212
- * @deprecated Use {@link setOwnTransports}, which takes the whole
1213
- * preference list and is the only way to change which protocols this
1214
- * node serves, or their order. Removed at 0.0.5.
1215
- */
1216
- setOwnTransport(uri: string, protocol: string): void;
1342
+ setCommunicationInfo(info: Record<string, string>): Promise<void>;
1217
1343
 
1218
1344
  /**
1219
1345
  * Replace every endpoint this node advertises, in preference order —
1220
- * the runtime counterpart to <c>withOwnTransports</c>, and the way to
1221
- * change the whole set (<c>setOwnTransport</c> replaces only the entry
1222
- * for the protocol its URI names).
1346
+ * the runtime counterpart to `withOwnTransports`.
1223
1347
  *
1224
1348
  * A node serves at most one endpoint per protocol, so this list is a
1225
1349
  * preference order over distinct protocols. Two entries of the same
@@ -1228,7 +1352,7 @@ export declare class DeRecProtocol {
1228
1352
  * Every entry is validated before any is stored, so a malformed URI
1229
1353
  * leaves the previous set intact. An empty array is rejected.
1230
1354
  */
1231
- setOwnTransports(transports: { uri: string; protocol: string }[]): void;
1355
+ setOwnTransports(transports: Endpoint[]): Promise<void>;
1232
1356
 
1233
1357
  process(message: Uint8Array): Promise<DeRecEvent[]>;
1234
1358
 
@@ -1242,14 +1366,14 @@ export declare class DeRecProtocol {
1242
1366
  * shorter than the configured timeout.
1243
1367
  *
1244
1368
  * Safe to call at any time; with nothing in flight it resolves to an empty
1245
- * array. It mutates the same round state an inbound response does, so it
1246
- * must be serialized against `process` for the same `secretId`.
1369
+ * array. Overlapping calls with `process` on the same instance queue
1370
+ * rather than collide.
1247
1371
  */
1248
1372
  tick(): Promise<DeRecEvent[]>;
1249
1373
 
1250
1374
  accept(actionBytes: Uint8Array): Promise<DeRecEvent[]>;
1251
1375
 
1252
- reject(actionBytes: Uint8Array, status: number, memo: string): Promise<void>;
1376
+ reject(actionBytes: Uint8Array, status: StatusEnum, memo: string): Promise<void>;
1253
1377
 
1254
1378
  /**
1255
1379
  * Derive the human-readable fingerprint for a paired channel. Both sides
@@ -1274,23 +1398,32 @@ export declare class DeRecProtocol {
1274
1398
  * threshold given even when that policy is disabled. The age comparison
1275
1399
  * is strict, so a channel created within the current second survives
1276
1400
  * even `0`.
1401
+ *
1402
+ * `olderThanSecs` is a `u64`: a `bigint`, a non-negative safe-integer
1403
+ * `number`, or a decimal string. Anything else throws `decode_error`.
1277
1404
  */
1278
- removeExpiredChannels(olderThanSecs: number): Promise<string[]>;
1405
+ removeExpiredChannels(olderThanSecs: bigint | number | string): Promise<string[]>;
1279
1406
 
1280
1407
  /**
1281
1408
  * Rebuild this protocol's `secret_id` namespace from a recovered
1282
1409
  * `Secret`. Mirrors the Rust `DeRecProtocol::restore` — pass the
1283
1410
  * typed `secret` carried by the `SecretRecovered` event verbatim.
1284
1411
  *
1285
- * Errors surface as structured objects with a `code` field:
1412
+ * A helper or member whose `transports` is empty gets no channel: it is
1413
+ * reported as a `PeerNotRestored` event in the returned array and the rest
1414
+ * of the roster is restored.
1415
+ *
1416
+ * Errors surface as a `DeRecError` (`category`, `code`, `message`):
1286
1417
  *
1287
1418
  * | code | meaning |
1288
1419
  * |--------------------|------------------------------------------------------------------|
1289
- * | `ALREADY_RESTORED` | A user-secret snapshot already exists for this `secret_id`. |
1290
- * | `CONFLICT` | Channels live at canonical helper / replica ids. The error |
1420
+ * | `already_restored` | A user-secret snapshot already exists for this `secret_id`. |
1421
+ * | `restore_conflict` | Channels live at ids restore is about to write. The error |
1291
1422
  * | | carries `channel_ids: string[]` listing the collisions. |
1292
- * | `INVARIANT` | The recovered `Secret` is internally inconsistent. |
1293
- * | `STORAGE` | A store I/O call failed mid-restore. |
1423
+ * | `invariant` | The recovered `Secret` is internally inconsistent. |
1424
+ * | `invalid_recovered_secret` | `recoveredSecret` is malformed — e.g. a missing or |
1425
+ * | | non-decimal `channel_id` / `replica_id`. |
1426
+ * | `store_error` | A store call failed mid-restore; `category` names the store. |
1294
1427
  */
1295
1428
  restore(
1296
1429
  recoveredSecret: Extract<DeRecEvent, { type: "SecretRecovered" }>["secret"],
@@ -1397,6 +1530,14 @@ export interface CommunicationInfo {
1397
1530
  * protocol can raise. Matches the Rust `PendingActionKind` discriminants
1398
1531
  * one-for-one.
1399
1532
  */
1533
+ /** Why a `MessageIgnored` event dropped a message. Matches the Rust
1534
+ * `IgnoreReason` discriminants one-for-one. */
1535
+ export type IgnoreReason = "PendingVerification" | "Expired";
1536
+
1537
+ /** Why a `PeerNotRestored` event left a roster entry without a channel.
1538
+ * Matches the Rust `NotRestoredReason` discriminants one-for-one. */
1539
+ export type NotRestoredReason = "NoTransports";
1540
+
1400
1541
  export type PendingActionKind =
1401
1542
  | "Pairing"
1402
1543
  | "PrePair"
@@ -1427,17 +1568,9 @@ export interface PairRequestMessage {
1427
1568
  nonce: bigint;
1428
1569
  communication_info?: CommunicationInfo;
1429
1570
  parameter_range?: ParameterRange;
1430
- /**
1431
- * @deprecated Reading this field directly is incorrect: its meaning narrowed
1432
- * to "one entry of a list, and possibly absent", so a peer advertising only
1433
- * `supported_transports` looks unreachable to a reader that was correct
1434
- * before 0.0.3. Call {@link advertisedEndpoints}, which resolves both
1435
- * spellings. Removed at 0.0.5.
1436
- */
1437
- transport_protocol?: TransportProtocol;
1438
1571
  timestamp?: Timestamp;
1439
1572
  /** Every transport endpoint the initiator can be reached on, in its own
1440
- * preference order. Empty means "only `transport_protocol` is offered". */
1573
+ * preference order. */
1441
1574
  supported_transports: TransportProtocol[];
1442
1575
  }
1443
1576
 
@@ -1459,21 +1592,12 @@ export interface PairResponseMessage {
1459
1592
 
1460
1593
  export interface PrePairRequestMessage {
1461
1594
  nonce: bigint;
1462
- /**
1463
- * @deprecated Reading this field directly is incorrect: its meaning narrowed
1464
- * to "one entry of a list, and possibly absent", so a peer advertising only
1465
- * `supported_transports` looks unreachable to a reader that was correct
1466
- * before 0.0.3. Call {@link advertisedEndpoints}, which resolves both
1467
- * spellings. Removed at 0.0.5.
1468
- */
1469
- transport_protocol?: TransportProtocol;
1470
1595
  timestamp?: Timestamp;
1471
1596
  /**
1472
1597
  * Every endpoint the sender can be reached on for the PrePair reply, in
1473
- * its own preference order. At least one of this and `transport_protocol`
1474
- * must be present.
1598
+ * its own preference order.
1475
1599
  */
1476
- supported_transports?: TransportProtocol[];
1600
+ supported_transports: TransportProtocol[];
1477
1601
  }
1478
1602
 
1479
1603
  export interface PrePairResponseMessage {
@@ -1659,6 +1783,15 @@ export interface ProducePrePairResult {
1659
1783
  envelope: Uint8Array;
1660
1784
  }
1661
1785
 
1786
+ export interface ProducePrePairNoKeysResult {
1787
+
1788
+ envelope: Uint8Array;
1789
+
1790
+ /** Secret key material generated for this pairing. The caller MUST persist
1791
+ * it: the `PairRequest` that follows is encrypted to it. */
1792
+ secret_key_material: Uint8Array;
1793
+ }
1794
+
1662
1795
  export interface PrePairRequestExtractResult {
1663
1796
 
1664
1797
  request: PrePairRequestMessage;
@@ -1711,6 +1844,7 @@ export type DeRecErrorCategory =
1711
1844
  | "secret_store"
1712
1845
  | "channel_store"
1713
1846
  | "share_store"
1847
+ | "state_store"
1714
1848
  | "input"
1715
1849
  | "protobuf"
1716
1850
  | "invariant"
@@ -1724,6 +1858,10 @@ export interface DeRecError {
1724
1858
  memo?: string;
1725
1859
  expected?: number;
1726
1860
  got?: number;
1861
+ /** The channel the failing inbound message arrived on, when `process()` could tell. */
1862
+ channel_id?: string;
1863
+ /** On a `restore` `CONFLICT`: the pre-existing channels at canonical ids. */
1864
+ channel_ids?: string[];
1727
1865
  }
1728
1866
 
1729
1867
  export declare const primitives: {
@@ -1758,16 +1896,32 @@ export declare const primitives: {
1758
1896
  * commitment and the scanner must complete a
1759
1897
  * `PrePair` round-trip first.
1760
1898
  * @param transport_protocols Every endpoint this initiator serves, in
1761
- * preference order. The first also fills the
1762
- * legacy singular field. For `HashedKeys`
1763
- * mode they MUST be ephemeral.
1899
+ * preference order. For `HashedKeys` mode
1900
+ * they MUST be ephemeral.
1901
+ * @param nonce Correlation nonce embedded in the contact. Omitted or
1902
+ * `null`, the library draws a random `u64` — the right
1903
+ * choice for `InlineKeys` / `HashedKeys`, where the nonce
1904
+ * is a security parameter. Required for `ContactMode.NoKeys`,
1905
+ * where the caller typically picks a short human-typable
1906
+ * value for manual entry. Must fit in a `u64`.
1764
1907
  */
1765
1908
  create_contact(
1766
1909
  channel_id: bigint,
1767
1910
  contact_mode: ContactMode | number,
1768
1911
  transport_protocols: TransportProtocol[],
1912
+ nonce?: bigint | number | null,
1769
1913
  ): CreateContactResult;
1914
+ /**
1915
+ * Serializes a `ContactMessage` to the bytes delivered out of band.
1916
+ * Throws on a contact that violates the invariants of its contact mode
1917
+ * or advertises no endpoint.
1918
+ */
1770
1919
  encode_contact(contact_message: ContactMessage): Uint8Array;
1920
+ /**
1921
+ * Parses out-of-band contact bytes back into a `ContactMessage`. Throws
1922
+ * on bytes that are not a contact, or a contact that violates the
1923
+ * invariants of its contact mode.
1924
+ */
1771
1925
  decode_contact(bytes: Uint8Array): ContactMessage;
1772
1926
  produce(
1773
1927
  kind: SenderKind,
@@ -1777,7 +1931,17 @@ export declare const primitives: {
1777
1931
  parameter_range: ParameterRange | null,
1778
1932
  ): PairingRequestProduceResult;
1779
1933
 
1780
- extract(envelope_bytes: Uint8Array, secret_key: Uint8Array): { request: PairRequestMessage };
1934
+ /**
1935
+ * @param parameter_range The range this side accepts, or `null` for
1936
+ * none. A request advertising a range that does
1937
+ * not overlap it is refused with
1938
+ * `incompatible_parameter_range`.
1939
+ */
1940
+ extract(
1941
+ envelope_bytes: Uint8Array,
1942
+ secret_key: Uint8Array,
1943
+ parameter_range: ParameterRange | null,
1944
+ ): { request: PairRequestMessage };
1781
1945
 
1782
1946
  /**
1783
1947
  * Scanner-side: build a plaintext `PrePairRequest` envelope when the
@@ -1789,8 +1953,6 @@ export declare const primitives: {
1789
1953
  /**
1790
1954
  * @param own_transports Every endpoint this scanner serves for the
1791
1955
  * PrePair reply, in its own preference order.
1792
- * The first entry also fills the deprecated
1793
- * singular field for peers predating the list.
1794
1956
  */
1795
1957
  produce_pre_pair(
1796
1958
  own_transports: TransportProtocol[],
@@ -1805,6 +1967,10 @@ export declare const primitives: {
1805
1967
  };
1806
1968
  response: {
1807
1969
  /**
1970
+ * @param parameter_range The range this side accepts and advertises,
1971
+ * or `null` for none. A request advertising a
1972
+ * range that does not overlap it is refused
1973
+ * with `incompatible_parameter_range`.
1808
1974
  * @param unsafe_connection Accept plaintext peer endpoints
1809
1975
  * (`http://`, `grpc://`). Development only.
1810
1976
  */
@@ -1818,10 +1984,18 @@ export declare const primitives: {
1818
1984
  ): PairingResponseProduceResult;
1819
1985
 
1820
1986
  extract(envelope_bytes: Uint8Array, secret_key: Uint8Array): { response: PairResponseMessage };
1987
+ /**
1988
+ * @param parameter_range The range this side accepts, or `null` for
1989
+ * none. A response advertising a range that
1990
+ * does not overlap it is refused with
1991
+ * `incompatible_parameter_range` before any key
1992
+ * is derived.
1993
+ */
1821
1994
  process(
1822
1995
  contact_message: ContactMessage,
1823
1996
  response: PairResponseMessage,
1824
1997
  secret_key: Uint8Array,
1998
+ parameter_range: ParameterRange | null,
1825
1999
  ): PairingProcessResult;
1826
2000
 
1827
2001
  /**
@@ -1849,7 +2023,41 @@ export declare const primitives: {
1849
2023
  contact_message: ContactMessage,
1850
2024
  response: PrePairResponseMessage,
1851
2025
  ): ProcessPrePairResult;
2026
+
2027
+ /**
2028
+ * Contact-creator side of a `NoKeys` pairing: generate key material and
2029
+ * answer the `PrePairRequest` with its public half.
2030
+ *
2031
+ * The caller MUST first match the request's `nonce` against the contact
2032
+ * it issued — the only thing that authenticates a `NoKeys` request — and
2033
+ * MUST persist `secret_key_material`. The resulting channel MUST stay
2034
+ * unusable until both sides confirm `pairing.fingerprint` out of band.
2035
+ */
2036
+ produce_pre_pair_no_keys(
2037
+ channel_id: bigint,
2038
+ request: PrePairRequestMessage,
2039
+ ): ProducePrePairNoKeysResult;
2040
+
2041
+ /**
2042
+ * Scanner side of a `NoKeys` pairing: accept the contact creator's
2043
+ * public keys. There is no binding hash to check them against, so the
2044
+ * resulting channel MUST stay unusable until both sides confirm
2045
+ * `pairing.fingerprint` out of band. Throws on a non-`Ok` status, a
2046
+ * missing key or a `nonce` mismatch.
2047
+ */
2048
+ process_pre_pair_no_keys(
2049
+ contact_message: ContactMessage,
2050
+ response: PrePairResponseMessage,
2051
+ ): ProcessPrePairResult;
1852
2052
  };
2053
+
2054
+ /**
2055
+ * The human-readable fingerprint of a pairing's 32-byte shared key. Both
2056
+ * ends derive the same value; comparing it out of band confirms the
2057
+ * pairing. A `NoKeys` pairing MUST be confirmed this way before use.
2058
+ * Throws on a key that is not 32 bytes.
2059
+ */
2060
+ fingerprint(shared_key: Uint8Array): string;
1853
2061
  };
1854
2062
  recovery: {
1855
2063
  request: {