@cero-base/cero 1.7.0 → 1.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cero-base/cero",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "The ideal p2p API — everything is a handle, handles contain refs, refs contain rows.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -95,7 +95,7 @@
95
95
  "test:node": "ls test/*.test.js | xargs -P1 -n1 brittle-node"
96
96
  },
97
97
  "dependencies": {
98
- "@cero-base/core": "^1.7.0",
98
+ "@cero-base/core": "^1.7.1",
99
99
  "b4a": "^1.8.1",
100
100
  "bare-abort-controller": "^1.1.2",
101
101
  "bare-crypto": "^1.15.3",
@@ -14,6 +14,7 @@ import { Pairing } from '@cero-base/core/pairing'
14
14
  import { toId, grants, addWriterPayload } from '@cero-base/core/utils'
15
15
  import { CeroError } from '@cero-base/core/errors'
16
16
  import { Blobs } from '@cero-base/core/blobs'
17
+ import { decodeId } from '@cero-base/core/blobs/codec'
17
18
  import { FileServer } from '@cero-base/core/blobs/server'
18
19
 
19
20
  import { NS, TIMEOUT } from '../lib/constants.js'
@@ -339,6 +340,50 @@ export class Handle extends ReadyResource {
339
340
  return blobs
340
341
  }
341
342
 
343
+ /**
344
+ * Remember the blob-core key a file id points at so the file server can
345
+ * open the core. Lives on the Handle (not the shared operators) because the
346
+ * epoch-key derivation pulls native crypto — the RPC client must stay
347
+ * bundleable without it.
348
+ *
349
+ * @param {string} id
350
+ * @param {number} [stamp]
351
+ */
352
+ _registerBlobCore(id, stamp) {
353
+ if (!id || !this.root?._coreKeys) return
354
+ try {
355
+ const { coreKey } = decodeId(id)
356
+ const hex = b4a.toString(coreKey, 'hex')
357
+ if (!this.root._coreKeys.has(hex)) {
358
+ // a file-field value carries no stamp — look it up from the files row
359
+ // (fire-and-forget: idempotent, resolution happens again per request)
360
+ if (stamp === undefined) {
361
+ this.store
362
+ .get('files', id)
363
+ .then(({ data }) => data && this._registerBlobCore(id, data.stamp || 0))
364
+ .catch(safetyCatch)
365
+ return
366
+ }
367
+ const key = this._blobCoreKey(stamp)
368
+ if (!key) return // unknown epoch — this device is not entitled to the core
369
+ this.root._coreKeys.set(hex, key)
370
+ }
371
+ // remember which handle read it, so close prunes the entry (re-registered
372
+ // on the next read if another handle still serves the same core)
373
+ if (this !== this.root) (this._blobKeys ??= new Set()).add(hex)
374
+ } catch {
375
+ // ignore invalid ids
376
+ }
377
+ }
378
+
379
+ // Base-era blob cores use the OWNING handle's key (not the root's — rooms
380
+ // have their own keys); rotated-era cores derive from the epoch entropy.
381
+ _blobCoreKey(stamp) {
382
+ if (!stamp) return this.store.encryptionKey
383
+ const entropy = this.store.keyring.entropy(stamp)
384
+ return entropy ? blobEpochKey(entropy) : null
385
+ }
386
+
342
387
  /** Canonical id — identity id for the root handle, store key for children. */
343
388
  get id() {
344
389
  if (!this.parent) return this.identity.id
@@ -2,7 +2,6 @@ import { Readable } from 'streamx'
2
2
  import b4a from 'b4a'
3
3
 
4
4
  import { encodeId, decodeId } from '@cero-base/core/blobs/codec'
5
- import { blobEpochKey } from '@cero-base/core/database/encryption'
6
5
  import { CeroError } from '@cero-base/core/errors'
7
6
  import { onAbort } from './utils.js'
8
7
 
@@ -169,7 +168,7 @@ function resolveRow(ref, row) {
169
168
  if (ref.handle.rpc) return ref.handle._resolveRow(ref.name, ref.handle._refInfo(ref.name), row)
170
169
  const handle = ref.handle
171
170
  const resolve = (id, name, stamp) => {
172
- registerBlobCore(handle, id, stamp)
171
+ handle._registerBlobCore(id, stamp)
173
172
  return resolveFile(handle, id, name)
174
173
  }
175
174
  if (ref.name === 'files') {
@@ -186,41 +185,6 @@ function resolveRow(ref, row) {
186
185
  return out
187
186
  }
188
187
 
189
- function registerBlobCore(handle, id, stamp) {
190
- if (!id || !handle.root?._coreKeys) return
191
- try {
192
- const { coreKey } = decodeId(id)
193
- const hex = b4a.toString(coreKey, 'hex')
194
- if (!handle.root._coreKeys.has(hex)) {
195
- // a file-field value carries no stamp — look it up from the files row
196
- // (fire-and-forget: idempotent, resolution happens again per request)
197
- if (stamp === undefined) {
198
- handle.store
199
- .get('files', id)
200
- .then(({ data }) => data && registerBlobCore(handle, id, data.stamp || 0))
201
- .catch(() => {})
202
- return
203
- }
204
- const key = blobCoreKey(handle, stamp)
205
- if (!key) return // unknown epoch — this device is not entitled to the core
206
- handle.root._coreKeys.set(hex, key)
207
- }
208
- // remember which handle read it, so close prunes the entry (re-registered
209
- // on the next read if another handle still serves the same core)
210
- if (handle !== handle.root) (handle._blobKeys ??= new Set()).add(hex)
211
- } catch {
212
- // ignore invalid ids
213
- }
214
- }
215
-
216
- // Base-era blob cores use the OWNING handle's key (not the root's — rooms have
217
- // their own keys); rotated-era cores derive from the epoch entropy.
218
- function blobCoreKey(handle, stamp) {
219
- if (!stamp) return handle.store.encryptionKey
220
- const entropy = handle.store.keyring.entropy(stamp)
221
- return entropy ? blobEpochKey(entropy) : null
222
- }
223
-
224
188
  /**
225
189
  * Read from `ref`. For data refs, dispatches to the underlying store. For
226
190
  * `handle`-kind refs, lists existing child handles of that type from the
@@ -172,6 +172,17 @@ export class Handle extends ReadyResource {
172
172
  get blobs(): Blobs;
173
173
  _baseBlobs(): Blobs;
174
174
  _makeBlobs(name: any, encryptionKey: any, stamp: any): Blobs;
175
+ /**
176
+ * Remember the blob-core key a file id points at so the file server can
177
+ * open the core. Lives on the Handle (not the shared operators) because the
178
+ * epoch-key derivation pulls native crypto — the RPC client must stay
179
+ * bundleable without it.
180
+ *
181
+ * @param {string} id
182
+ * @param {number} [stamp]
183
+ */
184
+ _registerBlobCore(id: string, stamp?: number): void;
185
+ _blobCoreKey(stamp: any): Uint8Array<ArrayBufferLike>;
175
186
  /** Canonical id — identity id for the root handle, store key for children. */
176
187
  get id(): any;
177
188
  /** This device's id + name. `null` on child handles. */