@noy-db/hub 0.4.0 → 0.5.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/dist/api-M7REIE7H.js +20 -0
- package/dist/attestation/index.js +16 -16
- package/dist/{backup-X7F3TCFV.js → backup-ENTZYW37.js} +9 -9
- package/dist/blobs/index.js +11 -11
- package/dist/broker/index.js +10 -10
- package/dist/cargo/index.js +26 -26
- package/dist/{chunk-6ZNSB3H6.js → chunk-3LYMOSY7.js} +2 -2
- package/dist/{chunk-LXRTP3AQ.js → chunk-4DGBZEV6.js} +2 -2
- package/dist/{chunk-LRRX57UA.js → chunk-4MSNAB64.js} +4 -4
- package/dist/{chunk-MXUT7FTE.js → chunk-4SG5TOXK.js} +2 -2
- package/dist/{chunk-W66AM5IB.js → chunk-4TXO442D.js} +2 -2
- package/dist/{chunk-UADPR6F4.js → chunk-5EKL6BXI.js} +3 -3
- package/dist/{chunk-ZBYFMI6D.js → chunk-5UKUOMO6.js} +247 -136
- package/dist/chunk-5UKUOMO6.js.map +1 -0
- package/dist/{chunk-SU6SGWXM.js → chunk-623QEHDB.js} +6 -6
- package/dist/{chunk-IVYQAVWO.js → chunk-653KOQ7A.js} +7 -7
- package/dist/{chunk-2BET5QTS.js → chunk-6PFST3FC.js} +4 -4
- package/dist/{chunk-PWPYO73B.js → chunk-6RDTG6E5.js} +5 -5
- package/dist/{chunk-DAFBJS4Y.js → chunk-7P4LL3JH.js} +3 -3
- package/dist/{chunk-S7GIXBXV.js → chunk-AJAOXFCF.js} +8 -8
- package/dist/chunk-AJAOXFCF.js.map +1 -0
- package/dist/{chunk-L3YYLT4E.js → chunk-ALUVACKT.js} +2 -2
- package/dist/{chunk-QZWTRENR.js → chunk-AR5D76T2.js} +5 -1
- package/dist/chunk-AR5D76T2.js.map +1 -0
- package/dist/{chunk-5DRROK5W.js → chunk-BFG7LD6Z.js} +2 -2
- package/dist/{chunk-6UBZ6I5Z.js → chunk-DDB626I7.js} +2 -2
- package/dist/{chunk-W3G5REIU.js → chunk-EAN4TAPC.js} +2 -2
- package/dist/{chunk-4JQK3L4V.js → chunk-EBETHILL.js} +2 -2
- package/dist/{chunk-467SWYT5.js → chunk-ECGDJU5R.js} +3 -3
- package/dist/{chunk-WYUAEZSC.js → chunk-ELJOLQIG.js} +14 -13
- package/dist/chunk-ELJOLQIG.js.map +1 -0
- package/dist/{chunk-ZFG5EBI3.js → chunk-F2WWHKO7.js} +5 -5
- package/dist/{chunk-NJ4DYYO5.js → chunk-F5UWVF6Z.js} +2 -2
- package/dist/{chunk-Q36YXK77.js → chunk-FHK6WWUE.js} +2 -2
- package/dist/{chunk-SHEEBRZ4.js → chunk-FT5Y4VSP.js} +2 -2
- package/dist/{chunk-JQ2AX4T5.js → chunk-FUECZSRM.js} +2 -2
- package/dist/{chunk-Q5YJCVJY.js → chunk-FZAP4JNC.js} +4 -4
- package/dist/{chunk-SFBYQMZM.js → chunk-G3ORJ3IQ.js} +4 -4
- package/dist/{chunk-HLKXPJ4V.js → chunk-G7ZIMGPQ.js} +5 -5
- package/dist/{chunk-QJNCB5SK.js → chunk-GIGLOQL7.js} +2 -2
- package/dist/{chunk-ERGECZEI.js → chunk-GTQF32TP.js} +5 -5
- package/dist/{chunk-JYJFGD2H.js → chunk-GYJRDALN.js} +2 -2
- package/dist/{chunk-HZZP2ZIG.js → chunk-HGDXJVK2.js} +4 -4
- package/dist/{chunk-ZNWSG7XC.js → chunk-IHYSIPEK.js} +6 -6
- package/dist/{chunk-5GQEU4DN.js → chunk-IJV56FWY.js} +2 -2
- package/dist/{chunk-5UQ7Y6SQ.js → chunk-IM2KST7E.js} +3 -3
- package/dist/{chunk-7H3WWH6J.js → chunk-IQ423YMK.js} +3 -3
- package/dist/{chunk-P4JO3H6R.js → chunk-J7YVY7TW.js} +2 -2
- package/dist/{chunk-YGK2HMLR.js → chunk-JLBCK7XM.js} +8 -8
- package/dist/chunk-JLBCK7XM.js.map +1 -0
- package/dist/{chunk-2J6UEN2V.js → chunk-K5OFKXUG.js} +2 -2
- package/dist/{chunk-JEI6JJNI.js → chunk-KDWBVCIX.js} +5 -5
- package/dist/{chunk-T2MOJF76.js → chunk-KJNMTE2W.js} +6 -6
- package/dist/{chunk-NFZGZ4AK.js → chunk-L67H6FAQ.js} +2 -2
- package/dist/{chunk-RBTVFPHF.js → chunk-LQVRK2XJ.js} +4 -4
- package/dist/{chunk-MHMLXJ3I.js → chunk-LXRVTJ5D.js} +2 -2
- package/dist/{chunk-LLYIMSQQ.js → chunk-MDRCEA56.js} +10 -10
- package/dist/{chunk-KRCBZYTZ.js → chunk-MFW466PX.js} +4 -4
- package/dist/{chunk-RO5NM6UI.js → chunk-MGXUAID4.js} +2 -2
- package/dist/{chunk-OWHP3H5C.js → chunk-MPW6AHTC.js} +3 -3
- package/dist/{chunk-35NMVJ7I.js → chunk-MYSNNMJ3.js} +3 -3
- package/dist/{chunk-QGIBI6GL.js → chunk-NG4AQIYJ.js} +4 -4
- package/dist/{chunk-BLBAQRBM.js → chunk-NIKQQXI2.js} +2 -2
- package/dist/{chunk-55LX6FSX.js → chunk-NJHQE4LT.js} +2 -2
- package/dist/{chunk-TS5XLBZR.js → chunk-NMYH55MW.js} +4 -4
- package/dist/{chunk-GTYFIVIZ.js → chunk-NWK6SVBM.js} +5 -5
- package/dist/chunk-NWK6SVBM.js.map +1 -0
- package/dist/{chunk-IFOH7RBI.js → chunk-NZEGQWQ5.js} +4 -4
- package/dist/{chunk-DTFA5DO4.js → chunk-ODCEOSJF.js} +2 -2
- package/dist/{chunk-NYEFP7DY.js → chunk-OKBNFUUX.js} +2 -2
- package/dist/{chunk-SGDPRCC4.js → chunk-OKRXXFZZ.js} +3 -3
- package/dist/{chunk-IG6KUWBX.js → chunk-P6UIVLZK.js} +2 -2
- package/dist/{chunk-V6UQZA2H.js → chunk-PKBONLCP.js} +2 -2
- package/dist/{chunk-MSMJJB4J.js → chunk-PTSVGKSR.js} +2 -2
- package/dist/{chunk-IXNCZTWA.js → chunk-PWEMRXPO.js} +2 -2
- package/dist/{chunk-PTHKZWEH.js → chunk-PZRYKOKP.js} +2 -2
- package/dist/{chunk-KD4HGBJV.js → chunk-Q6MXHDSE.js} +3 -3
- package/dist/{chunk-RQ4ZI4ZI.js → chunk-QITP2JGX.js} +5 -5
- package/dist/{chunk-WBKZUO5V.js → chunk-QMCWYN6O.js} +3 -3
- package/dist/{chunk-4ZKNC6A6.js → chunk-QQL6XPPG.js} +2 -2
- package/dist/{chunk-TG634KCO.js → chunk-QTMZDMJ6.js} +2 -2
- package/dist/{chunk-AT2JJEKJ.js → chunk-RM4FIXY2.js} +4 -4
- package/dist/{chunk-AKKRXT23.js → chunk-RPWMDIGK.js} +2 -2
- package/dist/{chunk-NBXFCQX4.js → chunk-RQILGK3N.js} +8 -8
- package/dist/chunk-RQILGK3N.js.map +1 -0
- package/dist/{chunk-NM5UYF6Q.js → chunk-SBGXQK5I.js} +2 -2
- package/dist/{chunk-SF2ZIRD6.js → chunk-SL3PLML2.js} +3 -3
- package/dist/{chunk-3ECU57YH.js → chunk-TJOUKMWF.js} +8 -8
- package/dist/{chunk-QD46HPZG.js → chunk-U2ID5A2I.js} +2 -2
- package/dist/{chunk-X4MVWPHV.js → chunk-UC5Y7OEJ.js} +3 -3
- package/dist/{chunk-SBHN6QUT.js → chunk-US2AR4BI.js} +2 -2
- package/dist/{chunk-DQZXPGGZ.js → chunk-V3EGXDFN.js} +3 -3
- package/dist/{chunk-ZHDSUZQU.js → chunk-V4O6YZSP.js} +3 -3
- package/dist/{chunk-3HUYRN24.js → chunk-W7C7MMIS.js} +3 -3
- package/dist/{chunk-NLYALJMY.js → chunk-WFYKIDCS.js} +30 -11
- package/dist/chunk-WFYKIDCS.js.map +1 -0
- package/dist/{chunk-TWPK7KUJ.js → chunk-XID22NOA.js} +5 -5
- package/dist/{chunk-E23UGFVC.js → chunk-XVK7TULT.js} +2 -2
- package/dist/{chunk-WUHEP3HH.js → chunk-XX7YB5BI.js} +4 -4
- package/dist/{chunk-ZTLOXFPB.js → chunk-Y2PTKKIN.js} +2 -2
- package/dist/{chunk-KYRDJI5O.js → chunk-YA7Y3EZP.js} +10 -10
- package/dist/classified/index.js +4 -4
- package/dist/{classified-marker-LE54HWSX.js → classified-marker-ZALWKOBC.js} +3 -3
- package/dist/{collection-facade-DO2AABZT.js → collection-facade-KI53O6QB.js} +15 -15
- package/dist/{computed-AB45PS4V.js → computed-YOQCGUCS.js} +3 -3
- package/dist/consent/index.js +7 -7
- package/dist/cover/index.js +2 -2
- package/dist/custody/index.js +9 -9
- package/dist/{dead-filter-V3LVWYEJ.js → dead-filter-6DVWST6U.js} +2 -2
- package/dist/debug/index.js +1 -1
- package/dist/{delegation-JDUMAF6L.js → delegation-WMO3Q3O5.js} +8 -8
- package/dist/derivations/index.js +10 -10
- package/dist/{derive-ZPGVLE2Q.js → derive-P2NXWUNB.js} +8 -8
- package/dist/directory/index.js +8 -8
- package/dist/{dispatch-W7NWQ637.js → dispatch-TKV4UCKB.js} +16 -16
- package/dist/{dispatch-6TV3DGO6.js → dispatch-VNR3TLBX.js} +5 -5
- package/dist/{enclave-YFCLPIDR.js → enclave-YWP3QAIQ.js} +7 -7
- package/dist/executor-M4ZZNX6S.js +28 -0
- package/dist/executor-TI64S4LE.js +9 -0
- package/dist/executor-YBK3I2OT.js +9 -0
- package/dist/export-accessible-GBLYLGW4.js +23 -0
- package/dist/extract-partition-SFV35U2R.js +38 -0
- package/dist/{fanout-sidecar-273RDMYS.js → fanout-sidecar-WYXO6QQT.js} +7 -7
- package/dist/find-HUSSZRBR.js +11 -0
- package/dist/forget/index.js +7 -7
- package/dist/guards/index.js +5 -5
- package/dist/history/index.js +10 -10
- package/dist/i18n/index.js +12 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.js +86 -84
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +2 -2
- package/dist/introspection/index.js +9 -9
- package/dist/issue-6TIJYWKS.js +19 -0
- package/dist/kernel/collection-config.d.ts +4 -0
- package/dist/kernel/collection.d.ts +24 -6
- package/dist/kernel/errors.d.ts +15 -0
- package/dist/kernel/put-many-atomic.d.ts +45 -0
- package/dist/kernel/vault.d.ts +14 -0
- package/dist/{ledger-XE7JF2I2.js → ledger-BLYDYRTQ.js} +9 -9
- package/dist/liberate-TSOW7IG5.js +24 -0
- package/dist/{link-set-CGYC3OH5.js → link-set-7F5ZPDEB.js} +9 -9
- package/dist/materialized-views/index.js +22 -22
- package/dist/money/index.js +3 -3
- package/dist/noydb-KFN6M6UJ.js +81 -0
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +11 -11
- package/dist/pod/index.js +9 -9
- package/dist/policy/index.js +2 -2
- package/dist/port/with/write-hooks.d.ts +6 -0
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-LY35EDTJ.js → post-register-PQSY544J.js} +6 -6
- package/dist/query/index.js +3 -3
- package/dist/reduce/index.js +4 -4
- package/dist/{register-6RIBIT3I.js → register-S3ECUZPV.js} +10 -10
- package/dist/registry-FZOHKUME.js +18 -0
- package/dist/registry-RISV7IDZ.js +19 -0
- package/dist/registry-ZE35A7WC.js +9 -0
- package/dist/{request-withdrawal-FT3ORAWN.js → request-withdrawal-KXVTQKR5.js} +12 -12
- package/dist/{reveal-T34ADTPY.js → reveal-HY7DZST4.js} +4 -4
- package/dist/{revoke-HC7WG3HE.js → revoke-J473JJ22.js} +9 -9
- package/dist/satellites/index.js +1 -1
- package/dist/schema-update/index.js +2 -2
- package/dist/sealed-record/index.js +10 -10
- package/dist/search/index.js +4 -4
- package/dist/{seed-JOV54PB3.js → seed-ALU3BHFS.js} +13 -13
- package/dist/seed-ALU3BHFS.js.map +1 -0
- package/dist/sequence/index.js +7 -7
- package/dist/session/index.js +7 -7
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-CTWOH6KT.js → signer-XLUKCCH3.js} +8 -8
- package/dist/snapshots/index.js +8 -8
- package/dist/{stale-T3V7XEOB.js → stale-U3WO5MYN.js} +8 -8
- package/dist/{storage-LLNLPWWB.js → storage-IQONEOYY.js} +8 -8
- package/dist/sync/index.js +8 -8
- package/dist/team/index.js +11 -11
- package/dist/tiers/index.js +8 -8
- package/dist/to/index.js +1 -1
- package/dist/transactions/index.js +3 -3
- package/dist/util/index.js +1 -1
- package/dist/{verify-7XWHJWUN.js → verify-JEIXSK5U.js} +5 -5
- package/dist/walk-BF36V4SR.js +21 -0
- package/dist/with-commit/tx/atomic-eligibility.d.ts +13 -11
- package/dist/with-sync/engine.d.ts +13 -0
- package/dist/{withdraw-accessible-ANSVG6XG.js → withdraw-accessible-TYTA23XH.js} +11 -11
- package/package.json +3 -3
- package/dist/api-SI2IVBQO.js +0 -20
- package/dist/chunk-GTYFIVIZ.js.map +0 -1
- package/dist/chunk-NBXFCQX4.js.map +0 -1
- package/dist/chunk-NLYALJMY.js.map +0 -1
- package/dist/chunk-QZWTRENR.js.map +0 -1
- package/dist/chunk-S7GIXBXV.js.map +0 -1
- package/dist/chunk-WYUAEZSC.js.map +0 -1
- package/dist/chunk-YGK2HMLR.js.map +0 -1
- package/dist/chunk-ZBYFMI6D.js.map +0 -1
- package/dist/executor-FPHKWDWT.js +0 -28
- package/dist/executor-IEBLTBCW.js +0 -9
- package/dist/executor-RM7XWQDU.js +0 -9
- package/dist/export-accessible-EKM3HGB2.js +0 -23
- package/dist/extract-partition-LS4ET5A6.js +0 -38
- package/dist/find-OZMTRIK3.js +0 -11
- package/dist/issue-LIA7FXBJ.js +0 -19
- package/dist/liberate-WZ3RHVKW.js +0 -24
- package/dist/noydb-ZLPVCDES.js +0 -81
- package/dist/registry-7Y5WF7GS.js +0 -9
- package/dist/registry-MUSL4AW6.js +0 -19
- package/dist/registry-RON4WHAP.js +0 -18
- package/dist/seed-JOV54PB3.js.map +0 -1
- package/dist/walk-G3NFEP5L.js +0 -21
- /package/dist/{api-SI2IVBQO.js.map → api-M7REIE7H.js.map} +0 -0
- /package/dist/{backup-X7F3TCFV.js.map → backup-ENTZYW37.js.map} +0 -0
- /package/dist/{chunk-6ZNSB3H6.js.map → chunk-3LYMOSY7.js.map} +0 -0
- /package/dist/{chunk-LXRTP3AQ.js.map → chunk-4DGBZEV6.js.map} +0 -0
- /package/dist/{chunk-LRRX57UA.js.map → chunk-4MSNAB64.js.map} +0 -0
- /package/dist/{chunk-MXUT7FTE.js.map → chunk-4SG5TOXK.js.map} +0 -0
- /package/dist/{chunk-W66AM5IB.js.map → chunk-4TXO442D.js.map} +0 -0
- /package/dist/{chunk-UADPR6F4.js.map → chunk-5EKL6BXI.js.map} +0 -0
- /package/dist/{chunk-SU6SGWXM.js.map → chunk-623QEHDB.js.map} +0 -0
- /package/dist/{chunk-IVYQAVWO.js.map → chunk-653KOQ7A.js.map} +0 -0
- /package/dist/{chunk-2BET5QTS.js.map → chunk-6PFST3FC.js.map} +0 -0
- /package/dist/{chunk-PWPYO73B.js.map → chunk-6RDTG6E5.js.map} +0 -0
- /package/dist/{chunk-DAFBJS4Y.js.map → chunk-7P4LL3JH.js.map} +0 -0
- /package/dist/{chunk-L3YYLT4E.js.map → chunk-ALUVACKT.js.map} +0 -0
- /package/dist/{chunk-5DRROK5W.js.map → chunk-BFG7LD6Z.js.map} +0 -0
- /package/dist/{chunk-6UBZ6I5Z.js.map → chunk-DDB626I7.js.map} +0 -0
- /package/dist/{chunk-W3G5REIU.js.map → chunk-EAN4TAPC.js.map} +0 -0
- /package/dist/{chunk-4JQK3L4V.js.map → chunk-EBETHILL.js.map} +0 -0
- /package/dist/{chunk-467SWYT5.js.map → chunk-ECGDJU5R.js.map} +0 -0
- /package/dist/{chunk-ZFG5EBI3.js.map → chunk-F2WWHKO7.js.map} +0 -0
- /package/dist/{chunk-NJ4DYYO5.js.map → chunk-F5UWVF6Z.js.map} +0 -0
- /package/dist/{chunk-Q36YXK77.js.map → chunk-FHK6WWUE.js.map} +0 -0
- /package/dist/{chunk-SHEEBRZ4.js.map → chunk-FT5Y4VSP.js.map} +0 -0
- /package/dist/{chunk-JQ2AX4T5.js.map → chunk-FUECZSRM.js.map} +0 -0
- /package/dist/{chunk-Q5YJCVJY.js.map → chunk-FZAP4JNC.js.map} +0 -0
- /package/dist/{chunk-SFBYQMZM.js.map → chunk-G3ORJ3IQ.js.map} +0 -0
- /package/dist/{chunk-HLKXPJ4V.js.map → chunk-G7ZIMGPQ.js.map} +0 -0
- /package/dist/{chunk-QJNCB5SK.js.map → chunk-GIGLOQL7.js.map} +0 -0
- /package/dist/{chunk-ERGECZEI.js.map → chunk-GTQF32TP.js.map} +0 -0
- /package/dist/{chunk-JYJFGD2H.js.map → chunk-GYJRDALN.js.map} +0 -0
- /package/dist/{chunk-HZZP2ZIG.js.map → chunk-HGDXJVK2.js.map} +0 -0
- /package/dist/{chunk-ZNWSG7XC.js.map → chunk-IHYSIPEK.js.map} +0 -0
- /package/dist/{chunk-5GQEU4DN.js.map → chunk-IJV56FWY.js.map} +0 -0
- /package/dist/{chunk-5UQ7Y6SQ.js.map → chunk-IM2KST7E.js.map} +0 -0
- /package/dist/{chunk-7H3WWH6J.js.map → chunk-IQ423YMK.js.map} +0 -0
- /package/dist/{chunk-P4JO3H6R.js.map → chunk-J7YVY7TW.js.map} +0 -0
- /package/dist/{chunk-2J6UEN2V.js.map → chunk-K5OFKXUG.js.map} +0 -0
- /package/dist/{chunk-JEI6JJNI.js.map → chunk-KDWBVCIX.js.map} +0 -0
- /package/dist/{chunk-T2MOJF76.js.map → chunk-KJNMTE2W.js.map} +0 -0
- /package/dist/{chunk-NFZGZ4AK.js.map → chunk-L67H6FAQ.js.map} +0 -0
- /package/dist/{chunk-RBTVFPHF.js.map → chunk-LQVRK2XJ.js.map} +0 -0
- /package/dist/{chunk-MHMLXJ3I.js.map → chunk-LXRVTJ5D.js.map} +0 -0
- /package/dist/{chunk-LLYIMSQQ.js.map → chunk-MDRCEA56.js.map} +0 -0
- /package/dist/{chunk-KRCBZYTZ.js.map → chunk-MFW466PX.js.map} +0 -0
- /package/dist/{chunk-RO5NM6UI.js.map → chunk-MGXUAID4.js.map} +0 -0
- /package/dist/{chunk-OWHP3H5C.js.map → chunk-MPW6AHTC.js.map} +0 -0
- /package/dist/{chunk-35NMVJ7I.js.map → chunk-MYSNNMJ3.js.map} +0 -0
- /package/dist/{chunk-QGIBI6GL.js.map → chunk-NG4AQIYJ.js.map} +0 -0
- /package/dist/{chunk-BLBAQRBM.js.map → chunk-NIKQQXI2.js.map} +0 -0
- /package/dist/{chunk-55LX6FSX.js.map → chunk-NJHQE4LT.js.map} +0 -0
- /package/dist/{chunk-TS5XLBZR.js.map → chunk-NMYH55MW.js.map} +0 -0
- /package/dist/{chunk-IFOH7RBI.js.map → chunk-NZEGQWQ5.js.map} +0 -0
- /package/dist/{chunk-DTFA5DO4.js.map → chunk-ODCEOSJF.js.map} +0 -0
- /package/dist/{chunk-NYEFP7DY.js.map → chunk-OKBNFUUX.js.map} +0 -0
- /package/dist/{chunk-SGDPRCC4.js.map → chunk-OKRXXFZZ.js.map} +0 -0
- /package/dist/{chunk-IG6KUWBX.js.map → chunk-P6UIVLZK.js.map} +0 -0
- /package/dist/{chunk-V6UQZA2H.js.map → chunk-PKBONLCP.js.map} +0 -0
- /package/dist/{chunk-MSMJJB4J.js.map → chunk-PTSVGKSR.js.map} +0 -0
- /package/dist/{chunk-IXNCZTWA.js.map → chunk-PWEMRXPO.js.map} +0 -0
- /package/dist/{chunk-PTHKZWEH.js.map → chunk-PZRYKOKP.js.map} +0 -0
- /package/dist/{chunk-KD4HGBJV.js.map → chunk-Q6MXHDSE.js.map} +0 -0
- /package/dist/{chunk-RQ4ZI4ZI.js.map → chunk-QITP2JGX.js.map} +0 -0
- /package/dist/{chunk-WBKZUO5V.js.map → chunk-QMCWYN6O.js.map} +0 -0
- /package/dist/{chunk-4ZKNC6A6.js.map → chunk-QQL6XPPG.js.map} +0 -0
- /package/dist/{chunk-TG634KCO.js.map → chunk-QTMZDMJ6.js.map} +0 -0
- /package/dist/{chunk-AT2JJEKJ.js.map → chunk-RM4FIXY2.js.map} +0 -0
- /package/dist/{chunk-AKKRXT23.js.map → chunk-RPWMDIGK.js.map} +0 -0
- /package/dist/{chunk-NM5UYF6Q.js.map → chunk-SBGXQK5I.js.map} +0 -0
- /package/dist/{chunk-SF2ZIRD6.js.map → chunk-SL3PLML2.js.map} +0 -0
- /package/dist/{chunk-3ECU57YH.js.map → chunk-TJOUKMWF.js.map} +0 -0
- /package/dist/{chunk-QD46HPZG.js.map → chunk-U2ID5A2I.js.map} +0 -0
- /package/dist/{chunk-X4MVWPHV.js.map → chunk-UC5Y7OEJ.js.map} +0 -0
- /package/dist/{chunk-SBHN6QUT.js.map → chunk-US2AR4BI.js.map} +0 -0
- /package/dist/{chunk-DQZXPGGZ.js.map → chunk-V3EGXDFN.js.map} +0 -0
- /package/dist/{chunk-ZHDSUZQU.js.map → chunk-V4O6YZSP.js.map} +0 -0
- /package/dist/{chunk-3HUYRN24.js.map → chunk-W7C7MMIS.js.map} +0 -0
- /package/dist/{chunk-TWPK7KUJ.js.map → chunk-XID22NOA.js.map} +0 -0
- /package/dist/{chunk-E23UGFVC.js.map → chunk-XVK7TULT.js.map} +0 -0
- /package/dist/{chunk-WUHEP3HH.js.map → chunk-XX7YB5BI.js.map} +0 -0
- /package/dist/{chunk-ZTLOXFPB.js.map → chunk-Y2PTKKIN.js.map} +0 -0
- /package/dist/{chunk-KYRDJI5O.js.map → chunk-YA7Y3EZP.js.map} +0 -0
- /package/dist/{classified-marker-LE54HWSX.js.map → classified-marker-ZALWKOBC.js.map} +0 -0
- /package/dist/{collection-facade-DO2AABZT.js.map → collection-facade-KI53O6QB.js.map} +0 -0
- /package/dist/{computed-AB45PS4V.js.map → computed-YOQCGUCS.js.map} +0 -0
- /package/dist/{dead-filter-V3LVWYEJ.js.map → dead-filter-6DVWST6U.js.map} +0 -0
- /package/dist/{delegation-JDUMAF6L.js.map → delegation-WMO3Q3O5.js.map} +0 -0
- /package/dist/{derive-ZPGVLE2Q.js.map → derive-P2NXWUNB.js.map} +0 -0
- /package/dist/{dispatch-W7NWQ637.js.map → dispatch-TKV4UCKB.js.map} +0 -0
- /package/dist/{dispatch-6TV3DGO6.js.map → dispatch-VNR3TLBX.js.map} +0 -0
- /package/dist/{enclave-YFCLPIDR.js.map → enclave-YWP3QAIQ.js.map} +0 -0
- /package/dist/{executor-FPHKWDWT.js.map → executor-M4ZZNX6S.js.map} +0 -0
- /package/dist/{executor-IEBLTBCW.js.map → executor-TI64S4LE.js.map} +0 -0
- /package/dist/{executor-RM7XWQDU.js.map → executor-YBK3I2OT.js.map} +0 -0
- /package/dist/{export-accessible-EKM3HGB2.js.map → export-accessible-GBLYLGW4.js.map} +0 -0
- /package/dist/{extract-partition-LS4ET5A6.js.map → extract-partition-SFV35U2R.js.map} +0 -0
- /package/dist/{fanout-sidecar-273RDMYS.js.map → fanout-sidecar-WYXO6QQT.js.map} +0 -0
- /package/dist/{find-OZMTRIK3.js.map → find-HUSSZRBR.js.map} +0 -0
- /package/dist/{issue-LIA7FXBJ.js.map → issue-6TIJYWKS.js.map} +0 -0
- /package/dist/{ledger-XE7JF2I2.js.map → ledger-BLYDYRTQ.js.map} +0 -0
- /package/dist/{liberate-WZ3RHVKW.js.map → liberate-TSOW7IG5.js.map} +0 -0
- /package/dist/{link-set-CGYC3OH5.js.map → link-set-7F5ZPDEB.js.map} +0 -0
- /package/dist/{noydb-ZLPVCDES.js.map → noydb-KFN6M6UJ.js.map} +0 -0
- /package/dist/{post-register-LY35EDTJ.js.map → post-register-PQSY544J.js.map} +0 -0
- /package/dist/{register-6RIBIT3I.js.map → register-S3ECUZPV.js.map} +0 -0
- /package/dist/{registry-7Y5WF7GS.js.map → registry-FZOHKUME.js.map} +0 -0
- /package/dist/{registry-MUSL4AW6.js.map → registry-RISV7IDZ.js.map} +0 -0
- /package/dist/{registry-RON4WHAP.js.map → registry-ZE35A7WC.js.map} +0 -0
- /package/dist/{request-withdrawal-FT3ORAWN.js.map → request-withdrawal-KXVTQKR5.js.map} +0 -0
- /package/dist/{reveal-T34ADTPY.js.map → reveal-HY7DZST4.js.map} +0 -0
- /package/dist/{revoke-HC7WG3HE.js.map → revoke-J473JJ22.js.map} +0 -0
- /package/dist/{signer-CTWOH6KT.js.map → signer-XLUKCCH3.js.map} +0 -0
- /package/dist/{stale-T3V7XEOB.js.map → stale-U3WO5MYN.js.map} +0 -0
- /package/dist/{storage-LLNLPWWB.js.map → storage-IQONEOYY.js.map} +0 -0
- /package/dist/{verify-7XWHJWUN.js.map → verify-JEIXSK5U.js.map} +0 -0
- /package/dist/{walk-G3NFEP5L.js.map → walk-BF36V4SR.js.map} +0 -0
- /package/dist/{withdraw-accessible-ANSVG6XG.js.map → withdraw-accessible-TYTA23XH.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-shape/blobs/blob-pinning.ts","../src/with-shape/blobs/mime-magic.ts","../src/with-shape/blobs/blob-intent.ts","../src/with-shape/blobs/blob-set.ts"],"sourcesContent":["/**\n * #808 — device-local blob pin registry + external side-cache + KPI counters.\n *\n * Pinning marks a blob slot \"keep this available offline\" **for this device\n * only**. Pin state is intentionally NEVER written to the vault's\n * `NoydbStore` — the vault store is the synced/shared substrate, and each\n * device pins for itself. Instead, `withBlobs()` owns one {@link BlobPinCache}\n * per strategy instance and threads it into every `BlobSet` it opens; the\n * registry rows live in a pluggable {@link BlobPinStore}.\n *\n * The default backend is in-memory ({@link memoryBlobPinStore}) — the hub has\n * NO device-persistence idiom of its own (it never touches IndexedDB or any\n * Node fs API; persistence backends are the `to-*` package family), so a\n * durable registry is supplied by the consumer:\n * `withBlobs({ pinStore: myIdbBackedStore })`. The interface is four async\n * methods, deliberately trivial to implement over IndexedDB / SQLite / a file.\n *\n * ## What a registry row holds\n *\n * One row per `{vault, collection, record, slot}` ({@link blobPinKey}):\n * - the pin flag + timestamps (LRU input for the cache-budget pass), and\n * - for `external: true` slots, the **local encrypted copy** of the object's\n * bytes ({@link BlobPinEntry.cipher}). The object-store copy of an external\n * blob lives OUTSIDE the zero-knowledge envelope by design; the local copy\n * does NOT — it is AES-256-GCM-encrypted under the vault's `_blob` DEK\n * through the same enclave path that encrypts blob chunks\n * (`encryptBytesWithAAD`, AAD-bound to the row's pin key), so a stolen\n * device store leaks nothing the vault store wouldn't.\n *\n * Internal (chunked) blobs never store bytes here — their ciphertext already\n * lives in `_blob_chunks`; the registry only carries their pin flag and\n * access times.\n *\n * @module\n */\n\n/** One device-local registry row — see the module doc for the field posture. */\nexport interface BlobPinEntry {\n /** Slot is pinned for offline on THIS device. */\n readonly pinned: boolean\n /** ISO timestamp of the pin (absent when not pinned). */\n readonly pinnedAt?: string\n /** ISO timestamp of the last local read — LRU input for the budget pass. */\n readonly lastAccessAt?: string\n /** Plaintext byte length of the cached external copy (external slots only). */\n readonly cachedBytes?: number\n /**\n * Encrypted local copy of an external slot's bytes: base64 IV + base64\n * AES-256-GCM ciphertext under the vault's `_blob` DEK, AAD-bound to the\n * row's {@link blobPinKey}. On an UNENCRYPTED vault (`encrypt: false`) the\n * fallback mirrors the envelope convention: `iv === ''`, `data` = base64\n * plaintext.\n */\n readonly cipher?: { readonly iv: string; readonly data: string }\n}\n\n/**\n * Pluggable device-local backend for the pin registry (#808). Implementations\n * MUST be device-local (never synced) — an IndexedDB- or file-backed store is\n * the expected production shape; {@link memoryBlobPinStore} is the default.\n */\nexport interface BlobPinStore {\n get(key: string): Promise<BlobPinEntry | null>\n set(key: string, entry: BlobPinEntry): Promise<void>\n delete(key: string): Promise<void>\n /** All rows whose key starts with `prefix` (`''` = every row). */\n entries(prefix: string): Promise<Array<{ key: string; entry: BlobPinEntry }>>\n}\n\n/**\n * In-memory {@link BlobPinStore} — the default. Pin state then lasts for the\n * strategy instance's lifetime (one app session); pass a durable store to\n * `withBlobs({ pinStore })` to keep pins across restarts.\n */\nexport function memoryBlobPinStore(): BlobPinStore {\n const rows = new Map<string, BlobPinEntry>()\n return {\n async get(key) {\n return rows.get(key) ?? null\n },\n async set(key, entry) {\n rows.set(key, entry)\n },\n async delete(key) {\n rows.delete(key)\n },\n async entries(prefix) {\n const out: Array<{ key: string; entry: BlobPinEntry }> = []\n for (const [key, entry] of rows) {\n if (key.startsWith(prefix)) out.push({ key, entry })\n }\n return out\n },\n }\n}\n\n/**\n * Device-local blob-cache KPI counters (#808) — read via\n * `withBlobs().cacheStats()`. \"Local\" reads (internal chunks, cached external\n * copies) count as hits; network fetches from the object store count as\n * misses and add to `bytesDownloaded` (the 4G-budget number).\n */\nexport interface BlobCacheStats {\n readonly hits: number\n readonly misses: number\n readonly bytesDownloaded: number\n}\n\n/**\n * The per-strategy bundle `withBlobs()` threads into every `BlobSet`: the\n * registry backend plus the shared mutable KPI counters.\n *\n * @internal\n */\nexport interface BlobPinCache {\n readonly store: BlobPinStore\n readonly stats: { hits: number; misses: number; bytesDownloaded: number }\n}\n\n/** @internal */\nexport function createBlobPinCache(store?: BlobPinStore): BlobPinCache {\n return {\n store: store ?? memoryBlobPinStore(),\n stats: { hits: 0, misses: 0, bytesDownloaded: 0 },\n }\n}\n\n/**\n * Registry key for one slot's row: `{vault}::{collection}::{record}::{slot}`\n * (the blobs' established `::` separator — record ids and slot names are\n * already `::`-refused at the write surface, see `assertKeyPartSafe`).\n * Omitting `slotName` yields the record's row PREFIX (for per-record scans).\n */\nexport function blobPinKey(vault: string, collection: string, recordId: string, slotName?: string): string {\n const prefix = `${vault}::${collection}::${recordId}::`\n return slotName === undefined ? prefix : prefix + slotName\n}\n","/**\n * Lightweight MIME type detection from magic bytes (file signatures).\n *\n * Designed for the blob store's auto-detection feature. Operates on the first 16 bytes of\n * plaintext — no filesystem access, no filename guessing.\n *\n * ## Detection strategies\n *\n * 1. **Prefix match** — magic bytes at offset 0 (most formats).\n * 2. **Offset match** — magic bytes at a fixed offset > 0 (ISOBMFF: offset 4).\n * 3. **Compound match** — two separate byte sequences at different offsets\n * (RIFF-based: bytes 0-3 + bytes 8-11).\n *\n * ## Formats excluded (require offset > 16 bytes)\n *\n * - TAR (`ustar` at offset 257)\n * - ISO 9660 (`CD001` at offset 32769)\n *\n * @module\n */\n\n// ─── Types ───────────────────────────────────────────────────────────────\n\ninterface MagicRule {\n /** IANA MIME type (or widely-used x- type). */\n readonly mime: string\n /** Human-readable format name for diagnostics. */\n readonly format: string\n /** Magic bytes to match, as a Uint8Array. */\n readonly bytes: Uint8Array\n /** Byte offset where the magic starts. Default 0. */\n readonly offset?: number\n /**\n * For compound checks (RIFF, FORM): a second byte sequence that must\n * also match at `secondaryOffset`.\n */\n readonly secondaryBytes?: Uint8Array\n /** Offset of the secondary match. */\n readonly secondaryOffset?: number\n /** If true, the format is already compressed — skip gzip in blob.put(). */\n readonly preCompressed?: true\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────\n\n/** Convert a hex string like `'FF D8 FF'` to Uint8Array. */\nfunction hex(s: string): Uint8Array {\n return new Uint8Array(s.split(' ').map((b) => parseInt(b, 16)))\n}\n\n// ─── Magic rules ─────────────────────────────────────────────────────────\n//\n// Ordered by detection priority: more specific (longer) signatures first\n// within the same offset group, so that e.g. RAR v5 (8 bytes) is tested\n// before RAR v4 (7 bytes).\n//\n// Sources verified against:\n// - Gary Kessler's File Signatures Table\n// - Wikipedia \"List of file signatures\"\n// - IANA MIME type registry\n// - Individual format specifications (PNG RFC 2083, PDF ISO 32000, etc.)\n//\n// Each entry includes the original CSV row number for traceability.\n\nconst MAGIC_RULES: readonly MagicRule[] = [\n // ── Images ───────────────────────────────────────────────────────────\n\n // #2 PNG — full 8-byte signature (RFC 2083)\n { mime: 'image/png', format: 'PNG', bytes: hex('89 50 4E 47 0D 0A 1A 0A'), preCompressed: true },\n\n // #1 JPEG — FF D8 FF (third byte is start of APP marker, always FF)\n { mime: 'image/jpeg', format: 'JPEG', bytes: hex('FF D8 FF'), preCompressed: true },\n\n // #7 WebP — RIFF compound: bytes 0-3 = RIFF, bytes 8-11 = WEBP\n {\n mime: 'image/webp',\n format: 'WebP',\n bytes: hex('52 49 46 46'),\n secondaryBytes: hex('57 45 42 50'),\n secondaryOffset: 8,\n preCompressed: true,\n },\n\n // #5 TIFF (little-endian) — II + version 42\n { mime: 'image/tiff', format: 'TIFF', bytes: hex('49 49 2A 00') },\n\n // #6 TIFF (big-endian) — MM + version 42\n { mime: 'image/tiff', format: 'TIFF', bytes: hex('4D 4D 00 2A') },\n\n // #3 GIF — GIF8 (covers GIF87a and GIF89a)\n { mime: 'image/gif', format: 'GIF', bytes: hex('47 49 46 38'), preCompressed: true },\n\n // #4 BMP — BM\n { mime: 'image/bmp', format: 'BMP', bytes: hex('42 4D') },\n\n // PSD — 8BPS\n { mime: 'image/vnd.adobe.photoshop', format: 'PSD', bytes: hex('38 42 50 53') },\n\n // #8 ICO — 00 00 01 00 (note: 00 00 02 00 is CUR cursor format)\n { mime: 'image/x-icon', format: 'ICO', bytes: hex('00 00 01 00') },\n\n // #9 HEIC — ISOBMFF: ftyp at offset 4, brand \"heic\" at offset 8\n {\n mime: 'image/heic',\n format: 'HEIC',\n bytes: hex('66 74 79 70'),\n offset: 4,\n secondaryBytes: hex('68 65 69 63'),\n secondaryOffset: 8,\n preCompressed: true,\n },\n\n // ── Documents ────────────────────────────────────────────────────────\n\n // PDF — %PDF\n { mime: 'application/pdf', format: 'PDF', bytes: hex('25 50 44 46') },\n\n // RTF — {\\rtf\n { mime: 'application/rtf', format: 'RTF', bytes: hex('7B 5C 72 74 66') },\n\n // ── Archives & compression ───────────────────────────────────────────\n\n // RAR v5 — 8-byte signature (test before RAR v4)\n { mime: 'application/vnd.rar', format: 'RAR v5', bytes: hex('52 61 72 21 1A 07 01 00'), preCompressed: true },\n\n // RAR v4 — 7-byte signature\n { mime: 'application/vnd.rar', format: 'RAR v4', bytes: hex('52 61 72 21 1A 07 00'), preCompressed: true },\n\n // 7-Zip — 6-byte signature\n { mime: 'application/x-7z-compressed', format: '7Z', bytes: hex('37 7A BC AF 27 1C'), preCompressed: true },\n\n // XZ — 6-byte stream header\n { mime: 'application/x-xz', format: 'XZ', bytes: hex('FD 37 7A 58 5A 00'), preCompressed: true },\n\n // ZIP — PK\\x03\\x04 (local file header)\n { mime: 'application/zip', format: 'ZIP', bytes: hex('50 4B 03 04'), preCompressed: true },\n\n // GZIP — 1F 8B\n { mime: 'application/gzip', format: 'GZIP', bytes: hex('1F 8B'), preCompressed: true },\n\n // BZIP2 — BZh\n { mime: 'application/x-bzip2', format: 'BZIP2', bytes: hex('42 5A 68'), preCompressed: true },\n\n // LZIP — LZIP\n { mime: 'application/x-lzip', format: 'LZIP', bytes: hex('4C 5A 49 50'), preCompressed: true },\n\n // ── Audio ────────────────────────────────────────────────────────────\n\n // WAV — RIFF compound: bytes 0-3 = RIFF, bytes 8-11 = WAVE\n {\n mime: 'audio/wav',\n format: 'WAV',\n bytes: hex('52 49 46 46'),\n secondaryBytes: hex('57 41 56 45'),\n secondaryOffset: 8,\n },\n\n // AIFF — FORM compound: bytes 0-3 = FORM, bytes 8-11 = AIFF\n {\n mime: 'audio/aiff',\n format: 'AIFF',\n bytes: hex('46 4F 52 4D'),\n secondaryBytes: hex('41 49 46 46'),\n secondaryOffset: 8,\n },\n\n // FLAC — fLaC\n { mime: 'audio/flac', format: 'FLAC', bytes: hex('66 4C 61 43') },\n\n // OGG — OggS (container — may hold Vorbis, Opus, Theora, etc.)\n { mime: 'application/ogg', format: 'OGG', bytes: hex('4F 67 67 53') },\n\n // MIDI — MThd\n { mime: 'audio/midi', format: 'MIDI', bytes: hex('4D 54 68 64') },\n\n // MP3 (ID3-tagged) — ID3\n { mime: 'audio/mpeg', format: 'MP3', bytes: hex('49 44 33'), preCompressed: true },\n\n // ── Video ────────────────────────────────────────────────────────────\n\n // AVI — RIFF compound: bytes 0-3 = RIFF, bytes 8-11 = AVI\\x20\n {\n mime: 'video/x-msvideo',\n format: 'AVI',\n bytes: hex('52 49 46 46'),\n secondaryBytes: hex('41 56 49 20'),\n secondaryOffset: 8,\n preCompressed: true,\n },\n\n // WMV/ASF — 8-byte ASF header GUID prefix\n { mime: 'video/x-ms-wmv', format: 'WMV', bytes: hex('30 26 B2 75 8E 66 CF 11'), preCompressed: true },\n\n // MKV/WebM — EBML header (Matroska container)\n { mime: 'video/x-matroska', format: 'MKV', bytes: hex('1A 45 DF A3'), preCompressed: true },\n\n // FLV — FLV\n { mime: 'video/x-flv', format: 'FLV', bytes: hex('46 4C 56'), preCompressed: true },\n\n // MOV — ISOBMFF: ftyp at offset 4, brand \"qt \" at offset 8\n {\n mime: 'video/quicktime',\n format: 'MOV',\n bytes: hex('66 74 79 70'),\n offset: 4,\n secondaryBytes: hex('71 74 20 20'),\n secondaryOffset: 8,\n preCompressed: true,\n },\n\n // MP4 — ISOBMFF: ftyp at offset 4 (brands vary: isom, mp41, mp42, etc.)\n // Tested AFTER MOV and HEIC so their specific brands match first.\n { mime: 'video/mp4', format: 'MP4', bytes: hex('66 74 79 70'), offset: 4, preCompressed: true },\n\n // ── Executables & binaries ───────────────────────────────────────────\n\n // SQLite — \"SQLite 3\" (first 8 bytes of the 16-byte header)\n { mime: 'application/vnd.sqlite3', format: 'SQLite', bytes: hex('53 51 4C 69 74 65 20 33') },\n\n // WASM — \\0asm\n { mime: 'application/wasm', format: 'WASM', bytes: hex('00 61 73 6D') },\n\n // ELF — \\x7FELF\n { mime: 'application/x-elf', format: 'ELF', bytes: hex('7F 45 4C 46') },\n\n // PE (EXE/DLL) — MZ\n { mime: 'application/vnd.microsoft.portable-executable', format: 'PE', bytes: hex('4D 5A') },\n\n // Mach-O — all four single-arch variants\n { mime: 'application/x-mach-binary', format: 'Mach-O 64 LE', bytes: hex('CF FA ED FE') },\n { mime: 'application/x-mach-binary', format: 'Mach-O 64 BE', bytes: hex('FE ED FA CF') },\n { mime: 'application/x-mach-binary', format: 'Mach-O 32 LE', bytes: hex('CE FA ED FE') },\n { mime: 'application/x-mach-binary', format: 'Mach-O 32 BE', bytes: hex('FE ED FA CE') },\n\n // Java Class — CA FE BA BE\n // Note: collides with Mach-O Universal Binary. Disambiguated by checking\n // bytes 4-7: Java class version is >= 0x002D (45), while fat binary\n // arch count is a small number (typically 0x00000002).\n // We place Java after Mach-O single-arch entries so the more common\n // Mach-O variants match first. The CA FE BA BE collision between Java\n // and Mach-O fat binary is resolved by the caller if needed.\n { mime: 'application/java-vm', format: 'Java Class', bytes: hex('CA FE BA BE') },\n\n // DEX — dex\\n (Android Dalvik Executable)\n { mime: 'application/vnd.android.dex', format: 'DEX', bytes: hex('64 65 78 0A') },\n\n // ── Package formats ──────────────────────────────────────────────────\n\n // DEB — !<arch> (ar archive; DEB-specific member follows)\n { mime: 'application/vnd.debian.binary-package', format: 'DEB', bytes: hex('21 3C 61 72 63 68 3E') },\n\n // RPM — ED AB EE DB\n { mime: 'application/x-rpm', format: 'RPM', bytes: hex('ED AB EE DB') },\n\n // CAB — MSCF\n { mime: 'application/vnd.ms-cab-compressed', format: 'CAB', bytes: hex('4D 53 43 46'), preCompressed: true },\n\n // ── Capture & Flash ──────────────────────────────────────────────────\n\n // PCAP (little-endian) — D4 C3 B2 A1\n { mime: 'application/vnd.tcpdump.pcap', format: 'PCAP', bytes: hex('D4 C3 B2 A1') },\n\n // PCAP (big-endian) — A1 B2 C3 D4\n { mime: 'application/vnd.tcpdump.pcap', format: 'PCAP BE', bytes: hex('A1 B2 C3 D4') },\n\n // PCAPNG — Section Header Block\n { mime: 'application/x-pcapng', format: 'PCAPNG', bytes: hex('0A 0D 0D 0A') },\n\n // SWF — all three variants (uncompressed, zlib, LZMA)\n { mime: 'application/x-shockwave-flash', format: 'SWF', bytes: hex('46 57 53') },\n { mime: 'application/x-shockwave-flash', format: 'SWF zlib', bytes: hex('43 57 53'), preCompressed: true },\n { mime: 'application/x-shockwave-flash', format: 'SWF LZMA', bytes: hex('5A 57 53'), preCompressed: true },\n\n // ── Data formats ─────────────────────────────────────────────────────\n\n // Parquet — PAR1 (no registered IANA MIME; using Apache's informal type)\n { mime: 'application/vnd.apache.parquet', format: 'Parquet', bytes: hex('50 41 52 31') },\n\n // Avro Object Container — Obj\\x01\n { mime: 'application/avro', format: 'Avro', bytes: hex('4F 62 6A 01') },\n\n // NES ROM — NES\\x1A (iNES header)\n { mime: 'application/x-nintendo-nes-rom', format: 'NES ROM', bytes: hex('4E 45 53 1A') },\n] as const\n\n// ─── MP3 sync word ───────────────────────────────────────────────────────\n//\n// MP3 files without an ID3 tag start with a frame sync word where the top\n// 11 bits are set: 0xFFE0 mask. The ID3 signature (49 44 33) is handled\n// as a normal rule above. The sync-word check is a fallback tested in\n// `detectMimeType` after all rules.\n\nfunction isMp3SyncWord(byte0: number, byte1: number): boolean {\n return byte0 === 0xff && (byte1 & 0xe0) === 0xe0\n}\n\n// ─── Detection ───────────────────────────────────────────────────────────\n\n/**\n * Detect MIME type from the first bytes of a file.\n *\n * @param header - The first 16 bytes (or more) of the plaintext. Passing\n * fewer than 16 bytes may miss compound and offset-based matches.\n * @returns Detected MIME type, or `'application/octet-stream'` if unknown.\n */\nexport function detectMimeType(header: Uint8Array): string {\n const result = detectMagic(header)\n return result?.mime ?? 'application/octet-stream'\n}\n\n/**\n * Detect MIME type and whether the format is already compressed.\n *\n * Used by `BlobSet.put()` to decide whether to skip gzip compression.\n *\n * @param header - The first 16 bytes (or more) of the plaintext.\n * @returns `{ mime, preCompressed }` or `null` if no match.\n */\nexport function detectMagic(\n header: Uint8Array,\n): { mime: string; format: string; preCompressed: boolean } | null {\n for (const rule of MAGIC_RULES) {\n if (matchRule(header, rule)) {\n return {\n mime: rule.mime,\n format: rule.format,\n preCompressed: rule.preCompressed ?? false,\n }\n }\n }\n\n // Fallback: MP3 sync word (no ID3 tag)\n if (header.length >= 2 && isMp3SyncWord(header[0]!, header[1]!)) {\n return { mime: 'audio/mpeg', format: 'MP3', preCompressed: true }\n }\n\n return null\n}\n\n/**\n * Check whether a format is already compressed (should skip gzip).\n *\n * @param mimeType - A MIME type string.\n * @returns `true` if the format is known to be pre-compressed.\n */\nexport function isPreCompressed(mimeType: string): boolean {\n return PRE_COMPRESSED_MIMES.has(mimeType)\n}\n\n// ─── Internal matching ───────────────────────────────────────────────────\n\nfunction matchRule(header: Uint8Array, rule: MagicRule): boolean {\n const offset = rule.offset ?? 0\n const end = offset + rule.bytes.length\n\n // Not enough data for the primary match\n if (header.length < end) return false\n\n // Primary byte sequence\n for (let i = 0; i < rule.bytes.length; i++) {\n if (header[offset + i] !== rule.bytes[i]) return false\n }\n\n // Secondary byte sequence (compound check)\n if (rule.secondaryBytes && rule.secondaryOffset !== undefined) {\n const sEnd = rule.secondaryOffset + rule.secondaryBytes.length\n if (header.length < sEnd) return false\n for (let i = 0; i < rule.secondaryBytes.length; i++) {\n if (header[rule.secondaryOffset + i] !== rule.secondaryBytes[i]) return false\n }\n }\n\n return true\n}\n\n// ─── Pre-compressed MIME set ─────────────────────────────────────────────\n//\n// Built from the rules above. Used by `isPreCompressed()` for callers who\n// already know the MIME type (e.g. from a Content-Type header) and want to\n// skip the magic-byte detection step.\n\nconst PRE_COMPRESSED_MIMES = new Set<string>(\n MAGIC_RULES.filter((r) => r.preCompressed).map((r) => r.mime),\n)\n","/**\n * `_blob_intent` — the blob durability journal marker (#753, spec §7).\n *\n * One row per in-flight multi-step blob operation (`shred` | `rehome`),\n * written BEFORE the first destructive/movement step and deleted as the\n * operation's LAST step. An absent marker means \"no operation in flight\"\n * (the normal state, zero cost on every path that doesn't crash); a present\n * marker means the operation MUST be resumed before any other blob work on\n * that record proceeds.\n *\n * **Pattern honesty (C9):** this is the family's FIRST content-bearing\n * ENCRYPTED reserved marker. The prior reserved-collection precedent,\n * `_mv_stale` (`with-formula/materialized-views/stale.ts`), is\n * content-free plaintext (`{ _iv: '', _data: '{}' }` — the row's only\n * payload is its key). `_blob_intent` carries real operational metadata\n * (op, tiers, eTags + counts) and is encrypted, so its codec/CAS/backup/\n * sweep conventions below are new to the codebase, not a copy of an\n * existing pattern.\n *\n * **Key management (spec §4):** encrypted under the record's OWNING\n * collection's tier-0 DEK — `getDEK(collection)`, which resolves to\n * `dekKey(collection, 0) === collection` (see `with-party/team/tiers.ts`).\n * Every legitimate resumer (forget()-retry, blob write entry points,\n * tier ops) already holds this DEK; minting an elevated DEK for the\n * marker itself would make resume impossible for a tier-0-only resumer.\n * Documented residue: a tier-0 DEK holder can learn that an elevated\n * record's shred/rehome was in flight and which eTags it touched —\n * existence-adjacent, consistent with the already-documented\n * dedup-policy residue (#741).\n *\n * **Key grammar (C11):** `{collection}::{recordId}`. Blob-bearing record\n * ids already refuse `::` at the write surface (`blob-set.ts`'s\n * `assertKeyPartSafe`, #752) — re-validated here defensively so the\n * sweep's prefix-free key list stays unambiguous regardless of caller.\n *\n * Wired by `shredAllForRecord`/`forget()` (PR-1 Task 3, shred) and by\n * `BlobSet.syncTierMove`/`rehomeForTier`/`resolvePendingIntent` (PR-2 Task\n * 2, rehome — mint/resume/consume for `op:'rehome'` markers).\n */\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport { writeEnvelopeBody, openEnvelopeJson, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { isConflictError, BlobIntentPendingError, ValidationError } from '../../kernel/errors.js'\n\n/** Reserved collection holding `_blob_intent` marker rows. */\nexport const BLOB_INTENT_COLLECTION = '_blob_intent'\n\n/**\n * One captured refCount hold on an eTag, authoritative on resume (spec §2a,\n * amended by C5). `chunkCount` lets a resumed release complete chunk\n * cleanup even if the `_blob_index` row itself is already gone (mirrors\n * `releaseRef`'s `chunkCountHint` parameter, Task 1).\n */\nexport interface BlobIntentHold {\n readonly eTag: string\n readonly n: number\n readonly chunkCount: number\n}\n\n/**\n * The intent marker's decrypted payload (spec §2a, amended by C5: `holds`\n * entries carry `{ eTag, n, chunkCount }` rather than the original bare\n * `{ eTag, n }`).\n */\nexport interface BlobIntent {\n readonly op: 'shred' | 'rehome'\n /** Random nonce minted at marker creation (`crypto.getRandomValues`) — the op-stamp identity. */\n readonly opId: string\n // shred:\n /** Captured ONCE at marker creation; authoritative on every resume. */\n readonly holds?: readonly BlobIntentHold[]\n readonly ownerTier?: number\n // rehome:\n readonly fromTier?: number\n readonly toTier?: number\n readonly policy?: 'isolate' | 'dedup'\n /**\n * Rehome destination `+1` row-stamps (`${opId}:${slotName}` /\n * `${opId}:${versionKey}`) CONFIRMED applied — #746 whole-branch review\n * (K=8 stamp-ring blocker). `BlobObject.lastOps` is a BOUNDED ring (K=8,\n * spec C2) — correct as the sole idempotency source for SHRED (one\n * decrement per eTag, count captured in `holds`) but not for REHOME,\n * whose row-scoped increments can exceed K on a single destination via\n * either ≥9 rows of identical content on ONE record, or ≥8 CONCURRENT\n * rehomes converging on one shared destination between a crashed op's\n * stamp-write and its resume — either way, an evicted-but-unapplied-\n * looking stamp gets re-applied, over-counting the destination's\n * refCount and permanently stranding it above 0. This field is THIS\n * op's own UNBOUNDED, per-record backstop: appended (via\n * {@link recordAppliedStamp}) immediately after a stamped `+1` lands\n * (whether freshly applied or found already in the ring), consulted\n * BEFORE attempting the CAS so a resume that finds its own row-stamp\n * here skips the increment entirely — independent of the shared\n * destination object's bounded ring and of how many OTHER ops have\n * touched it since. Absent/empty is the normal case (no rehome — or no\n * increments yet — in flight).\n */\n readonly appliedStamps?: readonly string[]\n}\n\n/** Resume callback signature for {@link sweepBlobIntents} — supplied by the caller (Task 3 / PR-2). */\nexport type BlobIntentResume = (collection: string, recordId: string, intent: BlobIntent) => Promise<void>\n\n/** C11: refuse a key part that would make the `::` marker-key grammar ambiguous. */\nfunction assertIntentKeyPartSafe(part: string, what: 'collection' | 'record id'): void {\n if (part.includes('::') || part.startsWith(':') || part.endsWith(':')) {\n throw new ValidationError(\n `Blob intent ${what} \"${part}\" must not contain '::' nor start/end with ':' ` +\n `(reserved as the _blob_intent marker-key separator; #753)`,\n )\n }\n}\n\n/** Build the marker's store key, `{collection}::{recordId}` (C11). */\nfunction intentKey(collection: string, recordId: string): string {\n assertIntentKeyPartSafe(collection, 'collection')\n assertIntentKeyPartSafe(recordId, 'record id')\n return `${collection}::${recordId}`\n}\n\n/** Split a marker key back into `{collection, recordId}`. `null` on a key that doesn't match the grammar. */\nfunction parseIntentKey(key: string): { collection: string; recordId: string } | null {\n const idx = key.indexOf('::')\n if (idx === -1) return null\n return { collection: key.slice(0, idx), recordId: key.slice(idx + 2) }\n}\n\n/**\n * Encrypt a `BlobIntent` into an envelope, mirroring the version-record\n * write shape (`blob-set.ts`'s `writeVersionRecordAtKey`): fresh IV per\n * write. `newVersion` defaults to `1` (the marker's original create-once\n * shape — `createIntent`'s only call site); {@link recordAppliedStamp}\n * (#746 whole-branch review) is the one caller that updates an\n * already-created marker in place and passes the incremented version\n * explicitly.\n */\nasync function encodeIntentEnvelope(intent: BlobIntent, dek: EnclaveKey, newVersion = 1): Promise<EncryptedEnvelope> {\n const json = JSON.stringify(intent)\n const body = await writeEnvelopeBody(json, dek)\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: newVersion,\n _ts: new Date().toISOString(),\n ...body,\n }\n}\n\n/** Decrypt an envelope back into its `BlobIntent` payload. */\nasync function decodeIntentEnvelope(envelope: EncryptedEnvelope, dek: EnclaveKey): Promise<BlobIntent> {\n const json = await openEnvelopeJson(envelope, dek)\n return JSON.parse(json) as BlobIntent\n}\n\n/**\n * Read the `_blob_intent` marker for `{collection}::{recordId}`, or `null`\n * if none is pending. The resume-gate check every future blob mutator\n * (Task 3 / PR-2) runs before proceeding.\n */\nexport async function getIntent(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n): Promise<BlobIntent | null> {\n const envelope = await adapter.get(vault, BLOB_INTENT_COLLECTION, intentKey(collection, recordId))\n if (!envelope) return null\n const dek = await getDEK(collection)\n return decodeIntentEnvelope(envelope, dek)\n}\n\n/**\n * Create a `_blob_intent` marker for `{collection}::{recordId}` — CAS\n * create-if-absent (C8). `store.put(..., expectedVersion: 0)` succeeds only\n * when no row is present (every real adapter treats a missing record +\n * `expectedVersion: 0` as a no-conflict write — the same convention\n * `loadOrCreateSigner` uses in `with-audit/attestation/signer.ts`); a\n * present row always carries `_v >= 1`, so the CAS throws `ConflictError`\n * and is converted here into {@link BlobIntentPendingError} — the signal\n * a caller catches to resume the PENDING operation before starting a new\n * one, rather than silently clobbering it (which would orphan the prior\n * op's op-stamps).\n */\nexport async function createIntent(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n intent: BlobIntent,\n): Promise<void> {\n const key = intentKey(collection, recordId)\n const dek = await getDEK(collection)\n const envelope = await encodeIntentEnvelope(intent, dek)\n try {\n await adapter.put(vault, BLOB_INTENT_COLLECTION, key, envelope, 0)\n } catch (err) {\n if (isConflictError(err)) {\n throw new BlobIntentPendingError(collection, recordId)\n }\n throw err\n }\n}\n\n/** Delete the `_blob_intent` marker for `{collection}::{recordId}` — the operation's LAST step. */\nexport async function deleteIntent(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n): Promise<void> {\n await adapter.delete(vault, BLOB_INTENT_COLLECTION, intentKey(collection, recordId))\n}\n\n/** CAS retry budget for {@link recordAppliedStamp} — mirrors `blob-set.ts`'s `MAX_CAS_RETRIES`. */\nconst MAX_APPLIED_STAMP_RETRIES = 5\n\n/**\n * Durably record that a rehome destination row-stamp has been CONFIRMED\n * applied — #746 whole-branch review (K=8 stamp-ring blocker). Appends\n * `stamp` to the marker's `appliedStamps` (idempotent no-op if already\n * present), CAS-retried against concurrent resumers touching the SAME\n * marker (mirrors `BlobSet.casUpdateSlots`'s retry shape).\n *\n * A no-op — never throws — when the marker is already gone: that only\n * happens if the whole op already completed and deleted it (this call is\n * racing its own operation's tail) or a sibling resumer already finished\n * it; either way there is nothing left to record against. A marker whose\n * `op` is NOT `'rehome'` (should be structurally impossible — only\n * `BlobSet`'s own rehome machinery calls this, against its own marker) is\n * treated the same way: nothing to record.\n */\nexport async function recordAppliedStamp(\n adapter: NoydbStore,\n vault: string,\n collection: string,\n recordId: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n stamp: string,\n): Promise<void> {\n const key = intentKey(collection, recordId)\n const dek = await getDEK(collection)\n for (let attempt = 0; attempt < MAX_APPLIED_STAMP_RETRIES; attempt++) {\n const envelope = await adapter.get(vault, BLOB_INTENT_COLLECTION, key)\n if (!envelope) return // marker already gone — nothing to record against\n const intent = await decodeIntentEnvelope(envelope, dek)\n if (intent.op !== 'rehome' || intent.appliedStamps?.includes(stamp)) return // already recorded (or not ours)\n const updated: BlobIntent = { ...intent, appliedStamps: [...(intent.appliedStamps ?? []), stamp] }\n const newEnvelope = await encodeIntentEnvelope(updated, dek, envelope._v + 1)\n try {\n await adapter.put(vault, BLOB_INTENT_COLLECTION, key, newEnvelope, envelope._v)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_APPLIED_STAMP_RETRIES - 1) continue\n throw err\n }\n }\n}\n\n/**\n * List every persisted `_blob_intent` marker and resume each via the\n * caller-supplied `resume` callback (the forget-entry / vault-open sweep,\n * mirroring `_mv_stale`'s `hydratePersistedStaleMarkers`/orphan-sweep\n * pattern).\n *\n * **Per-marker isolation (#753 Task 3, carried finding from the Task 2\n * review):** each marker's decode + `resume` runs in its own try/catch — a\n * corrupt/DEK-mismatched marker, or a marker whose resume genuinely fails\n * (e.g. a store error mid-release), is reported via `onResumeError` and\n * skipped, never aborting the loop and never silently swallowed. Earlier\n * (Task 2) this loop had no try/catch at all; that was safe only because\n * nothing called `resume` with real logic yet — `BlobSet.mintShredIntent`'s\n * collection-scoped sweep (this function's first real caller, Task 3) can\n * hit a genuinely corrupt sibling marker and must not let it block a\n * healthy one (mirrors `stale.ts`'s orphan-sweep defensive posture, #736).\n *\n * `resume` is the per-marker resume action (Task 3: `BlobSet`'s\n * `resolvePendingIntent`-equivalent for a shred marker; a rehome marker is\n * PR-2 scope and should be skipped by the callback, not thrown into here).\n */\nexport async function sweepBlobIntents(\n adapter: NoydbStore,\n vault: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n resume: BlobIntentResume,\n onResumeError?: (collection: string, recordId: string, err: unknown) => void,\n): Promise<void> {\n const keys = await adapter.list(vault, BLOB_INTENT_COLLECTION)\n for (const key of keys) {\n const parsed = parseIntentKey(key)\n if (!parsed) continue // defensive: not our grammar, not ours to sweep\n try {\n const envelope = await adapter.get(vault, BLOB_INTENT_COLLECTION, key)\n if (!envelope) continue // raced with a concurrent resume's delete\n const dek = await getDEK(parsed.collection)\n const intent = await decodeIntentEnvelope(envelope, dek)\n await resume(parsed.collection, parsed.recordId, intent)\n } catch (err) {\n onResumeError?.(parsed.collection, parsed.recordId, err)\n }\n }\n}\n","import type {\n NoydbStore,\n EncryptedEnvelope,\n BlobObject,\n SlotRecord,\n SlotInfo,\n VersionRecord,\n BlobPutOptions,\n BlobResponseOptions,\n} from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport type { ObjectProjection } from './object-projection.js'\nimport type { BlobFieldsConfig } from './blob-compaction.js'\nimport {\n encrypt,\n openEnvelopeJson,\n hmacSha256Hex,\n encryptBytesWithAAD,\n decryptBytesWithAAD,\n bufferToBase64,\n base64ToBuffer,\n generateDEK,\n wrapCek,\n unwrapCek,\n sha256Hex,\n type EnclaveKey,\n} from '../../kernel/enclave/index.js'\nimport { BlobIntentPendingError, BlobOfflineError, ConflictError, isConflictError, NotFoundError, TamperedError, TierNotGrantedError, UnsupportedTierCompositionError, ValidationError } from '../../kernel/errors.js'\nimport { blobPinKey, type BlobPinCache, type BlobPinEntry } from './blob-pinning.js'\nimport { liveRecordIsElevated, liveRecordTier } from '../../kernel/tier-visibility.js'\nimport { dekKey, assertTierAccess } from '../../with-party/team/tiers.js'\nimport type { UnlockedKeyring } from '../../with-party/team/keyring.js'\nimport { detectMagic, isPreCompressed } from './mime-magic.js'\nimport { createIntent, getIntent, deleteIntent, sweepBlobIntents, recordAppliedStamp, type BlobIntent, type BlobIntentHold } from './blob-intent.js'\n\n// ─── Internal collection names ─────────────────────────────────────────\n\n/**\n * DEK slot name for vault-shared blob data. Calling `getDEK('_blob')`\n * auto-creates a blob DEK the first time — same lazy-creation mechanism\n * used for any user-defined collection.\n */\nexport const BLOB_COLLECTION = '_blob'\n\n/** Stores `BlobObject` metadata envelopes, keyed by eTag. */\nexport const BLOB_INDEX_COLLECTION = '_blob_index'\n\n/**\n * Stores encrypted chunk envelopes, keyed by `{eTag}/{chunkIndex}`.\n * NOT loaded into the in-memory query layer. Fetched on demand by\n * `BlobSet.get()` / `BlobSet.response()`.\n */\nexport const BLOB_CHUNKS_COLLECTION = '_blob_chunks'\n\n/** Prefix for per-collection slot metadata collections. */\nexport const BLOB_SLOTS_PREFIX = '_blob_slots_'\n\n/** Prefix for per-collection version records. */\nexport const BLOB_VERSIONS_PREFIX = '_blob_versions_'\n\n/**\n * Default chunk size: 256 KB raw bytes.\n * After AES-GCM (same size) + base64 (~33% inflation) → ~342 KB per\n * envelope, safely within DynamoDB's 400 KB item limit.\n */\nexport const DEFAULT_CHUNK_SIZE = 256 * 1024\n\n/** Maximum CAS retry attempts for refCount and slot metadata updates. */\nconst MAX_CAS_RETRIES = 5\n\n/**\n * `BlobObject.lastOps` ring capacity — see that field's doc comment\n * (kernel/types.ts) for the audit-visible-bound rationale (#753 spec §7 C2).\n */\nconst LAST_OPS_RING_SIZE = 8\n\n/** Append `stamp` to the bounded `lastOps` ring, evicting the oldest entry past K. */\nfunction appendStamp(lastOps: readonly string[] | undefined, stamp: string): readonly string[] {\n const next = [...(lastOps ?? []), stamp]\n return next.length > LAST_OPS_RING_SIZE ? next.slice(next.length - LAST_OPS_RING_SIZE) : next\n}\n\n/**\n * Shared empty-set constant for {@link BlobSet.applyStampedIncrement}'s\n * `knownApplied` default — avoids allocating a fresh empty `Set` on every\n * unmarked (`knownApplied` omitted) call site.\n */\nconst EMPTY_STAMP_SET: ReadonlySet<string> = new Set()\n\n/**\n * Mint a fresh op-stamp identity for a `_blob_intent` marker (#753 spec §7\n * §2a: \"random nonce minted at marker creation\"). Mirrors `buildBacklink`'s\n * opaque-token minting below — `crypto.getRandomValues`, hex-encoded.\n */\nfunction mintOpId(): string {\n const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16))\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n// ─── Compression helpers ───────────────────────────────────────────────\n\nasync function compressBytes(\n data: Uint8Array,\n): Promise<{ bytes: Uint8Array; algorithm: 'gzip' | 'none' }> {\n if (typeof CompressionStream === 'undefined') {\n return { bytes: data, algorithm: 'none' }\n }\n // Pipe through the stream so `readable` is drained CONCURRENTLY with the\n // write. The await-write-then-read form deadlocks once the output exceeds the\n // stream's internal buffer (the writer backpressures waiting for a reader that\n // hasn't started yet).\n const piped = new Response(data as Uint8Array<ArrayBuffer>).body!.pipeThrough(new CompressionStream('gzip'))\n const buf = await new Response(piped).arrayBuffer()\n return { bytes: new Uint8Array(buf), algorithm: 'gzip' }\n}\n\nasync function decompressBytes(data: Uint8Array): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'undefined') {\n throw new Error(\n '[noy-db] DecompressionStream not available — cannot decompress blob chunk',\n )\n }\n // Concurrent read via pipeThrough — the await-write-then-read form deadlocks\n // when the DECOMPRESSED output exceeds the stream buffer (~16 KB): small\n // compressed input → large output → backpressure.\n const piped = new Response(data as Uint8Array<ArrayBuffer>).body!.pipeThrough(new DecompressionStream('gzip'))\n const buf = await new Response(piped).arrayBuffer()\n return new Uint8Array(buf)\n}\n\nfunction concatChunks(chunks: Uint8Array[]): Uint8Array {\n const total = chunks.reduce((s, c) => s + c.byteLength, 0)\n const out = new Uint8Array(total)\n let offset = 0\n for (const c of chunks) {\n out.set(c, offset)\n offset += c.byteLength\n }\n return out\n}\n\n/** Build the AAD binding for chunk integrity: \"{eTag}:{chunkIndex}:{chunkCount}\" */\nfunction chunkAAD(eTag: string, chunkIndex: number, chunkCount: number): Uint8Array {\n return new TextEncoder().encode(`${eTag}:${chunkIndex}:${chunkCount}`)\n}\n\n// ─── BlobSet ──────────────────────────────────────────────────────────\n\n/**\n * Handle for reading, writing, versioning, and deleting binary blobs\n * on a specific record.\n *\n * Obtained via `collection.blob(id)`. No I/O is performed until you\n * call a method.\n *\n * ## Storage layout\n *\n * ```\n * _blob_index/{eTag} BlobObject metadata (vault-shared DEK)\n * _blob_chunks/{eTag}/{chunkIndex} Encrypted chunk data (vault-shared DEK + AAD)\n * _blob_slots_{collection}/{recordId} Slot map (parent collection DEK)\n * _blob_versions_{collection}/{recordId}/{slot}/{label} Published versions (parent collection DEK)\n * ```\n *\n * ## Deduplication\n *\n * `put()` computes `eTag = HMAC-SHA-256(blobDEK, plaintext)` — keyed so the\n * store cannot predict eTags for known content. If another record already\n * uploaded the same bytes, the chunks are reused and `refCount` is incremented.\n *\n * ## Chunk integrity\n *\n * Each chunk is encrypted with AES-256-GCM using AAD = `{eTag}:{index}:{count}`,\n * preventing chunk reorder, substitution, and truncation attacks.\n */\nexport class BlobSet {\n private readonly store: NoydbStore\n private readonly vault: string\n private readonly collection: string\n private readonly recordId: string\n private readonly getDEK: (name: string) => Promise<EnclaveKey>\n private readonly encrypted: boolean\n private readonly userId: string | undefined\n private readonly maxBlobBytes: number | undefined\n private readonly erasableBlobs: boolean\n private readonly tiersActive: boolean\n private readonly debugPlaintext: boolean\n private readonly objectStore: ObjectProjection | undefined\n private readonly blobFields: BlobFieldsConfig | undefined\n private readonly keyring: UnlockedKeyring | undefined\n /**\n * Device-local pin registry + external side-cache + KPI counters (#808).\n * Injected by `withBlobs()` (one shared instance per strategy — per\n * \"device\"); absent on a direct construction that never threads one.\n */\n private readonly pinCache: BlobPinCache | undefined\n /**\n * Set only on the cleared clone `atTier()` returns (#749) — never by\n * `openSlot`/`Collection.blob()`. Its presence is what makes a `BlobSet`\n * a cleared view: see `ownerRecordElevated()`/`ownerTier()`.\n */\n private readonly clearedTier: number | undefined\n\n constructor(opts: {\n store: NoydbStore\n vault: string\n collection: string\n recordId: string\n getDEK: (name: string) => Promise<EnclaveKey>\n encrypted: boolean\n userId?: string\n maxBlobBytes?: number\n erasableBlobs?: boolean\n tiersActive?: boolean\n debugPlaintext?: boolean\n objectStore?: ObjectProjection\n blobFields?: BlobFieldsConfig\n keyring?: UnlockedKeyring\n clearedTier?: number\n pinCache?: BlobPinCache\n }) {\n this.store = opts.store\n this.vault = opts.vault\n this.collection = opts.collection\n this.recordId = opts.recordId\n this.getDEK = opts.getDEK\n this.encrypted = opts.encrypted\n this.userId = opts.userId\n this.maxBlobBytes = opts.maxBlobBytes\n this.erasableBlobs = opts.erasableBlobs === true\n this.tiersActive = opts.tiersActive === true\n this.debugPlaintext = opts.debugPlaintext === true\n this.objectStore = opts.objectStore\n this.blobFields = opts.blobFields\n this.keyring = opts.keyring\n this.clearedTier = opts.clearedTier\n this.pinCache = opts.pinCache\n }\n\n /**\n * #724 I1 completion: a legacy (non-`perRecordKeys`) blob has no per-blob\n * `_cek`, so `rehomeForTier` can never tier-isolate it on elevate/demote —\n * it stays decryptable under the flat tier-0 `_blob` DEK at rest even\n * after the owning record is elevated. The construction-time mandate\n * (`resolveCollectionConfig`) only catches this when `blobFields` is\n * DECLARED; a collection with an undeclared field (or no `blobFields` at\n * all) constructs fine and reaches this write path unguarded. Refuse the\n * write itself instead of broadening the construction mandate, which\n * would over-refuse blobless tiered collections (`blobsStrategy` is\n * vault-wide, not per-collection). Called from every content-write entry\n * (`put()`, `publish()`) — never from a read path, so a pre-existing\n * legacy blob stays readable (back-compat).\n */\n private assertBlobWritable(): void {\n if (this.tiersActive && !this.erasableBlobs) {\n throw new UnsupportedTierCompositionError(\n 'blobs',\n `Collection \"${this.collection}\": writing a blob to a tiered collection requires perRecordKeys ` +\n `(legacy blobs have no per-record key and cannot be tier-isolated) (#724).`,\n )\n }\n }\n\n /**\n * #748: `adoptExternal()`/`setExternalMeta()` write an `external` slot\n * reference directly — unlike `put()`, which only takes the external path\n * when `blobFields[slotName].external` is declared (the construction-time\n * tiers×blobFields mandate, `collection-config.ts`, then applies). Without\n * this gate, either method can attach an external reference to a slot the\n * collection never declared `external` — bypassing that mandate on a tiered\n * collection. Mirrors `put()`'s own gate.\n */\n private assertExternalDeclared(slotName: string): void {\n if (!this.blobFields?.[slotName]?.external) {\n throw new ValidationError(\n `Collection \"${this.collection}\": blob field \"${slotName}\" is not declared external `\n + `(blobFields[\"${slotName}\"].external must be true) — cannot attach an external `\n + `reference to an undeclared slot (#748).`,\n )\n }\n }\n\n /**\n * Resolve the key the blob's CHUNKS are encrypted under.\n *\n * - `_cek` present (erasable blob) → unwrap the per-blob content CEK under\n * `blobDEK`. Deleting the BlobObject (at `refCount → 0`) makes this\n * key unrecoverable → the chunks are crypto-shredded.\n * - `_cek` absent (legacy) → `blobDEK` encrypts chunks directly.\n * - unencrypted vault → `null` (chunks stored as plaintext base64).\n *\n * `blobDEK` defaults to this record's own (tier-0) `_blob` DEK — every\n * pre-#724-T3 call site. `rehomeForTier`'s isolate-fork path (#724 Arc 10\n * Task 3) passes the `fromTier`-scoped DEK explicitly to read a shared\n * blob's plaintext under the key it's CURRENTLY wrapped under, reusing\n * this one `_cek` unwrap site rather than adding a new raw access.\n */\n private async resolveChunkKey(blob: Pick<BlobObject, '_cek'>, blobDEK?: EnclaveKey): Promise<EnclaveKey | null> {\n if (!this.encrypted) return null\n const dek = blobDEK ?? await this.getDEK(BLOB_COLLECTION)\n return blob._cek !== undefined ? await unwrapCek(blob._cek, dek) : dek\n }\n\n /**\n * #724 Arc 10 Task 1: the same \"elevated ≡ invisible\" clearance check\n * `collection.get()` applies to the owning record (`liveRecordIsElevated`,\n * #701/#707/#709/#712) — reused here, not reinvented, so a blob content\n * read on an elevated record is refused exactly like a `get()` on it\n * would be. Reads only the owning record's `_tier` envelope metadata;\n * runs before any blob decrypt.\n *\n * #749: a cleared clone (`this.clearedTier !== undefined`, produced only by\n * `atTier()`) short-circuits to `false` — its whole purpose is to see past\n * this gate, having already paid the `assertTierAccess` cost `atTier()`\n * charged to get here. No live envelope peek needed on that path.\n */\n private async ownerRecordElevated(): Promise<boolean> {\n if (this.clearedTier !== undefined) return false\n return liveRecordIsElevated(this.store, this.vault, this.collection, this.recordId)\n }\n\n /**\n * #724 Arc 10 Task 4: the tier this record's LIVE envelope currently\n * carries — `loadSlots`/`saveSlots` default to resolving the slot map's\n * collection DEK here, so the slot map (filenames/eTags/mimeTypes) is\n * read/written wherever a prior `rehomeForTier` call physically re-keyed\n * it to. `rehomeForTier` itself passes an EXPLICIT tier instead of\n * relying on this default (see its own doc comment): by the time it\n * runs, the owning record's live `_tier` has ALREADY moved to `toTier`\n * (the collection-level `elevate`/`demote`/`putAtTier` writes the record\n * before invoking `syncBlobs`), while the slot map is still physically at\n * `fromTier` until `rehomeForTier`'s own move step lands — so this peek\n * would resolve the wrong DEK mid-move.\n *\n * #749: a cleared clone reports its fixed `clearedTier` instead of\n * re-peeking — a stable view for the caller's whole session with it. A\n * concurrent demote/elevate on the underlying record is invisible to an\n * already-cleared handle; call `atTier()` again to see it. This is not a\n * silent-corruption risk: a WRITE through a stale clone after a concurrent\n * move fails loud, not quiet — the slot map/index envelope has already\n * been physically re-keyed to the record's new tier by `rehomeForTier`, so\n * the stale clone's `clearedTier`-pinned DEK can no longer open it and the\n * write throws an AEAD decrypt error rather than silently writing under\n * the wrong key.\n */\n private async ownerTier(): Promise<number> {\n if (this.clearedTier !== undefined) return this.clearedTier\n return liveRecordTier(this.store, this.vault, this.collection, this.recordId)\n }\n\n /**\n * The sanctioned cleared-read path to an elevated record's blobs (#749).\n *\n * The law: `blob(id)` is the tier-0 surface — `get()`/`list()`/`blobInfo()`/\n * etc. all hide an elevated record's blobs from EVERYONE, unconditionally\n * (`ownerRecordElevated()`, #724 Task 1), with no cleared path of their own.\n * `atTier()` is that cleared path, the `getAtTier()` analogue for blobs:\n * live-peeks the owning record's tier, and for `tier > 0` runs\n * `assertTierAccess` — the SAME gate `putAtTier`/`elevate`/`demote` run\n * before ever touching a tier DEK — then returns a NEW `BlobSet` bound to\n * that tier. Every subsequent call on the returned handle (`get()`,\n * `list()`, `publish()`, …) sees through the gate as if the record were at\n * tier 0.\n *\n * `assertTierAccess` — not a bare `getDEK` call — IS the authorization\n * check: owner/admin/custodian bypass it (their on-demand tier-DEK mint is\n * sanctioned), everyone else must already hold the tier DEK (via a prior\n * grant or delegation) or it throws `TierNotGrantedError`. A bare `getDEK`\n * would not gate anything here — it silently mints a fresh DEK for ANY\n * caller when one is missing, which is exactly the \"non-cleared caller\n * creating tier key material inside the trust boundary\" hazard\n * `assertTierAccess`'s own doc comment (`with-party/team/tiers.ts`) warns\n * against; running it BEFORE any `getDEK` call is what keeps an ungranted\n * member's failed `atTier()` call from minting junk key material into\n * their keyring as a side effect. No tier DEK is resolved here at all —\n * every actual read still resolves its DEK lazily, same as today.\n *\n * `this.keyring` is undefined only on a construction path that never\n * threads one through `openSlot` (`Collection.blob()` always does) — that\n * can't prove clearance for anyone, so it throws the same\n * `TierNotGrantedError` an ungranted caller would get.\n */\n async atTier(): Promise<BlobSet> {\n const tier = await liveRecordTier(this.store, this.vault, this.collection, this.recordId)\n if (tier === 0) return this\n\n if (!this.keyring) throw new TierNotGrantedError(this.collection, tier)\n assertTierAccess(this.keyring, this.collection, tier)\n // M3 (whole-branch review): the first gate only proves clearance on the\n // DATA collection (`docs#N`) — a member holding that grant but not the\n // `_blob#N` DEK grant would otherwise pass straight through and have\n // the first content read auto-mint a junk `_blob#N` DEK as a side\n // effect. Gate the blob DEK's own tier too, in the same\n // before-any-mint spot as the line above.\n assertTierAccess(this.keyring, BLOB_COLLECTION, tier)\n\n return new BlobSet({\n store: this.store,\n vault: this.vault,\n collection: this.collection,\n recordId: this.recordId,\n getDEK: this.getDEK,\n encrypted: this.encrypted,\n erasableBlobs: this.erasableBlobs,\n tiersActive: this.tiersActive,\n debugPlaintext: this.debugPlaintext,\n keyring: this.keyring,\n clearedTier: tier,\n ...(this.userId !== undefined ? { userId: this.userId } : {}),\n ...(this.maxBlobBytes !== undefined ? { maxBlobBytes: this.maxBlobBytes } : {}),\n ...(this.objectStore !== undefined ? { objectStore: this.objectStore } : {}),\n ...(this.blobFields !== undefined ? { blobFields: this.blobFields } : {}),\n ...(this.pinCache !== undefined ? { pinCache: this.pinCache } : {}),\n })\n }\n\n /** The internal collection that holds slot metadata for this collection's blobs. */\n private get slotsCollection(): string {\n return `${BLOB_SLOTS_PREFIX}${this.collection}`\n }\n\n /** The internal collection that holds published versions for this collection's blobs. */\n private get versionsCollection(): string {\n return `${BLOB_VERSIONS_PREFIX}${this.collection}`\n }\n\n // ─── Slot Metadata I/O (CAS-protected) ─────────────────────────────\n\n /**\n * @param tier #724 Arc 10 Task 4: explicit tier to resolve the slot map's\n * collection DEK at — `getDEK(dekKey(this.collection, tier))`. Omitted →\n * resolves via `ownerTier()` (the record's CURRENT live tier), correct\n * for every call site except `rehomeForTier`'s own internal reads, which\n * must pin `fromTier` explicitly (see `ownerTier()`'s doc comment).\n */\n private async loadSlots(tier?: number): Promise<{\n slots: Record<string, SlotRecord>\n version: number\n }> {\n const envelope = await this.store.get(this.vault, this.slotsCollection, this.recordId)\n if (!envelope) return { slots: {}, version: 0 }\n\n if (!this.encrypted) {\n return {\n slots: JSON.parse(envelope._data) as Record<string, SlotRecord>,\n version: envelope._v,\n }\n }\n\n const dek = await this.getDEK(dekKey(this.collection, tier ?? await this.ownerTier()))\n const json = await openEnvelopeJson(envelope, dek)\n return {\n slots: JSON.parse(json) as Record<string, SlotRecord>,\n version: envelope._v,\n }\n }\n\n /**\n * Resumable slot-map read for `runRehomeSteps` (#746 spec §7 §2d bullet\n * 1): try `fromTier` (the map's pre-move physical location) first; on a\n * decrypt failure (wrong key, not \"missing\" — mirrors `loadBlobObject`'s\n * own TamperedError-only fallback trigger) retry `toTier` — the signal\n * that a prior (crashed) run's own move step already landed. `atTier`\n * reports which one opened it. A missing envelope reports `atTier:\n * fromTier` (arbitrary — the `Object.keys(slots).length > 0` gate the\n * caller applies makes the value moot for an empty map).\n */\n private async loadSlotsTolerant(fromTier: number, toTier: number): Promise<{\n slots: Record<string, SlotRecord>\n version: number\n atTier: number\n }> {\n const envelope = await this.store.get(this.vault, this.slotsCollection, this.recordId)\n if (!envelope) return { slots: {}, version: 0, atTier: fromTier }\n\n if (!this.encrypted) {\n return { slots: JSON.parse(envelope._data) as Record<string, SlotRecord>, version: envelope._v, atTier: fromTier }\n }\n\n const fromDek = await this.getDEK(dekKey(this.collection, fromTier))\n try {\n const json = await openEnvelopeJson(envelope, fromDek)\n return { slots: JSON.parse(json) as Record<string, SlotRecord>, version: envelope._v, atTier: fromTier }\n } catch (err) {\n if (!(err instanceof TamperedError) || fromTier === toTier) throw err\n }\n\n const toDek = await this.getDEK(dekKey(this.collection, toTier))\n const json = await openEnvelopeJson(envelope, toDek)\n return { slots: JSON.parse(json) as Record<string, SlotRecord>, version: envelope._v, atTier: toTier }\n }\n\n /** @param tier See {@link loadSlots}'s `tier` param — same resolution. */\n private async saveSlots(\n slots: Record<string, SlotRecord>,\n currentVersion: number,\n tier?: number,\n ): Promise<void> {\n const json = JSON.stringify(slots)\n const now = new Date().toISOString()\n let envelope: EncryptedEnvelope\n\n if (this.encrypted) {\n const dek = await this.getDEK(dekKey(this.collection, tier ?? await this.ownerTier()))\n const { iv, data } = await encrypt(json, dek)\n envelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: currentVersion + 1,\n _ts: now,\n _iv: iv,\n _data: data,\n }\n } else {\n envelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: currentVersion + 1,\n _ts: now,\n _iv: '',\n _data: json,\n }\n }\n\n await this.store.put(\n this.vault,\n this.slotsCollection,\n this.recordId,\n envelope,\n currentVersion > 0 ? currentVersion : undefined,\n )\n }\n\n /**\n * CAS retry loop for slot metadata updates. Re-reads slots on conflict\n * and re-applies the mutation function.\n *\n * #724 re-review (High): when the mutation empties the slot map (the\n * last slot was deleted), the `_blob_slots_{collection}/{recordId}` row\n * is DELETED rather than saved as an empty-but-present envelope. An\n * empty envelope left in place would still be tier-keyed at whatever\n * tier it was last written at; `rehomeForTier`'s `slots exist` guard\n * (`Object.keys(slots).length > 0`) would then skip re-keying it on a\n * later elevate/demote, stranding it at the wrong tier's DEK —\n * `TamperedError` on every later access. Deleting the row instead makes\n * \"no slots\" and \"no envelope\" the SAME state, so a record that once\n * had a blob and a record that never had one behave identically once\n * empty.\n *\n * @param tier See {@link loadSlots}'s `tier` param — threaded through so\n * `rehomeForTier`'s isolate-fork writes (via `putUnderDEK`) target the\n * slot map's CURRENT (fromTier) location during the move.\n */\n private async casUpdateSlots(\n mutate: (slots: Record<string, SlotRecord>) => Record<string, SlotRecord> | null,\n tier?: number,\n ): Promise<void> {\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const { slots, version } = await this.loadSlots(tier)\n const updated = mutate(slots)\n if (updated === null) return // no-op\n if (Object.keys(updated).length === 0) {\n if (version > 0) await this.store.delete(this.vault, this.slotsCollection, this.recordId)\n return\n }\n try {\n await this.saveSlots(updated, version, tier)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_CAS_RETRIES - 1) continue\n throw err\n }\n }\n }\n\n /**\n * Clear a slot's `pendingRelease` breadcrumb once its release has landed\n * (#746 spec §7 review, carried finding (b)) — a no-op CAS if the field is\n * already gone (idempotent, safe to call again on resume) or no longer\n * matches `expected` (a NEWER put() on this slot already moved past it).\n */\n private async clearPendingRelease(slotName: string, expected: string, tier?: number): Promise<void> {\n await this.casUpdateSlots((slots) => {\n const slot = slots[slotName]\n if (!slot || slot.pendingRelease !== expected) return null\n const rest: SlotRecord = { ...slot }\n delete (rest as { pendingRelease?: string }).pendingRelease\n slots[slotName] = rest\n return slots\n }, tier)\n }\n\n /**\n * `runRehomeSteps`'s FIRST step (#746 spec §7 review, carried finding\n * (b)) — reconcile any `pendingRelease` breadcrumb left on `slots` by a\n * PRIOR (crashed) run of THIS SAME marker-governed rehome (`opId`\n * identifies it). `putUnderDEK`'s slot-CAS (pointing the slot at its new\n * eTag) and its old-eTag release are two separate writes; a crash between\n * them strands the release — the slot map has already moved past the old\n * eTag, so a plain re-derivation of \"which eTags need releasing\" from the\n * CURRENT slot map can never find it again (a permanent leak, not merely\n * a delayed one). Durably recording the old eTag ON the slot record\n * itself (in the SAME CAS write that moves it) survives exactly the crash\n * window that loses an in-memory breadcrumb, and this pass is what\n * consumes it on the next attempt — completing the release (idempotent\n * via the SAME row-scoped stamp `putUnderDEK` used) and clearing the\n * field. C10: the release is NOT swallowed — a failure here must keep the\n * marker alive (propagates), not silently strand it a second way.\n */\n private async reconcilePendingReleases(\n slots: Record<string, SlotRecord>,\n fromTier: number,\n opId: string,\n ): Promise<void> {\n for (const [slotName, slot] of Object.entries(slots)) {\n if (!slot.pendingRelease) continue\n const stamp = `${opId}:${slotName}`\n await this.releaseRef(slot.pendingRelease, 1, false, fromTier, stamp)\n await this.clearPendingRelease(slotName, slot.pendingRelease, fromTier)\n }\n }\n\n // ─── Blob Index I/O (versioned for CAS refCount) ──────────────────\n\n /**\n * #747: the `_blob_index` envelope follows the eTag's HOME tier — the\n * same tier the `_cek` payload wrapped inside it has been scoped to since\n * #724. That fix only tier-scoped the WRAPPED content CEK; the outer\n * index envelope itself stayed flat, so a tier-0 DEK holder could still\n * read an elevated record's blob metadata (size/mimeType/compression/\n * chunkCount/refCount/createdAt) straight off `_blob_index` at rest.\n *\n * `tier` defaults to `ownerTier()`, mirroring `loadSlots`. Two-tier mode\n * (#753 spec §7 C7 / §2d): try `tier`'s DEK first; on a decrypt failure\n * (wrong key — AES-GCM auth fails, not \"missing\") retry under\n * `alsoTryTier`'s DEK. `atTier` reports which key actually opened it, so a\n * caller mutating the object (`casUpdateRefCount`) can write it back\n * under that SAME key — never lifting a flat dedup/legacy object onto the\n * owner's tier (or vice versa) as a side effect of a refCount change.\n *\n * `alsoTryTier` defaults to `0` when `tier` resolves `> 0` and no explicit\n * value is given — this is #747's original tier-then-flat fallback,\n * preserved byte-identical for every pre-#753 call site (none of which\n * pass `alsoTryTier`). It covers the two legitimate classes that stay\n * flat even for an elevated owner: a `dedup`-policy shared object (#741,\n * left in place on purpose) and a legacy `_cek`-less object (chunks\n * direct under the flat DEK, never tier-isolated — #724 I1). When `tier`\n * resolves to `0` and no `alsoTryTier` is given, there is no fallback\n * attempt at all (matches pre-#753 behavior exactly) — but a `tier === 0`\n * caller MAY now pass an elevated `alsoTryTier` explicitly (the rehome\n * resume path, §2d: \"try `fromTier`, fall back to `toTier`\", either of\n * which may be 0). Passing `alsoTryTier === tier` is a no-op fallback (no\n * second attempt — nothing new to try).\n *\n * No migration/compat path: the whole tiers×blobs arc is unpublished, so\n * there is no previously-published elevated blob whose index envelope is\n * stuck flat at rest — every elevated write from here on is born\n * tier-keyed.\n */\n private async loadBlobObject(\n eTag: string,\n tier?: number,\n alsoTryTier?: number,\n ): Promise<{ blob: BlobObject; version: number; atTier: number } | null> {\n const envelope = await this.store.get(this.vault, BLOB_INDEX_COLLECTION, eTag)\n if (!envelope) return null\n\n if (!this.encrypted) {\n return { blob: JSON.parse(envelope._data) as BlobObject, version: envelope._v, atTier: 0 }\n }\n\n const t = tier ?? await this.ownerTier()\n const fallbackTier = alsoTryTier ?? (t > 0 ? 0 : undefined)\n\n const primaryDek = await this.getDEK(dekKey(BLOB_COLLECTION, t))\n try {\n const json = await openEnvelopeJson(envelope, primaryDek)\n return { blob: JSON.parse(json) as BlobObject, version: envelope._v, atTier: t }\n } catch (err) {\n // M1 (whole-branch review): only a decrypt/auth failure under the\n // primary key (`decrypt()` throws `TamperedError` on AES-GCM auth\n // failure — wrong key or tampered ciphertext) means \"not ours at\n // this tier\" — fall through to the alsoTryTier retry below (see doc\n // comment). A JSON.parse failure AFTER a successful decrypt under the\n // CORRECT primary key is genuine corruption, not a wrong-key signal,\n // and must propagate instead of being masked by a misleading\n // fallback-DEK error.\n if (!(err instanceof TamperedError)) throw err\n if (fallbackTier === undefined || fallbackTier === t) throw err\n }\n\n const fallbackDek = await this.getDEK(dekKey(BLOB_COLLECTION, fallbackTier))\n const json = await openEnvelopeJson(envelope, fallbackDek)\n return { blob: JSON.parse(json) as BlobObject, version: envelope._v, atTier: fallbackTier }\n }\n\n /**\n * `runRehomeSteps`'s per-eTag resume tolerance (#746 spec §7 §2d bullet 2)\n * — `loadBlobObject(eTag, fromTier, toTier)`, `alsoTryTier`'s first real\n * caller: `atTier === toTier` on return means this eTag's object already\n * opens under the DESTINATION tier — either a prior (crashed) run's own\n * re-`put()` already landed for it (skip: already moved), or it's a\n * `dedup`-policy/legacy object that happens to sit flat at tier 0 and\n * `toTier === 0` (skip either way — see the call site's doc comment).\n *\n * When `toTier !== 0`, that flat-at-0 legacy/dedup case is NOT covered by\n * the two tiers already tried — `loadBlobObject`'s own default fallback\n * (implicit `alsoTryTier: 0` when the primary tier is `> 0`) is exactly\n * what closes it for a non-resumable caller, but passing `toTier`\n * explicitly here overrides that default. So on a `TamperedError` from\n * both `fromTier` and `toTier`, retry once more at flat `0` — the SAME\n * fallback `loadBlobObject`'s own default already provides, just chained\n * behind the resume signal instead of replacing it.\n */\n private async loadBlobObjectResumable(\n eTag: string, fromTier: number, toTier: number,\n ): Promise<{ blob: BlobObject; version: number; atTier: number } | null> {\n try {\n return await this.loadBlobObject(eTag, fromTier, toTier)\n } catch (err) {\n if (!(err instanceof TamperedError) || toTier === 0) throw err\n return this.loadBlobObject(eTag, fromTier, 0)\n }\n }\n\n /**\n * @param tier See {@link loadBlobObject}'s `tier` param — same resolution.\n * A caller mutating an EXISTING object must pass the tier that OPENED it\n * (`loadBlobObject`'s `atTier`), never its own ask — see\n * `casUpdateRefCount`.\n */\n private async writeBlobObject(blob: BlobObject, expectedVersion?: number, tier?: number): Promise<void> {\n const json = JSON.stringify(blob)\n const now = new Date().toISOString()\n const newVersion = (expectedVersion ?? 0) + 1\n let envelope: EncryptedEnvelope\n\n if (this.encrypted) {\n const dek = await this.getDEK(dekKey(BLOB_COLLECTION, tier ?? await this.ownerTier()))\n const { iv, data } = await encrypt(json, dek)\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: newVersion, _ts: now, _iv: iv, _data: data }\n } else {\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: newVersion, _ts: now, _iv: '', _data: json }\n }\n\n await this.store.put(\n this.vault,\n BLOB_INDEX_COLLECTION,\n blob.eTag,\n envelope,\n expectedVersion,\n )\n }\n\n /**\n * CAS retry loop for refCount changes on a BlobObject.\n *\n * @param tier Resolves the READ side (see {@link loadBlobObject}). The\n * WRITE-back always uses `atTier` — the tier that actually opened the\n * object — never `tier` itself: a dedup-shared or legacy object left flat\n * on purpose must stay flat even when asked for under an elevated\n * owner's tier, or a refCount change would silently lift it there (#747).\n */\n private async casUpdateRefCount(eTag: string, delta: number, tier?: number): Promise<number> {\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const result = await this.loadBlobObject(eTag, tier)\n if (!result) throw new NotFoundError(`BlobObject ${eTag} not found`)\n const { blob, version, atTier } = result\n const updated: BlobObject = { ...blob, refCount: blob.refCount + delta }\n try {\n await this.writeBlobObject(updated, version, atTier)\n return updated.refCount\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_CAS_RETRIES - 1) continue\n throw err\n }\n }\n throw new ConflictError(-1) // exhausted retries\n }\n\n /**\n * Stamp-aware CAS retry loop for refCount changes (#753 spec §7 C2/C4) —\n * the journal primitive `casUpdateRefCount` itself is deliberately left\n * untouched (every existing call site passes no stamp and must stay\n * byte-identical). A distinct method rather than an optional param on\n * `casUpdateRefCount` keeps the return shape type-clean: this one reports\n * whether ITS delta was newly applied, not just the resulting count.\n *\n * C4: the membership check lives INSIDE the retry loop — every (re)read,\n * including retries after a losing CAS attempt, checks `blob.lastOps` for\n * `stamp` FIRST, before computing a delta. This is what makes it a true\n * test-and-set rather than a check-then-act with a TOCTOU window: two\n * concurrent callers racing the SAME stamp can each land on any attempt,\n * but only the one that reads `lastOps` without the stamp present ever\n * applies the delta — the other, whichever attempt it lands on, always\n * observes the stamp (either already there, or freshly appended by the\n * winner) and skips.\n *\n * On apply: the stamp is appended to the bounded ring (`appendStamp`,\n * K=8) in the SAME `writeBlobObject` CAS write as the delta — no window\n * between them (spec C2's atomicity requirement).\n *\n * @returns `{ applied: true, refCount }` when this call's delta was freshly\n * applied (or `{ applied: false, refCount }` when `stamp` was already\n * present — the delta from a PRIOR call with this exact stamp already\n * landed; `refCount` reflects the object's current count, unchanged by\n * this call).\n * @param tier See {@link loadBlobObject}'s `tier` param — same resolution\n * and the same atTier write-back rule as `casUpdateRefCount`.\n */\n private async casUpdateRefCountStamped(\n eTag: string,\n delta: number,\n tier: number | undefined,\n stamp: string,\n ): Promise<{ applied: boolean; refCount: number }> {\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const result = await this.loadBlobObject(eTag, tier)\n if (!result) throw new NotFoundError(`BlobObject ${eTag} not found`)\n const { blob, version, atTier } = result\n if (blob.lastOps?.includes(stamp)) {\n return { applied: false, refCount: blob.refCount }\n }\n const updated: BlobObject = {\n ...blob,\n refCount: blob.refCount + delta,\n lastOps: appendStamp(blob.lastOps, stamp),\n }\n try {\n await this.writeBlobObject(updated, version, atTier)\n return { applied: true, refCount: updated.refCount }\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_CAS_RETRIES - 1) continue\n throw err\n }\n }\n throw new ConflictError(-1) // exhausted retries\n }\n\n /**\n * Release `n` references to a blob and reclaim it at refCount 0.\n *\n * The single reclaim choke point for every reference-drop path — slot\n * delete/overwrite, published-version delete, and `forget()` shred — so the\n * refCount-0 policy is uniform:\n * - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE\n * copy of the wrapped content CEK → chunks permanently undecryptable) and\n * reclaim the chunks. The crypto-shred is EAGER on every path: GDPR erasure\n * must not wait on orphan retention.\n * - **legacy blob** (no `_cek`) → reclaimed only when `reclaimLegacy` (the\n * `forget()` erasure path); otherwise left for deferred GC so the existing\n * `BlobLifecyclePolicy.orphanRetentionDays` semantics are preserved.\n *\n * @returns `'shredded'` (erasable, refCount 0, chunks dead) · `'retainedShared'`\n * (erasable, still referenced) · `'residue'` (legacy — not a crypto-shred).\n *\n * @param tier See {@link loadBlobObject}'s `tier` param — forwarded to both\n * the read and the refCount write-back (which resolves its own `atTier`\n * from the read, per `casUpdateRefCount`'s doc comment).\n *\n * @param stamp #753 spec §7 C1 journal primitive: when present, this call\n * is a (possibly-resumed) journaled release and the CAS goes through\n * {@link casUpdateRefCountStamped} instead, under the two-armed resume\n * rule:\n * - `lastOps` already has `stamp` && `refCount > 0` → this stamp's\n * decrement already landed and the object is still referenced elsewhere\n * — skip re-decrementing, report the same outcome a fresh call would\n * (`retainedShared`/`residue`).\n * - `lastOps` already has `stamp` && `refCount <= 0` → this stamp's\n * decrement already landed, but the crash window sat BETWEEN the\n * decrement CAS and the index+chunk deletion below — COMPLETE that\n * deletion now (idempotent: both `store.delete` calls are void on an\n * already-absent key).\n * - not yet stamped → apply via `casUpdateRefCountStamped`, then proceed\n * exactly like the unstamped path below.\n *\n * A crash can also land AFTER the index row is deleted but BEFORE every\n * chunk is — `loadBlobObject` then returns null (indistinguishable from\n * \"never existed\") and the object's `chunkCount` is gone with it. A\n * stamped caller who knows the true count (the journal marker's captured\n * `holds`, once that plumbing lands) supplies it via `chunkCountHint` so\n * this call can still finish the chunk deletion; without it, an\n * already-index-gone eTag is reported `'shredded'` same as today (best\n * effort — nothing left to key the cleanup off of).\n */\n private async releaseRef(\n eTag: string,\n n: number,\n reclaimLegacy: boolean,\n tier?: number,\n stamp?: string,\n chunkCountHint?: number,\n ): Promise<'shredded' | 'retainedShared' | 'residue'> {\n const loaded = await this.loadBlobObject(eTag, tier)\n if (!loaded) {\n // Index row already gone. A stamped caller who knows the true chunk\n // count (marker-supplied) can still complete cleanup; an unstamped\n // caller (or one without the hint) has nothing left to key it off —\n // best-effort, same posture as pre-#753.\n if (stamp !== undefined && chunkCountHint !== undefined) {\n for (let i = 0; i < chunkCountHint; i++) {\n await this.store.delete(this.vault, BLOB_CHUNKS_COLLECTION, `${eTag}_${i}`)\n }\n }\n return 'shredded'\n }\n const erasable = loaded.blob._cek !== undefined\n\n if (stamp !== undefined && loaded.blob.lastOps?.includes(stamp)) {\n // Already stamped: the decrement landed on a prior run. Two-armed\n // resume rule (spec C1).\n if (loaded.blob.refCount > 0) return erasable ? 'retainedShared' : 'residue'\n // refCount <= 0 but the object row is still here — the deletion step\n // never completed. Finish it now, idempotently.\n if (erasable || reclaimLegacy) {\n await this.store.delete(this.vault, BLOB_INDEX_COLLECTION, eTag)\n const chunkCount = chunkCountHint ?? loaded.blob.chunkCount\n for (let i = 0; i < chunkCount; i++) {\n await this.store.delete(this.vault, BLOB_CHUNKS_COLLECTION, `${eTag}_${i}`)\n }\n }\n return erasable ? 'shredded' : 'residue'\n }\n\n // Not yet stamped (or no stamp at all — the pre-#753 path): apply the\n // decrement now.\n const remaining = stamp !== undefined\n ? (await this.casUpdateRefCountStamped(eTag, -n, tier, stamp)).refCount\n : await this.casUpdateRefCount(eTag, -n, tier)\n if (remaining > 0) return erasable ? 'retainedShared' : 'residue'\n\n if (erasable || reclaimLegacy) {\n await this.store.delete(this.vault, BLOB_INDEX_COLLECTION, eTag)\n for (let i = 0; i < loaded.blob.chunkCount; i++) {\n await this.store.delete(this.vault, BLOB_CHUNKS_COLLECTION, `${eTag}_${i}`)\n }\n }\n return erasable ? 'shredded' : 'residue'\n }\n\n /**\n * Crypto-shred this record's blob attachments — called by\n * `vault.forget()`.\n *\n * For each distinct eTag the record references (a record may attach the same\n * content under several slot names → several refCount holds): decrement the\n * blob's refCount by that many. When it reaches 0:\n * - **erasable blob** (`_cek` present) → delete the `BlobObject` (the SOLE\n * recoverable copy of the wrapped content CEK → chunks permanently\n * undecryptable) and reclaim the chunk bytes. This is the crypto-shred.\n * - **legacy blob** (no `_cek`) → chunks are under the shared `_blob` DEK and\n * stay decryptable until byte-deleted; we delete the orphaned chunks +\n * index but report it as residue, not a cryptographic erasure.\n * When refCount stays > 0 the content legitimately persists for its other\n * owner — reported as `retainedShared` (or `residue` if legacy).\n *\n * Finally drops the record's slot map, severing the subject's link.\n *\n * @param ownerTier #724 Arc 10 C3: `vault.forget()` writes the record's\n * tombstone (which drops `_tier`) BEFORE calling this method, so the\n * `ownerTier()` default — a fresh peek at the now-tombstoned envelope —\n * would resolve tier 0 and decrypt the tier-scoped slot map under the\n * wrong DEK (`TamperedError`, erasure aborted mid-shred). `forget()` reads\n * the LIVE record first and passes its pre-tombstone tier here instead.\n *\n * #724 re-review (Critical) defense-in-depth: an unreadable slot map must\n * not abort the whole erasure cascade — `casUpdateSlots` deleting the row\n * instead of leaving it empty-but-present (rather than a stray mis-keyed\n * envelope) is what makes an ABSENT row the normal \"no blobs\" case, and\n * `loadSlots` already returns `{ slots: {}, version: 0 }` cleanly for\n * that, never throwing. So the only way this catch fires is a row that IS\n * present failing to decrypt — genuine corruption/mis-key/tamper — and\n * that must never be swallowed as \"no blobs to shred\": it is pushed onto\n * `residue` so `forget()` surfaces it via `blobResidueCollections` rather\n * than silently reporting a clean erasure while un-shredded blobs remain.\n *\n * #750: published versions (`_blob_versions_{collection}/{recordId}::*`)\n * take an INDEPENDENT refCount hold on a `BlobObject`, separate from the\n * slot map (see `publish()`) — the slot loop above never sees them. Left\n * untouched, that hold (and the version-held content, if the slot was\n * later overwritten) survives `forget()` — a GDPR-erasure hole. So this\n * method also enumerates the record's version rows via\n * `collectVersionHolds`, folds each version's hold into the SAME `holds`\n * map (a shared eTag reports ONE outcome, not two), and deletes the\n * readable version rows once their holds are released.\n *\n * **#753 spec §7 (crash-idempotent shred):** the holds used for the\n * actual release loop below come from a `_blob_intent` MARKER, never\n * re-collected from rows on resume:\n * - `forget()` mints the marker PRE-tombstone (`mintShredIntent`,\n * C5) — the normal case, so a marker is ALREADY present by the time\n * this method runs and `intent.ownerTier` (captured live, before the\n * tombstone dropped `_tier`) is authoritative even when the caller's\n * own `ownerTier` argument is stale (a crash-retried `forget()`\n * re-reads the now-tombstoned record and can only pass `0`).\n * - Called WITHOUT a pre-existing marker (defensive/non-forget callers,\n * or the direct-call path this suite exercises) — one is minted here\n * from the live rows, exactly like `mintShredIntent` does, so the\n * crash matrix holds regardless of caller (spec §2c).\n * - A pending `op:'rehome'` marker is resumed to completion FIRST (#746\n * spec §7 Q1 — \"supersession is resume-then-shred\": a half-done rehome\n * can leave a row-unreferenced destination object that shred's\n * row-derived holds can never see; replacing the marker instead of\n * resuming it would leak that object past `forget()` permanently), via\n * {@link resolveShredIntent} — then a fresh SHRED marker is minted (or\n * discovered, if the resume's own completion raced with another\n * minter) exactly as the no-marker branch below does.\n *\n * **Whole-branch review (#753):** the marker is a crash-safety\n * ENHANCEMENT, not a precondition for erasure. If the no-marker branch's\n * own mint fails (transient store error, `getDEK` failure — possibly the\n * same failure that already sent `forget()`'s pre-tombstone mint to\n * residue), this degrades to {@link unmarkedShred} — a best-effort\n * UNMARKED shred, exactly the pre-#753 behavior — rather than aborting\n * and leaving the blobs un-shredded.\n *\n * **#746 review Critical 1:** that degradation covers ONLY a genuine\n * no-marker mint failure — nothing was in flight, so a live-row\n * best-effort re-collection is safe. A REHOME-resume failure is a\n * DIFFERENT failure mode: a marker WAS present and its holds are\n * ambiguous mid-move (C10's whole point). `resolveShredIntent` below\n * therefore never wraps `consumeRehomeIntent` in the mint's degradation\n * try/catch — a resume failure propagates all the way out of this\n * method uncaught, leaving the rehome marker alive (never `deleteIntent`d)\n * for a later resume, instead of silently falling through to\n * `unmarkedShred`, which would both clobber rows the ambiguous rehome\n * still needs and abandon the marker forever (the exact permanent-leak\n * shape the journal exists to prevent).\n *\n * `collectShredHolds` is re-run here (idempotent — a corrupt/unreadable\n * row stays corrupt/unreadable on re-read; an already-deleted row reads\n * back cleanly empty) purely to derive the slot-map/version-row DELETE\n * targets and residue notices — never to recompute the release counts,\n * which are fixed for the operation's lifetime in the marker (C1/C2).\n */\n async shredAllForRecord(ownerTier?: number): Promise<{\n shredded: string[]\n retainedShared: string[]\n residue: string[]\n }> {\n // #808: device-local hygiene — the record's registry rows (pins, access\n // stamps, cached external copies) die with it. Best-effort and FIRST: a\n // failing device-local store must never abort the erasure cascade, and\n // dropping the encrypted cached copies before the shred means no local\n // copy outlives the record even if the shred itself later degrades.\n if (this.pinCache) {\n try {\n const rows = await this.pinCache.store.entries(blobPinKey(this.vault, this.collection, this.recordId))\n for (const { key } of rows) await this.pinCache.store.delete(key)\n } catch {\n // Device-local only — never part of the erasure accounting.\n }\n }\n const tierArg = ownerTier ?? 0\n const intent = await this.resolveShredIntent(tierArg)\n if (!intent) return this.unmarkedShred(tierArg)\n const tier = intent.ownerTier ?? tierArg\n const collected = await this.collectShredHolds(tier)\n return this.consumeShredIntent(intent, tier, collected)\n }\n\n /**\n * Resolve a SHRED `BlobIntent` for this record, or `null` when the entry\n * mint itself failed and nothing was in flight (the caller degrades to\n * {@link unmarkedShred}) — `shredAllForRecord`'s entry logic (#746 spec §7\n * Q1), factored out so the two failure modes stay structurally separate\n * (#746 review Critical 1):\n *\n * - A pending REHOME marker — found up front OR discovered via a raced\n * `createIntent` inside `mintFreshShredIntent` — is resumed via\n * {@link consumeRehomeIntent} OUTSIDE the `try` below. Its failure\n * PROPAGATES to the caller uncaught: holds are ambiguous mid-move, so\n * this must surface, never silently degrade.\n * - Only `mintFreshShredIntent` itself (the create-a-fresh-SHRED-marker\n * step, reached with no rehome in flight) is wrapped — its failure is\n * the ONLY thing `null` reports, matching the whole-branch-review\n * degrade contract exactly.\n *\n * The loop re-attempts the mint after resuming a raced rehome discovery,\n * mirroring `resolveToShredIntent`'s prior recursive shape without ever\n * routing a `consumeRehomeIntent` call through the mint's own catch.\n */\n private async resolveShredIntent(tierArg: number): Promise<BlobIntent | null> {\n let intent = await getIntent(this.store, this.vault, this.collection, this.recordId, this.getDEK)\n let effectiveTier = tierArg\n for (;;) {\n if (intent && intent.op === 'rehome') {\n await this.consumeRehomeIntent(intent) // #746 review Critical 1: never caught here — propagates\n // #746 whole-branch review Hardening 2: the resumed rehome's OWN\n // `toTier` is now this record's TRUE live tier — use it for the\n // fresh shred mint below, not the caller's `tierArg` (sound today\n // only because `forget()` happens to pass `live._tier`, which\n // equals it; a direct `shredAllForRecord(staleTier)` caller after a\n // resumed rehome must not `collectShredHolds` at the wrong tier).\n effectiveTier = intent.toTier!\n intent = null\n }\n if (intent) return intent\n try {\n intent = await this.mintFreshShredIntent(effectiveTier)\n } catch {\n // Whole-branch review (#753): the mint is a crash-safety enhancement,\n // never a precondition for erasure — report \"nothing resolved\" so\n // the caller degrades to a best-effort unmarked shred. Scoped to\n // ONLY the mint call above — a `consumeRehomeIntent` failure never\n // reaches this catch (#746 review Critical 1).\n return null\n }\n if (intent.op === 'shred') return intent\n // intent.op === 'rehome': raced with a concurrent minter (C4) — loop\n // back to resume IT (outside this try/catch) before retrying the mint.\n }\n }\n\n /**\n * Best-effort UNMARKED shred (#753 whole-branch review) — reached only\n * when `shredAllForRecord`'s own entry-mint threw, so there is no marker\n * to journal against. Reproduces the pre-#753 `shredAllForRecord` body:\n * collect this record's holds from the live rows, release each unstamped\n * (no journal — a crash here reverts to the pre-#753 stranding-eTag\n * exposure, exactly like main before this arc), then unconditionally drop\n * the slot map / readable version rows. The mint failure itself is\n * reported as `_blob_intent` residue so `forget()` surfaces the\n * degraded crash-safety posture via `blobResidueCollections`, never\n * silently.\n */\n private async unmarkedShred(tier: number): Promise<{\n shredded: string[]\n retainedShared: string[]\n residue: string[]\n }> {\n const shredded: string[] = []\n const retainedShared: string[] = []\n const collected = await this.collectShredHolds(tier)\n const residue: string[] = [...collected.residue, `${this.collection}:${this.recordId}:_blob_intent`]\n for (const hold of collected.holds) {\n const outcome = await this.releaseRef(hold.eTag, hold.n, true, tier)\n if (outcome === 'shredded') shredded.push(hold.eTag)\n else if (outcome === 'retainedShared') retainedShared.push(hold.eTag)\n else residue.push(hold.eTag)\n }\n if (collected.slotsPresent) await this.store.delete(this.vault, this.slotsCollection, this.recordId)\n for (const key of collected.versionKeysToDelete) await this.store.delete(this.vault, this.versionsCollection, key)\n return { shredded, retainedShared, residue }\n }\n\n /**\n * Collect this record's CURRENT blob holds (slot map + published\n * versions) at `tier` — the shred journal's single hold-collection\n * routine (#753 spec §7), shared by `mintFreshShredIntent` (to seed a\n * fresh marker's authoritative `holds`) and `shredAllForRecord`/\n * `resolvePendingIntent` (to derive delete targets + residue on\n * consumption — see {@link shredAllForRecord}'s doc comment for why that\n * second use never feeds the release loop). Mirrors the pre-#753\n * inline collection logic byte-for-byte; `chunkCount` per hold (C5) is\n * best-effort — an unreadable index row just means the marker's hint is\n * absent, `releaseRef`'s own chunkCountHint fallback still applies.\n */\n private async collectShredHolds(tier: number): Promise<{\n holds: BlobIntentHold[]\n slotsPresent: boolean\n versionKeysToDelete: string[]\n residue: string[]\n }> {\n const residue: string[] = []\n let slots: Record<string, SlotRecord> = {}\n try {\n slots = (await this.loadSlots(tier)).slots\n } catch {\n // See shredAllForRecord's pre-#753 doc comment (now on collectShredHolds):\n // `loadSlots` throws only when the row is PRESENT but undecodable.\n residue.push(`${this.collection}:${this.recordId}:_blob_slots`)\n }\n\n const holdsMap = new Map<string, number>()\n for (const name of Object.keys(slots)) {\n const eTag = slots[name]!.eTag\n holdsMap.set(eTag, (holdsMap.get(eTag) ?? 0) + 1)\n }\n const versionKeysToDelete = await this.collectVersionHolds(tier, holdsMap, residue)\n\n const holds: BlobIntentHold[] = []\n for (const [eTag, n] of holdsMap) {\n let chunkCount = 0\n try {\n chunkCount = (await this.loadBlobObject(eTag, tier))?.blob.chunkCount ?? 0\n } catch {\n // Best-effort (C5 doc comment): releaseRef's own chunkCountHint\n // fallback covers the index-row-already-gone case; a hint that\n // couldn't be captured here just means that fallback won't fire.\n }\n holds.push({ eTag, n, chunkCount })\n }\n return { holds, slotsPresent: Object.keys(slots).length > 0, versionKeysToDelete, residue }\n }\n\n /**\n * Collect this record's current holds and CAS-create a fresh `_blob_intent`\n * SHRED marker from them (#753 spec §7 C8). Used by `mintShredIntent`\n * (forget()'s pre-tombstone step) and `shredAllForRecord`'s no-marker\n * (defensive/direct-call) branch.\n *\n * C4: a concurrent minter for this SAME record can win the create race —\n * `createIntent` throws `BlobIntentPendingError`, and the winner's marker\n * (whatever op it turns out to be) is returned instead; the caller decides\n * how to handle a raced rehome marker.\n */\n private async mintFreshShredIntent(tier: number): Promise<BlobIntent> {\n const collected = await this.collectShredHolds(tier)\n const candidate: BlobIntent = { op: 'shred', opId: mintOpId(), holds: collected.holds, ownerTier: tier }\n try {\n await createIntent(this.store, this.vault, this.collection, this.recordId, this.getDEK, candidate)\n return candidate\n } catch (err) {\n if (!(err instanceof BlobIntentPendingError)) throw err\n const raced = await getIntent(this.store, this.vault, this.collection, this.recordId, this.getDEK)\n if (raced) return raced\n throw err // vanishingly unlikely: deleted between the failed create and this read\n }\n }\n\n /**\n * Release every hold in `intent.holds` (stamped with `intent.opId`), then\n * delete the slot map / readable version rows / the marker itself — the\n * shred journal's consume step (#753 spec §7 §2c), shared by\n * `shredAllForRecord` and `resolvePendingIntent`.\n *\n * **C10 (no swallowed releases under a marker):** a release that THROWS\n * (as opposed to returning `'residue'`, a normal non-error outcome for a\n * legacy blob) is caught, reported as residue, and marks the whole\n * consumption incomplete — the slot map, version rows, and the marker\n * itself are then left IN PLACE (not deleted) so a later resume retries\n * every hold. Stamped holds that already landed skip re-decrementing\n * (C1/C4's test-and-set), so redoing the full loop on retry is safe.\n */\n private async consumeShredIntent(\n intent: BlobIntent,\n tier: number,\n collected: { slotsPresent: boolean; versionKeysToDelete: string[]; residue: string[] },\n ): Promise<{ shredded: string[]; retainedShared: string[]; residue: string[] }> {\n const shredded: string[] = []\n const retainedShared: string[] = []\n const residue: string[] = [...collected.residue]\n let allApplied = true\n\n for (const hold of intent.holds ?? []) {\n try {\n const outcome = await this.releaseRef(hold.eTag, hold.n, true, tier, intent.opId, hold.chunkCount)\n if (outcome === 'shredded') shredded.push(hold.eTag)\n else if (outcome === 'retainedShared') retainedShared.push(hold.eTag)\n else residue.push(hold.eTag)\n } catch {\n residue.push(hold.eTag)\n allApplied = false\n }\n }\n\n if (allApplied) {\n if (collected.slotsPresent) await this.store.delete(this.vault, this.slotsCollection, this.recordId)\n for (const key of collected.versionKeysToDelete) await this.store.delete(this.vault, this.versionsCollection, key)\n await deleteIntent(this.store, this.vault, this.collection, this.recordId)\n }\n return { shredded, retainedShared, residue }\n }\n\n /**\n * Resume gate (#753/#746 spec §7 C6): every refCount/slot mutator calls\n * this FIRST. A pending SHRED marker means a previous `forget()`/\n * `shredAllForRecord()` crashed mid-flight — refCounts are ambiguous\n * until it's resumed, so no new blob write may proceed over them; this\n * resumes it to completion before the caller's own mutation runs. A\n * pending REHOME marker means a previous tier move crashed mid-flight —\n * resumed to completion the SAME way (§2d), via {@link consumeRehomeIntent}\n * using the marker's OWN captured `fromTier`/`toTier`/`policy`/`opId`,\n * never the caller's own ask (which may be a totally unrelated write).\n *\n * Never called from `shredAllForRecord`/`mintShredIntent`/\n * `mintFreshShredIntent`/`collectShredHolds`/`consumeShredIntent`/\n * `sweepPendingShredIntents`/`runRehomeSteps`/`consumeRehomeIntent`\n * themselves — those ARE the resume machinery; routing them back through\n * this gate would recurse.\n */\n private async resolvePendingIntent(): Promise<void> {\n const intent = await getIntent(this.store, this.vault, this.collection, this.recordId, this.getDEK)\n if (!intent) return\n if (intent.op === 'rehome') {\n await this.consumeRehomeIntent(intent)\n return\n }\n const tier = intent.ownerTier ?? 0\n const collected = await this.collectShredHolds(tier)\n await this.consumeShredIntent(intent, tier, collected)\n }\n\n /**\n * Resume a pending REHOME marker to completion: run {@link\n * runRehomeSteps} (never the public `rehomeForTier`/`syncTierMove` —\n * both would re-enter `resolvePendingIntent` on the SAME marker and\n * recurse) using the marker's own captured `fromTier`/`toTier`/`policy`/\n * `opId`, then delete it. Shared by `resolvePendingIntent`'s rehome\n * branch and `resolveToShredIntent` (#746 spec §7 Q1: forget() resumes a\n * pending rehome before shredding).\n */\n private async consumeRehomeIntent(intent: BlobIntent): Promise<void> {\n await this.runRehomeSteps(intent.fromTier!, intent.toTier!, intent.policy!, intent.opId)\n await deleteIntent(this.store, this.vault, this.collection, this.recordId)\n }\n\n /**\n * Resume every stranded SHRED marker in THIS collection (#753 spec §7 —\n * `sweepBlobIntents`'s first real production caller, Task 3 item 5).\n * Scoped to `this.collection` rather than the whole vault: `forget()`\n * already iterates per-ref-collection, so calling this from\n * `mintShredIntent` once per ref opportunistically heals any OTHER\n * record's marker left behind by a previous crashed operation in the\n * same collection — \"heals untouched ones\" (spec C5's retry contract),\n * without a separate vault-open hook. A pending REHOME marker on a\n * sibling record is left alone here (out of THIS sweep's scope — it heals\n * on its OWN record's next write/tier-op via `resolvePendingIntent`,\n * §2d's \"who resumes\") rather than surfaced as a resume failure.\n * `sweepBlobIntents`'s own per-marker\n * isolation (blob-intent.ts) means one corrupt sibling never blocks\n * another record's healthy resume.\n */\n private async sweepPendingShredIntents(): Promise<void> {\n await sweepBlobIntents(this.store, this.vault, this.getDEK, async (collection, recordId, marker) => {\n if (collection !== this.collection || marker.op !== 'shred') return\n const target = new BlobSet({\n store: this.store, vault: this.vault, collection, recordId,\n getDEK: this.getDEK, encrypted: this.encrypted,\n })\n await target.resolvePendingIntent()\n })\n }\n\n /**\n * Mint the `_blob_intent` SHRED marker for this record — `vault.forget()`'s\n * PRE-tombstone step (#753 spec §7 C5). Captures this record's CURRENT\n * blob holds (slot map + published versions, at `ownerTier` — the LIVE\n * pre-tombstone tier) into a fresh marker before any destructive write\n * happens, so a crash between here and `shredAllForRecord`'s completion —\n * including one straddling the tombstone itself, which drops `_tier` and\n * would otherwise strand an elevated record's holds unrecoverably on\n * retry — leaves a resumable, tier-correct record of what must still be\n * released.\n *\n * First sweeps this collection for any stranded marker\n * (`sweepPendingShredIntents`) — this resumes a PRE-EXISTING marker for\n * THIS record (a previous forget() attempt crashed mid-shred) to\n * completion before minting fresh (C8: never overwrite a pending\n * marker — that would orphan its op-stamps), as a side effect of the\n * same collection-wide sweep.\n */\n async mintShredIntent(ownerTier: number): Promise<void> {\n await this.sweepPendingShredIntents()\n await this.mintFreshShredIntent(ownerTier)\n }\n\n /**\n * #750: enumerate this record's published-version rows (`{recordId}::*` in\n * `_blob_versions_{collection}` — the same raw prefix scan as\n * `rehomeVersionRecords`) and fold each version's independent refCount hold\n * into `holds`. Returns the READABLE version keys — safe to delete once\n * their holds are released. An unreadable row is pushed onto `residue` and\n * NOT returned: deleting it blind would orphan its refCount hold and strand\n * the content undecryptable-but-undeleted forever, so it stays in place for\n * out-of-band repair (mirrors the unreadable-slot-map posture above).\n */\n private async collectVersionHolds(\n ownerTier: number | undefined,\n holds: Map<string, number>,\n residue: string[],\n ): Promise<string[]> {\n const prefix = `${this.recordId}::`\n const keys = (await this.store.list(this.vault, this.versionsCollection)).filter((k) => k.startsWith(prefix))\n if (keys.length === 0) return []\n const readable: string[] = []\n const dek = this.encrypted ? await this.getDEK(dekKey(this.collection, ownerTier ?? await this.ownerTier())) : null\n for (const key of keys) {\n const envelope = await this.store.get(this.vault, this.versionsCollection, key)\n if (!envelope) continue\n try {\n const record = this.encrypted\n ? JSON.parse(await openEnvelopeJson(envelope, dek!)) as VersionRecord\n : JSON.parse(envelope._data) as VersionRecord\n holds.set(record.eTag, (holds.get(record.eTag) ?? 0) + 1)\n readable.push(key)\n } catch {\n residue.push(`${this.collection}:${this.recordId}:${key}`)\n }\n }\n return readable\n }\n\n /**\n * Rehome this record's blobs after a tier move (#724 Arc 10 Task 2,\n * at-rest isolation) — called by `TiersContext.syncBlobs`. A blob's home\n * tier is its owning record's tier: the `_blob` DEK is tier-scoped via\n * `dekKey('_blob', tier)`, so moving a record's tier must move the\n * wrapping key of any blob it exclusively owns, or a tier-0-cleared\n * caller could still unwrap the content CEK off the store at rest even\n * though the runtime read gate (Task 1) hides it through the API.\n *\n * Every read/write of the slot map INSIDE this method pins `fromTier`\n * explicitly (never the `loadSlots()`/`saveSlots()` default) — by the\n * time this runs, the owning record's live `_tier` envelope has ALREADY\n * moved to `toTier` (the collection-level `elevate`/`demote`/`putAtTier`\n * writes the record before invoking `syncBlobs`), but the slot map itself\n * is still physically encrypted at `fromTier` until this method's own\n * move step (last) lands. `ownerTier()`'s default would resolve the\n * wrong DEK for every read until then.\n *\n * Enumerates the record's eTags via the slot map — mirrors\n * `shredAllForRecord`'s `loadSlots()` → `holds` enumeration above. For\n * each distinct eTag:\n * - **solo** (`refCount === 1`) and **shared `isolate`** (`refCount > 1`,\n * default policy) are UNIFIED (#724 Arc 10 correction, closes C1): both\n * re-`put()` the plaintext under `toBlobDEK` for every one of this\n * record's slots pointing at the eTag. That mints a fresh, tier-scoped\n * eTag/`_cek` (HMAC + wrap keyed by `toBlobDEK`) and, via `put()`'s own\n * existing old-eTag decrement, releases this record's hold on the old\n * object — a solo blob's old object drops to refCount 0 and is\n * crypto-shredded; a shared co-owner keeps its unchanged refCount. The\n * OLD in-place rewrap (`wrapCek(unwrapCek(_cek, fromDEK), toDEK)`, eTag\n * held stable) is UNSAFE — it leaves the eTag in the FROM tier's HMAC\n * namespace, so a later same-bytes put computed under that tier's DEK\n * dedup-*hits* the wrapped object (cross-tier corruption of an\n * uninvolved writer). Re-`put()`ing makes that structurally\n * impossible: different tiers hash to different eTags. Landing at\n * `toTier === 0` naturally REJOINS the tier-0 dedup pool if a co-owner\n * already holds the content there (`put()`'s own dedup check matches\n * on the recomputed tier-0-native eTag) — this is how `demote(→0)`\n * reverses an `elevate()` fork.\n * - **shared `dedup`** (#741, opt-in, only for `refCount > 1`): NO-OP.\n * The slot keeps pointing at the shared eTag. The Task-1 runtime read\n * gate still hides it from a tier-0 caller; the chunks remain\n * decryptable at rest under the flat `_blob` DEK (documented residue).\n * - **legacy** (`_cek` absent — chunks direct under the flat `_blob`\n * DEK): NO-OP. Tiered collections mandate `perRecordKeys`, so new\n * blob data is always erasable; a legacy blob reaching this method\n * would need a chunk re-encrypt (not attempted here).\n *\n * #724 Arc 10 Task 4 — slot-map metadata move: LAST step, after every\n * fork/rewrap/reconcile write above (which all operated on the slot map\n * via the explicit `fromTier`, its still-current physical location).\n * Re-reads (to pick up any fork/reconcile-produced eTag changes from the\n * loop) and re-encrypts the SAME `_blob_slots_{collection}/{recordId}`\n * row under the `toTier` collection DEK — filenames/sizes/mimeTypes/eTags\n * are no longer tier-0-readable at rest for an elevated record. No\n * separate delete: it's the same physical row, just re-keyed in place\n * (mirrors `history.ts`'s `rewrapHistory`).\n *\n * @param opId #753/#746 spec §7 C3: the governing `_blob_intent` rehome\n * marker's op-stamp identity, threaded through by `syncTierMove`. Omitted\n * (a direct call, e.g. tests) → byte-identical unstamped behavior. When\n * present, every DESTINATION refCount `+1` this call performs (the slot\n * loop's dedup-hit re-put below, and `rehomeVersionETag`'s own increment)\n * carries a ROW-SCOPED stamp — `${opId}:${slotName}` / `${opId}:${versionKey}`,\n * never the bare opId — so a resumed re-put's `+1` is idempotent per row\n * without collapsing N legitimate `+1`s onto one destination eTag into one.\n *\n * The direct-call entry point: resumes a pending SHRED marker first\n * (unchanged, #753 C6), or a pending REHOME marker for a DIFFERENT prior\n * op (#746 Q1 — resumed via its OWN stored fromTier/toTier/opId before\n * this call's requested move proceeds), then runs {@link runRehomeSteps}.\n * `syncTierMove` — the real tier-op seam (`syncBlobs`) — calls\n * `runRehomeSteps` directly instead, after minting/consuming its own\n * marker (calling back through here would re-enter `resolvePendingIntent`\n * on the marker `syncTierMove` itself just created).\n */\n async rehomeForTier(fromTier: number, toTier: number, policy: 'isolate' | 'dedup', opId?: string): Promise<void> {\n if (!this.encrypted || fromTier === toTier) return\n await this.resolvePendingIntent() // #753/#746 spec §7 C6/Q1: resume a pending shred OR a stale rehome first\n await this.runRehomeSteps(fromTier, toTier, policy, opId)\n }\n\n /**\n * The tier-op → rehome seam (#746 spec §7 §2d) — called by\n * `TiersContext.syncBlobs` (`collection.ts`), replacing the pre-#746\n * unstamped `rehomeForTier` call. Mints the governing `_blob_intent`\n * REHOME marker BEFORE the first write (CAS create-if-absent), resuming\n * any marker already pending for this record first — a stale rehome from\n * an earlier, different move, or a shred, per the same C6/Q1 resume-first\n * law `rehomeForTier`'s own entry follows. Deletes the marker as the LAST\n * step, once every phase of {@link runRehomeSteps} completes without\n * throwing (a failure — including a C10 unswallowed release — leaves the\n * marker in place for the next resume).\n */\n async syncTierMove(fromTier: number, toTier: number, policy: 'isolate' | 'dedup'): Promise<void> {\n if (!this.encrypted || fromTier === toTier) return\n await this.resolvePendingIntent() // resume whatever is already pending for this record first\n if (await this.isBlobFree(fromTier)) return // #746 whole-branch review Hardening 1: nothing to journal\n const opId = mintOpId()\n const intent: BlobIntent = { op: 'rehome', opId, fromTier, toTier, policy }\n try {\n await createIntent(this.store, this.vault, this.collection, this.recordId, this.getDEK, intent)\n } catch (err) {\n if (!(err instanceof BlobIntentPendingError)) throw err\n // Raced with a concurrent minter for this record (C8) — resume\n // whichever marker won, then retry this request once resolved.\n await this.resolvePendingIntent()\n return this.syncTierMove(fromTier, toTier, policy)\n }\n await this.runRehomeSteps(fromTier, toTier, policy, opId)\n await deleteIntent(this.store, this.vault, this.collection, this.recordId)\n }\n\n /**\n * #746 whole-branch review Hardening 1: `syncTierMove` runs on EVERY tier\n * move (`elevate`/`demote`/`putAtTier`) — including the common case of a\n * record that has never attached a blob. Minting-then-deleting a marker\n * for such a record is pure overhead (~4 extra adapter ops on every\n * bulk elevate/demote of blob-less records) protecting nothing: an empty\n * slot map AND no published versions means there is genuinely no content\n * this op could strand. Checked AFTER `resolvePendingIntent` (a pending\n * marker, however stale, is always resumed first regardless of current\n * blob state — it may reference content this check alone wouldn't see)\n * and BEFORE minting a new one.\n */\n private async isBlobFree(fromTier: number): Promise<boolean> {\n const { slots } = await this.loadSlots(fromTier)\n if (Object.keys(slots).length > 0) return false\n const prefix = `${this.recordId}::`\n const versionKeys = await this.store.list(this.vault, this.versionsCollection)\n return !versionKeys.some((k) => k.startsWith(prefix))\n }\n\n /**\n * The resumable rehome executor (#746 spec §7 §2d) — `rehomeForTier`'s\n * pre-#746 body, factored out so it can be invoked WITHOUT re-entering\n * `resolvePendingIntent` (which would recurse: this method IS part of the\n * resume machinery, called from `resolvePendingIntent`'s own rehome\n * branch and from `syncTierMove` after its marker is already settled).\n *\n * Per-step resume tolerance:\n * - Slot map: {@link loadSlotsTolerant} tries `fromTier` then `toTier` —\n * if it opens at `toTier`, a prior run's move step already landed, so\n * the ENTIRE per-eTag loop (strictly sequenced before the move) is\n * also already done — skip both, but first reconstruct `rehomedETags`\n * (see below) so the version pass's same-eTag fast path still works\n * (carried finding (a)).\n * - Per-eTag: {@link loadBlobObjectResumable} — an eTag that only opens\n * under `toTier` means THIS slot's re-put already landed on a prior\n * run — skip re-processing it.\n * - Slot-CAS→release gap (carried finding (b)): {@link\n * reconcilePendingReleases} runs FIRST, completing any old-eTag\n * release a prior run's `putUnderDEK` left stranded (slot already\n * moved to the new eTag, release not yet applied) before either the\n * \"already moved\" or \"still moving\" branch below runs.\n * - Version pass: same per-key tolerance, in `rehomeVersionRecords`.\n * - Destination `+1`s (#746 whole-branch review, K=8 stamp-ring\n * blocker): `knownApplied` — this op's marker-backed\n * `appliedStamps`, read ONCE here — makes every row-scoped\n * increment idempotent INDEPENDENTLY of `BlobObject.lastOps`'s\n * bounded ring; see {@link applyStampedIncrement}'s doc comment.\n */\n private async runRehomeSteps(fromTier: number, toTier: number, policy: 'isolate' | 'dedup', opId?: string): Promise<void> {\n const { slots, atTier: slotsAtTier } = await this.loadSlotsTolerant(fromTier, toTier)\n if (opId !== undefined) await this.reconcilePendingReleases(slots, fromTier, opId)\n const knownApplied: ReadonlySet<string> = opId !== undefined\n ? new Set((await getIntent(this.store, this.vault, this.collection, this.recordId, this.getDEK))?.appliedStamps ?? [])\n : EMPTY_STAMP_SET\n\n // Old eTag → new eTag for every object the slot loop below physically\n // re-`put()`s. Passed on to `rehomeVersionRecords` (#724 Arc 10 Task 2,\n // C4) so a version pinned to the SAME eTag a slot held can skip a\n // redundant fetch+re-encrypt — same plaintext + same `toBlobDEK` always\n // hashes to the same destination eTag (content-addressing).\n const rehomedETags = new Map<string, string>()\n\n if (Object.keys(slots).length > 0) {\n if (slotsAtTier === toTier) {\n // Already fully moved by a prior (crashed) run — the move step is\n // strictly sequenced AFTER the per-eTag loop, so its completion\n // proves the loop already ran to completion too. Reconstruct\n // `rehomedETags` (old → new) so a version pass below sharing one of\n // these eTags still hits the fast path (carried finding (a)) rather\n // than falling through to a fromTier lookup for an eTag that may no\n // longer exist there.\n const toBlobDEK = await this.getDEK(dekKey(BLOB_COLLECTION, toTier))\n const fromBlobDEK = await this.getDEK(dekKey(BLOB_COLLECTION, fromTier))\n const doneETags = new Set(Object.values(slots).map((s) => s.eTag).filter((eTag) => eTag !== ''))\n for (const eTag of doneETags) {\n let loaded: { blob: BlobObject; version: number; atTier: number } | null\n try {\n // #746 review Critical 2: NO `alsoTryTier` — `loadBlobObject`'s\n // own DEFAULT fallback (implicit tier 0 when `toTier > 0`) would\n // otherwise silently open a still-flat `dedup`-left-in-place\n // object (#741, refCount > 1) at tier 0 and report success; the\n // `loaded.atTier !== toTier` guard below is what actually\n // catches that case (a `TamperedError` here only covers the\n // rarer \"no fallback exists at all\", e.g. `toTier === 0`).\n loaded = await this.loadBlobObject(eTag, toTier)\n } catch (err) {\n if (err instanceof TamperedError) continue // legacy/dedup, left flat — never moved, nothing to reconstruct\n throw err\n }\n // `loaded.atTier !== toTier`: the object opened via a FALLBACK\n // tier (not `toTier` itself) — it never actually moved to\n // `toTier` (a `dedup`-policy shared object left flat on purpose,\n // #741). Fetching it under `toBlobDEK` below would unwrap a\n // `_cek` that was never wrapped under that key — an uncaught\n // `TamperedError`/unwrap failure, permanently stuck on resume.\n // Nothing to reconstruct for it: its slot never held a DIFFERENT\n // eTag to begin with.\n if (!loaded || loaded.atTier !== toTier || loaded.blob._cek === undefined) continue\n const plaintext = await this.fetchAllChunks(loaded.blob, toBlobDEK)\n const oldETag = await hmacSha256Hex(fromBlobDEK, plaintext)\n if (oldETag !== eTag) rehomedETags.set(oldETag, eTag)\n }\n } else {\n const eTags = new Set(Object.values(slots).map((s) => s.eTag).filter((eTag) => eTag !== ''))\n\n if (eTags.size > 0) {\n const fromBlobDEK = await this.getDEK(dekKey(BLOB_COLLECTION, fromTier))\n const toBlobDEK = await this.getDEK(dekKey(BLOB_COLLECTION, toTier))\n\n for (const eTag of eTags) {\n const loaded = await this.loadBlobObjectResumable(eTag, fromTier, toTier)\n if (!loaded) continue\n if (loaded.atTier === toTier) continue // already moved by a prior run — see doc comment\n const { blob } = loaded\n if (blob._cek === undefined) continue // legacy: no rewrap (see doc comment)\n if (policy === 'dedup' && blob.refCount > 1) continue // #741: leave the shared object; read gate covers runtime\n\n // solo + shared-isolate, unified (#724 Arc 10 correction): fork for\n // every slot on THIS record pointing at the eTag, then release this\n // record's hold on the old object (via put()'s own old-eTag\n // decrement) — a solo blob's old object is crypto-shredded at\n // refCount 0, a shared co-owner keeps its unchanged refCount.\n const plaintext = await this.fetchAllChunks(blob, fromBlobDEK)\n rehomedETags.set(eTag, await hmacSha256Hex(toBlobDEK, plaintext))\n for (const [slotName, slot] of Object.entries(slots)) {\n if (slot.eTag !== eTag) continue\n // #747: `fromTier` still pins the slot-map CAS (it's physically\n // there until this method's own move step, last); `toTier` is the\n // NEW object's home — thread it alongside `toBlobDEK` so the fresh\n // `_blob_index` envelope is born tier-keyed, not flat.\n await this.putUnderDEK(slotName, plaintext, toBlobDEK, {\n filename: slot.filename,\n ...(blob.mimeType !== undefined ? { mimeType: blob.mimeType } : {}),\n compress: blob.compression === 'gzip',\n ...(slot.uploadedBy !== undefined ? { uploadedBy: slot.uploadedBy } : {}),\n }, fromTier, toTier, opId, knownApplied)\n }\n }\n }\n\n const { slots: finalSlots, version: finalVersion } = await this.loadSlots(fromTier)\n await this.saveSlots(finalSlots, finalVersion, toTier)\n }\n }\n\n // #724 Arc 10 Task 2 (C4): published versions follow too. `publish()`\n // takes an INDEPENDENT refCount hold on a `BlobObject`, separate from\n // the slot map — the loop above only walks\n // `_blob_slots_{collection}/{recordId}` and never sees it. A version\n // whose eTag was superseded in the slot map (overwritten after publish)\n // would otherwise never be rehomed at all, and the version RECORD\n // itself (label/eTag/timestamps) stays on the fromTier collection DEK.\n await this.rehomeVersionRecords(fromTier, toTier, policy, rehomedETags, opId, knownApplied)\n }\n\n /**\n * Rehome this record's PUBLISHED VERSIONS after a tier move — the second\n * half of `rehomeForTier` (#724 Arc 10 Task 2, closes C4). Enumerates\n * every `_blob_versions_{collection}/{recordId}::*` row (mirrors\n * `listVersions`'s raw prefix scan, but across ALL slots on this record,\n * not one) and, for each:\n * - rehomes its held eTag via {@link rehomeVersionETag} (reusing the slot\n * loop's result when the version happens to hold the SAME eTag a slot\n * held — see that method's doc comment), and\n * - re-keys the version RECORD itself onto the `toTier` collection DEK,\n * regardless of whether the content moved — metadata protection is\n * orthogonal to the shared-content dedup policy (matches the slot-map\n * move, which always relocates even for a `dedup`-left-in-place blob).\n *\n * Per-key resume tolerance (#746 spec §7 §2d bullet 3): each key's\n * metadata is read via {@link loadVersionRecordAtKeyTolerant} (try\n * `fromTier` then `toTier`) — a key whose metadata already opens at\n * `toTier` had its `resolveRehomedVersionETag` call AND its\n * `writeVersionRecordAtKey` BOTH already land on a prior run (the\n * metadata write is sequenced strictly after the eTag rehome, which\n * itself completes the old-eTag release under C1's two-armed resume rule\n * — same order as the slot loop's own CAS-then-release), so it's skipped\n * entirely.\n *\n * Known residual gap (documented, not closed by this task — see the T2\n * report): if a version's OLD eTag is held ONLY by that version (never\n * shared with any slot), a crash strictly between\n * `resolveRehomedVersionETag`'s release (drops it to refCount 0,\n * crypto-shredding a solo-held erasable object) and THIS method's\n * metadata write would leave the version pointing at an eTag whose sole\n * copy is already gone. This mirrors the slot-side \"carried finding (b)\"\n * shape but on the version side; closing it durably (a `pendingRelease`-\n * style breadcrumb on `VersionRecord`, filtered from the public\n * `listVersions()` surface) is out of scope for this task.\n */\n private async rehomeVersionRecords(\n fromTier: number,\n toTier: number,\n policy: 'isolate' | 'dedup',\n rehomedETags: Map<string, string>,\n opId?: string,\n knownApplied?: ReadonlySet<string>,\n ): Promise<void> {\n const prefix = `${this.recordId}::`\n const allKeys = await this.store.list(this.vault, this.versionsCollection)\n const matchingKeys = allKeys.filter((k) => k.startsWith(prefix))\n if (matchingKeys.length === 0) return\n\n const fromBlobDEK = await this.getDEK(dekKey(BLOB_COLLECTION, fromTier))\n const toBlobDEK = await this.getDEK(dekKey(BLOB_COLLECTION, toTier))\n\n for (const key of matchingKeys) {\n const loaded = await this.loadVersionRecordAtKeyTolerant(key, fromTier, toTier)\n if (!loaded) continue\n if (loaded.atTier === toTier) continue // already fully rehomed on a prior run — see doc comment\n\n const stamp = opId !== undefined ? `${opId}:${key}` : undefined\n const newETag = await this.resolveRehomedVersionETag(loaded.record, fromTier, toTier, fromBlobDEK, toBlobDEK, policy, rehomedETags, stamp, knownApplied)\n const moved: VersionRecord = newETag === loaded.record.eTag ? loaded.record : { ...loaded.record, eTag: newETag }\n await this.writeVersionRecordAtKey(key, moved, toTier)\n }\n }\n\n /**\n * Resumable per-key version-record metadata read (#746 spec §7 §2d bullet\n * 3) — mirrors {@link loadSlotsTolerant}: try `fromTier` first, fall back\n * to `toTier` on a decrypt failure. `atTier === toTier` on return is the\n * \"already fully rehomed\" signal `rehomeVersionRecords` skips on.\n */\n private async loadVersionRecordAtKeyTolerant(\n key: string, fromTier: number, toTier: number,\n ): Promise<{ record: VersionRecord; atTier: number } | null> {\n const envelope = await this.store.get(this.vault, this.versionsCollection, key)\n if (!envelope) return null\n if (!this.encrypted) return { record: JSON.parse(envelope._data) as VersionRecord, atTier: fromTier }\n\n const fromDek = await this.getDEK(dekKey(this.collection, fromTier))\n try {\n const json = await openEnvelopeJson(envelope, fromDek)\n return { record: JSON.parse(json) as VersionRecord, atTier: fromTier }\n } catch (err) {\n if (!(err instanceof TamperedError) || fromTier === toTier) throw err\n }\n const toDek = await this.getDEK(dekKey(this.collection, toTier))\n const json = await openEnvelopeJson(envelope, toDek)\n return { record: JSON.parse(json) as VersionRecord, atTier: toTier }\n }\n\n /**\n * Rehome ONE version's independently-held eTag — both the DESTINATION\n * `+1`/create AND the OLD eTag's release, in that order (mirrors the slot\n * loop's own CAS-then-release sequencing, and matches C1's two-armed\n * resume rule: a crash after the release CAS lands but before the index\n * row's delete completes is resumable — `already`/`loadBlobObject` still\n * find the row at `fromTier` on a re-run, since the CALLER's metadata\n * write hasn't happened yet). Returns the eTag the version should now\n * point at (unchanged if legacy/missing/left `dedup`-shared) — the caller\n * (`rehomeVersionRecords`) writes the version's metadata after this\n * returns.\n *\n * If the version happened to hold the SAME eTag a slot held (the common\n * case: publish right after put, no later overwrite), the slot loop in\n * `runRehomeSteps` already re-`put()` the content under `toBlobDEK` —\n * `rehomedETags` (content-addressed, so deterministic) tells us the\n * resulting destination eTag without a redundant fetch+re-encrypt; we\n * only need to move THIS hold's refCount onto it. Otherwise (the version\n * outlived its slot, or was never in the slot map) this re-`put()`s the\n * plaintext itself via `writeBlobContent` — the same content-write core\n * `put()`/the slot loop use, no new crypto — mirroring the slot case's\n * legacy/`dedup`-shared skip conditions.\n *\n * @param fromTier / @param toTier #747: same from/to split as the slot\n * loop above — the read of the version's CURRENTLY-held eTag pins\n * `fromTier`; the re-put and the refCount bump onto an already-rehomed\n * (slot-loop-produced) eTag pin `toTier`.\n *\n * @param stamp #753/#746 spec §7 C3: when present (a marker-governed\n * rehome), every destination `+1` this call performs — both the\n * `already`-rehomed fast path's explicit CAS and the `writeBlobContent`\n * re-put's own dedup-hit CAS — carries the row-scoped stamp\n * `${opId}:${versionKey}` the caller computed, never the bare opId (see\n * `rehomeForTier`'s `opId` doc comment).\n * @param knownApplied #746 whole-branch review (K=8 stamp-ring blocker):\n * this op's marker-backed confirmed-stamps set — see\n * {@link applyStampedIncrement}. Threaded through to the\n * `writeBlobContent` re-put's own dedup-hit CAS too.\n */\n private async resolveRehomedVersionETag(\n record: VersionRecord,\n fromTier: number,\n toTier: number,\n fromBlobDEK: EnclaveKey,\n toBlobDEK: EnclaveKey,\n policy: 'isolate' | 'dedup',\n rehomedETags: Map<string, string>,\n stamp?: string,\n knownApplied?: ReadonlySet<string>,\n ): Promise<string> {\n const already = rehomedETags.get(record.eTag)\n if (already !== undefined) {\n if (stamp !== undefined) {\n await this.applyStampedIncrement(already, toTier, stamp, knownApplied ?? EMPTY_STAMP_SET)\n } else {\n await this.casUpdateRefCount(already, +1, toTier)\n }\n await this.releaseOldETagAfterMove(record.eTag, fromTier, stamp) // C10: not swallowed under a marker\n return already\n }\n\n const loaded = await this.loadBlobObject(record.eTag, fromTier)\n if (!loaded || loaded.blob._cek === undefined) return record.eTag // legacy/missing: no-op\n if (policy === 'dedup' && loaded.blob.refCount > 1) return record.eTag // #741: same residue as the slot case\n\n const plaintext = await this.fetchAllChunks(loaded.blob, fromBlobDEK)\n const { eTag: newETag } = await this.writeBlobContent(plaintext, toBlobDEK, {\n filename: record.label,\n ...(loaded.blob.mimeType !== undefined ? { mimeType: loaded.blob.mimeType } : {}),\n compress: loaded.blob.compression === 'gzip',\n }, toTier, stamp, knownApplied)\n rehomedETags.set(record.eTag, newETag) // memoize: two versions may share an eTag outside the slot map too\n if (newETag !== record.eTag) {\n await this.releaseOldETagAfterMove(record.eTag, fromTier, stamp) // C10: not swallowed under a marker\n }\n return newETag\n }\n\n /**\n * CAS retry loop for an arbitrary BlobObject mutation. Used only by\n * `migrate()`, which is legacy-only by definition — a legacy object is\n * always flat-keyed — so the tier is pinned to `0` explicitly rather than\n * left to `loadBlobObject`/`writeBlobObject`'s independent `ownerTier()`\n * defaults, which could diverge on an elevated owner and re-key the\n * envelope under the elevated tier DEK while chunks stay flat (#747 review).\n */\n private async casUpdateBlobObject(\n eTag: string,\n mutate: (blob: BlobObject) => BlobObject,\n ): Promise<void> {\n for (let attempt = 0; attempt < MAX_CAS_RETRIES; attempt++) {\n const result = await this.loadBlobObject(eTag, 0)\n if (!result) throw new NotFoundError(`BlobObject ${eTag} not found`)\n try {\n await this.writeBlobObject(mutate(result.blob), result.version, 0)\n return\n } catch (err) {\n if (isConflictError(err) && attempt < MAX_CAS_RETRIES - 1) continue\n throw err\n }\n }\n throw new ConflictError(-1) // exhausted retries\n }\n\n /**\n * Migrate this record's LEGACY blobs (no `_cek`, chunks under the shared\n * `_blob` DEK) to per-blob content CEKs so they become crypto-shreddable.\n * Returns the eTags migrated vs. already-erasable.\n *\n * **Explicit maintenance pass** (mirrors the record-CEK migration posture):\n * re-encrypts the existing compressed chunks IN PLACE under a fresh content\n * CEK — preserving the eTag, chunkCount, chunkSize, and compression — then\n * flips the `_cek` discriminant. Crash-safe + idempotent via `_cekPending`:\n * 1. persist the wrapped content CEK in `_cekPending` (readers ignore it →\n * the blob stays readable under the `_blob` DEK; the key survives a crash);\n * 2. re-encrypt each chunk under the content CEK (a resume reads an\n * already-migrated chunk under the content CEK, else under the `_blob` DEK);\n * 3. promote `_cekPending` → `_cek` (atomic flip). Reads now use the CEK.\n * A re-run after a crash resumes from whichever phase was reached.\n *\n * Dedup-safe: migrating a shared blob (refCount > 1) re-keys it for every\n * referencer at once; a non-erasable collection still reads it (it unwraps\n * `_cek` under the `_blob` DEK it holds).\n */\n async migrate(): Promise<{ migrated: string[]; alreadyErasable: string[] }> {\n const migrated: string[] = []\n const alreadyErasable: string[] = []\n if (!this.encrypted) return { migrated, alreadyErasable }\n await this.resolvePendingIntent() // #753/#756 spec §7 C6/C7: resume a pending shred or rehome marker before migrating\n\n const blobDEK = await this.getDEK(BLOB_COLLECTION)\n // #756 spec §3/C7: read the slot map at the record's OWNER tier, not a\n // hardcoded 0 — `elevate()`/`rehomeForTier` re-keys a non-empty slot map\n // onto the destination tier's DEK regardless of whether its blobs are\n // legacy, so a previously-elevated record's slot map is no longer\n // openable under the flat tier-0 DEK and pinning `0` here threw\n // `TamperedError` before migrate() did anything.\n const ownerTier = await this.ownerTier()\n const { slots } = await this.loadSlots(ownerTier)\n const eTags = new Set(Object.values(slots).map((s) => s.eTag))\n\n for (const eTag of eTags) {\n // #747 two-tier fallback (`loadBlobObject(eTag, ownerTier, 0)`): a\n // mixed slot map can hold both rehomed tier-keyed eTags and genuinely\n // legacy-flat ones — `t === 0` has no fallback by default, so this\n // must be passed explicitly. An object that opens at `atTier > 0` is\n // erasable by construction (rehome only ever moves per-record-CEK\n // objects onto a tier) — record it as already-erasable and never\n // touch it; migrate() only upgrades genuinely flat legacy objects\n // (`_cek === undefined`, opened at tier 0).\n const loaded = await this.loadBlobObject(eTag, ownerTier, 0)\n if (!loaded) continue\n const blob = loaded.blob\n if (loaded.atTier > 0 || blob._cek !== undefined) { alreadyErasable.push(eTag); continue }\n\n // Phase 1 — persist the content CEK (resume reuses an existing pending one).\n let contentCek: EnclaveKey\n if (blob._cekPending !== undefined) {\n contentCek = await unwrapCek(blob._cekPending, blobDEK)\n } else {\n contentCek = await generateDEK()\n const wrapped = await wrapCek(contentCek, blobDEK)\n await this.casUpdateBlobObject(eTag, (b) => ({ ...b, _cekPending: wrapped }))\n }\n\n // Phase 2 — re-encrypt each chunk under the content CEK, in place.\n for (let i = 0; i < blob.chunkCount; i++) {\n let raw: Uint8Array | null\n try {\n raw = await this.readChunk(eTag, i, blob.chunkCount, blobDEK)\n } catch {\n // Already re-encrypted under the content CEK (crash resume).\n raw = await this.readChunk(eTag, i, blob.chunkCount, contentCek)\n }\n if (!raw) {\n throw new NotFoundError(\n `Blob chunk ${i}/${blob.chunkCount} missing for eTag \"${eTag}\" during migration`,\n )\n }\n await this.writeChunk(eTag, i, blob.chunkCount, raw, contentCek)\n }\n\n // Phase 3 — promote _cekPending → _cek (atomic flip).\n await this.casUpdateBlobObject(eTag, (b) => {\n const { _cekPending, ...rest } = b\n return _cekPending === undefined ? b : { ...rest, _cek: _cekPending }\n })\n migrated.push(eTag)\n }\n return { migrated, alreadyErasable }\n }\n\n // ─── Chunk I/O (with AAD binding) ─────────────────────────────────\n\n private async writeChunk(\n eTag: string,\n index: number,\n chunkCount: number,\n chunk: Uint8Array,\n dek: EnclaveKey | null,\n ): Promise<void> {\n const id = `${eTag}_${index}`\n const now = new Date().toISOString()\n let envelope: EncryptedEnvelope\n\n if (dek) {\n const aad = chunkAAD(eTag, index, chunkCount)\n const { iv, data } = await encryptBytesWithAAD(chunk, dek, aad)\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: now, _iv: iv, _data: data }\n } else {\n envelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: now,\n _iv: '',\n _data: bufferToBase64(chunk),\n }\n }\n\n await this.store.put(this.vault, BLOB_CHUNKS_COLLECTION, id, envelope)\n }\n\n private async readChunk(\n eTag: string,\n index: number,\n chunkCount: number,\n dek: EnclaveKey | null,\n ): Promise<Uint8Array | null> {\n const envelope = await this.store.get(this.vault, BLOB_CHUNKS_COLLECTION, `${eTag}_${index}`)\n if (!envelope) return null\n\n if (dek) {\n const aad = chunkAAD(eTag, index, chunkCount)\n return await decryptBytesWithAAD(envelope._iv, envelope._data, dek, aad)\n }\n\n return base64ToBuffer(envelope._data)\n }\n\n // ─── Version record I/O ───────────────────────────────────────────\n\n /** #752: '::' is the version-key separator (`versionKey`) — a recordId/slotName/label\n * containing it makes the `{recordId}::` prefix scans (listVersions / rehomeVersionRecords /\n * collectVersionHolds) match ACROSS records, which escalated from mis-read to destructive\n * with #750's shred path. Also refused: a part that starts or ends with ':' — otherwise a\n * boundary colon re-segments the key (recordId `\"a:\"` + slotName `\"slot\"` yields\n * `\"a:::slot::label\"`, which starts with `\"a::\"` and prefix-matches record `\"a\"`'s rows).\n * With both rules, every '::' in a stored key is exactly a component separator — the grammar\n * is unambiguous by construction. Refused at the write surface only: legacy '::' data stays\n * readable/sheddable. */\n private assertKeyPartSafe(part: string, what: 'record id' | 'slot name' | 'version label'): void {\n if (part.includes('::') || part.startsWith(':') || part.endsWith(':')) {\n throw new ValidationError(\n `Blob ${what} \"${part}\" must not contain '::' nor start/end with ':' (reserved as the blob version-key separator; #752)`,\n )\n }\n }\n\n /** #752: `recordId`/`slotName`/`label` must not contain '::' — see `assertKeyPartSafe`. */\n private versionKey(slotName: string, label: string): string {\n return `${this.recordId}::${slotName}::${label}`\n }\n\n /**\n * @param tier #724 Arc 10 Task 2: explicit tier to resolve the version\n * record's collection DEK at — `getDEK(dekKey(this.collection, tier))`,\n * mirroring `loadSlots`'s `tier` param. Omitted → resolves via\n * `ownerTier()` (the record's CURRENT live tier); `rehomeForTier`'s own\n * reads pin `fromTier`/`toTier` explicitly instead (see `ownerTier()`'s\n * doc comment for why the default would be wrong mid-move).\n */\n private async loadVersionRecord(slotName: string, label: string, tier?: number): Promise<VersionRecord | null> {\n const key = this.versionKey(slotName, label)\n const envelope = await this.store.get(this.vault, this.versionsCollection, key)\n if (!envelope) return null\n\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as VersionRecord\n }\n\n const dek = await this.getDEK(dekKey(this.collection, tier ?? await this.ownerTier()))\n const json = await openEnvelopeJson(envelope, dek)\n return JSON.parse(json) as VersionRecord\n }\n\n /** @param tier See {@link loadVersionRecord}'s `tier` param — same resolution. */\n private async writeVersionRecord(slotName: string, record: VersionRecord, tier?: number): Promise<void> {\n await this.writeVersionRecordAtKey(\n this.versionKey(slotName, record.label),\n record,\n tier ?? await this.ownerTier(),\n )\n }\n\n /**\n * `writeVersionRecord`'s body, addressed by an explicit raw store key\n * instead of `slotName`+`label` — used by `rehomeVersionRecords` (#724\n * Arc 10 Task 2), which already has the key from its raw `store.list()`\n * scan across ALL of this record's version slots.\n */\n private async writeVersionRecordAtKey(key: string, record: VersionRecord, tier: number): Promise<void> {\n const json = JSON.stringify(record)\n const now = new Date().toISOString()\n let envelope: EncryptedEnvelope\n\n if (this.encrypted) {\n const dek = await this.getDEK(dekKey(this.collection, tier))\n const { iv, data } = await encrypt(json, dek)\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: now, _iv: iv, _data: data }\n } else {\n envelope = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: now, _iv: '', _data: json }\n }\n\n await this.store.put(this.vault, this.versionsCollection, key, envelope)\n }\n\n private async deleteVersionRecord(slotName: string, label: string): Promise<void> {\n const key = this.versionKey(slotName, label)\n await this.store.delete(this.vault, this.versionsCollection, key)\n }\n\n // ─── Effective chunk size ─────────────────────────────────────────\n\n private effectiveChunkSize(opts?: BlobPutOptions): number {\n if (opts?.chunkSize) return opts.chunkSize\n if (this.maxBlobBytes) return this.maxBlobBytes\n return DEFAULT_CHUNK_SIZE\n }\n\n // ─── Fetch all chunks for a blob ──────────────────────────────────\n\n /**\n * #747/#749 whole-branch review I1: when `loadBlobObject`'s tier-scoped open\n * fell through to the flat retry while a CLEARED higher-tier view (#749)\n * was reading (`this.clearedTier > 0`, `result.atTier === 0`), the\n * `_blob_index` row that \"won\" the read is not necessarily the one this\n * handle's `assertTierAccess` grant vouches for. Chunk AAD\n * (`{eTag}:{index}:{count}`) is attacker-computable, and a legacy\n * (`_cek`-less) object has no wrapped content CEK to unwrap under a key\n * the attacker doesn't hold — so ANY flat `_blob` DEK holder with store\n * write access can plant a `_cek`-less forgery at `_blob_index/{eTag}`\n * (this elevated blob's own address) whose chunks are their own flat\n * bytes, and it will decrypt cleanly under the flat DEK the fallback\n * already tries.\n *\n * `verifyFlatETag`, when set, is the eTag the CALLER originally asked\n * for (the slot/version's `.eTag` — never `blob.eTag`, which is\n * attacker-controlled content pulled from the same forged row and would\n * make the check tautological). After assembling the plaintext, we\n * recompute the SAME content address every write path mints\n * (`hmacSha256Hex(flatBlobDEK, plaintext)` — `writeBlobContent`'s Step 1,\n * unconditional on `_cek`) and compare it to that requested eTag. A\n * forged row can produce valid ciphertext under the flat DEK, but can't\n * produce plaintext that re-hashes to an address it doesn't control.\n *\n * Both legitimate flat-fallback classes — a `dedup`-policy shared object\n * (#741) and a legacy `_cek`-less object (#724 I1) — were minted this\n * same way, so an honest read's recomputed hash always matches and this\n * is a no-op for them.\n */\n private async fetchAllChunks(\n blob: BlobObject,\n blobDEK?: EnclaveKey,\n verifyFlatETag?: string,\n ): Promise<Uint8Array> {\n // Chunks are keyed under the per-blob content CEK (erasable) or directly\n // under the `_blob` DEK (legacy) — resolveChunkKey discriminates on `_cek`.\n const chunkKey = await this.resolveChunkKey(blob, blobDEK)\n const chunks: Uint8Array[] = []\n\n for (let i = 0; i < blob.chunkCount; i++) {\n const chunk = await this.readChunk(blob.eTag, i, blob.chunkCount, chunkKey)\n if (!chunk) {\n // #808 offline taxonomy: the index/slot row is present but the chunk\n // bytes are not IN THIS STORE — a cold local copy (not yet synced to\n // this device, or evicted), not a missing slot. Typed so UIs can\n // render \"available when online\" instead of a silent empty file.\n throw new BlobOfflineError(\n this.collection, this.recordId, undefined,\n `chunk ${i}/${blob.chunkCount} for eTag \"${blob.eTag}\" is absent from the local store — ` +\n 'content not yet synced to this device, or evicted (#808)',\n )\n }\n chunks.push(chunk)\n }\n\n const assembled = concatChunks(chunks)\n const plaintext = blob.compression === 'gzip' ? await decompressBytes(assembled) : assembled\n\n if (verifyFlatETag !== undefined && blobDEK) {\n const recomputed = await hmacSha256Hex(blobDEK, plaintext)\n if (recomputed !== verifyFlatETag) {\n throw new TamperedError(\n `Blob content for eTag \"${verifyFlatETag}\" failed content-address verification after a ` +\n 'flat-tier fallback open — the _blob_index/_blob_chunks rows do not match the requested ' +\n 'content address (#747/#749 review I1)',\n )\n }\n }\n\n return plaintext\n }\n\n /**\n * #747/#749 review I1: the eTag to pass as `fetchAllChunks`'s `verifyFlatETag` — set\n * only when `loadBlobObject` fell through to the flat retry\n * (`resolvedAtTier === 0`) on a CLEARED higher-tier view\n * (`this.clearedTier > 0`). Every ordinary (non-cleared) call reaching\n * a content-fetch site already has `ownerTier() === 0` (elevated records\n * are hidden by `ownerRecordElevated()` before this point), so\n * `loadBlobObject` never even attempts the tier branch for them — no\n * fallback occurred, nothing to verify.\n */\n private flatFallbackVerifyETag(requestedETag: string, resolvedAtTier: number): string | undefined {\n if (this.clearedTier === undefined || this.clearedTier <= 0) return undefined\n return resolvedAtTier === 0 ? requestedETag : undefined\n }\n\n // ─── Public API: Slot management ──────────────────────────────────\n\n /**\n * Upload bytes and attach them to this record under `slotName`.\n *\n * 1. Computes `eTag = HMAC-SHA-256(blobDEK, plaintext)` for keyed content-addressing.\n * 2. Auto-detects MIME type from magic bytes if not provided.\n * 3. If a blob with this eTag already exists, skips chunk upload (deduplication)\n * and CAS-increments refCount.\n * 4. Otherwise: compresses → splits into chunks → encrypts each chunk with\n * AAD binding → writes `_blob_chunks` → writes `BlobObject` to `_blob_index`.\n * 5. CAS-updates the slot metadata in `_blob_slots_{collection}`.\n * If overwriting an existing slot, decrements the old eTag's refCount.\n */\n async put(slotName: string, data: Uint8Array, opts?: BlobPutOptions): Promise<void> {\n this.assertKeyPartSafe(this.recordId, 'record id')\n this.assertKeyPartSafe(slotName, 'slot name')\n await this.resolvePendingIntent() // #753 spec §7 C6: resume a pending shred before accepting a new write\n\n // External-projection path: the field is declared `external` and an\n // ObjectProjection is configured → write the raw bytes as ONE native object\n // (unencrypted, servable) and record an `external` slot (the catalog entry).\n // Bypasses eTag/chunk/CEK/dedup entirely.\n if (this.objectStore && this.blobFields?.[slotName]?.external) {\n const policy = this.blobFields[slotName]\n let contentType = opts?.mimeType\n if (!contentType) {\n const detected = detectMagic(data.subarray(0, 16))\n contentType = detected?.mime ?? 'application/octet-stream'\n }\n const key = `${this.collection}/${this.recordId}/${slotName}`\n const isPublic = policy.public === true\n // Stamp a self-describing backlink onto the object's metadata (the\n // \"secondary store\"): default opaque-token (no name leak).\n const backlink = await this.buildBacklink(slotName, policy.backlink ?? 'opaque-token')\n await this.objectStore.putObject(key, data, {\n contentType,\n public: isPublic,\n ...(backlink.userMeta ? { userMeta: backlink.userMeta } : {}),\n })\n\n const uploaderUserId = opts?.uploadedBy ?? this.userId\n let oldETag: string | undefined\n await this.casUpdateSlots((slots) => {\n oldETag = slots[slotName]?.eTag || undefined\n // Preserve any previously-synced derived metadata across re-upload.\n const prevMeta = slots[slotName]?.external?.meta\n slots[slotName] = {\n eTag: '',\n external: {\n key,\n contentType,\n ...(isPublic ? { public: true } : {}),\n ...(backlink.token ? { backlink: backlink.token } : {}),\n ...(prevMeta ? { meta: prevMeta } : {}),\n },\n filename: opts?.filename ?? slotName,\n size: data.byteLength,\n mimeType: contentType,\n uploadedAt: new Date().toISOString(),\n ...(uploaderUserId !== undefined ? { uploadedBy: uploaderUserId } : {}),\n }\n return slots\n })\n // If this slot previously held a chunk-based blob, release that eTag.\n if (oldETag) {\n await this.releaseRef(oldETag, 1, false).catch(() => {})\n }\n return\n }\n\n // #724 I1 completion: the chunk-based path below is what has (or lacks)\n // a per-blob `_cek` — refuse here, not in the external-projection branch\n // above (that path never had a `_cek` to begin with; out of scope).\n this.assertBlobWritable()\n\n // #724 Arc 10 correction (C2): key the eTag HMAC + content-CEK wrap\n // under the OWNING RECORD's current tier, not a flat tier-0 DEK — a\n // blob attached to an already-elevated record must be born tier-scoped,\n // or it stays tier-0-decryptable at rest despite the read gate hiding\n // it through the API. `dekKey(BLOB_COLLECTION, 0) === BLOB_COLLECTION`,\n // so a tier-0 (or untiered) record's write is byte-identical to before.\n const blobDEK = this.encrypted\n ? await this.getDEK(dekKey(BLOB_COLLECTION, await this.ownerTier()))\n : null\n return this.putUnderDEK(slotName, data, blobDEK, opts)\n }\n\n /**\n * The slot-attachment core of `put()` (Step 7 — CAS the slot metadata),\n * parameterized by which `_blob` DEK to hash the eTag and wrap the\n * content CEK under. Steps 1-6 (hash/dedup/compress/chunk/index-write)\n * are `writeBlobContent` (#724 Arc 10 Task 2 extraction, so `rehomeForTier`\n * can reuse them for a version-held eTag without touching the slot map).\n * `put()` always passes this record's own OWNER-TIER blob DEK (#724 Arc\n * 10 correction) — `dekKey(BLOB_COLLECTION, 0)` for a tier-0 record.\n *\n * `rehomeForTier` is the other caller: on every tier move it re-`put()`s\n * each owned blob's plaintext under the `toTier`-scoped DEK — solo and\n * shared-isolate alike — so the moved copy gets a private, tier-scoped\n * eTag and `_cek` wrap from the moment it's written. The move never\n * routes through public `put()` under the wrong key, and this method\n * adds no new raw `_cek`/`_iv`/`_data` access (identical bodies, just\n * parameterized).\n *\n * @param slotsTier #724 Arc 10 Task 4: pins the slot-map CAS update\n * (Step 7) to a specific tier instead of the caller's `ownerTier()`\n * default — `rehomeForTier` passes `fromTier`, since mid-move the slot\n * map is still physically there (see `ownerTier()`'s doc comment). The\n * old eTag being replaced lives wherever the slot map itself is\n * physically keyed, so its release (#747, below) reuses this SAME value\n * rather than a separate param.\n * @param contentTier #747: pins the `_blob_index` read/write inside\n * `writeBlobContent` to a specific tier — `rehomeForTier` passes `toTier`\n * (matching `blobDEK`, already resolved at `toTier`), since mid-move\n * `ownerTier()` already reads `toTier` too but this is more direct.\n * Omitted → `ownerTier()`, correct for `put()`'s ordinary (non-rehome) call.\n * @param opId #753/#746 spec §7 C3: `rehomeForTier`'s governing marker\n * op-stamp identity (omitted → `undefined`, `put()`'s ordinary call and\n * every pre-existing caller, byte-identical unstamped behavior). When\n * present, the ROW-SCOPED stamp `${opId}:${slotName}` — never the bare\n * opId — governs BOTH this call's destination `+1` (via\n * `writeBlobContent`'s dedup-hit CAS) and its old-eTag release below: two\n * different `BlobObject`s (destination vs. source), so the shared stamp\n * string collides with neither, and a resumed re-put of THIS slot can\n * neither double-apply the `+1` nor double-release the old object. Two\n * DIFFERENT slots on this record legitimately re-putting the SAME\n * destination eTag each get their OWN stamp (their own `slotName`) — the\n * row scope is what lets N slots' `+1`s all land on one destination\n * object instead of the first silently absorbing the rest.\n */\n private async putUnderDEK(\n slotName: string,\n data: Uint8Array,\n blobDEK: EnclaveKey | null,\n opts?: BlobPutOptions,\n slotsTier?: number,\n contentTier?: number,\n opId?: string,\n knownApplied?: ReadonlySet<string>,\n ): Promise<void> {\n const rowStamp = opId !== undefined ? `${opId}:${slotName}` : undefined\n const { eTag, mimeType } = await this.writeBlobContent(data, blobDEK, opts, contentTier, rowStamp, knownApplied)\n\n // Step 7 — CAS-update slot metadata\n const uploaderUserId = opts?.uploadedBy ?? this.userId\n let deferredOldETag: string | undefined\n await this.casUpdateSlots((slots) => {\n const oldETag = slots[slotName]?.eTag\n const pending = oldETag && oldETag !== eTag ? oldETag : undefined\n slots[slotName] = {\n eTag,\n filename: opts?.filename ?? slotName,\n size: data.byteLength,\n ...(mimeType !== undefined ? { mimeType } : {}),\n uploadedAt: new Date().toISOString(),\n ...(uploaderUserId !== undefined ? { uploadedBy: uploaderUserId } : {}),\n // #746 spec §7 review, carried finding (b): durably record the OLD\n // eTag ON the slot row itself, in the SAME CAS write that moves the\n // slot to `eTag` — see `SlotRecord.pendingRelease`'s doc comment.\n // Gated to marker-governed (stamped) calls only: ordinary put()\n // (`rowStamp` undefined) keeps today's in-memory best-effort\n // posture, zero footprint.\n ...(pending !== undefined && rowStamp !== undefined ? { pendingRelease: pending } : {}),\n }\n deferredOldETag = pending\n return slots\n }, slotsTier)\n\n // Release the old eTag outside the CAS loop. An erasable blob dropping to\n // refCount 0 here is crypto-shredded eagerly; a legacy one defers to GC.\n if (deferredOldETag) {\n // #747: the old eTag lives at `slotsTier` (the slot map's CURRENT\n // physical location — see the param doc above), not this record's\n // live tier — those diverge mid-rehome.\n await this.releaseOldETagAfterMove(deferredOldETag, slotsTier, rowStamp)\n // The pendingRelease breadcrumb above is only meaningful once the\n // release genuinely landed — clear it now (idempotent; a crash before\n // this point leaves it for `reconcilePendingReleases` to finish).\n if (rowStamp !== undefined) await this.clearPendingRelease(slotName, deferredOldETag, slotsTier)\n }\n }\n\n /**\n * C10 (#753/#746 spec §7): under a marker-governed (`stamp` present)\n * rehome, a failed old-eTag release must not be silently swallowed — it\n * is the crypto-shred of the FROM-tier object, and swallowing it during a\n * documented-exactly-once op is the bug C10 closes. Propagating keeps the\n * governing `_blob_intent` marker alive (never deleted) so a later resume\n * retries it, rather than completing the op over a residue that never\n * surfaces. Unstamped (ordinary `put()`/`delete()`) calls keep the\n * pre-#753 best-effort posture unchanged — a missed decrement there is\n * reconciled by a later pass, same as always.\n */\n private async releaseOldETagAfterMove(eTag: string, tier: number | undefined, stamp: string | undefined): Promise<void> {\n if (stamp !== undefined) {\n await this.releaseRef(eTag, 1, false, tier, stamp)\n return\n }\n await this.releaseRef(eTag, 1, false, tier).catch(() => {\n // Best-effort — a missed decrement is reconciled by a later pass.\n })\n }\n\n /**\n * Durably confirm a rehome row-stamp's `+1` in the governing marker —\n * #746 whole-branch review (K=8 stamp-ring blocker). No-op when there is\n * no marker to record against (a direct `rehomeForTier(..., opId)` call\n * with no real `_blob_intent` row, e.g. this suite's Task-1 tests —\n * byte-identical ring-only behavior for that path, unchanged). NOT\n * swallowed on failure (mirrors C10): a failed confirmation write must\n * keep the governing marker's op from completing silently over an\n * unconfirmed increment, not be dropped.\n */\n private async recordAppliedRehomeStamp(stamp: string): Promise<void> {\n await recordAppliedStamp(this.store, this.vault, this.collection, this.recordId, this.getDEK, stamp)\n }\n\n /**\n * Apply ONE destination `+1` under a row-scoped rehome stamp, made\n * idempotent INDEPENDENTLY of `BlobObject.lastOps`'s bounded ring (#746\n * whole-branch review — see `BlobIntent.appliedStamps`'s doc comment for\n * the full hazard). `knownApplied` — this op's OWN marker-backed,\n * unbounded record of confirmed stamps, read ONCE at the top of\n * `runRehomeSteps` — is consulted FIRST: a hit means this exact row's\n * `+1` was already confirmed on a PRIOR run, so the CAS is skipped\n * entirely regardless of whether the shared destination's ring has since\n * evicted it (another 8+ rows — from THIS record's own fan-out, or from\n * unrelated CONCURRENT rehomes converging on the same destination —\n * could have pushed it out). A miss falls through to the existing\n * ring-based stamped CAS (correct and sufficient for the overwhelming\n * majority of resumes — same-session or shortly-after), and on success\n * (freshly applied OR the ring itself already had it) records the stamp\n * into the marker so THIS row is never re-examined again by a later\n * resume, no matter how much unrelated activity lands on the destination\n * in between.\n */\n private async applyStampedIncrement(\n eTag: string, tier: number | undefined, stamp: string, knownApplied: ReadonlySet<string>,\n ): Promise<void> {\n if (knownApplied.has(stamp)) return\n await this.casUpdateRefCountStamped(eTag, +1, tier, stamp)\n await this.recordAppliedRehomeStamp(stamp)\n }\n\n /**\n * The chunk/CEK/dedup core of `put()` (former Steps 1-6 of `putUnderDEK`,\n * extracted #724 Arc 10 Task 2 so `rehomeVersionETag` can write a\n * version-held blob's content under a target tier's DEK WITHOUT touching\n * the slot map — `putUnderDEK`'s remaining Step 7 is slot-specific).\n * Content-addressed and dedup-aware exactly like `put()`: hashing the same\n * plaintext under the same `blobDEK` twice always lands on the same eTag.\n *\n * @param tier #747: the index-envelope tier to read/write at (see\n * {@link loadBlobObject}'s `tier` param) — `putUnderDEK`'s `contentTier`,\n * matching whichever tier `blobDEK` itself was resolved at. Omitted →\n * `ownerTier()`, correct for `put()`'s ordinary (non-rehome) call.\n * @param incrementStamp #753/#746 spec §7 C3: when present, a dedup hit at\n * Step 3 applies its `+1` via {@link casUpdateRefCountStamped} under this\n * stamp instead of the plain {@link casUpdateRefCount} — the row-scoped\n * identity `putUnderDEK`/`rehomeVersionETag` compute so a resumed re-put's\n * destination increment is idempotent per row. Omitted (every call site\n * before this arc) → byte-identical unstamped behavior.\n *\n * ALSO seeds `lastOps: [incrementStamp]` on the fresh-object create path\n * (Step 6, review finding on #746 C3): a solo blob has no pre-existing\n * destination object for a resumed re-put to dedup-hit against a stamp on\n * — the FIRST attempt's create is itself the only write, and a crash\n * after it lands but before the slot/version CAS leaves the object\n * present at refCount 1 with NO stamp. An unseeded resume then\n * re-executes Step 3, finds that same object, and — finding no matching\n * stamp — applies a SECOND, spurious `+1` (1 → 2), the exact over-count\n * hazard this stamping arc exists to close. Seeding the ring at create\n * time closes that: the resumed re-put's Step 3 dedup-hit against THIS\n * object finds its own stamp already present and skips, matching the\n * dedup-hit branch's behavior exactly.\n */\n private async writeBlobContent(\n data: Uint8Array,\n blobDEK: EnclaveKey | null,\n opts?: BlobPutOptions,\n tier?: number,\n incrementStamp?: string,\n knownApplied?: ReadonlySet<string>,\n ): Promise<{ eTag: string; mimeType: string | undefined }> {\n // Step 1 — keyed content-hash (plaintext, before compression)\n const eTag = blobDEK\n ? await hmacSha256Hex(blobDEK, data)\n : await plainSha256Hex(data)\n\n // Step 2 — MIME detection\n let mimeType = opts?.mimeType\n if (!mimeType) {\n const detected = detectMagic(data.subarray(0, 16))\n if (detected) mimeType = detected.mime\n }\n\n // Determine compression: explicit opt > auto-detect > default true\n let shouldCompress: boolean\n if (opts?.compress !== undefined) {\n shouldCompress = opts.compress\n } else if (mimeType && isPreCompressed(mimeType)) {\n shouldCompress = false\n } else {\n shouldCompress = true\n }\n // Debug-plaintext: write the blob as a single un-gzipped object so the\n // stored chunk's base64 `_data` decodes directly to the original bytes\n // (`base64 -d`) — no gzip layer, no multi-chunk reassembly. (encrypt:false\n // already stores chunks unencrypted; this drops the remaining indirection.)\n if (this.debugPlaintext) shouldCompress = false\n\n // Step 3 — deduplication check\n const existingBlob = await this.loadBlobObject(eTag, tier)\n\n if (existingBlob) {\n // eTag already exists — just increment refCount (CAS retry). Dedup is\n // preserved across the content-CEK split: the chunks (and the BlobObject's\n // `_cek`, if any) are reused as-is; a new referencer never re-encrypts.\n if (incrementStamp !== undefined) {\n // #746 whole-branch review (K=8 stamp-ring blocker): ring-independent\n // via `knownApplied` — see `applyStampedIncrement`'s doc comment.\n await this.applyStampedIncrement(eTag, tier, incrementStamp, knownApplied ?? EMPTY_STAMP_SET)\n } else {\n await this.casUpdateRefCount(eTag, +1, tier)\n }\n return { eTag, mimeType }\n }\n\n // Step 4 — compress\n const { bytes: compressed, algorithm } = shouldCompress\n ? await compressBytes(data)\n : { bytes: data, algorithm: 'none' as const }\n\n // Debug-plaintext stores the whole blob as one object (single chunk) so\n // it is one directly-openable file in the store rather than N shards.\n const chunkSize = this.debugPlaintext\n ? Math.max(compressed.byteLength, 1)\n : this.effectiveChunkSize(opts)\n const chunkCount = Math.max(1, Math.ceil(compressed.byteLength / chunkSize))\n\n // Erasable collection (`perRecordKeys`): mint a fresh per-blob content\n // CEK and encrypt the chunks under it instead of the shared `_blob` DEK,\n // so the BlobObject's wrapped `_cek` is the sole recoverable copy → a\n // refCount-0 delete crypto-shreds the chunks. `eTag` (the dedup address)\n // is still keyed off the `_blob` DEK, unchanged.\n let chunkKey = blobDEK\n let wrappedCek: string | undefined\n if (blobDEK && this.erasableBlobs) {\n const contentCek = await generateDEK()\n wrappedCek = await wrapCek(contentCek, blobDEK)\n chunkKey = contentCek\n }\n\n // Step 5 — write chunks FIRST with AAD binding (safe failure order)\n for (let i = 0; i < chunkCount; i++) {\n const start = i * chunkSize\n await this.writeChunk(\n eTag, i, chunkCount,\n compressed.subarray(start, start + chunkSize),\n chunkKey,\n )\n }\n\n // Step 6 — write blob index entry after all chunks succeed. #746 C3\n // review: seed `lastOps` with `incrementStamp` (when present) so a\n // resumed re-put's Step 3 dedup-hit against THIS freshly-created object\n // finds its own stamp already there and skips — see this method's\n // `incrementStamp` doc comment.\n await this.writeBlobObject({\n eTag,\n size: data.byteLength,\n compressedSize: compressed.byteLength,\n compression: algorithm,\n chunkSize,\n chunkCount,\n ...(mimeType !== undefined ? { mimeType } : {}),\n createdAt: new Date().toISOString(),\n refCount: 1,\n ...(wrappedCek !== undefined ? { _cek: wrappedCek } : {}),\n ...(incrementStamp !== undefined ? { lastOps: appendStamp(undefined, incrementStamp) } : {}),\n }, undefined, tier)\n // #746 whole-branch review (K=8 stamp-ring blocker): also confirm the\n // fresh-create in the marker, same as the dedup-hit path — a resumed\n // dedup-hit against THIS object (Step 3, a later run) then skips via\n // `knownApplied` even if the ring's own seeded entry has since been\n // evicted by unrelated activity on this destination.\n if (incrementStamp !== undefined) await this.recordAppliedRehomeStamp(incrementStamp)\n\n return { eTag, mimeType }\n }\n\n /**\n * Fetch all bytes for the named slot.\n * Returns `null` if the slot does not exist.\n * Throws `BlobOfflineError` if the content is not available locally (#808):\n * an internal blob whose chunk envelopes are absent from the local store, or\n * an external slot with no local cached copy while the object store is\n * unreachable. External reads populate the device-local encrypted\n * side-cache, so a repeat read is served locally (see `pin()`).\n */\n async get(slotName: string): Promise<Uint8Array | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n\n if (slot.external) {\n return this.readExternal(slotName, slot)\n }\n\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) return null\n\n // #749: `fetchAllChunks`'s own `blobDEK` default (`resolveChunkKey`'s\n // flat `_blob` fallback) predates #724 Task 3's tier-scoped content CEKs\n // and was never wrong before `atTier()` existed — every pre-#749 caller\n // reaching this line was, by construction, a tier-0 record (the\n // `ownerRecordElevated()` gate above refused anything else). A cleared\n // view's elevated content has its `_cek` wrapped under the tier that\n // OPENED the index envelope (`result.atTier` — #747), not the flat one,\n // so it must be resolved explicitly here.\n const blobDEK = this.encrypted ? await this.getDEK(dekKey(BLOB_COLLECTION, result.atTier)) : undefined\n const bytes = await this.fetchAllChunks(result.blob, blobDEK, this.flatFallbackVerifyETag(slot.eTag, result.atTier))\n await this.recordLocalHit(slotName) // #808: KPI + LRU access stamp\n return bytes\n }\n\n /**\n * A URL to fetch an `external` slot's object directly — presigned\n * (time-limited) or public, per the projection. Returns `null` if the slot\n * does not exist. Throws for a non-external slot (use `get()`/`response()`).\n */\n async url(slotName: string, opts?: { expiresInSeconds?: number }): Promise<string | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n if (!slot.external) {\n throw new NotFoundError(`Blob slot \"${slotName}\" is not external — url() is only for external fields`)\n }\n if (!this.objectStore) {\n throw new NotFoundError(`Blob slot \"${slotName}\" is external but no objectStore is configured`)\n }\n return this.objectStore.objectUrl(slot.external.key, opts)\n }\n\n /**\n * Build the backlink stamped onto an external object's metadata — the\n * self-describing \"secondary store\" reference back to this record. See\n * {@link BlobFieldPolicy.backlink}. Returns the `userMeta` to attach and, for\n * `opaque-token`, the `token` to record on the slot.\n */\n private async buildBacklink(\n slotName: string,\n mode: 'opaque-token' | 'encrypted' | 'plain' | 'none',\n ): Promise<{ userMeta?: Record<string, string>; token?: string }> {\n if (mode === 'none') return {}\n const ref = { vault: this.vault, collection: this.collection, record: this.recordId, field: slotName }\n if (mode === 'plain') {\n return {\n userMeta: {\n 'noydb-vault': ref.vault,\n 'noydb-collection': ref.collection,\n 'noydb-record': ref.record,\n 'noydb-field': ref.field,\n },\n }\n }\n if (mode === 'encrypted' && this.encrypted) {\n const dek = await this.getDEK(BLOB_COLLECTION)\n const { iv, data } = await encrypt(JSON.stringify(ref), dek)\n return { userMeta: { 'noydb-backlink-enc': `${iv}.${data}` } }\n }\n // opaque-token — also the fallback when `encrypted` is requested on a\n // plaintext vault (no DEK to encrypt under).\n const bytes = globalThis.crypto.getRandomValues(new Uint8Array(16))\n const token = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n return { userMeta: { 'noydb-backlink': token }, token }\n }\n\n /**\n * Adopt an EXISTING object in the projection into this slot **without\n * re-uploading** — used by import/bootstrap to anchor objects already in the\n * bucket. Writes the external slot record (the catalog entry).\n */\n async adoptExternal(\n slotName: string,\n ref: {\n key: string\n size?: number\n contentType?: string\n public?: boolean\n backlink?: string\n meta?: Record<string, unknown>\n },\n ): Promise<void> {\n this.assertKeyPartSafe(this.recordId, 'record id')\n this.assertKeyPartSafe(slotName, 'slot name')\n this.assertExternalDeclared(slotName)\n await this.resolvePendingIntent() // #753 spec §7 C6: resume a pending shred before accepting a new write\n\n const uploaderUserId = this.userId\n await this.casUpdateSlots((slots) => {\n slots[slotName] = {\n eTag: '',\n external: {\n key: ref.key,\n ...(ref.contentType ? { contentType: ref.contentType } : {}),\n ...(ref.public ? { public: true } : {}),\n ...(ref.backlink ? { backlink: ref.backlink } : {}),\n ...(ref.meta ? { meta: ref.meta } : {}),\n },\n filename: slotName,\n size: ref.size ?? 0,\n ...(ref.contentType ? { mimeType: ref.contentType } : {}),\n uploadedAt: new Date().toISOString(),\n ...(uploaderUserId !== undefined ? { uploadedBy: uploaderUserId } : {}),\n }\n return slots\n })\n }\n\n /**\n * Merge derived metadata (video `duration`, image `width`/`height`, arbitrary\n * metatags) into an external slot's secondary metadata store. Typically called\n * from an AWS-side processing callback (MediaConvert / ffprobe / Rekognition)\n * once it has probed the object. No-op for a missing or non-external slot.\n */\n async setExternalMeta(slotName: string, meta: Record<string, unknown>): Promise<void> {\n this.assertExternalDeclared(slotName)\n await this.resolvePendingIntent() // #753 spec §7 C6: resume a pending shred before accepting a new write\n await this.casUpdateSlots((slots) => {\n const slot = slots[slotName]\n if (!slot?.external) return null\n slots[slotName] = {\n ...slot,\n external: { ...slot.external, meta: { ...slot.external.meta, ...meta } },\n }\n return slots\n })\n }\n\n /**\n * Read an external slot's synced derived metadata (the secondary store).\n * Returns `null` for a missing or non-external slot, `{}` if none synced yet.\n */\n async externalMeta(slotName: string): Promise<Record<string, unknown> | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot?.external) return null\n return slot.external.meta ?? {}\n }\n\n /**\n * List all slot entries for this record.\n * Returns metadata only — no chunk data is loaded.\n *\n * #808: when a pin registry is present (`withBlobs()`), each entry is\n * annotated with this DEVICE's view — `pinned` / `lastAccessAt` /\n * `cachedBytes` (see their `SlotInfo` doc comments). The annotations come\n * from the device-local registry, never the vault store, and are what\n * `vault.compact()`'s pin exemption + cache-budget LRU pass key off.\n */\n async list(): Promise<SlotInfo[]> {\n if (await this.ownerRecordElevated()) return [] // #724: elevated ≡ invisible, mirrors get()\n const { slots } = await this.loadSlots()\n // #746: `pendingRelease` is internal rehome-journal bookkeeping (see\n // `SlotRecord`'s doc comment) — never surfaced through the public API.\n const infos = Object.entries(slots).map(([name, slot]) => {\n const pub: SlotRecord = { ...slot }\n delete (pub as { pendingRelease?: string }).pendingRelease\n return { name, ...pub }\n })\n if (!this.pinCache) return infos\n const prefix = blobPinKey(this.vault, this.collection, this.recordId)\n const rows = await this.pinCache.store.entries(prefix)\n const byName = new Map(rows.map(({ key, entry }) => [key.slice(prefix.length), entry]))\n return infos.map((info) => {\n const entry = byName.get(info.name)\n if (!entry) return info\n return {\n ...info,\n ...(entry.pinned ? { pinned: true } : {}),\n ...(entry.lastAccessAt !== undefined ? { lastAccessAt: entry.lastAccessAt } : {}),\n ...(entry.cachedBytes !== undefined ? { cachedBytes: entry.cachedBytes } : {}),\n }\n })\n }\n\n /**\n * Delete the named slot from this record.\n * Decrements refCount on the blob. Chunks are GC'd by `vault.blobGC()`.\n */\n async delete(slotName: string): Promise<void> {\n await this.resolvePendingIntent() // #753 spec §7 C6: resume a pending shred before accepting a new write\n let eTagToRelease: string | undefined\n let externalKeyToDelete: string | undefined\n\n await this.casUpdateSlots((slots) => {\n if (!(slotName in slots)) return null\n const slot = slots[slotName]!\n if (slot.external) externalKeyToDelete = slot.external.key\n else eTagToRelease = slot.eTag\n delete slots[slotName]\n return slots\n })\n\n if (externalKeyToDelete && this.objectStore) {\n // External objects are hard-deleted (no crypto-shred — they were never\n // encrypted). CDN/replica cache TTLs may retain a copy; see the design.\n await this.objectStore.deleteObject(externalKeyToDelete).catch(() => {})\n }\n\n if (eTagToRelease) {\n // Erasable blobs are crypto-shredded at refCount 0 (this also covers\n // compaction eviction, which routes through delete()); legacy blobs keep\n // deferred-GC / orphan-retention semantics.\n await this.releaseRef(eTagToRelease, 1, false).catch(() => {\n // Best-effort — a missed decrement is reconciled by a later pass.\n })\n }\n\n // #808: the slot is gone — drop its device-local registry row (pin flag /\n // access stamp / cached external copy). Best-effort hygiene: a failing\n // device-local store must not fail the vault-store delete that already\n // landed above.\n if (this.pinCache) {\n await this.pinCache.store.delete(this.pinKeyFor(slotName)).catch(() => {})\n }\n }\n\n // ─── Public API: Offline pinning + device-local cache (#808) ──────\n\n /**\n * Pin the named slot for offline availability ON THIS DEVICE.\n *\n * - Downloads eagerly (call while online): an internal blob's chunks are\n * fetched once to prove they are locally readable; an `external` slot's\n * bytes are fetched from the object store and kept in the device-local\n * side-cache, **encrypted** under the vault's `_blob` DEK via the same\n * enclave path as chunk encryption (the object-store copy sits outside\n * the ZK envelope by design; the local copy does not).\n * - A pinned slot is EXEMPT from `vault.compact()` eviction — both the\n * `blobFields` policy pass and the `cacheBudget` LRU pass.\n * - Pin state is device-local (never synced): it lives in the `withBlobs()`\n * pin registry, not in the vault store. Each device pins for itself.\n *\n * Throws `NotFoundError` for a missing slot and `BlobOfflineError` when the\n * eager download cannot complete (offline).\n */\n async pin(slotName: string): Promise<void> {\n this.assertKeyPartSafe(this.recordId, 'record id')\n this.assertKeyPartSafe(slotName, 'slot name')\n const pc = this.requirePinCache('pin')\n // #724: elevated ≡ invisible — same posture as get(), surfaced as the\n // not-found a caller without clearance would legitimately see.\n if (await this.ownerRecordElevated()) {\n throw new NotFoundError(`Slot \"${slotName}\" not found on record \"${this.recordId}\"`)\n }\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) throw new NotFoundError(`Slot \"${slotName}\" not found on record \"${this.recordId}\"`)\n\n const now = new Date().toISOString()\n if (slot.external) {\n // Eager download (or local cache hit) — readExternal caches the fetched\n // bytes encrypted, so after this the registry row always carries the\n // local copy; then flip its pin flag on.\n const bytes = await this.readExternal(slotName, slot)\n if (bytes === null) {\n throw new NotFoundError(`External object for blob slot \"${slotName}\" is missing from the object store`)\n }\n const key = this.pinKeyFor(slotName)\n const entry = await pc.store.get(key)\n if (entry?.cipher) {\n await pc.store.set(key, { ...entry, pinned: true, pinnedAt: entry.pinnedAt ?? now })\n } else {\n // Defensive: readExternal always caches on success when a registry is\n // present — but never leave a pin without its local copy.\n await this.cacheExternalCopy(slotName, bytes, { pin: true })\n }\n return\n }\n\n // Internal (chunked) blob: prove the content is locally readable NOW —\n // a cold chunk surfaces BlobOfflineError here, not at first offline read.\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) throw new NotFoundError(`BlobObject ${slot.eTag} not found`)\n const blobDEK = this.encrypted ? await this.getDEK(dekKey(BLOB_COLLECTION, result.atTier)) : undefined\n await this.fetchAllChunks(result.blob, blobDEK, this.flatFallbackVerifyETag(slot.eTag, result.atTier))\n const key = this.pinKeyFor(slotName)\n const prev = await pc.store.get(key)\n await pc.store.set(key, { ...(prev ?? {}), pinned: true, pinnedAt: now, lastAccessAt: now })\n }\n\n /**\n * Unpin the named slot on this device — it returns to the normal lifecycle:\n * `blobFields` policy eviction applies again, and any local cached copy\n * (external slots) stays but becomes budget-managed (`cacheBudget` LRU).\n * A no-op when the slot is not pinned here.\n */\n async unpin(slotName: string): Promise<void> {\n this.assertKeyPartSafe(slotName, 'slot name')\n const pc = this.requirePinCache('unpin')\n const key = this.pinKeyFor(slotName)\n const entry = await pc.store.get(key)\n if (!entry?.pinned) return\n const rest = { ...entry, pinned: false }\n delete (rest as { pinnedAt?: string }).pinnedAt\n await pc.store.set(key, rest)\n }\n\n /** Whether the named slot is pinned for offline ON THIS DEVICE (#808). */\n async isPinned(slotName: string): Promise<boolean> {\n const pc = this.requirePinCache('isPinned')\n const entry = await pc.store.get(this.pinKeyFor(slotName))\n return entry?.pinned === true\n }\n\n /**\n * Drop this slot's device-local cached copy (the encrypted external\n * side-cache) WITHOUT touching the slot, the vault store, or the object\n * store. Returns the plaintext bytes freed (0 when nothing was cached, the\n * slot is pinned — pinned copies are exempt — or no registry is present).\n * The cache-budget pass inside `vault.compact()` is the expected caller;\n * the LRU access row itself is kept.\n */\n async dropLocalCache(slotName: string): Promise<number> {\n const pc = this.pinCache\n if (!pc) return 0\n const key = this.pinKeyFor(slotName)\n const entry = await pc.store.get(key)\n if (!entry || entry.pinned || !entry.cipher) return 0\n const freed = entry.cachedBytes ?? 0\n const rest = { ...entry }\n delete (rest as { cipher?: unknown }).cipher\n delete (rest as { cachedBytes?: number }).cachedBytes\n await pc.store.set(key, rest)\n return freed\n }\n\n // ─── Internal: device-local pin registry plumbing (#808) ──────────\n\n /** This slot's registry key — see {@link blobPinKey}. */\n private pinKeyFor(slotName: string): string {\n return blobPinKey(this.vault, this.collection, this.recordId, slotName)\n }\n\n /** AAD binding for a cached external copy: the row's own registry key. */\n private pinCopyAAD(slotName: string): Uint8Array {\n return new TextEncoder().encode(`pin:${this.pinKeyFor(slotName)}`)\n }\n\n private requirePinCache(op: string): BlobPinCache {\n if (!this.pinCache) {\n throw new ValidationError(\n `BlobSet.${op}() requires the withBlobs() pin registry — this handle was constructed without one (#808)`,\n )\n }\n return this.pinCache\n }\n\n /** KPI hit + LRU access stamp for a successful LOCAL read. No-op without a registry. */\n private async recordLocalHit(slotName: string, prev?: BlobPinEntry | null): Promise<void> {\n const pc = this.pinCache\n if (!pc) return\n pc.stats.hits += 1\n const key = this.pinKeyFor(slotName)\n const entry = prev ?? await pc.store.get(key)\n await pc.store.set(key, { ...(entry ?? { pinned: false }), lastAccessAt: new Date().toISOString() })\n }\n\n /**\n * Read an `external` slot (#808 taxonomy): local encrypted side-cache first\n * (hit); else the object store (miss + `bytesDownloaded`), auto-caching the\n * fetched bytes so the NEXT read — and any later offline read — is local.\n * A network failure with no local copy is the typed `BlobOfflineError`;\n * a genuinely absent object (fetch succeeded, nothing there) stays `null`.\n */\n private async readExternal(slotName: string, slot: SlotRecord): Promise<Uint8Array | null> {\n const pc = this.pinCache\n if (pc) {\n const key = this.pinKeyFor(slotName)\n const entry = await pc.store.get(key)\n const cached = entry?.cipher ? await this.decryptCachedCopy(slotName, entry.cipher) : null\n if (cached) {\n await this.recordLocalHit(slotName, entry)\n return cached\n }\n }\n\n if (!this.objectStore) {\n throw new NotFoundError(`Blob slot \"${slotName}\" is external but no objectStore is configured`)\n }\n let bytes: Uint8Array | null\n try {\n bytes = await this.objectStore.getObject(slot.external!.key)\n } catch (err) {\n throw new BlobOfflineError(\n this.collection, this.recordId, slotName,\n 'no local cached copy and the object store is unreachable — pin() the slot while online to keep it readable offline (#808)',\n err,\n )\n }\n if (bytes === null) return null\n if (pc) {\n pc.stats.misses += 1\n pc.stats.bytesDownloaded += bytes.byteLength\n // Best-effort: a failing device-local cache write must not fail a read\n // that already holds the bytes (e.g. a full IndexedDB quota). The next\n // read simply fetches again.\n await this.cacheExternalCopy(slotName, bytes).catch(() => {})\n }\n return bytes\n }\n\n /**\n * Decrypt a cached external copy. `null` (treated as a cache miss → network\n * refresh) when the vault keys can no longer open it — e.g. the copy was\n * cached before the owning record moved tiers, so it is sealed under a DEK\n * this handle no longer resolves. Genuine non-crypto failures propagate.\n */\n private async decryptCachedCopy(slotName: string, cipher: { iv: string; data: string }): Promise<Uint8Array | null> {\n if (!this.encrypted) return base64ToBuffer(cipher.data)\n const dek = await this.getDEK(dekKey(BLOB_COLLECTION, await this.ownerTier()))\n try {\n return await decryptBytesWithAAD(cipher.iv, cipher.data, dek, this.pinCopyAAD(slotName))\n } catch (err) {\n if (err instanceof TamperedError) return null\n throw err\n }\n }\n\n /**\n * Write an external slot's bytes into the device-local side-cache —\n * ENCRYPTED under the vault's `_blob` DEK (owner tier) via the enclave's\n * AAD-bound path, mirroring chunk encryption; base64 plaintext with\n * `iv: ''` only on an unencrypted vault (the envelope convention). A\n * pre-existing pin flag survives the rewrite.\n */\n private async cacheExternalCopy(slotName: string, bytes: Uint8Array, opts?: { pin?: boolean }): Promise<void> {\n const pc = this.pinCache\n if (!pc) return\n let cipher: { iv: string; data: string }\n if (this.encrypted) {\n const dek = await this.getDEK(dekKey(BLOB_COLLECTION, await this.ownerTier()))\n const enc = await encryptBytesWithAAD(bytes, dek, this.pinCopyAAD(slotName))\n cipher = { iv: enc.iv, data: enc.data }\n } else {\n cipher = { iv: '', data: bufferToBase64(bytes) }\n }\n const now = new Date().toISOString()\n const key = this.pinKeyFor(slotName)\n const prev = await pc.store.get(key)\n const pinned = opts?.pin === true || prev?.pinned === true\n await pc.store.set(key, {\n pinned,\n ...(pinned ? { pinnedAt: (opts?.pin === true ? now : prev?.pinnedAt) ?? now } : {}),\n lastAccessAt: now,\n cachedBytes: bytes.byteLength,\n cipher,\n })\n }\n\n /**\n * Return a native `Response` whose body streams the decrypted,\n * decompressed blob bytes with full HTTP metadata headers.\n *\n * Note: implementation is buffered — all chunks are loaded into\n * memory before being enqueued. True streaming deferred to.\n *\n * Returns `null` if the slot does not exist.\n */\n async response(slotName: string, opts?: BlobResponseOptions): Promise<Response | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) return null\n\n // #749: see the matching comment in `get()`.\n const blobDEK = this.encrypted ? await this.getDEK(dekKey(BLOB_COLLECTION, result.atTier)) : undefined\n return this.buildResponse(slot, result.blob, opts, blobDEK, this.flatFallbackVerifyETag(slot.eTag, result.atTier))\n }\n\n /**\n * Decrypt the slot and wrap the bytes in a browser ObjectURL ready\n * to feed into `<img src>`, `<a href>`, etc. The caller MUST call\n * `revoke()` when the URL is no longer needed — otherwise the URL\n * (and the underlying decrypted Blob) are pinned for the lifetime\n * of the document, which leaks memory in long-lived pages.\n *\n * Returns `null` when the slot does not exist.\n *\n * Throws when `URL.createObjectURL` is unavailable in the host\n * environment (Node without DOM, restricted workers). Framework\n * adapters — `useBlobURL` in `@noy-db/in-vue`, etc. — guard against\n * this for SSR contexts and stay at `null` instead of propagating.\n */\n async objectURL(\n slotName: string,\n opts?: { mimeType?: string },\n ): Promise<{ url: string; revoke: () => void } | null> {\n if (typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function') {\n throw new Error(\n 'BlobSet.objectURL: URL.createObjectURL is unavailable in this environment. ' +\n 'Call this from the browser, or use BlobSet.get() and create the URL yourself.',\n )\n }\n const bytes = await this.get(slotName)\n if (!bytes) return null\n\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n const type = opts?.mimeType ?? slot?.mimeType ?? 'application/octet-stream'\n\n // Pinning the underlying ArrayBuffer in a Blob is what backs the\n // ObjectURL — once we createObjectURL the URL holds a strong ref\n // to the Blob, so the local `blob` variable can fall out of scope.\n // Copy through a fresh ArrayBuffer so TS narrows away the\n // SharedArrayBuffer branch of `ArrayBufferLike` (Uint8Array is\n // generic over the backing buffer type since TS 5.7).\n const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer\n const blob = new Blob([buffer], { type })\n const url = URL.createObjectURL(blob)\n let revoked = false\n const revoke = (): void => {\n if (revoked) return\n revoked = true\n URL.revokeObjectURL(url)\n }\n return { url, revoke }\n }\n\n // ─── Public API: Published versions (UC-3 amendment versioning) ───\n\n /**\n * Publish the current slot content as a named version snapshot.\n *\n * The published version holds an independent refCount reference to\n * the blob. Even if the slot is later overwritten or deleted, the\n * published version keeps the blob data alive.\n *\n * Publishing with an existing label overwrites it — if the eTags differ,\n * refCounts are adjusted accordingly.\n *\n * #724 Arc 10 Task 2 (C4): the version record is written under the owning\n * record's CURRENT tier — a version published on a tier-N record is keyed\n * at tier N, mirroring `put()`'s owner-tier resolution (Task 1). The\n * refCount hold it takes lands on `slot.eTag`, which is already\n * tier-scoped (born there by `put()`, or moved there by a prior\n * `rehomeForTier`) — no separate tier-scoping needed for the content side.\n */\n async publish(slotName: string, label: string): Promise<void> {\n this.assertKeyPartSafe(this.recordId, 'record id')\n this.assertKeyPartSafe(slotName, 'slot name')\n this.assertKeyPartSafe(label, 'version label')\n this.assertBlobWritable() // #724 I1 completion: same write-time refusal as put()\n await this.resolvePendingIntent() // #753 spec §7 C6: resume a pending shred before accepting a new write\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) throw new NotFoundError(`Slot \"${slotName}\" not found on record \"${this.recordId}\"`)\n\n const tier = await this.ownerTier()\n\n // Check for existing version with this label\n const existing = await this.loadVersionRecord(slotName, label, tier)\n if (existing && existing.eTag === slot.eTag) return // no-op: same blob\n\n // Write the version record\n const record: VersionRecord = {\n label,\n eTag: slot.eTag,\n publishedAt: new Date().toISOString(),\n ...(this.userId !== undefined ? { publishedBy: this.userId } : {}),\n }\n await this.writeVersionRecord(slotName, record, tier)\n\n // Increment refCount for the new version's eTag\n await this.casUpdateRefCount(slot.eTag, +1)\n\n // If overwriting an existing version with a different eTag, release the old\n // one (crypto-shred at refCount 0 when erasable).\n if (existing && existing.eTag !== slot.eTag) {\n await this.releaseRef(existing.eTag, 1, false).catch(() => {})\n }\n }\n\n /**\n * Fetch bytes for a published version.\n * Returns `null` if the version does not exist.\n */\n async getVersion(slotName: string, label: string): Promise<Uint8Array | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const record = await this.loadVersionRecord(slotName, label)\n if (!record) return null\n\n const result = await this.loadBlobObject(record.eTag)\n if (!result) return null\n\n // #749: see the matching comment in `get()` — `result.atTier` is the\n // tier that actually opened the index envelope, which is what the\n // wrapped content `_cek` is scoped under.\n const blobDEK = this.encrypted ? await this.getDEK(dekKey(BLOB_COLLECTION, result.atTier)) : undefined\n return this.fetchAllChunks(result.blob, blobDEK, this.flatFallbackVerifyETag(record.eTag, result.atTier))\n }\n\n /**\n * List all published versions for a slot.\n */\n async listVersions(slotName: string): Promise<VersionRecord[]> {\n if (await this.ownerRecordElevated()) return [] // #724: elevated ≡ invisible, mirrors get()\n const prefix = `${this.recordId}::${slotName}::`\n const allKeys = await this.store.list(this.vault, this.versionsCollection)\n const matchingKeys = allKeys.filter((k) => k.startsWith(prefix))\n\n const versions: VersionRecord[] = []\n for (const key of matchingKeys) {\n const envelope = await this.store.get(this.vault, this.versionsCollection, key)\n if (!envelope) continue\n\n if (!this.encrypted) {\n versions.push(JSON.parse(envelope._data) as VersionRecord)\n } else {\n const dek = await this.getDEK(this.collection)\n const json = await openEnvelopeJson(envelope, dek)\n versions.push(JSON.parse(json) as VersionRecord)\n }\n }\n\n return versions\n }\n\n /**\n * Delete a published version. Decrements refCount on its blob.\n */\n async deleteVersion(slotName: string, label: string): Promise<void> {\n await this.resolvePendingIntent() // #753 spec §7 C6: resume a pending shred before accepting a new write\n const record = await this.loadVersionRecord(slotName, label)\n if (!record) return\n\n await this.deleteVersionRecord(slotName, label)\n await this.releaseRef(record.eTag, 1, false).catch(() => {})\n }\n\n /**\n * Return a `Response` for a published version — same as `response()`\n * but reads from the version record's eTag instead of the current slot.\n */\n async responseVersion(\n slotName: string,\n label: string,\n opts?: BlobResponseOptions,\n ): Promise<Response | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const record = await this.loadVersionRecord(slotName, label)\n if (!record) return null\n\n const result = await this.loadBlobObject(record.eTag)\n if (!result) return null\n\n // Build a synthetic SlotRecord from the version + blob data\n const slotLike: SlotRecord = {\n eTag: record.eTag,\n filename: opts?.filename ?? `${slotName}-${label}`,\n size: result.blob.size,\n ...(result.blob.mimeType !== undefined ? { mimeType: result.blob.mimeType } : {}),\n uploadedAt: record.publishedAt,\n ...(record.publishedBy !== undefined ? { uploadedBy: record.publishedBy } : {}),\n }\n\n // #749: see the matching comment in `get()`.\n const blobDEK = this.encrypted ? await this.getDEK(dekKey(BLOB_COLLECTION, result.atTier)) : undefined\n return this.buildResponse(slotLike, result.blob, opts, blobDEK, this.flatFallbackVerifyETag(record.eTag, result.atTier))\n }\n\n // ─── Diagnostics ──────────────────────────────────────────────────\n\n /**\n * Return the `BlobObject` metadata for the named slot.\n * Returns `null` if the slot or blob does not exist.\n */\n async blobInfo(slotName: string): Promise<BlobObject | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n const result = await this.loadBlobObject(slot.eTag)\n return result?.blob ?? null\n }\n\n // ─── Presigned URL (E5) ────────────────────────────────────────────\n\n /**\n * Generate a presigned URL for direct client download of the blob's\n * ciphertext. Only works when the blob store supports `presignUrl`.\n *\n * **Important:** The URL returns encrypted data. The caller must\n * decrypt client-side using `decryptResponse()` or a service worker.\n *\n * Returns `null` if the slot doesn't exist or the store doesn't support presigning.\n */\n async presignedUrl(slotName: string, expiresInSeconds = 3600): Promise<string | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) return null\n\n // Only works for single-chunk blobs where the store supports presigning\n if (result.blob.chunkCount !== 1) return null\n if (!this.store.presignUrl) return null\n\n const chunkId = `${slot.eTag}_0`\n return this.store.presignUrl(this.vault, '_blob_chunks', chunkId, expiresInSeconds)\n }\n\n /**\n * Decrypt a ciphertext Response (e.g. from a presigned URL fetch)\n * back into a plaintext Response with correct headers.\n *\n * Usage with service worker or client-side fetch:\n * ```ts\n * const url = await blobs.presignedUrl('invoice.pdf')\n * const cipherResponse = await fetch(url)\n * const plainResponse = await blobs.decryptResponse('invoice.pdf', cipherResponse)\n * ```\n */\n async decryptResponse(slotName: string, cipherResponse: Response): Promise<Response | null> {\n if (await this.ownerRecordElevated()) return null // #724: elevated ≡ invisible, mirrors get()\n const { slots } = await this.loadSlots()\n const slot = slots[slotName]\n if (!slot) return null\n\n const result = await this.loadBlobObject(slot.eTag)\n if (!result) return null\n\n // #757: `presignedUrl()` only ever mints a URL (and this method only ever\n // receives one caller-fetched ciphertext Response) for a single-chunk\n // blob — a multi-chunk object can't be assembled from that one Response.\n // Refuse loudly instead of silently decrypting/returning only chunk 0.\n if (result.blob.chunkCount !== 1) {\n throw new ValidationError(\n `decryptResponse() does not support multi-chunk blobs (slot \"${slotName}\" has ` +\n `${result.blob.chunkCount} chunks) — presignedUrl() only mints URLs for single-chunk blobs (#757)`,\n )\n }\n\n // #757: tier-aware DEK, mirroring get()/response() post-#749 — the\n // index envelope's `_cek` (if any) is wrapped under the tier that\n // actually opened it (`result.atTier`), not necessarily this record's\n // live/flat tier.\n const blobDEK = this.encrypted ? await this.getDEK(dekKey(BLOB_COLLECTION, result.atTier)) : null\n if (!blobDEK) {\n return this.buildResponse(slot, result.blob, { inline: true })\n }\n\n // Parse the envelope from the caller-provided ciphertext response\n const text = await cipherResponse.text()\n const envelope = JSON.parse(text) as { _iv: string; _data: string }\n\n // #757: unwrap the per-blob content CEK when present (mirrors\n // fetchAllChunks/resolveChunkKey) instead of decrypting chunk bytes\n // directly under the flat DEK — an erasable (perRecordKeys) blob's\n // chunks are never keyed that way.\n const chunkKey = await this.resolveChunkKey(result.blob, blobDEK)\n if (!chunkKey) {\n // Unreachable: resolveChunkKey only returns null for an unencrypted\n // vault, already handled by the blobDEK guard above.\n return this.buildResponse(slot, result.blob, { inline: true })\n }\n\n // Decrypt the single chunk\n const aad = chunkAAD(slot.eTag, 0, result.blob.chunkCount)\n const decrypted = await decryptBytesWithAAD(envelope._iv, envelope._data, chunkKey, aad)\n const plaintext = result.blob.compression === 'gzip'\n ? await decompressBytes(decrypted)\n : decrypted\n\n // #757: content-address verification. Unlike the store-driven read\n // paths, `decryptResponse` decrypts a CALLER-PROVIDED ciphertext (the\n // `cipherResponse` a caller fetched from a presigned URL) — GCM auth\n // alone doesn't catch a self-consistent forgery planted under a DEK the\n // attacker also holds. Recompute the same content address every write\n // path mints (`hmacSha256Hex(blobDEK, plaintext)` — `writeBlobContent`'s\n // Step 1, unconditional on `_cek`; mirrors `fetchAllChunks`'s\n // `verifyFlatETag` block) and refuse silently-wrong bytes.\n const recomputed = await hmacSha256Hex(blobDEK, plaintext)\n if (recomputed !== slot.eTag) {\n throw new TamperedError(\n `Blob content for eTag \"${slot.eTag}\" failed content-address verification in decryptResponse() ` +\n '— the caller-provided ciphertext does not match the requested content address (#757)',\n )\n }\n\n const body = new ReadableStream<Uint8Array>({\n start(controller) {\n controller.enqueue(plaintext)\n controller.close()\n },\n })\n\n const filename = slot.filename\n return new Response(body, {\n headers: {\n 'Content-Type': slot.mimeType ?? 'application/octet-stream',\n 'Content-Length': String(slot.size),\n 'ETag': `\"${slot.eTag}\"`,\n 'Content-Disposition': `inline; filename=\"${filename}\"`,\n 'Last-Modified': new Date(slot.uploadedAt).toUTCString(),\n },\n })\n }\n\n // ─── Internal: build Response from slot + blob ────────────────────\n\n private async buildResponse(\n slot: SlotRecord,\n blob: BlobObject,\n opts?: BlobResponseOptions,\n blobDEK?: EnclaveKey,\n verifyFlatETag?: string,\n ): Promise<Response> {\n const fetchAllChunks = this.fetchAllChunks.bind(this)\n\n // buffered — all chunks loaded into memory then enqueued.\n const body = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n const output = await fetchAllChunks(blob, blobDEK, verifyFlatETag)\n controller.enqueue(output)\n controller.close()\n } catch (err) {\n controller.error(err)\n }\n },\n })\n\n const filename = opts?.filename ?? slot.filename\n const disposition = opts?.inline\n ? `inline; filename=\"${filename}\"`\n : `attachment; filename=\"${filename}\"`\n\n return new Response(body, {\n headers: {\n 'Content-Type': slot.mimeType ?? 'application/octet-stream',\n 'Content-Length': String(slot.size),\n 'ETag': `\"${slot.eTag}\"`,\n 'Content-Disposition': disposition,\n 'Last-Modified': new Date(slot.uploadedAt).toUTCString(),\n },\n })\n }\n}\n\n// ─── Fallback for unencrypted mode ──────────────────────────────────────\n\nasync function plainSha256Hex(data: Uint8Array): Promise<string> {\n return sha256Hex(data)\n}\n"],"mappings":"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","names":["json","key"]}
|