@derec-alliance/react-native 0.0.4 → 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/README.md +9 -15
- package/android/CMakeLists.txt +1 -1
- package/android/src/main/jniLibs/arm64-v8a/libderec_library.a +0 -0
- package/android/src/main/jniLibs/armeabi-v7a/libderec_library.a +0 -0
- package/android/src/main/jniLibs/x86_64/libderec_library.a +0 -0
- package/cpp/Convert.cpp +125 -0
- package/cpp/Convert.h +35 -0
- package/cpp/DeRecInstaller.cpp +0 -12
- package/cpp/Primitives.cpp +159 -26
- package/cpp/Primitives.h +16 -0
- package/cpp/ProtocolHost.cpp +22 -47
- package/cpp/StoreCallbacks.cpp +97 -304
- package/cpp/UserSecretsJson.cpp +109 -0
- package/cpp/UserSecretsJson.h +28 -0
- package/cpp/derec_ffi.h +256 -120
- package/ios/DeRecFFI.xcframework/ios-arm64/Headers/derec_ffi.h +256 -120
- package/ios/DeRecFFI.xcframework/ios-arm64/libderec_library.a +0 -0
- package/ios/DeRecFFI.xcframework/ios-arm64_x86_64-simulator/Headers/derec_ffi.h +256 -120
- package/ios/DeRecFFI.xcframework/ios-arm64_x86_64-simulator/libderec_library.a +2 -2
- package/lib/errors.d.ts +7 -0
- package/lib/native.d.ts +5 -1
- package/lib/primitives.d.ts +18 -21
- package/lib/primitives.js +48 -25
- package/lib/protocol.d.ts +49 -97
- package/lib/protocol.js +71 -162
- package/lib/types.d.ts +218 -107
- package/lib/types.js +43 -13
- package/package.json +1 -1
- package/src/errors.ts +7 -0
- package/src/native.ts +2 -1
- package/src/primitives.ts +82 -25
- package/src/protocol.ts +79 -179
- package/src/types.ts +232 -96
package/lib/types.d.ts
CHANGED
|
@@ -2,11 +2,12 @@ export interface SecretStore {
|
|
|
2
2
|
load(secretId: string, channelId: string, kind: 0 | 1 | 2): Promise<Uint8Array | null | undefined>;
|
|
3
3
|
/**
|
|
4
4
|
* Load secrets of the same `kind` for several channels in one call,
|
|
5
|
-
* scoped to `secretId`. Must return an array with one entry per
|
|
6
|
-
* id, in the same order, using `null` (or `undefined`) for channels
|
|
7
|
-
* with no stored secret of `kind`.
|
|
5
|
+
* scoped to `secretId`. Must return an array with exactly one entry per
|
|
6
|
+
* input id, in the same order, using `null` (or `undefined`) for channels
|
|
7
|
+
* with no stored secret of `kind`. Whether a missing entry is an error is
|
|
8
|
+
* decided by the library.
|
|
8
9
|
*/
|
|
9
|
-
loadMany(secretId: string, channelIds: string[], kind: 0 | 1 | 2
|
|
10
|
+
loadMany(secretId: string, channelIds: string[], kind: 0 | 1 | 2): Promise<Array<Uint8Array | null | undefined>>;
|
|
10
11
|
save(secretId: string, channelId: string, kind: 0 | 1 | 2, value: Uint8Array): Promise<void>;
|
|
11
12
|
remove(secretId: string, channelId: string, kind: 0 | 1 | 2): Promise<void>;
|
|
12
13
|
}
|
|
@@ -78,19 +79,11 @@ export declare function channelFilterMatches(filter: HelperFilter | ReplicaFilte
|
|
|
78
79
|
/**
|
|
79
80
|
* The endpoints a peer-supplied message advertises, in the peer's own order.
|
|
80
81
|
*
|
|
81
|
-
* Yields `supported_transports` when it is non-empty, and otherwise the
|
|
82
|
-
* singular `transport_protocol` — which is how every implementation predating
|
|
83
|
-
* the offer list advertises, and the reason this is a function rather than a
|
|
84
|
-
* field read. Reading `transport_protocol` directly is a bug: its meaning
|
|
85
|
-
* narrowed to "one entry of a list, and possibly absent", so a peer that has
|
|
86
|
-
* moved past it looks unreachable to a reader that was correct before 0.0.3.
|
|
87
|
-
*
|
|
88
82
|
* Reports what was advertised, not what is acceptable — nothing here is
|
|
89
83
|
* validated, and the protocol still applies its own transport policy to
|
|
90
84
|
* whatever it records.
|
|
91
85
|
*/
|
|
92
86
|
export declare function advertisedEndpoints(message: {
|
|
93
|
-
transport_protocol?: TransportProtocol;
|
|
94
87
|
supported_transports?: TransportProtocol[];
|
|
95
88
|
} | null | undefined): TransportProtocol[];
|
|
96
89
|
/**
|
|
@@ -172,7 +165,7 @@ export interface ChannelStore {
|
|
|
172
165
|
* Order explicitly to make succession predictable.
|
|
173
166
|
*/
|
|
174
167
|
listReplicas(secretId: string, filter: ReplicaFilter): Promise<Uint8Array | null | undefined>;
|
|
175
|
-
linkChannel(secretId: string,
|
|
168
|
+
linkChannel(secretId: string, a: string, b: string): Promise<void>;
|
|
176
169
|
linkedChannels(secretId: string, channelId: string): Promise<string[]>;
|
|
177
170
|
}
|
|
178
171
|
export interface Share {
|
|
@@ -180,6 +173,17 @@ export interface Share {
|
|
|
180
173
|
version: number;
|
|
181
174
|
bytes: Uint8Array;
|
|
182
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Share-record persistence.
|
|
178
|
+
*
|
|
179
|
+
* Rows are keyed by `(secretId, channelId, version)` where `secretId` is
|
|
180
|
+
* the partition passed to every method. `Share.secretId` is a different
|
|
181
|
+
* value: it names the secret the bytes belong to, which on a helper is
|
|
182
|
+
* the owner's id and routinely differs from the partition this device
|
|
183
|
+
* stores under. Key on the argument and carry `share.secretId` alongside
|
|
184
|
+
* as data — keying on it instead puts rows where no `load` looks, since
|
|
185
|
+
* every read filters on the partition.
|
|
186
|
+
*/
|
|
183
187
|
export interface ShareStore {
|
|
184
188
|
load(secretId: string, channelId: string, versions: number[]): Promise<Share[]>;
|
|
185
189
|
loadMany(secretId: string, channelIds: string[], versions: number[]): Promise<Share[]>;
|
|
@@ -197,6 +201,10 @@ export interface UserSecrets {
|
|
|
197
201
|
version: number;
|
|
198
202
|
secrets: UserSecretEntry[];
|
|
199
203
|
description?: string;
|
|
204
|
+
/** Decimal `replica_id` of the member that published `version`. Absent
|
|
205
|
+
* when none is recorded. Store and return it unchanged: replica members
|
|
206
|
+
* compare it to detect a conflicting copy of the same version. */
|
|
207
|
+
author_replica_id?: string;
|
|
200
208
|
}
|
|
201
209
|
/**
|
|
202
210
|
* Persistence for the user-facing secret contents, keyed by `secretId`.
|
|
@@ -234,6 +242,16 @@ export interface StateStore {
|
|
|
234
242
|
remove(secretId: string, keyJson: Uint8Array): Promise<boolean>;
|
|
235
243
|
loadAll(secretId: string, kind: 0 | 1 | 2 | 3 | 4): Promise<Uint8Array[]>;
|
|
236
244
|
}
|
|
245
|
+
/** A transport protocol, by name. */
|
|
246
|
+
export type TransportProtocolName = "https" | "grpc";
|
|
247
|
+
/**
|
|
248
|
+
* One endpoint a node serves or a peer advertised, as every app-facing call
|
|
249
|
+
* and event carries it.
|
|
250
|
+
*/
|
|
251
|
+
export interface Endpoint {
|
|
252
|
+
uri: string;
|
|
253
|
+
protocol: TransportProtocolName;
|
|
254
|
+
}
|
|
237
255
|
/**
|
|
238
256
|
* Outbound message delivery.
|
|
239
257
|
*
|
|
@@ -278,10 +296,7 @@ export interface Transport {
|
|
|
278
296
|
* provide — if that is genuinely wanted, say so with
|
|
279
297
|
* {@link singleEndpointTransport} rather than by indexing.
|
|
280
298
|
*/
|
|
281
|
-
send(endpoints: ReadonlyArray<
|
|
282
|
-
protocol: string;
|
|
283
|
-
uri: string;
|
|
284
|
-
}>, message: Uint8Array): Promise<void>;
|
|
299
|
+
send(endpoints: ReadonlyArray<Endpoint>, message: Uint8Array): Promise<void>;
|
|
285
300
|
}
|
|
286
301
|
/**
|
|
287
302
|
* Delivers one message to one endpoint.
|
|
@@ -297,10 +312,22 @@ export interface Transport {
|
|
|
297
312
|
* which is the safe reading. Trying an endpoint that would have refused costs
|
|
298
313
|
* a round trip; skipping one that would have worked costs the delivery.
|
|
299
314
|
*/
|
|
300
|
-
export type SendOne = (endpoint:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
315
|
+
export type SendOne = (endpoint: Endpoint, message: Uint8Array) => Promise<void>;
|
|
316
|
+
/**
|
|
317
|
+
* The DeRec protocol version this build speaks — the `protocolVersionMajor` /
|
|
318
|
+
* `protocolVersionMinor` it writes into every envelope it produces. Not the
|
|
319
|
+
* package version.
|
|
320
|
+
*/
|
|
321
|
+
export declare function protocol_version(): {
|
|
322
|
+
major: number;
|
|
323
|
+
minor: number;
|
|
324
|
+
};
|
|
325
|
+
/**
|
|
326
|
+
* A fresh, random replica id — never `0`. Generate it once per device,
|
|
327
|
+
* persist it, and pass the same value to
|
|
328
|
+
* `DeRecProtocolBuilder.withReplicaId` on every init.
|
|
329
|
+
*/
|
|
330
|
+
export declare function generate_replica_id(): bigint;
|
|
304
331
|
/**
|
|
305
332
|
* Builds a {@link Transport} that tries each endpoint in the order the peer
|
|
306
333
|
* offered it and stops at the first success.
|
|
@@ -342,7 +369,7 @@ export declare enum SenderKind {
|
|
|
342
369
|
* the scanner must fetch the actual keys over the wire via the `PrePair`
|
|
343
370
|
* round-trip and verify them against the commitment before pairing.
|
|
344
371
|
* - `NoKeys`: no key material and no commitment. The contact carries only
|
|
345
|
-
* `channel_id`, `nonce`, and `
|
|
372
|
+
* `channel_id`, `nonce`, and `supported_transports` — small enough to be
|
|
346
373
|
* hand-typed or dictated. Keys are generated on the fly by the contact
|
|
347
374
|
* creator when the `PrePairRequest` arrives; the scanner accepts them
|
|
348
375
|
* without cryptographic verification. Trust rests entirely on the OOB
|
|
@@ -382,19 +409,31 @@ export declare enum FlowKind {
|
|
|
382
409
|
* string so ids above 2^53 survive JS number handling. */
|
|
383
410
|
UnpairReplica = 8
|
|
384
411
|
}
|
|
412
|
+
/** Result status carried in every protocol response (`result.proto`). Passed
|
|
413
|
+
* to `reject()` to say why an inbound request was refused. */
|
|
414
|
+
export declare enum StatusEnum {
|
|
415
|
+
Ok = 0,
|
|
416
|
+
Partial = 1,
|
|
417
|
+
Fail = 2,
|
|
418
|
+
SizeLimitExceeded = 3,
|
|
419
|
+
TooFrequent = 4,
|
|
420
|
+
UnknownSecretId = 5,
|
|
421
|
+
UnknownShareVersion = 6,
|
|
422
|
+
DecryptionFailed = 7,
|
|
423
|
+
VerificationFailed = 8,
|
|
424
|
+
FormatError = 9,
|
|
425
|
+
Rejected = 10,
|
|
426
|
+
IncompatibleParameterRange = 11,
|
|
427
|
+
UnsupportedTransportProtocol = 12,
|
|
428
|
+
VersionConflict = 13,
|
|
429
|
+
ReplicaIdConflict = 14,
|
|
430
|
+
RequestToClose = 99
|
|
431
|
+
}
|
|
385
432
|
export type UnpairAck = "required" | "not_required";
|
|
386
433
|
export interface ContactMessage {
|
|
387
434
|
channel_id: bigint;
|
|
388
435
|
/** `ContactMode` numeric value (0 = INLINE_KEYS, 1 = HASHED_KEYS, 2 = NO_KEYS). */
|
|
389
436
|
contact_mode: number;
|
|
390
|
-
/**
|
|
391
|
-
* @deprecated Reading this field directly is incorrect: its meaning narrowed
|
|
392
|
-
* to "one entry of a list, and possibly absent", so a peer advertising only
|
|
393
|
-
* `supported_transports` looks unreachable to a reader that was correct
|
|
394
|
-
* before 0.0.3. Call {@link advertisedEndpoints}, which resolves both
|
|
395
|
-
* spellings. Removed at 0.0.5.
|
|
396
|
-
*/
|
|
397
|
-
transport_protocol?: TransportProtocol;
|
|
398
437
|
nonce: bigint;
|
|
399
438
|
/** Present only when `contact_mode === ContactMode.InlineKeys`. */
|
|
400
439
|
mlkem_encapsulation_key?: Uint8Array;
|
|
@@ -404,8 +443,7 @@ export interface ContactMessage {
|
|
|
404
443
|
contact_binding_hash?: Uint8Array;
|
|
405
444
|
timestamp?: Timestamp;
|
|
406
445
|
/** Every transport endpoint the creator of this contact can be reached
|
|
407
|
-
* on, in its own preference order.
|
|
408
|
-
* `transport_protocol` is offered". */
|
|
446
|
+
* on, in its own preference order. */
|
|
409
447
|
supported_transports: TransportProtocol[];
|
|
410
448
|
}
|
|
411
449
|
export interface UserSecret {
|
|
@@ -444,23 +482,11 @@ export interface UpdateChannelInfoParams {
|
|
|
444
482
|
/** New communication-info map. `null`/absent leaves the peer's stored
|
|
445
483
|
* map untouched; pass an empty object to clear it. */
|
|
446
484
|
communication_info?: Record<string, string>;
|
|
447
|
-
/**
|
|
448
|
-
* New transport endpoint. Absent leaves it untouched.
|
|
449
|
-
*
|
|
450
|
-
* @deprecated Use `own_transports`, which carries every endpoint this node
|
|
451
|
-
* now serves; its first entry also fills this field for peers predating the
|
|
452
|
-
* list. Removed at 0.0.5.
|
|
453
|
-
*/
|
|
454
|
-
transport_protocol?: {
|
|
455
|
-
uri: string;
|
|
456
|
-
protocol: number;
|
|
457
|
-
};
|
|
458
485
|
/**
|
|
459
486
|
* Every endpoint this node now serves, in its own preference order.
|
|
460
|
-
* Omitted leaves the target(s)' stored set untouched.
|
|
461
|
-
* over `transport_protocol`, whose first entry it also fills.
|
|
487
|
+
* Omitted leaves the target(s)' stored set untouched.
|
|
462
488
|
*/
|
|
463
|
-
own_transports?:
|
|
489
|
+
own_transports?: Endpoint[];
|
|
464
490
|
}
|
|
465
491
|
/**
|
|
466
492
|
* How long the protocol waits on each thing that can keep it waiting. Every
|
|
@@ -513,11 +539,30 @@ export type DeRecEvent = {
|
|
|
513
539
|
channel_id: string;
|
|
514
540
|
action: Uint8Array;
|
|
515
541
|
action_kind: PendingActionKind;
|
|
542
|
+
/** Correlation token of the inbound request, decimal-encoded. */
|
|
543
|
+
trace_id: string;
|
|
544
|
+
/** Pairing only. */
|
|
516
545
|
peer_communication_info?: Record<string, string>;
|
|
546
|
+
/** `sender_kind` of the inbound pair request (Pairing only). */
|
|
517
547
|
sender_kind?: SenderKind;
|
|
548
|
+
/** Share version (StoreShare / VerifyShare / GetShare). */
|
|
518
549
|
version?: number;
|
|
550
|
+
/** Description of the secret version (StoreShare only). */
|
|
519
551
|
share_description?: string;
|
|
552
|
+
/** Secret identifier, decimal-encoded (StoreShare / VerifyShare /
|
|
553
|
+
* GetShare). On GetShare, with `version`, names the requested share. */
|
|
520
554
|
share_secret_id?: string;
|
|
555
|
+
/** Length in bytes of the share the helper would store (StoreShare
|
|
556
|
+
* only) — what a size or quota decision is made on. */
|
|
557
|
+
share_size?: number;
|
|
558
|
+
/** The peer's memo (Unpair only). */
|
|
559
|
+
unpair_memo?: string;
|
|
560
|
+
/** Communication info the peer replaces its stored map with
|
|
561
|
+
* (UpdateChannelInfo only). Absent: unchanged. Empty: cleared. */
|
|
562
|
+
updated_communication_info?: Record<string, string>;
|
|
563
|
+
/** Endpoints the peer is moving to (UpdateChannelInfo only). Absent:
|
|
564
|
+
* unchanged. */
|
|
565
|
+
updated_transports?: Endpoint[];
|
|
521
566
|
} | {
|
|
522
567
|
type: "ShareStored";
|
|
523
568
|
channel_id: string;
|
|
@@ -530,7 +575,7 @@ export type DeRecEvent = {
|
|
|
530
575
|
type: "ShareRejected";
|
|
531
576
|
channel_id: string;
|
|
532
577
|
version: number;
|
|
533
|
-
status:
|
|
578
|
+
status: StatusEnum;
|
|
534
579
|
memo: string;
|
|
535
580
|
}
|
|
536
581
|
/** A publishing round finished — every targeted helper confirmed,
|
|
@@ -563,7 +608,7 @@ export type DeRecEvent = {
|
|
|
563
608
|
replica_id: string;
|
|
564
609
|
secret_id: string;
|
|
565
610
|
version: number;
|
|
566
|
-
status:
|
|
611
|
+
status: StatusEnum;
|
|
567
612
|
memo: string;
|
|
568
613
|
}
|
|
569
614
|
/** A secret sync could not be delivered to a member at all — distinct from
|
|
@@ -649,12 +694,9 @@ export type DeRecEvent = {
|
|
|
649
694
|
helpers: Array<{
|
|
650
695
|
channel_id: string;
|
|
651
696
|
/** Every endpoint this peer advertised, in the order it offered them. */
|
|
652
|
-
transports:
|
|
653
|
-
uri: string;
|
|
654
|
-
protocol: number;
|
|
655
|
-
}>;
|
|
697
|
+
transports: Endpoint[];
|
|
656
698
|
shared_key: Uint8Array;
|
|
657
|
-
communication_info
|
|
699
|
+
communication_info?: Record<string, string>;
|
|
658
700
|
}>;
|
|
659
701
|
secrets: Array<{
|
|
660
702
|
id: Uint8Array;
|
|
@@ -674,12 +716,9 @@ export type DeRecEvent = {
|
|
|
674
716
|
members: Array<{
|
|
675
717
|
replica_id: string;
|
|
676
718
|
/** Every endpoint this peer advertised, in the order it offered them. */
|
|
677
|
-
transports:
|
|
678
|
-
uri: string;
|
|
679
|
-
protocol: number;
|
|
680
|
-
}>;
|
|
719
|
+
transports: Endpoint[];
|
|
681
720
|
role: "Source" | "Destination";
|
|
682
|
-
communication_info
|
|
721
|
+
communication_info?: Record<string, string>;
|
|
683
722
|
}>;
|
|
684
723
|
shared_key: Uint8Array;
|
|
685
724
|
};
|
|
@@ -690,7 +729,7 @@ export type DeRecEvent = {
|
|
|
690
729
|
} | {
|
|
691
730
|
type: "UnpairRejected";
|
|
692
731
|
channel_id: string;
|
|
693
|
-
status:
|
|
732
|
+
status: StatusEnum;
|
|
694
733
|
memo: string;
|
|
695
734
|
}
|
|
696
735
|
/** Contact creator answered the scanner's `PrePairRequest` with a
|
|
@@ -699,7 +738,7 @@ export type DeRecEvent = {
|
|
|
699
738
|
| {
|
|
700
739
|
type: "PrePairRejected";
|
|
701
740
|
channel_id: string;
|
|
702
|
-
status:
|
|
741
|
+
status: StatusEnum;
|
|
703
742
|
memo: string;
|
|
704
743
|
}
|
|
705
744
|
/** Fires alongside `PairingCompleted` on replica-mode pair handshakes.
|
|
@@ -718,25 +757,28 @@ export type DeRecEvent = {
|
|
|
718
757
|
/** A `ReplicaSource` peer pushed a secret sync on a
|
|
719
758
|
* `ReplicaDestination` channel. The library decoded the
|
|
720
759
|
* `ReplicaSecretPayload`; the app installs `secret.secrets` and
|
|
721
|
-
* optionally uses `shares` for recovery. `from_replica_id
|
|
722
|
-
* `replica_id` fields inside `secret` are
|
|
723
|
-
* strings.
|
|
760
|
+
* optionally uses `shares` for recovery. `from_replica_id`,
|
|
761
|
+
* `author_replica_id` and the `replica_id` fields inside `secret` are
|
|
762
|
+
* `u64` as **decimal** strings.
|
|
763
|
+
*
|
|
764
|
+
* `from_replica_id` is the member this copy came from: the publisher on
|
|
765
|
+
* a push, the serving member on a catch-up. `author_replica_id` is the
|
|
766
|
+
* member that published `version`, or `null` when the serving member's
|
|
767
|
+
* snapshot records none. */
|
|
724
768
|
| {
|
|
725
769
|
type: "ReplicaSecretReceived";
|
|
726
770
|
channel_id: string;
|
|
727
771
|
from_replica_id: string;
|
|
772
|
+
author_replica_id: string | null;
|
|
728
773
|
secret_id: string;
|
|
729
774
|
version: number;
|
|
730
775
|
secret: {
|
|
731
776
|
helpers: Array<{
|
|
732
777
|
channel_id: string;
|
|
733
778
|
/** Every endpoint this peer advertised, in the order it offered them. */
|
|
734
|
-
transports:
|
|
735
|
-
uri: string;
|
|
736
|
-
protocol: number;
|
|
737
|
-
}>;
|
|
779
|
+
transports: Endpoint[];
|
|
738
780
|
shared_key: Uint8Array;
|
|
739
|
-
communication_info
|
|
781
|
+
communication_info?: Record<string, string>;
|
|
740
782
|
}>;
|
|
741
783
|
secrets: Array<{
|
|
742
784
|
id: Uint8Array;
|
|
@@ -754,12 +796,9 @@ export type DeRecEvent = {
|
|
|
754
796
|
members: Array<{
|
|
755
797
|
replica_id: string;
|
|
756
798
|
/** Every endpoint this peer advertised, in the order it offered them. */
|
|
757
|
-
transports:
|
|
758
|
-
uri: string;
|
|
759
|
-
protocol: number;
|
|
760
|
-
}>;
|
|
799
|
+
transports: Endpoint[];
|
|
761
800
|
role: "Source" | "Destination";
|
|
762
|
-
communication_info
|
|
801
|
+
communication_info?: Record<string, string>;
|
|
763
802
|
}>;
|
|
764
803
|
shared_key: Uint8Array;
|
|
765
804
|
};
|
|
@@ -782,18 +821,16 @@ export type DeRecEvent = {
|
|
|
782
821
|
type: "ReplicaSecretInstalled";
|
|
783
822
|
channel_id: string;
|
|
784
823
|
from_replica_id: string;
|
|
824
|
+
author_replica_id: string | null;
|
|
785
825
|
secret_id: string;
|
|
786
826
|
version: number;
|
|
787
827
|
secret: {
|
|
788
828
|
helpers: Array<{
|
|
789
829
|
channel_id: string;
|
|
790
830
|
/** Every endpoint this peer advertised, in the order it offered them. */
|
|
791
|
-
transports:
|
|
792
|
-
uri: string;
|
|
793
|
-
protocol: number;
|
|
794
|
-
}>;
|
|
831
|
+
transports: Endpoint[];
|
|
795
832
|
shared_key: Uint8Array;
|
|
796
|
-
communication_info
|
|
833
|
+
communication_info?: Record<string, string>;
|
|
797
834
|
}>;
|
|
798
835
|
secrets: Array<{
|
|
799
836
|
id: Uint8Array;
|
|
@@ -811,12 +848,9 @@ export type DeRecEvent = {
|
|
|
811
848
|
members: Array<{
|
|
812
849
|
replica_id: string;
|
|
813
850
|
/** Every endpoint this peer advertised, in the order it offered them. */
|
|
814
|
-
transports:
|
|
815
|
-
uri: string;
|
|
816
|
-
protocol: number;
|
|
817
|
-
}>;
|
|
851
|
+
transports: Endpoint[];
|
|
818
852
|
role: "Source" | "Destination";
|
|
819
|
-
communication_info
|
|
853
|
+
communication_info?: Record<string, string>;
|
|
820
854
|
}>;
|
|
821
855
|
shared_key: Uint8Array;
|
|
822
856
|
};
|
|
@@ -826,6 +860,56 @@ export type DeRecEvent = {
|
|
|
826
860
|
committed_share: Uint8Array;
|
|
827
861
|
}>;
|
|
828
862
|
}
|
|
863
|
+
/** A member offered a different copy of the version this device holds.
|
|
864
|
+
* Nothing was written: this device keeps its own copy and refuses the
|
|
865
|
+
* incoming one with `VERSION_CONFLICT`, so the publisher sees
|
|
866
|
+
* `ReplicaSyncRejected`. Both copies are complete states — the held one
|
|
867
|
+
* is in the local stores, the incoming one is `secret`. Resolve by
|
|
868
|
+
* publishing the chosen state with `start(FlowKind.ProtectSecret)`; the
|
|
869
|
+
* next version supersedes both on every member and helper.
|
|
870
|
+
*
|
|
871
|
+
* `held_author_replica_id` / `incoming_author_replica_id` are the
|
|
872
|
+
* decimal `replica_id` of each copy's publisher, or `null` when that
|
|
873
|
+
* copy records none. */
|
|
874
|
+
| {
|
|
875
|
+
type: "ReplicaVersionConflict";
|
|
876
|
+
channel_id: string;
|
|
877
|
+
from_replica_id: string;
|
|
878
|
+
secret_id: string;
|
|
879
|
+
version: number;
|
|
880
|
+
held_author_replica_id: string | null;
|
|
881
|
+
incoming_author_replica_id: string | null;
|
|
882
|
+
secret: {
|
|
883
|
+
helpers: Array<{
|
|
884
|
+
channel_id: string;
|
|
885
|
+
/** Every endpoint this peer advertised, in the order it offered them. */
|
|
886
|
+
transports: Endpoint[];
|
|
887
|
+
shared_key: Uint8Array;
|
|
888
|
+
communication_info?: Record<string, string>;
|
|
889
|
+
}>;
|
|
890
|
+
secrets: Array<{
|
|
891
|
+
id: Uint8Array;
|
|
892
|
+
name: string;
|
|
893
|
+
data: Uint8Array;
|
|
894
|
+
}>;
|
|
895
|
+
/** The same shape as `SecretRecovered.secret.replicas`. */
|
|
896
|
+
replicas?: {
|
|
897
|
+
/** The one channel every member is addressed on. */
|
|
898
|
+
channel_id: string;
|
|
899
|
+
/** Every member of the group, including the writer. Exactly one
|
|
900
|
+
* carries `role: "Source"` — that member is where the secret
|
|
901
|
+
* originated, which is why no separate owner field is needed. */
|
|
902
|
+
members: Array<{
|
|
903
|
+
replica_id: string;
|
|
904
|
+
/** Every endpoint this peer advertised, in the order it offered them. */
|
|
905
|
+
transports: Endpoint[];
|
|
906
|
+
role: "Source" | "Destination";
|
|
907
|
+
communication_info?: Record<string, string>;
|
|
908
|
+
}>;
|
|
909
|
+
shared_key: Uint8Array;
|
|
910
|
+
};
|
|
911
|
+
};
|
|
912
|
+
}
|
|
829
913
|
/** Peer's ack of a secret sync we sent. `status` is the `StatusEnum`
|
|
830
914
|
* integer (0 = Ok), `memo` is the peer's explanation. */
|
|
831
915
|
| {
|
|
@@ -834,7 +918,7 @@ export type DeRecEvent = {
|
|
|
834
918
|
from_replica_id: string;
|
|
835
919
|
secret_id: string;
|
|
836
920
|
version: number;
|
|
837
|
-
status:
|
|
921
|
+
status: StatusEnum;
|
|
838
922
|
memo: string;
|
|
839
923
|
}
|
|
840
924
|
/** A peer announced an updated `communication_info` map and/or
|
|
@@ -851,7 +935,7 @@ export type DeRecEvent = {
|
|
|
851
935
|
| {
|
|
852
936
|
type: "ChannelInfoUpdateRejected";
|
|
853
937
|
channel_id: string;
|
|
854
|
-
status:
|
|
938
|
+
status: StatusEnum;
|
|
855
939
|
memo: string;
|
|
856
940
|
}
|
|
857
941
|
/** Emitted by `process()` in place of `ActionRequired` when the
|
|
@@ -868,6 +952,38 @@ export type DeRecEvent = {
|
|
|
868
952
|
} | {
|
|
869
953
|
type: "NoOp";
|
|
870
954
|
}
|
|
955
|
+
/** An inbound message was dropped untouched: no store was written and
|
|
956
|
+
* nothing was sent back. `reason` says why.
|
|
957
|
+
*
|
|
958
|
+
* `"PendingVerification"` means the peer sent something before this
|
|
959
|
+
* device confirmed the channel's fingerprint — typically a replica source
|
|
960
|
+
* pushing its first copy while this destination still shows the code.
|
|
961
|
+
* Confirming does not replay it: after `verifyFingerprint` succeeds, a
|
|
962
|
+
* replica destination calls `start(FlowKind.ReplicaDiscovery)` to pull
|
|
963
|
+
* the copy itself. `"Expired"` means the message was older than the
|
|
964
|
+
* inbound timeout. `trace_id` matches the peer's `*Started` event for
|
|
965
|
+
* the same round (`"0"` when the sender set none). */
|
|
966
|
+
| {
|
|
967
|
+
type: "MessageIgnored";
|
|
968
|
+
channel_id: string;
|
|
969
|
+
reason: IgnoreReason;
|
|
970
|
+
trace_id: string;
|
|
971
|
+
}
|
|
972
|
+
/** Returned by `restore`: a roster entry got no channel, so this device
|
|
973
|
+
* cannot reach that peer. Every other entry and the user-secret snapshot
|
|
974
|
+
* were restored; `reason` says why this one was not. `"NoTransports"`
|
|
975
|
+
* means the recovered roster names no endpoint for it.
|
|
976
|
+
*
|
|
977
|
+
* For a helper, `channel_id` is its channel and `replica_id` is absent.
|
|
978
|
+
* For a replica group member, `channel_id` is the group's channel and
|
|
979
|
+
* `replica_id` names the member. The peer itself is untouched — a helper
|
|
980
|
+
* still holds its share — and pairing with it again makes it reachable. */
|
|
981
|
+
| {
|
|
982
|
+
type: "PeerNotRestored";
|
|
983
|
+
channel_id: string;
|
|
984
|
+
replica_id?: string;
|
|
985
|
+
reason: NotRestoredReason;
|
|
986
|
+
}
|
|
871
987
|
/** A pairing handshake was dispatched successfully. `kind` is the
|
|
872
988
|
* local party's role — same value the subsequent `PairingCompleted`
|
|
873
989
|
* will carry. Emitted by `start(Pairing)`. */
|
|
@@ -978,8 +1094,8 @@ export type DeRecEvent = {
|
|
|
978
1094
|
* inbound shares. The protocol enforces no size, quota or rate limit
|
|
979
1095
|
* of its own, and `maxShareSize` is checked for range overlap at
|
|
980
1096
|
* pairing time only, never against an actual share. While this is
|
|
981
|
-
* `false`, `ActionRequired` carries
|
|
982
|
-
*
|
|
1097
|
+
* `false`, `ActionRequired` carries `share_size`, so the application
|
|
1098
|
+
* can compare it to its quota and call `reject()` with
|
|
983
1099
|
* `StatusEnum.SizeLimitExceeded`. Setting it `true` removes that
|
|
984
1100
|
* opportunity entirely: every share from every paired Owner is stored
|
|
985
1101
|
* unconditionally, at whatever size it arrives. Keep off in any
|
|
@@ -1055,6 +1171,12 @@ export interface CommunicationInfoKeyValue {
|
|
|
1055
1171
|
export interface CommunicationInfo {
|
|
1056
1172
|
communication_info_entries: CommunicationInfoKeyValue[];
|
|
1057
1173
|
}
|
|
1174
|
+
/** Why a `MessageIgnored` event dropped a message. Matches the Rust
|
|
1175
|
+
* `IgnoreReason` discriminants one-for-one. */
|
|
1176
|
+
export type IgnoreReason = "PendingVerification" | "Expired";
|
|
1177
|
+
/** Why a `PeerNotRestored` event left a roster entry without a channel.
|
|
1178
|
+
* Matches the Rust `NotRestoredReason` discriminants one-for-one. */
|
|
1179
|
+
export type NotRestoredReason = "NoTransports";
|
|
1058
1180
|
/**
|
|
1059
1181
|
* The label vocabulary for `ActionRequired.action_kind` and
|
|
1060
1182
|
* `AutoAccepted.action_kind` — one value per pending-action kind the
|
|
@@ -1081,17 +1203,9 @@ export interface PairRequestMessage {
|
|
|
1081
1203
|
nonce: bigint;
|
|
1082
1204
|
communication_info?: CommunicationInfo;
|
|
1083
1205
|
parameter_range?: ParameterRange;
|
|
1084
|
-
/**
|
|
1085
|
-
* @deprecated Reading this field directly is incorrect: its meaning narrowed
|
|
1086
|
-
* to "one entry of a list, and possibly absent", so a peer advertising only
|
|
1087
|
-
* `supported_transports` looks unreachable to a reader that was correct
|
|
1088
|
-
* before 0.0.3. Call {@link advertisedEndpoints}, which resolves both
|
|
1089
|
-
* spellings. Removed at 0.0.5.
|
|
1090
|
-
*/
|
|
1091
|
-
transport_protocol?: TransportProtocol;
|
|
1092
1206
|
timestamp?: Timestamp;
|
|
1093
1207
|
/** Every transport endpoint the initiator can be reached on, in its own
|
|
1094
|
-
* preference order.
|
|
1208
|
+
* preference order. */
|
|
1095
1209
|
supported_transports: TransportProtocol[];
|
|
1096
1210
|
}
|
|
1097
1211
|
export interface PairResponseMessage {
|
|
@@ -1111,21 +1225,12 @@ export interface PairResponseMessage {
|
|
|
1111
1225
|
}
|
|
1112
1226
|
export interface PrePairRequestMessage {
|
|
1113
1227
|
nonce: bigint;
|
|
1114
|
-
/**
|
|
1115
|
-
* @deprecated Reading this field directly is incorrect: its meaning narrowed
|
|
1116
|
-
* to "one entry of a list, and possibly absent", so a peer advertising only
|
|
1117
|
-
* `supported_transports` looks unreachable to a reader that was correct
|
|
1118
|
-
* before 0.0.3. Call {@link advertisedEndpoints}, which resolves both
|
|
1119
|
-
* spellings. Removed at 0.0.5.
|
|
1120
|
-
*/
|
|
1121
|
-
transport_protocol?: TransportProtocol;
|
|
1122
1228
|
timestamp?: Timestamp;
|
|
1123
1229
|
/**
|
|
1124
1230
|
* Every endpoint the sender can be reached on for the PrePair reply, in
|
|
1125
|
-
* its own preference order.
|
|
1126
|
-
* must be present.
|
|
1231
|
+
* its own preference order.
|
|
1127
1232
|
*/
|
|
1128
|
-
supported_transports
|
|
1233
|
+
supported_transports: TransportProtocol[];
|
|
1129
1234
|
}
|
|
1130
1235
|
export interface PrePairResponseMessage {
|
|
1131
1236
|
result?: DeRecResult;
|
|
@@ -1281,6 +1386,12 @@ export interface PairingProcessResult {
|
|
|
1281
1386
|
export interface ProducePrePairResult {
|
|
1282
1387
|
envelope: Uint8Array;
|
|
1283
1388
|
}
|
|
1389
|
+
export interface ProducePrePairNoKeysResult {
|
|
1390
|
+
envelope: Uint8Array;
|
|
1391
|
+
/** Secret key material generated for this pairing. The caller MUST persist
|
|
1392
|
+
* it: the `PairRequest` that follows is encrypted to it. */
|
|
1393
|
+
secret_key_material: Uint8Array;
|
|
1394
|
+
}
|
|
1284
1395
|
export interface PrePairRequestExtractResult {
|
|
1285
1396
|
request: PrePairRequestMessage;
|
|
1286
1397
|
}
|
package/lib/types.js
CHANGED
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
// Copyright (c) 2026 DeRec Alliance. All rights reserved.
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.FlowKind = exports.ContactMode = exports.SenderKind = void 0;
|
|
5
|
+
exports.StatusEnum = exports.FlowKind = exports.ContactMode = exports.SenderKind = void 0;
|
|
6
6
|
exports.channelFilterMatches = channelFilterMatches;
|
|
7
7
|
exports.advertisedEndpoints = advertisedEndpoints;
|
|
8
|
+
exports.protocol_version = protocol_version;
|
|
9
|
+
exports.generate_replica_id = generate_replica_id;
|
|
8
10
|
exports.sequentialFailover = sequentialFailover;
|
|
9
11
|
exports.singleEndpointTransport = singleEndpointTransport;
|
|
12
|
+
const native_1 = require("./native");
|
|
10
13
|
/**
|
|
11
14
|
* Whether a channel or member with these attributes survives `filter`.
|
|
12
15
|
*
|
|
@@ -37,13 +40,6 @@ function channelFilterMatches(filter, id, status, role) {
|
|
|
37
40
|
/**
|
|
38
41
|
* The endpoints a peer-supplied message advertises, in the peer's own order.
|
|
39
42
|
*
|
|
40
|
-
* Yields `supported_transports` when it is non-empty, and otherwise the
|
|
41
|
-
* singular `transport_protocol` — which is how every implementation predating
|
|
42
|
-
* the offer list advertises, and the reason this is a function rather than a
|
|
43
|
-
* field read. Reading `transport_protocol` directly is a bug: its meaning
|
|
44
|
-
* narrowed to "one entry of a list, and possibly absent", so a peer that has
|
|
45
|
-
* moved past it looks unreachable to a reader that was correct before 0.0.3.
|
|
46
|
-
*
|
|
47
43
|
* Reports what was advertised, not what is acceptable — nothing here is
|
|
48
44
|
* validated, and the protocol still applies its own transport policy to
|
|
49
45
|
* whatever it records.
|
|
@@ -51,10 +47,23 @@ function channelFilterMatches(filter, id, status, role) {
|
|
|
51
47
|
function advertisedEndpoints(message) {
|
|
52
48
|
if (!message)
|
|
53
49
|
return [];
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
return message.supported_transports ?? [];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The DeRec protocol version this build speaks — the `protocolVersionMajor` /
|
|
54
|
+
* `protocolVersionMinor` it writes into every envelope it produces. Not the
|
|
55
|
+
* package version.
|
|
56
|
+
*/
|
|
57
|
+
function protocol_version() {
|
|
58
|
+
return (0, native_1.getNative)().version();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A fresh, random replica id — never `0`. Generate it once per device,
|
|
62
|
+
* persist it, and pass the same value to
|
|
63
|
+
* `DeRecProtocolBuilder.withReplicaId` on every init.
|
|
64
|
+
*/
|
|
65
|
+
function generate_replica_id() {
|
|
66
|
+
return (0, native_1.getNative)().generate_replica_id();
|
|
58
67
|
}
|
|
59
68
|
/**
|
|
60
69
|
* Builds a {@link Transport} that tries each endpoint in the order the peer
|
|
@@ -126,7 +135,7 @@ var SenderKind;
|
|
|
126
135
|
* the scanner must fetch the actual keys over the wire via the `PrePair`
|
|
127
136
|
* round-trip and verify them against the commitment before pairing.
|
|
128
137
|
* - `NoKeys`: no key material and no commitment. The contact carries only
|
|
129
|
-
* `channel_id`, `nonce`, and `
|
|
138
|
+
* `channel_id`, `nonce`, and `supported_transports` — small enough to be
|
|
130
139
|
* hand-typed or dictated. Keys are generated on the fly by the contact
|
|
131
140
|
* creator when the `PrePairRequest` arrives; the scanner accepts them
|
|
132
141
|
* without cryptographic verification. Trust rests entirely on the OOB
|
|
@@ -168,3 +177,24 @@ var FlowKind;
|
|
|
168
177
|
* string so ids above 2^53 survive JS number handling. */
|
|
169
178
|
FlowKind[FlowKind["UnpairReplica"] = 8] = "UnpairReplica";
|
|
170
179
|
})(FlowKind || (exports.FlowKind = FlowKind = {}));
|
|
180
|
+
/** Result status carried in every protocol response (`result.proto`). Passed
|
|
181
|
+
* to `reject()` to say why an inbound request was refused. */
|
|
182
|
+
var StatusEnum;
|
|
183
|
+
(function (StatusEnum) {
|
|
184
|
+
StatusEnum[StatusEnum["Ok"] = 0] = "Ok";
|
|
185
|
+
StatusEnum[StatusEnum["Partial"] = 1] = "Partial";
|
|
186
|
+
StatusEnum[StatusEnum["Fail"] = 2] = "Fail";
|
|
187
|
+
StatusEnum[StatusEnum["SizeLimitExceeded"] = 3] = "SizeLimitExceeded";
|
|
188
|
+
StatusEnum[StatusEnum["TooFrequent"] = 4] = "TooFrequent";
|
|
189
|
+
StatusEnum[StatusEnum["UnknownSecretId"] = 5] = "UnknownSecretId";
|
|
190
|
+
StatusEnum[StatusEnum["UnknownShareVersion"] = 6] = "UnknownShareVersion";
|
|
191
|
+
StatusEnum[StatusEnum["DecryptionFailed"] = 7] = "DecryptionFailed";
|
|
192
|
+
StatusEnum[StatusEnum["VerificationFailed"] = 8] = "VerificationFailed";
|
|
193
|
+
StatusEnum[StatusEnum["FormatError"] = 9] = "FormatError";
|
|
194
|
+
StatusEnum[StatusEnum["Rejected"] = 10] = "Rejected";
|
|
195
|
+
StatusEnum[StatusEnum["IncompatibleParameterRange"] = 11] = "IncompatibleParameterRange";
|
|
196
|
+
StatusEnum[StatusEnum["UnsupportedTransportProtocol"] = 12] = "UnsupportedTransportProtocol";
|
|
197
|
+
StatusEnum[StatusEnum["VersionConflict"] = 13] = "VersionConflict";
|
|
198
|
+
StatusEnum[StatusEnum["ReplicaIdConflict"] = 14] = "ReplicaIdConflict";
|
|
199
|
+
StatusEnum[StatusEnum["RequestToClose"] = 99] = "RequestToClose";
|
|
200
|
+
})(StatusEnum || (exports.StatusEnum = StatusEnum = {}));
|