@interop/was-react 0.13.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -29
- package/dist/auth/walletRequestTypes.d.ts +30 -47
- package/dist/auth/walletRequestTypes.d.ts.map +1 -1
- package/dist/config.d.ts +28 -18
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -0
- package/dist/config.js.map +1 -1
- package/dist/dev/provisionDevGrants.d.ts +7 -3
- package/dist/dev/provisionDevGrants.d.ts.map +1 -1
- package/dist/dev/provisionDevGrants.js +19 -7
- package/dist/dev/provisionDevGrants.js.map +1 -1
- package/dist/identity/seedCredential.d.ts +15 -13
- package/dist/identity/seedCredential.d.ts.map +1 -1
- package/dist/identity/seedCredential.js +15 -16
- package/dist/identity/seedCredential.js.map +1 -1
- package/dist/identity/seedStore.d.ts.map +1 -1
- package/dist/identity/seedStore.js +4 -3
- package/dist/identity/seedStore.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/react/documentApp.d.ts +5 -5
- package/dist/react/documentApp.d.ts.map +1 -1
- package/dist/react/documentApp.js +14 -17
- package/dist/react/documentApp.js.map +1 -1
- package/dist/session/authStore.d.ts +4 -3
- package/dist/session/authStore.d.ts.map +1 -1
- package/dist/session/authStore.js +89 -55
- package/dist/session/authStore.js.map +1 -1
- package/dist/storage/adopt.d.ts +6 -5
- package/dist/storage/adopt.d.ts.map +1 -1
- package/dist/storage/adopt.js +22 -10
- package/dist/storage/adopt.js.map +1 -1
- package/dist/storage/entityStore.d.ts +37 -9
- package/dist/storage/entityStore.d.ts.map +1 -1
- package/dist/storage/entityStore.js +19 -9
- package/dist/storage/entityStore.js.map +1 -1
- package/dist/storage/localStore.d.ts.map +1 -1
- package/dist/storage/localStore.js +51 -61
- package/dist/storage/localStore.js.map +1 -1
- package/dist/storage/sharedCollectionReader.d.ts.map +1 -1
- package/dist/storage/sharedCollectionReader.js +15 -14
- package/dist/storage/sharedCollectionReader.js.map +1 -1
- package/dist/storage/storageManager.d.ts +30 -0
- package/dist/storage/storageManager.d.ts.map +1 -1
- package/dist/storage/storageManager.js +46 -0
- package/dist/storage/storageManager.js.map +1 -1
- package/dist/storage/wasRemoteStore.d.ts +99 -28
- package/dist/storage/wasRemoteStore.d.ts.map +1 -1
- package/dist/storage/wasRemoteStore.js +205 -30
- package/dist/storage/wasRemoteStore.js.map +1 -1
- package/dist/storage/wasSync.d.ts +17 -6
- package/dist/storage/wasSync.d.ts.map +1 -1
- package/dist/storage/wasSync.js +39 -9
- package/dist/storage/wasSync.js.map +1 -1
- package/package.json +4 -4
|
@@ -2,6 +2,18 @@ import { WasClient } from '@interop/was-client';
|
|
|
2
2
|
import { createEdvEncryption } from '@interop/was-client/edv';
|
|
3
3
|
import { collectionItems, collectionPath, resourcePath, toUrl } from '@interop/was-client/paths';
|
|
4
4
|
import { errorStatus, errorMessage } from '../sync/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Maps one configured index attribute name to the blinded-index attribute path
|
|
7
|
+
* the EDV codec addresses it by. The codec walks the path from the stored
|
|
8
|
+
* document's root, and a JSON payload is written as that document's `content`
|
|
9
|
+
* verbatim, so an app-level `author` is `content.author`.
|
|
10
|
+
*
|
|
11
|
+
* @param name {string} the attribute name as declared in the collection config
|
|
12
|
+
* @returns {string}
|
|
13
|
+
*/
|
|
14
|
+
function blindedAttribute(name) {
|
|
15
|
+
return `content.${name}`;
|
|
16
|
+
}
|
|
5
17
|
export class WasRemoteStore {
|
|
6
18
|
was;
|
|
7
19
|
serverUrl;
|
|
@@ -10,31 +22,49 @@ export class WasRemoteStore {
|
|
|
10
22
|
// Per WAS collection id: the effective visibility + declared equality
|
|
11
23
|
// indexes (the registry guarantees one declaration per id).
|
|
12
24
|
#configById;
|
|
13
|
-
|
|
25
|
+
// Whether `fromGrants` was given this app's identity keys, i.e. whether the
|
|
26
|
+
// client's EDV keystore can build a codec (the blinded-index verbs need one).
|
|
27
|
+
#hasIdentityKeys;
|
|
28
|
+
constructor({ was, parsed, configById, hasIdentityKeys }) {
|
|
14
29
|
this.was = was;
|
|
15
30
|
this.serverUrl = parsed.serverUrl;
|
|
16
31
|
this.spaceId = parsed.spaceId;
|
|
17
32
|
this.#byCollectionId = parsed.byCollectionId;
|
|
18
33
|
this.#configById = configById;
|
|
34
|
+
this.#hasIdentityKeys = hasIdentityKeys;
|
|
19
35
|
}
|
|
20
36
|
/**
|
|
21
37
|
* Builds a delegated remote store from a parsed grant set and the app's
|
|
22
38
|
* ZcapClient (whose invocation signer is the controller the grants target).
|
|
23
|
-
*
|
|
24
|
-
*
|
|
39
|
+
*
|
|
40
|
+
* Replication itself needs no keystore at all: it moves opaque envelopes
|
|
41
|
+
* verbatim through `was.request()`, which bypasses the codec, and
|
|
42
|
+
* encrypt/decrypt is a local read/write concern. The keystore matters only
|
|
43
|
+
* for the codec-driven verbs -- the blinded-index query and schema
|
|
44
|
+
* declaration -- so it answers with the app's identity keys when they are
|
|
45
|
+
* supplied and with `null` (fail-closed: "this client holds no keys for that
|
|
46
|
+
* collection") when they are not. The blinding key itself is never passed
|
|
47
|
+
* here: the codec unwraps it from the collection's own encryption descriptor,
|
|
48
|
+
* so a collection provisioned without one simply has no blinded index.
|
|
25
49
|
*
|
|
26
50
|
* @param options {object}
|
|
27
51
|
* @param options.parsed {ParsedGrants}
|
|
28
52
|
* @param options.zcapClient {ZcapClient}
|
|
29
53
|
* @param [options.collections] {WasCollectionConfig[]} the collection
|
|
30
54
|
* registry; entries with `visibility: 'public'` are never marked encrypted
|
|
55
|
+
* @param [options.keys] {object} this app's identity key-agreement key and
|
|
56
|
+
* its resolver (the same pair `IdentityAgents` carries)
|
|
57
|
+
* @param options.keys.keyAgreementKey {IKeyAgreementKey}
|
|
58
|
+
* @param options.keys.keyResolver {IKeyResolver}
|
|
31
59
|
* @returns {WasRemoteStore}
|
|
32
60
|
*/
|
|
33
|
-
static fromGrants({ parsed, zcapClient, collections = [] }) {
|
|
61
|
+
static fromGrants({ parsed, zcapClient, collections = [], keys }) {
|
|
34
62
|
const was = new WasClient({
|
|
35
63
|
serverUrl: parsed.serverUrl,
|
|
36
64
|
zcapClient,
|
|
37
|
-
encryption: createEdvEncryption({
|
|
65
|
+
encryption: createEdvEncryption({
|
|
66
|
+
resolveKeys: async () => keys ?? null
|
|
67
|
+
})
|
|
38
68
|
});
|
|
39
69
|
const configById = new Map(collections.map(entry => [
|
|
40
70
|
entry.id,
|
|
@@ -43,7 +73,12 @@ export class WasRemoteStore {
|
|
|
43
73
|
...(entry.indexes && { indexes: entry.indexes })
|
|
44
74
|
}
|
|
45
75
|
]));
|
|
46
|
-
return new WasRemoteStore({
|
|
76
|
+
return new WasRemoteStore({
|
|
77
|
+
was,
|
|
78
|
+
parsed,
|
|
79
|
+
configById,
|
|
80
|
+
hasIdentityKeys: keys !== undefined
|
|
81
|
+
});
|
|
47
82
|
}
|
|
48
83
|
/**
|
|
49
84
|
* The delegated capability for one WAS collection, or `undefined` when no
|
|
@@ -102,23 +137,19 @@ export class WasRemoteStore {
|
|
|
102
137
|
* no-op fallback for servers/wallets that did not provision the roster.
|
|
103
138
|
*
|
|
104
139
|
* @param collectionId {string} the WAS collection id
|
|
105
|
-
* @param
|
|
106
|
-
* collection description, to spend one authenticated GET rather than two:
|
|
107
|
-
* the read-before-PUT guard then turns on the descriptor handed in here
|
|
108
|
-
* instead of re-reading it. `undefined` means "read, and the collection
|
|
109
|
-
* carries no descriptor" -- not "unknown"
|
|
140
|
+
* @param options {object}
|
|
110
141
|
* @param options.encryption {CollectionEncryption | undefined} the
|
|
111
|
-
* already-read descriptor
|
|
142
|
+
* already-read descriptor the caller fetched from the collection
|
|
143
|
+
* description (the bootstrap reads it once and feeds both the cipher
|
|
144
|
+
* rebuild and this guard). `undefined` means "read, and the collection
|
|
145
|
+
* carries no descriptor" -- not "unknown"
|
|
112
146
|
* @returns {Promise<DeclarationResult>}
|
|
113
147
|
*/
|
|
114
|
-
async markCollectionEncrypted(collectionId,
|
|
148
|
+
async markCollectionEncrypted(collectionId, { encryption }) {
|
|
115
149
|
if (this.#configById.get(collectionId)?.visibility === 'public') {
|
|
116
150
|
return { collectionId, ok: true, skipped: true };
|
|
117
151
|
}
|
|
118
|
-
|
|
119
|
-
? options.encryption
|
|
120
|
-
: await this.readCollectionEncryption(collectionId);
|
|
121
|
-
if (existing) {
|
|
152
|
+
if (encryption) {
|
|
122
153
|
return { collectionId, ok: true, skipped: true };
|
|
123
154
|
}
|
|
124
155
|
return this.#putDescription({
|
|
@@ -152,15 +183,112 @@ export class WasRemoteStore {
|
|
|
152
183
|
});
|
|
153
184
|
}
|
|
154
185
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* the collection's
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
186
|
+
* Best-effort declaration of a PRIVATE collection's blinded-index attributes.
|
|
187
|
+
* Unlike the public `indexes` PUT, the schema is collection state stored in
|
|
188
|
+
* the collection's own ENCRYPTED metadata (a compare-and-swap write through
|
|
189
|
+
* `Collection.declareIndex`), so every recipient discovers what is queryable
|
|
190
|
+
* without out-of-band coordination and the server never sees the attribute
|
|
191
|
+
* names. Only the attributes not already in the persisted schema are
|
|
192
|
+
* declared, so a returning session issues no writes at all. Non-fatal like
|
|
193
|
+
* the descriptor PUTs: returns the outcome rather than throwing. Skipped
|
|
194
|
+
* (reported `ok` + `skipped`) for a public collection or one that declares no
|
|
195
|
+
* indexes.
|
|
196
|
+
*
|
|
197
|
+
* A collection whose descriptor carries no `hmac` member is reported NOT ok
|
|
198
|
+
* (rather than skipped): the blinding key is installed with the collection's
|
|
199
|
+
* first key epoch or never, so this is a provisioning gap the caller should
|
|
200
|
+
* warn about -- the declarations cannot be made and queries on the collection
|
|
201
|
+
* will keep failing.
|
|
202
|
+
*
|
|
203
|
+
* Declarations are prospective: a document written before its attribute was
|
|
204
|
+
* declared carries no blinded entry for it and is not findable until it is
|
|
205
|
+
* rewritten.
|
|
206
|
+
*
|
|
207
|
+
* @param collectionId {string} the WAS collection id
|
|
208
|
+
* @param options {object}
|
|
209
|
+
* @param options.encryption {CollectionEncryption | undefined} the
|
|
210
|
+
* already-read descriptor the caller fetched from the collection
|
|
211
|
+
* description; `undefined` means the collection carries none
|
|
212
|
+
* @returns {Promise<DeclarationResult>}
|
|
213
|
+
*/
|
|
214
|
+
async declareBlindedIndexes(collectionId, { encryption }) {
|
|
215
|
+
const config = this.#configById.get(collectionId);
|
|
216
|
+
if (config?.visibility !== 'private' ||
|
|
217
|
+
!config.indexes ||
|
|
218
|
+
config.indexes.length === 0) {
|
|
219
|
+
return { collectionId, ok: true, skipped: true };
|
|
220
|
+
}
|
|
221
|
+
if (!encryption?.hmac) {
|
|
222
|
+
return {
|
|
223
|
+
collectionId,
|
|
224
|
+
ok: false,
|
|
225
|
+
error: 'the collection was provisioned without a blinded-index key (the ' +
|
|
226
|
+
'key is installed with the first key epoch or never)'
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const capability = this.collectionCapability(collectionId);
|
|
230
|
+
if (!capability) {
|
|
231
|
+
return { collectionId, ok: false, error: 'no capability' };
|
|
232
|
+
}
|
|
233
|
+
if (!this.#hasIdentityKeys) {
|
|
234
|
+
return { collectionId, ok: false, error: 'no identity keys' };
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
const collection = this.was
|
|
238
|
+
.space(this.spaceId)
|
|
239
|
+
.collection(collectionId, { capability });
|
|
240
|
+
const persisted = await collection.indexes();
|
|
241
|
+
const already = new Set(persisted.map(declaration => Array.isArray(declaration.attribute)
|
|
242
|
+
? declaration.attribute.join(',')
|
|
243
|
+
: declaration.attribute));
|
|
244
|
+
for (const name of config.indexes) {
|
|
245
|
+
const attribute = blindedAttribute(name);
|
|
246
|
+
if (!already.has(attribute)) {
|
|
247
|
+
await collection.declareIndex({ attribute });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return { collectionId, ok: true };
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
const status = errorStatus(err);
|
|
254
|
+
const message = errorMessage(err);
|
|
255
|
+
return {
|
|
256
|
+
collectionId,
|
|
257
|
+
ok: false,
|
|
258
|
+
...(status !== undefined && { status }),
|
|
259
|
+
error: message
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Runs one equality query against a registered collection, routing on its
|
|
265
|
+
* visibility and answering the same `{ documents, hasMore, cursor? }` page
|
|
266
|
+
* either way. Values are string equality only, and multiple `equals`
|
|
267
|
+
* attributes AND together.
|
|
268
|
+
*
|
|
269
|
+
* - A PUBLIC (plaintext) collection uses the canonical GET
|
|
270
|
+
* `filter[attr]=value` form of the server's `equality` profile, invoked
|
|
271
|
+
* with the collection's delegated zcap (an anonymous reader would issue the
|
|
272
|
+
* same URL unsigned against a `PublicCanRead` collection). Filter
|
|
273
|
+
* attributes are emitted in sorted order so identical queries produce
|
|
274
|
+
* identical URLs (cache-friendly).
|
|
275
|
+
* - A PRIVATE (encrypted) collection uses the `blinded-index` query profile:
|
|
276
|
+
* each attribute name and value is blinded client-side with the
|
|
277
|
+
* collection's blinding key before it leaves the browser, the server
|
|
278
|
+
* matches opaque tokens, and the returned envelopes are decrypted here.
|
|
279
|
+
* Attribute names are rooted at the EDV document's `content`, which for a
|
|
280
|
+
* JSON payload IS the stored payload verbatim, so the configured `author`
|
|
281
|
+
* is queried as `content.author`.
|
|
282
|
+
*
|
|
283
|
+
* Fails closed before any network round trip on a collection the registry
|
|
284
|
+
* does not know, an empty term set, an attribute missing from the
|
|
285
|
+
* collection's declared `indexes`, an uncovered collection, and -- on the
|
|
286
|
+
* private path -- a store built without this app's identity keys.
|
|
287
|
+
*
|
|
288
|
+
* Standing limitation of the private path: documents written through the
|
|
289
|
+
* local-first sync path do not yet carry blinded index entries, so a blinded
|
|
290
|
+
* query finds only documents written through an index-aware cipher. They
|
|
291
|
+
* become findable once they are rewritten that way.
|
|
164
292
|
*
|
|
165
293
|
* @param options {object}
|
|
166
294
|
* @param options.collectionId {string} the WAS collection id
|
|
@@ -173,10 +301,9 @@ export class WasRemoteStore {
|
|
|
173
301
|
*/
|
|
174
302
|
async queryCollectionByEquality({ collectionId, equals, limit, cursor }) {
|
|
175
303
|
const config = this.#configById.get(collectionId);
|
|
176
|
-
if (config
|
|
177
|
-
throw new Error(`Equality queries require a
|
|
178
|
-
`
|
|
179
|
-
`blinded-index query path is not yet supported).`);
|
|
304
|
+
if (!config) {
|
|
305
|
+
throw new Error(`Equality queries require a registered collection; "${collectionId}" ` +
|
|
306
|
+
`is not in the collection registry.`);
|
|
180
307
|
}
|
|
181
308
|
const attributes = Object.keys(equals);
|
|
182
309
|
if (attributes.length === 0) {
|
|
@@ -193,6 +320,15 @@ export class WasRemoteStore {
|
|
|
193
320
|
if (!capability) {
|
|
194
321
|
throw new Error(`No delegated capability covers collection "${collectionId}".`);
|
|
195
322
|
}
|
|
323
|
+
if (config.visibility === 'private') {
|
|
324
|
+
return await this.#queryBlinded({
|
|
325
|
+
collectionId,
|
|
326
|
+
capability,
|
|
327
|
+
equals,
|
|
328
|
+
...(limit !== undefined && { limit }),
|
|
329
|
+
...(cursor !== undefined && { cursor })
|
|
330
|
+
});
|
|
331
|
+
}
|
|
196
332
|
// Canonical query string: sorted filter attributes first, then the
|
|
197
333
|
// reserved pagination params. Literal brackets around a percent-encoded
|
|
198
334
|
// attribute name; the server decodes either spelling identically.
|
|
@@ -261,6 +397,45 @@ export class WasRemoteStore {
|
|
|
261
397
|
path: resourcePath(this.spaceId, collectionId, id)
|
|
262
398
|
});
|
|
263
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* The private-collection half of {@link queryCollectionByEquality}: the
|
|
402
|
+
* `blinded-index` query profile through the collection handle, whose codec
|
|
403
|
+
* blinds the terms before the request and decrypts the returned envelopes.
|
|
404
|
+
* The caller has already run every guard.
|
|
405
|
+
*/
|
|
406
|
+
async #queryBlinded({ collectionId, capability, equals, limit, cursor }) {
|
|
407
|
+
if (!this.#hasIdentityKeys) {
|
|
408
|
+
throw new Error(`Equality queries on the encrypted collection "${collectionId}" ` +
|
|
409
|
+
`require a wallet-connected session with this app's identity keys ` +
|
|
410
|
+
`(the terms are blinded, and the results decrypted, with the ` +
|
|
411
|
+
`collection's own keys).`);
|
|
412
|
+
}
|
|
413
|
+
const page = await this.was
|
|
414
|
+
.space(this.spaceId)
|
|
415
|
+
.collection(collectionId, { capability })
|
|
416
|
+
.find({
|
|
417
|
+
equals: Object.fromEntries(Object.entries(equals).map(([name, value]) => [
|
|
418
|
+
blindedAttribute(name),
|
|
419
|
+
value
|
|
420
|
+
])),
|
|
421
|
+
...(limit !== undefined && { limit }),
|
|
422
|
+
...(cursor !== undefined && { cursor })
|
|
423
|
+
});
|
|
424
|
+
if (!('items' in page)) {
|
|
425
|
+
throw new Error(`Malformed equality query response for "${collectionId}": expected a ` +
|
|
426
|
+
`page of items.`);
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
// A blob resource decrypts to a `Blob`, which is not a payload the entity
|
|
430
|
+
// layer can use; its id still reports the match, with `data` omitted.
|
|
431
|
+
documents: page.items.map(({ id, data }) => ({
|
|
432
|
+
id,
|
|
433
|
+
...(!(data instanceof Blob) && { data })
|
|
434
|
+
})),
|
|
435
|
+
hasMore: page.hasMore,
|
|
436
|
+
...(typeof page.cursor === 'string' && { cursor: page.cursor })
|
|
437
|
+
};
|
|
438
|
+
}
|
|
264
439
|
/**
|
|
265
440
|
* The shared best-effort collection-description PUT behind the encryption
|
|
266
441
|
* descriptor and the indexes declaration: invokes the collection's delegated RW
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasRemoteStore.js","sourceRoot":"","sources":["../../src/storage/wasRemoteStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wasRemoteStore.js","sourceRoot":"","sources":["../../src/storage/wasRemoteStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Shared WAS replication bootstrap: given a parsed grant set and the invoking
|
|
6
6
|
* ZcapClient, builds the delegated {@link WasRemoteStore}, best-effort marks
|
|
7
|
-
* each private collection encrypted and declares each
|
|
8
|
-
*
|
|
7
|
+
* each private collection encrypted and declares each collection's equality
|
|
8
|
+
* indexes -- in the collection description for a public one, in the
|
|
9
|
+
* collection's encrypted metadata (as blinded-index attributes) for a private
|
|
10
|
+
* one -- and starts the supplied {@link SyncController} with
|
|
9
11
|
* reactive store patching. The caller injects the opened localStore, the
|
|
10
12
|
* controller, and the per-doc `onRemoteChange` patcher (typically wired to the
|
|
11
13
|
* rehydrate mechanism over the app's store registry) rather than this module
|
|
@@ -24,7 +26,7 @@ import type { RxChangeEvent } from 'rxdb/plugins/core';
|
|
|
24
26
|
import type { CollectionEncryption } from '@interop/was-client';
|
|
25
27
|
import type { IKeyAgreementKey, IKeyResolver } from '@interop/data-integrity-core';
|
|
26
28
|
import { type SyncedDoc } from '../sync/index.js';
|
|
27
|
-
import type
|
|
29
|
+
import { type SharedCollectionConfig, type WasCollectionConfig } from '../config.js';
|
|
28
30
|
import type { ParsedGrants } from '../grants.js';
|
|
29
31
|
import { WasRemoteStore } from './wasRemoteStore.js';
|
|
30
32
|
import { SharedCollectionReader } from './sharedCollectionReader.js';
|
|
@@ -46,7 +48,8 @@ export interface WasSyncBootstrap {
|
|
|
46
48
|
* cipher). Used at login time, BEFORE any replication exists: the connected
|
|
47
49
|
* replica must open epoch-aware -- epoch-from-birth leaves no single-key
|
|
48
50
|
* fallback, and the adoption merge writes into it before sync starts. The
|
|
49
|
-
* sync bootstrap
|
|
51
|
+
* sync bootstrap reuses these reads (its `knownDescriptors` input) rather
|
|
52
|
+
* than re-issuing them.
|
|
50
53
|
*
|
|
51
54
|
* @param options {object}
|
|
52
55
|
* @param options.parsed {ParsedGrants}
|
|
@@ -77,7 +80,8 @@ export declare function readRemoteDescriptors({ parsed, zcapClient, collections
|
|
|
77
80
|
* (read-only, wallet-owned) registry; never replicated, never written to
|
|
78
81
|
* @param [options.identityKeys] {object} this app's IDENTITY key-agreement key
|
|
79
82
|
* and its resolver, the recipient identity a wallet writes into a shared
|
|
80
|
-
* collection's epoch roster. Required to open any shared-collection reader
|
|
83
|
+
* collection's epoch roster. Required to open any shared-collection reader,
|
|
84
|
+
* and to run the codec-driven blinded-index verbs on a private collection
|
|
81
85
|
* @param [options.identityKeys.keyAgreementKey] {IKeyAgreementKey}
|
|
82
86
|
* @param [options.identityKeys.keyResolver] {IKeyResolver}
|
|
83
87
|
* @param [options.onAuthError] {() => void} fired when replication hits a
|
|
@@ -85,9 +89,15 @@ export declare function readRemoteDescriptors({ parsed, zcapClient, collections
|
|
|
85
89
|
* @param [options.onDescriptorsFetched] {(descriptors) => void | Promise<void>} given
|
|
86
90
|
* the freshly fetched per-collection encryption descriptors (by WAS collection
|
|
87
91
|
* id), to refresh the offline descriptor cache
|
|
92
|
+
* @param [options.knownDescriptors] {Record<string, CollectionEncryption>}
|
|
93
|
+
* descriptors the caller ALREADY read live in this same bring-up (the
|
|
94
|
+
* login-time {@link readRemoteDescriptors} pass); the bootstrap reuses them
|
|
95
|
+
* instead of re-issuing the same GET seconds later. A hot restore passes
|
|
96
|
+
* none -- there the bootstrap read IS the freshness refresh over the
|
|
97
|
+
* offline cache
|
|
88
98
|
* @returns {Promise<WasSyncBootstrap>}
|
|
89
99
|
*/
|
|
90
|
-
export declare function startWasSync({ parsed, zcapClient, collections, localStore, syncController, onRemoteChange, sharedCollections, identityKeys, onAuthError, onDescriptorsFetched }: {
|
|
100
|
+
export declare function startWasSync({ parsed, zcapClient, collections, localStore, syncController, onRemoteChange, sharedCollections, identityKeys, onAuthError, onDescriptorsFetched, knownDescriptors }: {
|
|
91
101
|
parsed: ParsedGrants;
|
|
92
102
|
zcapClient: ZcapClient;
|
|
93
103
|
collections: WasCollectionConfig[];
|
|
@@ -101,5 +111,6 @@ export declare function startWasSync({ parsed, zcapClient, collections, localSto
|
|
|
101
111
|
};
|
|
102
112
|
onAuthError?: () => void;
|
|
103
113
|
onDescriptorsFetched?: (descriptors: Record<string, CollectionEncryption>) => void | Promise<void>;
|
|
114
|
+
knownDescriptors?: Record<string, CollectionEncryption>;
|
|
104
115
|
}): Promise<WasSyncBootstrap>;
|
|
105
116
|
//# sourceMappingURL=wasSync.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasSync.d.ts","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH
|
|
1
|
+
{"version":3,"file":"wasSync.d.ts","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EACV,gBAAgB,EAChB,YAAY,EACb,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC/D,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACzB,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,cAAc,EAA0B,MAAM,qBAAqB,CAAA;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEzD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,cAAc,CAAA;IAC3B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAA;CAC1D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,MAAM,EACN,UAAU,EACV,WAAW,EACZ,EAAE;IACD,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,UAAU,CAAA;IACtB,WAAW,EAAE,mBAAmB,EAAE,CAAA;CACnC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAoBhD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,YAAY,CAAC,EACjC,MAAM,EACN,UAAU,EACV,WAAW,EACX,UAAU,EACV,cAAc,EACd,cAAc,EACd,iBAAsB,EACtB,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,gBAAqB,EACtB,EAAE;IACD,MAAM,EAAE,YAAY,CAAA;IACpB,UAAU,EAAE,UAAU,CAAA;IACtB,WAAW,EAAE,mBAAmB,EAAE,CAAA;IAClC,UAAU,EAAE,UAAU,CAAA;IACtB,cAAc,EAAE,cAAc,CAAA;IAC9B,cAAc,EAAE,CACd,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,KAC5B,IAAI,CAAA;IACT,iBAAiB,CAAC,EAAE,sBAAsB,EAAE,CAAA;IAC5C,YAAY,CAAC,EAAE;QACb,eAAe,EAAE,gBAAgB,CAAA;QACjC,WAAW,EAAE,YAAY,CAAA;KAC1B,CAAA;IACD,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,oBAAoB,CAAC,EAAE,CACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,KAC9C,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAA;CACxD,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgJ5B"}
|
package/dist/storage/wasSync.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { hasKeyEpochs } from '../sync/index.js';
|
|
2
|
+
import { isPublicCollection } from '../config.js';
|
|
2
3
|
import { WasRemoteStore, remoteDescriptorSource } from './wasRemoteStore.js';
|
|
3
4
|
import { SharedCollectionReader } from './sharedCollectionReader.js';
|
|
4
5
|
/**
|
|
@@ -8,7 +9,8 @@ import { SharedCollectionReader } from './sharedCollectionReader.js';
|
|
|
8
9
|
* cipher). Used at login time, BEFORE any replication exists: the connected
|
|
9
10
|
* replica must open epoch-aware -- epoch-from-birth leaves no single-key
|
|
10
11
|
* fallback, and the adoption merge writes into it before sync starts. The
|
|
11
|
-
* sync bootstrap
|
|
12
|
+
* sync bootstrap reuses these reads (its `knownDescriptors` input) rather
|
|
13
|
+
* than re-issuing them.
|
|
12
14
|
*
|
|
13
15
|
* @param options {object}
|
|
14
16
|
* @param options.parsed {ParsedGrants}
|
|
@@ -24,8 +26,9 @@ export async function readRemoteDescriptors({ parsed, zcapClient, collections })
|
|
|
24
26
|
collections
|
|
25
27
|
});
|
|
26
28
|
const descriptors = {};
|
|
27
|
-
await Promise.all(collections.map(async (
|
|
28
|
-
|
|
29
|
+
await Promise.all(collections.map(async (collection) => {
|
|
30
|
+
const { id } = collection;
|
|
31
|
+
if (isPublicCollection(collection) || !parsed.byCollectionId[id]) {
|
|
29
32
|
return;
|
|
30
33
|
}
|
|
31
34
|
const encryption = await remoteStore.readCollectionEncryption(id);
|
|
@@ -52,7 +55,8 @@ export async function readRemoteDescriptors({ parsed, zcapClient, collections })
|
|
|
52
55
|
* (read-only, wallet-owned) registry; never replicated, never written to
|
|
53
56
|
* @param [options.identityKeys] {object} this app's IDENTITY key-agreement key
|
|
54
57
|
* and its resolver, the recipient identity a wallet writes into a shared
|
|
55
|
-
* collection's epoch roster. Required to open any shared-collection reader
|
|
58
|
+
* collection's epoch roster. Required to open any shared-collection reader,
|
|
59
|
+
* and to run the codec-driven blinded-index verbs on a private collection
|
|
56
60
|
* @param [options.identityKeys.keyAgreementKey] {IKeyAgreementKey}
|
|
57
61
|
* @param [options.identityKeys.keyResolver] {IKeyResolver}
|
|
58
62
|
* @param [options.onAuthError] {() => void} fired when replication hits a
|
|
@@ -60,13 +64,22 @@ export async function readRemoteDescriptors({ parsed, zcapClient, collections })
|
|
|
60
64
|
* @param [options.onDescriptorsFetched] {(descriptors) => void | Promise<void>} given
|
|
61
65
|
* the freshly fetched per-collection encryption descriptors (by WAS collection
|
|
62
66
|
* id), to refresh the offline descriptor cache
|
|
67
|
+
* @param [options.knownDescriptors] {Record<string, CollectionEncryption>}
|
|
68
|
+
* descriptors the caller ALREADY read live in this same bring-up (the
|
|
69
|
+
* login-time {@link readRemoteDescriptors} pass); the bootstrap reuses them
|
|
70
|
+
* instead of re-issuing the same GET seconds later. A hot restore passes
|
|
71
|
+
* none -- there the bootstrap read IS the freshness refresh over the
|
|
72
|
+
* offline cache
|
|
63
73
|
* @returns {Promise<WasSyncBootstrap>}
|
|
64
74
|
*/
|
|
65
|
-
export async function startWasSync({ parsed, zcapClient, collections, localStore, syncController, onRemoteChange, sharedCollections = [], identityKeys, onAuthError, onDescriptorsFetched }) {
|
|
75
|
+
export async function startWasSync({ parsed, zcapClient, collections, localStore, syncController, onRemoteChange, sharedCollections = [], identityKeys, onAuthError, onDescriptorsFetched, knownDescriptors = {} }) {
|
|
66
76
|
const remoteStore = WasRemoteStore.fromGrants({
|
|
67
77
|
parsed,
|
|
68
78
|
zcapClient,
|
|
69
|
-
collections
|
|
79
|
+
collections,
|
|
80
|
+
// The same identity keys that open a shared-collection reader also let the
|
|
81
|
+
// client's EDV keystore build a codec, which the blinded-index verbs need.
|
|
82
|
+
...(identityKeys && { keys: identityKeys })
|
|
70
83
|
});
|
|
71
84
|
// One pass per REGISTERED collection the grant set covers -- the registry is
|
|
72
85
|
// what this app declared, so a granted id it never registered is none of this
|
|
@@ -93,9 +106,14 @@ export async function startWasSync({ parsed, zcapClient, collections, localStore
|
|
|
93
106
|
// pointless 403.
|
|
94
107
|
const granted = collections.filter(collection => parsed.byCollectionId[collection.id] !== undefined);
|
|
95
108
|
const descriptors = {};
|
|
96
|
-
await Promise.all(granted.map(async (
|
|
97
|
-
|
|
98
|
-
|
|
109
|
+
await Promise.all(granted.map(async (collection) => {
|
|
110
|
+
const { id: collectionId } = collection;
|
|
111
|
+
if (!isPublicCollection(collection)) {
|
|
112
|
+
// A descriptor the login flow read seconds ago is reused as-is; only
|
|
113
|
+
// ids it did not cover (or a hot restore, which passes none) are read
|
|
114
|
+
// live here.
|
|
115
|
+
const encryption = knownDescriptors[collectionId] ??
|
|
116
|
+
(await remoteStore.readCollectionEncryption(collectionId));
|
|
99
117
|
// Only an epoch-bearing descriptor enters the offline cache or
|
|
100
118
|
// rebuilds a cipher; a collection without a key-epoch roster stays
|
|
101
119
|
// fail-closed, stated plainly here rather than surfacing later as
|
|
@@ -113,6 +131,18 @@ export async function startWasSync({ parsed, zcapClient, collections, localStore
|
|
|
113
131
|
if (!declared.ok) {
|
|
114
132
|
console.warn(`Encryption descriptor PUT not authorized for "${collectionId}" (status ${declared.status ?? 'n/a'}).`);
|
|
115
133
|
}
|
|
134
|
+
// The private-collection counterpart of the public `indexes` PUT: the
|
|
135
|
+
// blinded-index schema lives in the collection's own encrypted
|
|
136
|
+
// metadata, so it is written through the collection handle rather than
|
|
137
|
+
// the description. Non-fatal: an unqueryable collection is still a
|
|
138
|
+
// fully replicating one.
|
|
139
|
+
const blinded = await remoteStore.declareBlindedIndexes(collectionId, {
|
|
140
|
+
encryption
|
|
141
|
+
});
|
|
142
|
+
if (!blinded.ok) {
|
|
143
|
+
console.warn(`Blinded-index declaration failed for "${collectionId}": ` +
|
|
144
|
+
`${blinded.error ?? 'unknown error'} (status ${blinded.status ?? 'n/a'}).`);
|
|
145
|
+
}
|
|
116
146
|
}
|
|
117
147
|
const indexes = await remoteStore.declareCollectionIndexes(collectionId);
|
|
118
148
|
if (!indexes.ok) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasSync.js","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wasSync.js","sourceRoot":"","sources":["../../src/storage/wasSync.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interop/was-react",
|
|
3
3
|
"description": "React library for building 'Bring Your Own Everything' (BYOE) apps on Wallet Attached Storage: DID Auth login via CHAPI wallet, local-first encrypted storage, and background sync to a WAS server.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.15.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "pnpm run clear && tsc",
|
|
@@ -53,8 +53,8 @@
|
|
|
53
53
|
"@interop/security-document-loader": "^10.0.0",
|
|
54
54
|
"@interop/vc": "^11.0.6",
|
|
55
55
|
"@interop/verifier-core": "^3.5.4",
|
|
56
|
-
"@interop/wallet-core": "^0.
|
|
57
|
-
"@interop/was-client": "^0.
|
|
56
|
+
"@interop/wallet-core": "^0.35.0",
|
|
57
|
+
"@interop/was-client": "^0.35.1",
|
|
58
58
|
"@interop/webkms-client": "^14.7.1",
|
|
59
59
|
"@scure/base": "^2.2.0",
|
|
60
60
|
"byoe-context": "^0.3.0",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"typescript-eslint": "^8.59.4",
|
|
111
111
|
"vite": "^8.0.14",
|
|
112
112
|
"vitest": "^4.1.7",
|
|
113
|
-
"was-teaching-server": "^0.
|
|
113
|
+
"was-teaching-server": "^0.21.0",
|
|
114
114
|
"zustand": "^5.0.12"
|
|
115
115
|
},
|
|
116
116
|
"publishConfig": {
|