OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-note commits / 5935f3b

Fix cross-instance blob retrieval: store coreKey in blobId, index by filename
Each instance's blob core is unique. To retrieve blobs uploaded by another instance, the blobId must include the source core key. getBlob now opens a remote Hyperblobs session when the coreKey differs from local. Also adds media-file: index for filename-based blob lookup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5935f3b31073e40c27d78713dc6bf2b6ca571e85
by plankatron <fleeky@gmail.com> · 2026-03-16T21:26:35.000Z

Changed files (3) +31 -10

lib/multiwriter/notebook-apply.js +9 -0
……@@ -90,6 +90,15 @@ export async function notebookApply (batch, view, base) {
9090 author: op.author,
9191 addedAt: op.timestamp
9292 })
93 // Also index by filename for blob lookup
94 if (op.filename) {
95 await b.put(`media-file:${op.filename}`, {
96 hash: op.hash,
97 noteId: op.noteId || null,
98 blobId: op.blobId || null,
99 mimeType: op.mimeType
100 })
101 }
93102 break
94103 }
95104
lib/multiwriter/shared-notebook.js +14 -5
……@@ -244,17 +244,26 @@ export class SharedNotebook {
244244 }
245245
246246 async storeBlob (buffer) {
247 return this._blobs.put(buffer)
247 const id = await this._blobs.put(buffer)
248 // Include the blob core key so other instances can find this data
249 id.coreKey = this._blobCore.key.toString('hex')
250 return id
248251 }
249252
250253 async getBlob (id) {
254 // If blob is on a remote core, open a Hyperblobs session for it
255 if (id.coreKey) {
256 const keyBuf = typeof id.coreKey === 'string' ? Buffer.from(id.coreKey, 'hex') : id.coreKey
257 if (!this._blobCore.key.equals(keyBuf)) {
258 const remoteCore = this._store.get(keyBuf)
259 await remoteCore.ready()
260 const remoteBlobs = new Hyperblobs(remoteCore)
261 return remoteBlobs.get(id)
262 }
263 }
251264 return this._blobs.get(id)
252265 }
253266
254 get blobCoreKey () {
255 return this._blobCore.key
256 }
257
258267 async removeMedia (noteId, hash, author) {
259268 await this._base.append(encodeOp({
260269 type: 'remove-media',
lib/routes/notebooks.js +8 -5
……@@ -329,11 +329,14 @@ export function registerNotebookRoutes (router, server) {
329329 if (!buf) {
330330 const snb = server.noteManager._getSharedNotebook(req.params.notebook)
331331 if (snb) {
332 const media = await snb.getMedia(null, req.params.filename)
333 if (media && media.blobId) {
334 try {
335 buf = await snb.getBlob(media.blobId)
336 } catch {}
332 try {
333 await snb.update()
334 const entry = await snb.view.get('media-file:' + req.params.filename)
335 if (entry && entry.value && entry.value.blobId) {
336 buf = await snb.getBlob(entry.value.blobId)
337 }
338 } catch (err) {
339 console.error('[Media Serve] Blob fallback error:', err.message)
337340 }
338341 }
339342 }