@interop/wallet-core 0.78.1 → 0.79.1
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 +18 -3
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts +2 -2
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts.map +1 -1
- package/dist/clientAnnex/credentialAnchoredGenesis.js +1 -1
- package/dist/clientAnnex/credentialAnchoredGenesis.js.map +1 -1
- package/dist/clientAnnex/establish.d.ts +2 -2
- package/dist/clientAnnex/establish.d.ts.map +1 -1
- package/dist/clientAnnex/forget.d.ts +1 -1
- package/dist/clientAnnex/forget.d.ts.map +1 -1
- package/dist/clientAnnex/heal.d.ts.map +1 -1
- package/dist/clientAnnex/heal.js +3 -1
- package/dist/clientAnnex/heal.js.map +1 -1
- package/dist/clientAnnex/ladder.d.ts +3 -98
- package/dist/clientAnnex/ladder.d.ts.map +1 -1
- package/dist/clientAnnex/ladder.js +4 -140
- package/dist/clientAnnex/ladder.js.map +1 -1
- package/dist/clientAnnex/log.d.ts +26 -10
- package/dist/clientAnnex/log.d.ts.map +1 -1
- package/dist/clientAnnex/log.js +39 -19
- package/dist/clientAnnex/log.js.map +1 -1
- package/dist/clientAnnex/mend.d.ts +2 -2
- package/dist/clientAnnex/mend.d.ts.map +1 -1
- package/dist/clientAnnex/mend.js +1 -1
- package/dist/clientAnnex/mend.js.map +1 -1
- package/dist/clientAnnex/rosterDeliveredEpochs.d.ts +1 -1
- package/dist/clientAnnex/rosterDeliveredEpochs.d.ts.map +1 -1
- package/dist/clientAnnex/selfEnroll.d.ts +1 -1
- package/dist/clientAnnex/selfEnroll.d.ts.map +1 -1
- package/dist/clientAnnex/selfEnroll.js +2 -1
- package/dist/clientAnnex/selfEnroll.js.map +1 -1
- package/dist/clients/revocation.d.ts +1 -1
- package/dist/clients/revocation.d.ts.map +1 -1
- package/dist/clients/rosterPolicy.d.ts +1 -1
- package/dist/clients/rosterPolicy.d.ts.map +1 -1
- package/dist/descriptors/logSource.d.ts +2 -1
- package/dist/descriptors/logSource.d.ts.map +1 -1
- package/dist/descriptors/logSource.js +1 -1
- package/dist/descriptors/logSource.js.map +1 -1
- package/dist/enrollment/enrollment.d.ts +2 -2
- package/dist/enrollment/enrollment.d.ts.map +1 -1
- package/dist/enrollment/enrollment.js +2 -1
- package/dist/enrollment/enrollment.js.map +1 -1
- package/dist/genesis/accountGenesis.d.ts +1 -1
- package/dist/genesis/accountGenesis.d.ts.map +1 -1
- package/dist/genesis/accountGenesis.js +11 -5
- package/dist/genesis/accountGenesis.js.map +1 -1
- package/dist/keyring/fetch.d.ts +1 -1
- package/dist/keyring/fetch.d.ts.map +1 -1
- package/dist/keyring/fetch.js +1 -1
- package/dist/keyring/fetch.js.map +1 -1
- package/dist/keyring/index.d.ts +15 -8
- package/dist/keyring/index.d.ts.map +1 -1
- package/dist/keyring/index.js +11 -6
- package/dist/keyring/index.js.map +1 -1
- package/dist/keyring/kdf.d.ts +0 -69
- package/dist/keyring/kdf.d.ts.map +1 -1
- package/dist/keyring/kdf.js +7 -86
- package/dist/keyring/kdf.js.map +1 -1
- package/dist/keyring/record.d.ts +1 -200
- package/dist/keyring/record.d.ts.map +1 -1
- package/dist/keyring/record.js +5 -251
- package/dist/keyring/record.js.map +1 -1
- package/dist/keyring/recordEnvelope.d.ts +270 -0
- package/dist/keyring/recordEnvelope.d.ts.map +1 -0
- package/dist/keyring/recordEnvelope.js +282 -0
- package/dist/keyring/recordEnvelope.js.map +1 -0
- package/dist/keyring/unlockIdentity.d.ts +71 -0
- package/dist/keyring/unlockIdentity.d.ts.map +1 -0
- package/dist/keyring/unlockIdentity.js +96 -0
- package/dist/keyring/unlockIdentity.js.map +1 -0
- package/dist/keyring/unlockSpace.d.ts +12 -1
- package/dist/keyring/unlockSpace.d.ts.map +1 -1
- package/dist/keyring/unlockSpace.js +15 -2
- package/dist/keyring/unlockSpace.js.map +1 -1
- package/dist/keys/index.d.ts +6 -3
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +6 -3
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/rosterLogStore.d.ts +1 -1
- package/dist/keys/rosterLogStore.d.ts.map +1 -1
- package/dist/keys/rosterLogStore.js +1 -1
- package/dist/keys/rosterLogStore.js.map +1 -1
- package/dist/keys/rosterRecipientKid.d.ts +28 -0
- package/dist/keys/rosterRecipientKid.d.ts.map +1 -0
- package/dist/keys/rosterRecipientKid.js +27 -0
- package/dist/keys/rosterRecipientKid.js.map +1 -0
- package/dist/keys/spaceEpochs.d.ts +1 -1
- package/dist/keys/spaceEpochs.d.ts.map +1 -1
- package/dist/keys/spaceEpochs.js +2 -2
- package/dist/keys/spaceEpochs.js.map +1 -1
- package/dist/keys/userKey.d.ts +4 -46
- package/dist/keys/userKey.d.ts.map +1 -1
- package/dist/keys/userKey.js +8 -60
- package/dist/keys/userKey.js.map +1 -1
- package/dist/keys/userKeyCascade.d.ts +1 -33
- package/dist/keys/userKeyCascade.d.ts.map +1 -1
- package/dist/keys/userKeyCascade.js +2 -49
- package/dist/keys/userKeyCascade.js.map +1 -1
- package/dist/keys/userKeyGenerations.d.ts +47 -0
- package/dist/keys/userKeyGenerations.d.ts.map +1 -0
- package/dist/keys/userKeyGenerations.js +50 -0
- package/dist/keys/userKeyGenerations.js.map +1 -0
- package/dist/keys/userKeyRoster.d.ts +1 -19
- package/dist/keys/userKeyRoster.d.ts.map +1 -1
- package/dist/keys/userKeyRoster.js +13 -19
- package/dist/keys/userKeyRoster.js.map +1 -1
- package/dist/keys/userKeyRosterCascade.d.ts +1 -1
- package/dist/keys/userKeyRosterCascade.d.ts.map +1 -1
- package/dist/keys/userKeySigning.d.ts +44 -0
- package/dist/keys/userKeySigning.d.ts.map +1 -0
- package/dist/keys/userKeySigning.js +65 -0
- package/dist/keys/userKeySigning.js.map +1 -0
- package/dist/recovery/recoveryCode.d.ts.map +1 -1
- package/dist/recovery/recoveryCode.js +3 -4
- package/dist/recovery/recoveryCode.js.map +1 -1
- package/dist/recovery/recoveryDelegation.d.ts +1 -1
- package/dist/recovery/recoveryDelegation.d.ts.map +1 -1
- package/dist/space/collections.d.ts +1 -43
- package/dist/space/collections.d.ts.map +1 -1
- package/dist/space/collections.js +2 -34
- package/dist/space/collections.js.map +1 -1
- package/dist/space/systemCollections.d.ts +55 -0
- package/dist/space/systemCollections.d.ts.map +1 -0
- package/dist/space/systemCollections.js +46 -0
- package/dist/space/systemCollections.js.map +1 -0
- package/dist/sync/contactsConflict.d.ts +56 -10
- package/dist/sync/contactsConflict.d.ts.map +1 -1
- package/dist/sync/contactsConflict.js +82 -18
- package/dist/sync/contactsConflict.js.map +1 -1
- package/dist/sync/engine.d.ts +9 -3
- package/dist/sync/engine.d.ts.map +1 -1
- package/dist/sync/engine.js.map +1 -1
- package/dist/sync/pull.d.ts +25 -4
- package/dist/sync/pull.d.ts.map +1 -1
- package/dist/sync/pull.js +27 -3
- package/dist/sync/pull.js.map +1 -1
- package/dist/sync/remint.d.ts +10 -4
- package/dist/sync/remint.d.ts.map +1 -1
- package/dist/sync/remint.js +28 -6
- package/dist/sync/remint.js.map +1 -1
- package/dist/unlock/ladderDerivation.d.ts +98 -0
- package/dist/unlock/ladderDerivation.d.ts.map +1 -0
- package/dist/unlock/ladderDerivation.js +157 -0
- package/dist/unlock/ladderDerivation.js.map +1 -0
- package/dist/unlock/retire.d.ts +1 -1
- package/dist/unlock/retire.d.ts.map +1 -1
- package/dist/unlock/standingClient.js +1 -1
- package/dist/unlock/standingClient.js.map +1 -1
- package/dist/unlock/standingWebvh.d.ts.map +1 -1
- package/dist/unlock/standingWebvh.js +3 -3
- package/dist/unlock/standingWebvh.js.map +1 -1
- package/dist/unlock/unlockRecord.d.ts +2 -1
- package/dist/unlock/unlockRecord.d.ts.map +1 -1
- package/dist/unlock/unlockRecord.js +11 -2
- package/dist/unlock/unlockRecord.js.map +1 -1
- package/dist/webvh/accountEntry.d.ts +2 -1
- package/dist/webvh/accountEntry.d.ts.map +1 -1
- package/dist/webvh/accountEntry.js.map +1 -1
- package/dist/webvh/didWebvh.d.ts +1 -11
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +5 -13
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/updateKeyMultibase.d.ts +12 -0
- package/dist/webvh/updateKeyMultibase.d.ts.map +1 -0
- package/dist/webvh/updateKeyMultibase.js +23 -0
- package/dist/webvh/updateKeyMultibase.js.map +1 -0
- package/dist/webvh/wasIdStore.d.ts +5 -3
- package/dist/webvh/wasIdStore.d.ts.map +1 -1
- package/dist/webvh/wasIdStore.js.map +1 -1
- package/package.json +59 -11
|
@@ -17,14 +17,33 @@
|
|
|
17
17
|
* with no duplicates and no round trips.
|
|
18
18
|
*
|
|
19
19
|
* **Fail-safe to remote.** Whenever the fields the rule needs are unreachable
|
|
20
|
-
* -- a tombstone on either side, a missing cipher, an envelope
|
|
21
|
-
*
|
|
22
|
-
* That is the always-converging default, so a malformed side
|
|
23
|
-
* valid local over a malformed remote re-pushes and repairs the
|
|
24
|
-
* valid remote over a malformed local is adopted, and two
|
|
25
|
-
* the server version rather than re-fighting the conflict
|
|
26
|
-
* in the same class deliberately: a tombstone carries no
|
|
27
|
-
* delete does not rewrite the head payload), so deletion wins.
|
|
20
|
+
* -- a tombstone on either side, a missing cipher, an envelope this replica
|
|
21
|
+
* holds no key for, a body that is not a valid head payload -- the remote
|
|
22
|
+
* master wins. That is the always-converging default, so a malformed side
|
|
23
|
+
* simply loses: a valid local over a malformed remote re-pushes and repairs the
|
|
24
|
+
* server copy, a valid remote over a malformed local is adopted, and two
|
|
25
|
+
* malformed sides adopt the server version rather than re-fighting the conflict
|
|
26
|
+
* forever. A delete is in the same class deliberately: a tombstone carries no
|
|
27
|
+
* fresher stamp (a delete does not rewrite the head payload), so deletion wins.
|
|
28
|
+
*
|
|
29
|
+
* **An integrity refusal is not an unreachable side.** A decrypt refused by the
|
|
30
|
+
* cipher's envelope-to-resource binding check (was-client's `IntegrityError`)
|
|
31
|
+
* says the stored body was written for a different resource than the one it was
|
|
32
|
+
* read under: the host altered the data or served it under another id. Scoring
|
|
33
|
+
* that as one more unreachable side would let the fail-safe default settle the
|
|
34
|
+
* conflict silently and discard the refusal, which is the binding check's whole
|
|
35
|
+
* point. It is rethrown instead, so it leaves the resolver and fails the
|
|
36
|
+
* replication cycle -- the rule `@interop/was-sync`'s own last-write-wins
|
|
37
|
+
* resolver follows on the same seam. A side this replica simply holds no key
|
|
38
|
+
* for (`UnknownEpochError`, `KeyUnwrapError`) is not that: it stays unreachable
|
|
39
|
+
* and stays on the fail-safe path above.
|
|
40
|
+
*
|
|
41
|
+
* Both directions fail the cycle, and no winner is returned: a misbound remote
|
|
42
|
+
* side and a misbound local side are refused alike. The caller learns WHICH
|
|
43
|
+
* side was refused through the optional `onIntegrityRefusal` callback, which
|
|
44
|
+
* fires once per refused side (both sides can be refused in one conflict)
|
|
45
|
+
* before the refusal is rethrown. The callback is a reporting seam alone, so it
|
|
46
|
+
* decides nothing.
|
|
28
47
|
*
|
|
29
48
|
* Everything imported here is crypto-free: the envelope predicate and the
|
|
30
49
|
* `DocCipher` seam come from was-client's plain `sync` module (the same one the
|
|
@@ -44,14 +63,30 @@ export type ContactConflictWinner = 'remote' | 'local';
|
|
|
44
63
|
* envelope, passes plaintext through, and resolves `undefined` when the
|
|
45
64
|
* payload (and so the fields the rule compares) cannot be reached.
|
|
46
65
|
*
|
|
66
|
+
* A side this replica holds no key for is unreachable. What the fail-safe rule
|
|
67
|
+
* below then does with it depends on which side it was: an unreachable local
|
|
68
|
+
* side hands the conflict to the remote master, while an unreachable remote
|
|
69
|
+
* side leaves the reachable local body to win. Neither side is ever compared on
|
|
70
|
+
* a body it could not open.
|
|
71
|
+
*
|
|
72
|
+
* The decrypt is addressed with the row's own id, and an envelope sealed for
|
|
73
|
+
* another resource is refused with an `IntegrityError` rather than read as one
|
|
74
|
+
* more unreachable side. That refusal is rethrown, out of the resolver and into
|
|
75
|
+
* the replication cycle. See the module doc.
|
|
76
|
+
*
|
|
47
77
|
* @param options {object}
|
|
78
|
+
* @param options.id {string} the contact head row's resource id, the id the
|
|
79
|
+
* stored envelope must be sealed for
|
|
48
80
|
* @param options.data {Json} the stored body: an encrypted envelope or a
|
|
49
81
|
* plaintext payload
|
|
50
82
|
* @param [options.cipher] {DocCipher} the collection's document cipher;
|
|
51
83
|
* absent for a plaintext store, in which case an envelope is unreachable
|
|
52
84
|
* @returns {Promise<ContactHeadPayload | undefined>}
|
|
85
|
+
* @throws {Error} the cipher's `IntegrityError`: the stored envelope was
|
|
86
|
+
* sealed for a different resource than `id`
|
|
53
87
|
*/
|
|
54
|
-
export declare function contactHeadPayloadOf({ data, cipher }: {
|
|
88
|
+
export declare function contactHeadPayloadOf({ id, data, cipher }: {
|
|
89
|
+
id: string;
|
|
55
90
|
data: Json | undefined;
|
|
56
91
|
cipher?: DocCipher;
|
|
57
92
|
}): Promise<ContactHeadPayload | undefined>;
|
|
@@ -59,18 +94,29 @@ export declare function contactHeadPayloadOf({ data, cipher }: {
|
|
|
59
94
|
* Decides a contact-head conflict. See the module doc for the fail-safe rule.
|
|
60
95
|
*
|
|
61
96
|
* @param options {object}
|
|
97
|
+
* @param options.id {string} the contested row's resource id, which both
|
|
98
|
+
* sides' envelopes must be sealed for
|
|
62
99
|
* @param options.remote {Json} the remote (master) row body
|
|
63
100
|
* @param options.local {Json} the local row body
|
|
64
101
|
* @param [options.cipher] {DocCipher} the collection's document cipher
|
|
65
102
|
* @param [options.remoteDeleted] {boolean} the remote side is a tombstone
|
|
66
103
|
* @param [options.localDeleted] {boolean} the local side is a tombstone
|
|
104
|
+
* @param [options.onIntegrityRefusal] {Function} reports each side the
|
|
105
|
+
* binding check refused, as `{ side, err }`, before the refusal is rethrown
|
|
67
106
|
* @returns {Promise<ContactConflictWinner>}
|
|
107
|
+
* @throws {Error} the cipher's `IntegrityError` from either side: the
|
|
108
|
+
* conflict is left undecided and the replication cycle fails
|
|
68
109
|
*/
|
|
69
|
-
export declare function resolveContactHeadConflict({ remote, local, cipher, remoteDeleted, localDeleted }: {
|
|
110
|
+
export declare function resolveContactHeadConflict({ id, remote, local, cipher, remoteDeleted, localDeleted, onIntegrityRefusal }: {
|
|
111
|
+
id: string;
|
|
70
112
|
remote: Json | undefined;
|
|
71
113
|
local: Json | undefined;
|
|
72
114
|
cipher?: DocCipher;
|
|
73
115
|
remoteDeleted?: boolean;
|
|
74
116
|
localDeleted?: boolean;
|
|
117
|
+
onIntegrityRefusal?: (refusal: {
|
|
118
|
+
side: 'remote' | 'local';
|
|
119
|
+
err: unknown;
|
|
120
|
+
}) => void;
|
|
75
121
|
}): Promise<ContactConflictWinner>;
|
|
76
122
|
//# sourceMappingURL=contactsConflict.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contactsConflict.d.ts","sourceRoot":"","sources":["../../src/sync/contactsConflict.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"contactsConflict.d.ts","sourceRoot":"","sources":["../../src/sync/contactsConflict.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,IAAI,EACV,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAA;AAE7B;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,OAAO,CAAA;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,EAAE,EACF,IAAI,EACJ,MAAM,EACP,EAAE;IACD,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,IAAI,GAAG,SAAS,CAAA;IACtB,MAAM,CAAC,EAAE,SAAS,CAAA;CACnB,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAmB1C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,0BAA0B,CAAC,EAC/C,EAAE,EACF,MAAM,EACN,KAAK,EACL,MAAM,EACN,aAAqB,EACrB,YAAoB,EACpB,kBAAkB,EACnB,EAAE;IACD,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,IAAI,GAAG,SAAS,CAAA;IACxB,KAAK,EAAE,IAAI,GAAG,SAAS,CAAA;IACvB,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC7B,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAA;QACxB,GAAG,EAAE,OAAO,CAAA;KACb,KAAK,IAAI,CAAA;CACX,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA0CjC"}
|
|
@@ -17,14 +17,33 @@
|
|
|
17
17
|
* with no duplicates and no round trips.
|
|
18
18
|
*
|
|
19
19
|
* **Fail-safe to remote.** Whenever the fields the rule needs are unreachable
|
|
20
|
-
* -- a tombstone on either side, a missing cipher, an envelope
|
|
21
|
-
*
|
|
22
|
-
* That is the always-converging default, so a malformed side
|
|
23
|
-
* valid local over a malformed remote re-pushes and repairs the
|
|
24
|
-
* valid remote over a malformed local is adopted, and two
|
|
25
|
-
* the server version rather than re-fighting the conflict
|
|
26
|
-
* in the same class deliberately: a tombstone carries no
|
|
27
|
-
* delete does not rewrite the head payload), so deletion wins.
|
|
20
|
+
* -- a tombstone on either side, a missing cipher, an envelope this replica
|
|
21
|
+
* holds no key for, a body that is not a valid head payload -- the remote
|
|
22
|
+
* master wins. That is the always-converging default, so a malformed side
|
|
23
|
+
* simply loses: a valid local over a malformed remote re-pushes and repairs the
|
|
24
|
+
* server copy, a valid remote over a malformed local is adopted, and two
|
|
25
|
+
* malformed sides adopt the server version rather than re-fighting the conflict
|
|
26
|
+
* forever. A delete is in the same class deliberately: a tombstone carries no
|
|
27
|
+
* fresher stamp (a delete does not rewrite the head payload), so deletion wins.
|
|
28
|
+
*
|
|
29
|
+
* **An integrity refusal is not an unreachable side.** A decrypt refused by the
|
|
30
|
+
* cipher's envelope-to-resource binding check (was-client's `IntegrityError`)
|
|
31
|
+
* says the stored body was written for a different resource than the one it was
|
|
32
|
+
* read under: the host altered the data or served it under another id. Scoring
|
|
33
|
+
* that as one more unreachable side would let the fail-safe default settle the
|
|
34
|
+
* conflict silently and discard the refusal, which is the binding check's whole
|
|
35
|
+
* point. It is rethrown instead, so it leaves the resolver and fails the
|
|
36
|
+
* replication cycle -- the rule `@interop/was-sync`'s own last-write-wins
|
|
37
|
+
* resolver follows on the same seam. A side this replica simply holds no key
|
|
38
|
+
* for (`UnknownEpochError`, `KeyUnwrapError`) is not that: it stays unreachable
|
|
39
|
+
* and stays on the fail-safe path above.
|
|
40
|
+
*
|
|
41
|
+
* Both directions fail the cycle, and no winner is returned: a misbound remote
|
|
42
|
+
* side and a misbound local side are refused alike. The caller learns WHICH
|
|
43
|
+
* side was refused through the optional `onIntegrityRefusal` callback, which
|
|
44
|
+
* fires once per refused side (both sides can be refused in one conflict)
|
|
45
|
+
* before the refusal is rethrown. The callback is a reporting seam alone, so it
|
|
46
|
+
* decides nothing.
|
|
28
47
|
*
|
|
29
48
|
* Everything imported here is crypto-free: the envelope predicate and the
|
|
30
49
|
* `DocCipher` seam come from was-client's plain `sync` module (the same one the
|
|
@@ -33,21 +52,36 @@
|
|
|
33
52
|
* in a plain test runner -- the EDV graph is the app's to pull in, at the point
|
|
34
53
|
* where it builds the cipher it passes down.
|
|
35
54
|
*/
|
|
36
|
-
import { isEncryptedEnvelope } from '@interop/was-client/sync';
|
|
55
|
+
import { isEncryptedEnvelope, isIntegrityError } from '@interop/was-client/sync';
|
|
37
56
|
import { isContactHeadPayload, remotePayloadWins } from '@interop/social-core';
|
|
38
57
|
/**
|
|
39
58
|
* Recovers a validated head payload from a stored row body: decrypts an
|
|
40
59
|
* envelope, passes plaintext through, and resolves `undefined` when the
|
|
41
60
|
* payload (and so the fields the rule compares) cannot be reached.
|
|
42
61
|
*
|
|
62
|
+
* A side this replica holds no key for is unreachable. What the fail-safe rule
|
|
63
|
+
* below then does with it depends on which side it was: an unreachable local
|
|
64
|
+
* side hands the conflict to the remote master, while an unreachable remote
|
|
65
|
+
* side leaves the reachable local body to win. Neither side is ever compared on
|
|
66
|
+
* a body it could not open.
|
|
67
|
+
*
|
|
68
|
+
* The decrypt is addressed with the row's own id, and an envelope sealed for
|
|
69
|
+
* another resource is refused with an `IntegrityError` rather than read as one
|
|
70
|
+
* more unreachable side. That refusal is rethrown, out of the resolver and into
|
|
71
|
+
* the replication cycle. See the module doc.
|
|
72
|
+
*
|
|
43
73
|
* @param options {object}
|
|
74
|
+
* @param options.id {string} the contact head row's resource id, the id the
|
|
75
|
+
* stored envelope must be sealed for
|
|
44
76
|
* @param options.data {Json} the stored body: an encrypted envelope or a
|
|
45
77
|
* plaintext payload
|
|
46
78
|
* @param [options.cipher] {DocCipher} the collection's document cipher;
|
|
47
79
|
* absent for a plaintext store, in which case an envelope is unreachable
|
|
48
80
|
* @returns {Promise<ContactHeadPayload | undefined>}
|
|
81
|
+
* @throws {Error} the cipher's `IntegrityError`: the stored envelope was
|
|
82
|
+
* sealed for a different resource than `id`
|
|
49
83
|
*/
|
|
50
|
-
export async function contactHeadPayloadOf({ data, cipher }) {
|
|
84
|
+
export async function contactHeadPayloadOf({ id, data, cipher }) {
|
|
51
85
|
if (data === null || data === undefined) {
|
|
52
86
|
return undefined;
|
|
53
87
|
}
|
|
@@ -57,9 +91,12 @@ export async function contactHeadPayloadOf({ data, cipher }) {
|
|
|
57
91
|
return undefined;
|
|
58
92
|
}
|
|
59
93
|
try {
|
|
60
|
-
body = await cipher.decrypt({ envelope: data });
|
|
94
|
+
body = await cipher.decrypt({ id, envelope: data });
|
|
61
95
|
}
|
|
62
|
-
catch {
|
|
96
|
+
catch (err) {
|
|
97
|
+
if (isIntegrityError(err)) {
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
63
100
|
return undefined;
|
|
64
101
|
}
|
|
65
102
|
}
|
|
@@ -69,25 +106,52 @@ export async function contactHeadPayloadOf({ data, cipher }) {
|
|
|
69
106
|
* Decides a contact-head conflict. See the module doc for the fail-safe rule.
|
|
70
107
|
*
|
|
71
108
|
* @param options {object}
|
|
109
|
+
* @param options.id {string} the contested row's resource id, which both
|
|
110
|
+
* sides' envelopes must be sealed for
|
|
72
111
|
* @param options.remote {Json} the remote (master) row body
|
|
73
112
|
* @param options.local {Json} the local row body
|
|
74
113
|
* @param [options.cipher] {DocCipher} the collection's document cipher
|
|
75
114
|
* @param [options.remoteDeleted] {boolean} the remote side is a tombstone
|
|
76
115
|
* @param [options.localDeleted] {boolean} the local side is a tombstone
|
|
116
|
+
* @param [options.onIntegrityRefusal] {Function} reports each side the
|
|
117
|
+
* binding check refused, as `{ side, err }`, before the refusal is rethrown
|
|
77
118
|
* @returns {Promise<ContactConflictWinner>}
|
|
119
|
+
* @throws {Error} the cipher's `IntegrityError` from either side: the
|
|
120
|
+
* conflict is left undecided and the replication cycle fails
|
|
78
121
|
*/
|
|
79
|
-
export async function resolveContactHeadConflict({ remote, local, cipher, remoteDeleted = false, localDeleted = false }) {
|
|
122
|
+
export async function resolveContactHeadConflict({ id, remote, local, cipher, remoteDeleted = false, localDeleted = false, onIntegrityRefusal }) {
|
|
80
123
|
if (remoteDeleted || localDeleted) {
|
|
81
124
|
return 'remote';
|
|
82
125
|
}
|
|
83
126
|
// Both sides decrypt independently, so they decrypt concurrently. Each
|
|
84
|
-
// side's own unreachability (
|
|
127
|
+
// side's own unreachability (no key for the envelope, a malformed payload) is
|
|
85
128
|
// already resolved as `undefined` inside the helper, so the join changes
|
|
86
|
-
// nothing about the fail-safe rule below.
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
contactHeadPayloadOf({ data:
|
|
129
|
+
// nothing about the fail-safe rule below. An integrity refusal on either side
|
|
130
|
+
// rejects instead, and no winner is returned.
|
|
131
|
+
const [remoteResult, localResult] = await Promise.allSettled([
|
|
132
|
+
contactHeadPayloadOf({ id, data: remote, cipher }),
|
|
133
|
+
contactHeadPayloadOf({ id, data: local, cipher })
|
|
90
134
|
]);
|
|
135
|
+
// `allSettled` rather than `all`, so a conflict whose two sides are both
|
|
136
|
+
// misbound reports both before the first refusal leaves. The refusal itself
|
|
137
|
+
// is rethrown unchanged, so the caller still matches it on `name`.
|
|
138
|
+
const sides = [
|
|
139
|
+
{ side: 'remote', result: remoteResult },
|
|
140
|
+
{ side: 'local', result: localResult }
|
|
141
|
+
];
|
|
142
|
+
for (const { side, result } of sides) {
|
|
143
|
+
if (result.status === 'rejected' && isIntegrityError(result.reason)) {
|
|
144
|
+
onIntegrityRefusal?.({ side, err: result.reason });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (remoteResult.status === 'rejected') {
|
|
148
|
+
throw remoteResult.reason;
|
|
149
|
+
}
|
|
150
|
+
if (localResult.status === 'rejected') {
|
|
151
|
+
throw localResult.reason;
|
|
152
|
+
}
|
|
153
|
+
const remoteHead = remoteResult.value;
|
|
154
|
+
const localHead = localResult.value;
|
|
91
155
|
if (remoteHead && localHead) {
|
|
92
156
|
return remotePayloadWins(remoteHead, localHead) ? 'remote' : 'local';
|
|
93
157
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contactsConflict.js","sourceRoot":"","sources":["../../src/sync/contactsConflict.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"contactsConflict.js","sourceRoot":"","sources":["../../src/sync/contactsConflict.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/engine.d.ts
CHANGED
|
@@ -25,9 +25,15 @@ export interface SyncEngineDeps {
|
|
|
25
25
|
port: WasSyncPort;
|
|
26
26
|
store: SyncStore;
|
|
27
27
|
/**
|
|
28
|
-
* Decrypts a pulled body to its plaintext payload (DocCipher).
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
* Decrypts a pulled body to its plaintext payload (DocCipher). `id` is the
|
|
29
|
+
* resource id the replica read the body under, and the cipher verifies the
|
|
30
|
+
* envelope was sealed for it -- an envelope addressed to some other
|
|
31
|
+
* resource is refused with was-client's `IntegrityError`.
|
|
32
|
+
*/
|
|
33
|
+
decryptDoc: (options: {
|
|
34
|
+
id: string;
|
|
35
|
+
envelope: Json;
|
|
36
|
+
}) => Promise<Json>;
|
|
31
37
|
/**
|
|
32
38
|
* The collection's payload guard, when it has one: a pulled document that
|
|
33
39
|
* decrypts but fails it is stored without being projected (see `pull.ts`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/sync/engine.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAQ/E;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,EAAE,SAAS,CAAA;IAChB
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/sync/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/sync/engine.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,MAAM,kBAAkB,GAAG,GAAG,CAAA;AAC9B,MAAM,qBAAqB,GAAG,KAAK,CAAA;AACnC,MAAM,oBAAoB,GAAG,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/sync/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/pull.d.ts
CHANGED
|
@@ -15,12 +15,29 @@ import type { Json, ProjectionAction, SyncStore, WasSyncPort, WireDoc } from './
|
|
|
15
15
|
* possibly a buggy or schema-incompatible writer) is skipped the same way:
|
|
16
16
|
* stored, checkpoint advanced, never projected.
|
|
17
17
|
*
|
|
18
|
+
* The decrypt is addressed: the feed row's own `id` goes to `decryptDoc`, and
|
|
19
|
+
* the cipher refuses an envelope sealed for some other resource with was-
|
|
20
|
+
* client's `IntegrityError`. That refusal has two causes and the reader
|
|
21
|
+
* cannot tell them apart: a host that moved one resource's stored envelope
|
|
22
|
+
* under another resource's id, and a row minted by a writer that predates
|
|
23
|
+
* addressed sealing (the legacy contacts rows
|
|
24
|
+
* `docs/cross-replica-sync-compatibility.md` records as a stated loss, which
|
|
25
|
+
* every fresh replica bootstrap re-reads). It is logged apart from the
|
|
26
|
+
* ordinary undecryptable skip so a caller can count it, but the benign cause
|
|
27
|
+
* is the common one and an alert on it needs a rate rather than a single
|
|
28
|
+
* event. The projection outcome is the same `none`: the row is not applied,
|
|
29
|
+
* the body is still stored, and the checkpoint advances past it.
|
|
30
|
+
*
|
|
18
31
|
* @param doc {WireDoc}
|
|
19
|
-
* @param decryptDoc {(envelope: Json) =>
|
|
32
|
+
* @param decryptDoc {(options: { id: string, envelope: Json }) =>
|
|
33
|
+
* Promise<Json>}
|
|
20
34
|
* @param [validatePayload] {(payload: Json) => boolean}
|
|
21
35
|
* @returns {Promise<ProjectionAction>}
|
|
22
36
|
*/
|
|
23
|
-
export declare function projectionForDoc(doc: WireDoc, decryptDoc: (
|
|
37
|
+
export declare function projectionForDoc(doc: WireDoc, decryptDoc: (options: {
|
|
38
|
+
id: string;
|
|
39
|
+
envelope: Json;
|
|
40
|
+
}) => Promise<Json>, validatePayload?: (payload: Json) => boolean): Promise<ProjectionAction>;
|
|
24
41
|
/**
|
|
25
42
|
* Runs the pull loop to exhaustion for one feed. Fetches a page from the current
|
|
26
43
|
* checkpoint, decrypts it to projections, and applies it (upserts + projection +
|
|
@@ -36,7 +53,8 @@ export declare function projectionForDoc(doc: WireDoc, decryptDoc: (envelope: Js
|
|
|
36
53
|
* @param options.port {WasSyncPort}
|
|
37
54
|
* @param options.store {SyncStore}
|
|
38
55
|
* @param options.batchSize {number} pull `limit` (server clamps at 1000)
|
|
39
|
-
* @param options.decryptDoc {(envelope: Json) =>
|
|
56
|
+
* @param options.decryptDoc {(options: { id: string, envelope: Json }) =>
|
|
57
|
+
* Promise<Json>}
|
|
40
58
|
* @param [options.validatePayload] {(payload: Json) => boolean} collection
|
|
41
59
|
* payload guard; a decrypted document failing it is stored but not projected
|
|
42
60
|
* @param [options.signal] {AbortSignal}
|
|
@@ -46,7 +64,10 @@ export declare function runPull({ port, store, batchSize, decryptDoc, validatePa
|
|
|
46
64
|
port: WasSyncPort;
|
|
47
65
|
store: SyncStore;
|
|
48
66
|
batchSize: number;
|
|
49
|
-
decryptDoc: (
|
|
67
|
+
decryptDoc: (options: {
|
|
68
|
+
id: string;
|
|
69
|
+
envelope: Json;
|
|
70
|
+
}) => Promise<Json>;
|
|
50
71
|
validatePayload?: (payload: Json) => boolean;
|
|
51
72
|
signal?: AbortSignal;
|
|
52
73
|
}): Promise<{
|
package/dist/sync/pull.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/sync/pull.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/sync/pull.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EACV,IAAI,EACJ,gBAAgB,EAEhB,SAAS,EACT,WAAW,EACX,OAAO,EACR,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,OAAO,EACZ,UAAU,EAAE,CAAC,OAAO,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,IAAI,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtE,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,OAAO,GAC3C,OAAO,CAAC,gBAAgB,CAAC,CAkC3B;AAqBD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,OAAO,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,MAAM,EACP,EAAE;IACD,IAAI,EAAE,WAAW,CAAA;IACjB,KAAK,EAAE,SAAS,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACtE,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,KAAK,OAAO,CAAA;IAC5C,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAyD/B"}
|
package/dist/sync/pull.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* - A tombstone deletes the projected row; a live document decrypts its `data`
|
|
21
21
|
* body to the payload to upsert.
|
|
22
22
|
*/
|
|
23
|
+
import { isIntegrityError } from '@interop/was-client/sync';
|
|
23
24
|
import { log } from '../log.js';
|
|
24
25
|
/**
|
|
25
26
|
* Maps one pulled wire document to the projection action for the decrypted
|
|
@@ -37,8 +38,22 @@ import { log } from '../log.js';
|
|
|
37
38
|
* possibly a buggy or schema-incompatible writer) is skipped the same way:
|
|
38
39
|
* stored, checkpoint advanced, never projected.
|
|
39
40
|
*
|
|
41
|
+
* The decrypt is addressed: the feed row's own `id` goes to `decryptDoc`, and
|
|
42
|
+
* the cipher refuses an envelope sealed for some other resource with was-
|
|
43
|
+
* client's `IntegrityError`. That refusal has two causes and the reader
|
|
44
|
+
* cannot tell them apart: a host that moved one resource's stored envelope
|
|
45
|
+
* under another resource's id, and a row minted by a writer that predates
|
|
46
|
+
* addressed sealing (the legacy contacts rows
|
|
47
|
+
* `docs/cross-replica-sync-compatibility.md` records as a stated loss, which
|
|
48
|
+
* every fresh replica bootstrap re-reads). It is logged apart from the
|
|
49
|
+
* ordinary undecryptable skip so a caller can count it, but the benign cause
|
|
50
|
+
* is the common one and an alert on it needs a rate rather than a single
|
|
51
|
+
* event. The projection outcome is the same `none`: the row is not applied,
|
|
52
|
+
* the body is still stored, and the checkpoint advances past it.
|
|
53
|
+
*
|
|
40
54
|
* @param doc {WireDoc}
|
|
41
|
-
* @param decryptDoc {(envelope: Json) =>
|
|
55
|
+
* @param decryptDoc {(options: { id: string, envelope: Json }) =>
|
|
56
|
+
* Promise<Json>}
|
|
42
57
|
* @param [validatePayload] {(payload: Json) => boolean}
|
|
43
58
|
* @returns {Promise<ProjectionAction>}
|
|
44
59
|
*/
|
|
@@ -50,7 +65,7 @@ export async function projectionForDoc(doc, decryptDoc, validatePayload) {
|
|
|
50
65
|
return { kind: 'none' };
|
|
51
66
|
}
|
|
52
67
|
try {
|
|
53
|
-
const payload = await decryptDoc(doc.data);
|
|
68
|
+
const payload = await decryptDoc({ id: doc.id, envelope: doc.data });
|
|
54
69
|
if (validatePayload !== undefined && !validatePayload(payload)) {
|
|
55
70
|
log.warn('Skipping malformed synced document (no projection)', {
|
|
56
71
|
id: doc.id
|
|
@@ -60,6 +75,14 @@ export async function projectionForDoc(doc, decryptDoc, validatePayload) {
|
|
|
60
75
|
return { kind: 'upsert', payload };
|
|
61
76
|
}
|
|
62
77
|
catch (err) {
|
|
78
|
+
if (isIntegrityError(err)) {
|
|
79
|
+
log.warn('Skipping synced document sealed for another resource id (no ' +
|
|
80
|
+
'projection)', {
|
|
81
|
+
id: doc.id,
|
|
82
|
+
err
|
|
83
|
+
});
|
|
84
|
+
return { kind: 'none' };
|
|
85
|
+
}
|
|
63
86
|
log.warn('Skipping undecryptable synced document (no projection)', {
|
|
64
87
|
id: doc.id,
|
|
65
88
|
err
|
|
@@ -97,7 +120,8 @@ function sameCheckpoint(left, right) {
|
|
|
97
120
|
* @param options.port {WasSyncPort}
|
|
98
121
|
* @param options.store {SyncStore}
|
|
99
122
|
* @param options.batchSize {number} pull `limit` (server clamps at 1000)
|
|
100
|
-
* @param options.decryptDoc {(envelope: Json) =>
|
|
123
|
+
* @param options.decryptDoc {(options: { id: string, envelope: Json }) =>
|
|
124
|
+
* Promise<Json>}
|
|
101
125
|
* @param [options.validatePayload] {(payload: Json) => boolean} collection
|
|
102
126
|
* payload guard; a decrypted document failing it is stored but not projected
|
|
103
127
|
* @param [options.signal] {AbortSignal}
|
package/dist/sync/pull.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/sync/pull.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAU/B
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/sync/pull.ts"],"names":[],"mappings":"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"}
|
package/dist/sync/remint.d.ts
CHANGED
|
@@ -13,10 +13,16 @@ import type { DocCipher, Json, SyncStore } from './types.js';
|
|
|
13
13
|
* and handed to {@link SyncStore.replacePending} -- which may re-key the row,
|
|
14
14
|
* since the re-mint is a fresh encryption. Rows already readable under the
|
|
15
15
|
* adopted descriptor, acked rows (`version > 0` -- they HAVE feed existence
|
|
16
|
-
* and are never re-minted), and tombstones are left untouched.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* helper
|
|
16
|
+
* and are never re-minted), and tombstones are left untouched.
|
|
17
|
+
*
|
|
18
|
+
* Two decrypt failures are told apart. An unknown epoch is the create-loss
|
|
19
|
+
* shape this helper exists for, and the row is re-minted. An addressing
|
|
20
|
+
* refusal (was-client's `IntegrityError`: the envelope is sealed for some
|
|
21
|
+
* other resource id, the shape a row minted by a pre-addressed writer has) is
|
|
22
|
+
* left where it is, logged once, and the pass moves to the next row -- the
|
|
23
|
+
* row is not this helper's to settle, and aborting the pass over it would
|
|
24
|
+
* strand every other pending row under the losing epoch and block the
|
|
25
|
+
* adoption from ever completing. Any other decrypt failure still propagates.
|
|
20
26
|
*
|
|
21
27
|
* A replace the store SKIPS (`{ applied: false }` -- a local write bumped the
|
|
22
28
|
* row's revision between the snapshot and the replace, so the row now holds a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remint.d.ts","sourceRoot":"","sources":["../../src/sync/remint.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"remint.d.ts","sourceRoot":"","sources":["../../src/sync/remint.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAU5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,wBAAsB,sBAAsB,CAAC,EAC3C,KAAK,EACL,MAAM,EACN,YAAY,EACZ,MAAM,EACP,EAAE;IACD,KAAK,EAAE,SAAS,GAAG;QACjB,cAAc,EAAE,WAAW,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAA;KACzD,CAAA;IACD,MAAM,EAAE,SAAS,CAAA;IACjB,YAAY,EAAE,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACxE,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA2EjD"}
|
package/dist/sync/remint.js
CHANGED
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
* re-encryption may therefore re-key the row (a content-derived id hashes
|
|
30
30
|
* the ciphertext, so a re-mint mints a new id).
|
|
31
31
|
*/
|
|
32
|
-
import { isUnknownEpochError } from '@interop/was-client/sync';
|
|
32
|
+
import { isIntegrityError, isUnknownEpochError } from '@interop/was-client/sync';
|
|
33
|
+
import { log } from '../log.js';
|
|
33
34
|
/**
|
|
34
35
|
* How many full re-mint passes are attempted before giving up. A pass that
|
|
35
36
|
* ends with skipped rows (a local write bumped a row's revision between the
|
|
@@ -51,10 +52,16 @@ const MAX_REMINT_ATTEMPTS = 5;
|
|
|
51
52
|
* and handed to {@link SyncStore.replacePending} -- which may re-key the row,
|
|
52
53
|
* since the re-mint is a fresh encryption. Rows already readable under the
|
|
53
54
|
* adopted descriptor, acked rows (`version > 0` -- they HAVE feed existence
|
|
54
|
-
* and are never re-minted), and tombstones are left untouched.
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* helper
|
|
55
|
+
* and are never re-minted), and tombstones are left untouched.
|
|
56
|
+
*
|
|
57
|
+
* Two decrypt failures are told apart. An unknown epoch is the create-loss
|
|
58
|
+
* shape this helper exists for, and the row is re-minted. An addressing
|
|
59
|
+
* refusal (was-client's `IntegrityError`: the envelope is sealed for some
|
|
60
|
+
* other resource id, the shape a row minted by a pre-addressed writer has) is
|
|
61
|
+
* left where it is, logged once, and the pass moves to the next row -- the
|
|
62
|
+
* row is not this helper's to settle, and aborting the pass over it would
|
|
63
|
+
* strand every other pending row under the losing epoch and block the
|
|
64
|
+
* adoption from ever completing. Any other decrypt failure still propagates.
|
|
58
65
|
*
|
|
59
66
|
* A replace the store SKIPS (`{ applied: false }` -- a local write bumped the
|
|
60
67
|
* row's revision between the snapshot and the replace, so the row now holds a
|
|
@@ -88,6 +95,10 @@ export async function remintPendingEnvelopes({ store, cipher, decryptStale, sign
|
|
|
88
95
|
let pending;
|
|
89
96
|
let reminted = 0;
|
|
90
97
|
let skipped = [];
|
|
98
|
+
// Rows the cipher refused as sealed for another resource id. The refusal is
|
|
99
|
+
// deterministic over the same envelope, so a later attempt's pass skips such
|
|
100
|
+
// a row instead of paying for the decrypt (and warning) again.
|
|
101
|
+
const misaddressed = new Set();
|
|
91
102
|
for (let attempt = 0; attempt < MAX_REMINT_ATTEMPTS; attempt += 1) {
|
|
92
103
|
if (signal?.aborted) {
|
|
93
104
|
break;
|
|
@@ -100,12 +111,23 @@ export async function remintPendingEnvelopes({ store, cipher, decryptStale, sign
|
|
|
100
111
|
if (signal?.aborted) {
|
|
101
112
|
break;
|
|
102
113
|
}
|
|
114
|
+
if (misaddressed.has(row.id)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
103
117
|
const envelope = row.data;
|
|
104
118
|
try {
|
|
105
|
-
await cipher.decrypt({ envelope });
|
|
119
|
+
await cipher.decrypt({ id: row.id, envelope });
|
|
106
120
|
continue;
|
|
107
121
|
}
|
|
108
122
|
catch (err) {
|
|
123
|
+
if (isIntegrityError(err)) {
|
|
124
|
+
misaddressed.add(row.id);
|
|
125
|
+
log.warn('Leaving a pending row sealed for another resource id', {
|
|
126
|
+
id: row.id,
|
|
127
|
+
err
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
109
131
|
if (!isUnknownEpochError(err)) {
|
|
110
132
|
throw err;
|
|
111
133
|
}
|
package/dist/sync/remint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remint.js","sourceRoot":"","sources":["../../src/sync/remint.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"remint.js","sourceRoot":"","sources":["../../src/sync/remint.ts"],"names":[],"mappings":"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"}
|