@noy-db/hub 0.2.0-pre.9 → 0.3.0-pre.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -3
- package/dist/adapter/index.d.ts +5 -0
- package/dist/adapter/index.js +15 -0
- package/dist/aggregate/index.d.ts +6 -2
- package/dist/aggregate/index.js +24 -16
- package/dist/aggregate/index.js.map +1 -1
- package/dist/api-RW3KP7WU.js +14 -0
- package/dist/as/index.d.ts +7 -0
- package/dist/as/index.js +24 -0
- package/dist/at/index.d.ts +5 -0
- package/dist/at/index.js +1 -0
- package/dist/attestation/index.d.ts +15 -47
- package/dist/attestation/index.js +54 -10
- package/dist/attestation/index.js.map +1 -1
- package/dist/backup-KMJQ66MF.js +219 -0
- package/dist/backup-KMJQ66MF.js.map +1 -0
- package/dist/blobs/index.d.ts +6 -8
- package/dist/blobs/index.js +19 -12
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.d.ts +16 -70
- package/dist/bundle/index.js +39 -476
- package/dist/bundle/index.js.map +1 -1
- package/dist/{ulid-CJ8RzRrm.d.ts → bundle-PwBGkhpe.d.ts} +31 -86
- package/dist/by/index.d.ts +1 -0
- package/dist/by/index.js +11 -0
- package/dist/cargo/index.d.ts +9 -0
- package/dist/cargo/index.js +89 -0
- package/dist/chunk-26LGBE4T.js +42 -0
- package/dist/chunk-26LGBE4T.js.map +1 -0
- package/dist/{chunk-K3DK3NU5.js → chunk-3YFXJ3ZP.js} +20 -5
- package/dist/chunk-3YFXJ3ZP.js.map +1 -0
- package/dist/chunk-4Q6HSHHL.js +90 -0
- package/dist/chunk-4Q6HSHHL.js.map +1 -0
- package/dist/chunk-5LUY7UTV.js +380 -0
- package/dist/chunk-5LUY7UTV.js.map +1 -0
- package/dist/chunk-5N6CZTCY.js +367 -0
- package/dist/chunk-5N6CZTCY.js.map +1 -0
- package/dist/chunk-5O3AOPDT.js +992 -0
- package/dist/chunk-5O3AOPDT.js.map +1 -0
- package/dist/chunk-5R3ERCDH.js +239 -0
- package/dist/chunk-5R3ERCDH.js.map +1 -0
- package/dist/chunk-5TDJ56JE.js +32 -0
- package/dist/chunk-5TDJ56JE.js.map +1 -0
- package/dist/{chunk-T7JEBOGN.js → chunk-62QYRORB.js} +18 -11
- package/dist/chunk-62QYRORB.js.map +1 -0
- package/dist/{chunk-J66GRPNH.js → chunk-6S7T6WC4.js} +2 -2
- package/dist/chunk-6S7T6WC4.js.map +1 -0
- package/dist/chunk-76722EBH.js +451 -0
- package/dist/chunk-76722EBH.js.map +1 -0
- package/dist/{chunk-Z6FNBOTC.js → chunk-AIWULCUO.js} +13 -17
- package/dist/chunk-AIWULCUO.js.map +1 -0
- package/dist/{chunk-O3VZPBZG.js → chunk-AQTBSL7R.js} +47 -11
- package/dist/chunk-AQTBSL7R.js.map +1 -0
- package/dist/chunk-AURFOK3D.js +35 -0
- package/dist/chunk-AURFOK3D.js.map +1 -0
- package/dist/{chunk-53XBRIRT.js → chunk-AXRXJTE2.js} +9 -9
- package/dist/chunk-AXRXJTE2.js.map +1 -0
- package/dist/chunk-AZAOEIKW.js +155 -0
- package/dist/chunk-AZAOEIKW.js.map +1 -0
- package/dist/{chunk-HNRHLRDC.js → chunk-BG3SPSR4.js} +3 -3
- package/dist/chunk-BG3SPSR4.js.map +1 -0
- package/dist/chunk-BGIZAFY7.js +1 -0
- package/dist/chunk-CHFZDSE4.js +1 -0
- package/dist/{chunk-DJHHA6XH.js → chunk-CMGR4ZEW.js} +50 -20
- package/dist/chunk-CMGR4ZEW.js.map +1 -0
- package/dist/chunk-CWPPNPOH.js +23 -0
- package/dist/chunk-CWPPNPOH.js.map +1 -0
- package/dist/{chunk-QODLLGQ7.js → chunk-DFEMTNPJ.js} +371 -38
- package/dist/chunk-DFEMTNPJ.js.map +1 -0
- package/dist/{chunk-ZYWZWG6G.js → chunk-DGDI2D4M.js} +10 -10
- package/dist/chunk-DGDI2D4M.js.map +1 -0
- package/dist/{chunk-SYMWGEET.js → chunk-EIZUR2XW.js} +3 -3
- package/dist/chunk-EIZUR2XW.js.map +1 -0
- package/dist/{chunk-BVRYKATC.js → chunk-FISN7WE4.js} +36 -33
- package/dist/chunk-FISN7WE4.js.map +1 -0
- package/dist/chunk-GHVIKOHT.js +1 -0
- package/dist/chunk-GYGWYIOZ.js +200 -0
- package/dist/chunk-GYGWYIOZ.js.map +1 -0
- package/dist/chunk-HCIPRAGS.js +45 -0
- package/dist/chunk-HCIPRAGS.js.map +1 -0
- package/dist/{chunk-PX35GA7L.js → chunk-I2NOIW44.js} +10 -10
- package/dist/chunk-I2NOIW44.js.map +1 -0
- package/dist/{chunk-OIF6LZUR.js → chunk-I3TIKTJO.js} +3 -3
- package/dist/chunk-I3TIKTJO.js.map +1 -0
- package/dist/chunk-I7FD46FF.js +9 -0
- package/dist/chunk-I7FD46FF.js.map +1 -0
- package/dist/chunk-IAGBDSCS.js +40 -0
- package/dist/chunk-IAGBDSCS.js.map +1 -0
- package/dist/{chunk-JWRVQKRZ.js → chunk-IELYAOGG.js} +6 -18
- package/dist/chunk-IELYAOGG.js.map +1 -0
- package/dist/chunk-IGUYVGGI.js +205 -0
- package/dist/chunk-IGUYVGGI.js.map +1 -0
- package/dist/{chunk-U26HQ6KJ.js → chunk-IO6GRPT6.js} +4 -4
- package/dist/chunk-IO6GRPT6.js.map +1 -0
- package/dist/chunk-IPB7FTQX.js +273 -0
- package/dist/chunk-IPB7FTQX.js.map +1 -0
- package/dist/chunk-ITNUCOXM.js +148 -0
- package/dist/chunk-ITNUCOXM.js.map +1 -0
- package/dist/{chunk-WPLVTJ5D.js → chunk-IUEN57TV.js} +10 -10
- package/dist/chunk-IUEN57TV.js.map +1 -0
- package/dist/{chunk-DL3HWOQ5.js → chunk-JNUWZ6D3.js} +9 -9
- package/dist/chunk-JNUWZ6D3.js.map +1 -0
- package/dist/chunk-K2WCO3NX.js +1 -0
- package/dist/chunk-KVOXU4T5.js +30 -0
- package/dist/chunk-KVOXU4T5.js.map +1 -0
- package/dist/chunk-KXGNJJXB.js +135 -0
- package/dist/chunk-KXGNJJXB.js.map +1 -0
- package/dist/chunk-LB7FD65R.js +163 -0
- package/dist/chunk-LB7FD65R.js.map +1 -0
- package/dist/{chunk-22DWZL57.js → chunk-LFLXAY2W.js} +43 -218
- package/dist/chunk-LFLXAY2W.js.map +1 -0
- package/dist/chunk-LMTH2RM6.js +129 -0
- package/dist/chunk-LMTH2RM6.js.map +1 -0
- package/dist/{aggregate/index.cjs → chunk-LV7M27WM.js} +390 -219
- package/dist/chunk-LV7M27WM.js.map +1 -0
- package/dist/{chunk-PE2FR4J3.js → chunk-LXQT4WMP.js} +16 -18
- package/dist/chunk-LXQT4WMP.js.map +1 -0
- package/dist/chunk-M2YKN37S.js +524 -0
- package/dist/chunk-M2YKN37S.js.map +1 -0
- package/dist/chunk-M6OST55S.js +14 -0
- package/dist/chunk-M6OST55S.js.map +1 -0
- package/dist/{chunk-F3GDRNUT.js → chunk-MD3Y6J3L.js} +35 -69
- package/dist/chunk-MD3Y6J3L.js.map +1 -0
- package/dist/{chunk-XJFLDA7A.js → chunk-MTMJVJ5W.js} +8 -7
- package/dist/chunk-MTMJVJ5W.js.map +1 -0
- package/dist/{chunk-DFMLEQZB.js → chunk-NF5JWERG.js} +5 -10
- package/dist/chunk-NF5JWERG.js.map +1 -0
- package/dist/{chunk-DO7C2TOG.js → chunk-PKHL44ER.js} +3 -3
- package/dist/{chunk-DO7C2TOG.js.map → chunk-PKHL44ER.js.map} +1 -1
- package/dist/{chunk-6CME4UEK.js → chunk-PSHOXR6C.js} +7008 -4827
- package/dist/chunk-PSHOXR6C.js.map +1 -0
- package/dist/chunk-PSMV73A5.js +117 -0
- package/dist/chunk-PSMV73A5.js.map +1 -0
- package/dist/chunk-PY4KJPYU.js +9 -0
- package/dist/chunk-PY4KJPYU.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/{chunk-DE6GKS6G.js → chunk-Q7SS3IME.js} +50 -9
- package/dist/chunk-Q7SS3IME.js.map +1 -0
- package/dist/chunk-QHJW5EXU.js +54 -0
- package/dist/chunk-QHJW5EXU.js.map +1 -0
- package/dist/chunk-QZISFCVG.js +233 -0
- package/dist/chunk-QZISFCVG.js.map +1 -0
- package/dist/{chunk-NONAAENK.js → chunk-RUEKROOS.js} +11 -4
- package/dist/chunk-RUEKROOS.js.map +1 -0
- package/dist/chunk-RUIMKQTA.js +23 -0
- package/dist/chunk-RUIMKQTA.js.map +1 -0
- package/dist/{chunk-TFBP23BX.js → chunk-SKF73JOP.js} +66 -7
- package/dist/chunk-SKF73JOP.js.map +1 -0
- package/dist/chunk-SM3AVUHC.js +42 -0
- package/dist/chunk-SM3AVUHC.js.map +1 -0
- package/dist/{chunk-ML236QKC.js → chunk-SMXWYP24.js} +5 -5
- package/dist/chunk-SMXWYP24.js.map +1 -0
- package/dist/chunk-SRB2XEWB.js +148 -0
- package/dist/chunk-SRB2XEWB.js.map +1 -0
- package/dist/chunk-SVLR4POP.js +64 -0
- package/dist/chunk-SVLR4POP.js.map +1 -0
- package/dist/chunk-TA66UMI4.js +123 -0
- package/dist/chunk-TA66UMI4.js.map +1 -0
- package/dist/chunk-TEILUWOI.js +664 -0
- package/dist/chunk-TEILUWOI.js.map +1 -0
- package/dist/chunk-TJYQIGAP.js +82 -0
- package/dist/chunk-TJYQIGAP.js.map +1 -0
- package/dist/{chunk-H2X3ISXG.js → chunk-UAG5KWVA.js} +7 -7
- package/dist/chunk-UAG5KWVA.js.map +1 -0
- package/dist/chunk-UDRIWL7A.js +382 -0
- package/dist/chunk-UDRIWL7A.js.map +1 -0
- package/dist/{chunk-2GLDA55J.js → chunk-UIU2THRG.js} +498 -133
- package/dist/chunk-UIU2THRG.js.map +1 -0
- package/dist/{chunk-3YPCK6JX.js → chunk-UPJNJGGA.js} +165 -423
- package/dist/chunk-UPJNJGGA.js.map +1 -0
- package/dist/chunk-UV5MFVO3.js +21 -0
- package/dist/chunk-UV5MFVO3.js.map +1 -0
- package/dist/{chunk-2WUSG3IT.js → chunk-V7RWQRG7.js} +5 -5
- package/dist/chunk-V7RWQRG7.js.map +1 -0
- package/dist/{chunk-VTKGMEPP.js → chunk-VCTFO5CO.js} +13 -3
- package/dist/chunk-VCTFO5CO.js.map +1 -0
- package/dist/{chunk-5T22KDPN.js → chunk-VFQGRQIH.js} +8 -8
- package/dist/chunk-VFQGRQIH.js.map +1 -0
- package/dist/{chunk-2QR2PQTT.js → chunk-VQNJ7UZL.js} +5 -3
- package/dist/chunk-VQNJ7UZL.js.map +1 -0
- package/dist/{chunk-DONMZAD2.js → chunk-WWGT2MDV.js} +43 -165
- package/dist/chunk-WWGT2MDV.js.map +1 -0
- package/dist/{chunk-EMIGCR7X.js → chunk-WXSYDNBT.js} +2 -2
- package/dist/chunk-WXSYDNBT.js.map +1 -0
- package/dist/chunk-WYSO2NIH.js +30 -0
- package/dist/chunk-WYSO2NIH.js.map +1 -0
- package/dist/chunk-XXYIOFUB.js +164 -0
- package/dist/chunk-XXYIOFUB.js.map +1 -0
- package/dist/{chunk-3BFJOWSU.js → chunk-Y22T3KQE.js} +35 -7
- package/dist/chunk-Y22T3KQE.js.map +1 -0
- package/dist/{chunk-A3JMGXPG.js → chunk-YMUGBZN2.js} +2 -2
- package/dist/chunk-YMUGBZN2.js.map +1 -0
- package/dist/{chunk-I5FOWO4J.js → chunk-ZCGAHGUS.js} +52 -73
- package/dist/chunk-ZCGAHGUS.js.map +1 -0
- package/dist/{chunk-FZU343FL.js → chunk-ZJADGNYF.js} +2 -2
- package/dist/chunk-ZJADGNYF.js.map +1 -0
- package/dist/{chunk-B6PB7JLN.js → chunk-ZYUC53LT.js} +427 -6
- package/dist/chunk-ZYUC53LT.js.map +1 -0
- package/dist/collection-facade-GQAOGJP2.js +33 -0
- package/dist/consent/index.d.ts +5 -7
- package/dist/consent/index.js +7 -5
- package/dist/consent/index.js.map +1 -1
- package/dist/crdt/index.d.ts +6 -2
- package/dist/crdt/index.js +3 -2
- package/dist/crdt/index.js.map +1 -1
- package/dist/decrypt-partition-IHtxEnTi.d.ts +21 -0
- package/dist/delegation-UL5HKLER.js +19 -0
- package/dist/derivations/index.d.ts +7 -9
- package/dist/derivations/index.js +11 -6
- package/dist/describe/index.d.ts +1 -0
- package/dist/describe/index.js +1 -0
- package/dist/describe-Ccd1hS7a.d.ts +143 -0
- package/dist/{dev-unlock-B4kDxep_.d.ts → dev-unlock-h0K-UZTk.d.ts} +1 -1
- package/dist/discriminant-BN9REW3o.d.ts +60 -0
- package/dist/enclave-UL5LW66V.js +88 -0
- package/dist/executor-2FWQRLMG.js +15 -0
- package/dist/executor-QZ7BXICW.js +9 -0
- package/dist/executor-Z5ZLJVTV.js +9 -0
- package/dist/export-accessible-KRV5W4GH.js +17 -0
- package/dist/extract-partition-GLKBOXFQ.js +30 -0
- package/dist/{fanout-sidecar-OKPMMPLG.js → fanout-sidecar-GF3DJ6KR.js} +34 -14
- package/dist/fanout-sidecar-GF3DJ6KR.js.map +1 -0
- package/dist/forget/index.d.ts +36 -0
- package/dist/forget/index.js +19 -0
- package/dist/forget/index.js.map +1 -0
- package/dist/guards/index.d.ts +13 -9
- package/dist/guards/index.js +12 -5
- package/dist/{hash-DdpVypUp.d.cts → hash-CdSr06sm.d.ts} +5 -1
- package/dist/history/index.d.ts +6 -8
- package/dist/history/index.js +19 -12
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.d.ts +5 -7
- package/dist/i18n/index.js +104 -15
- package/dist/i18n/index.js.map +1 -1
- package/dist/in/index.d.ts +5 -0
- package/dist/in/index.js +1 -0
- package/dist/in/index.js.map +1 -0
- package/dist/index-B9QdHytu.d.ts +123 -0
- package/dist/index-CGLLRtFc.d.ts +123 -0
- package/dist/{types-Df28QFKb.d.ts → index-_6At2_9_.d.ts} +16270 -8358
- package/dist/index.d.ts +1088 -450
- package/dist/index.js +1165 -293
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.d.ts +6 -3
- package/dist/indexing/index.js +6 -5
- package/dist/indexing/index.js.map +1 -1
- package/dist/issue-Y6UVIR43.js +13 -0
- package/dist/issue-Y6UVIR43.js.map +1 -0
- package/dist/kernel/index.d.ts +32 -0
- package/dist/kernel/index.js +53 -0
- package/dist/kernel/index.js.map +1 -0
- package/dist/{ledger-TMRZYNVJ.js → ledger-RYI35CDS.js} +12 -9
- package/dist/ledger-RYI35CDS.js.map +1 -0
- package/dist/liberate-CRPZEV7O.js +18 -0
- package/dist/liberate-CRPZEV7O.js.map +1 -0
- package/dist/materialized-views/index.d.ts +6 -19
- package/dist/materialized-views/index.js +16 -12
- package/dist/mime-magic-B4RoFj3B.d.ts +103 -0
- package/dist/noydb-LDEBLNHY.js +50 -0
- package/dist/noydb-LDEBLNHY.js.map +1 -0
- package/dist/on/index.d.ts +5 -0
- package/dist/on/index.js +11 -0
- package/dist/on/index.js.map +1 -0
- package/dist/overlay-views/index.d.ts +27 -11
- package/dist/overlay-views/index.js +7 -6
- package/dist/periods/index.d.ts +5 -7
- package/dist/periods/index.js +13 -9
- package/dist/pod/index.d.ts +63 -0
- package/dist/pod/index.js +61 -0
- package/dist/pod/index.js.map +1 -0
- package/dist/policy-IXK4FVXP.js +41 -0
- package/dist/policy-IXK4FVXP.js.map +1 -0
- package/dist/portability/index.d.ts +20 -0
- package/dist/portability/index.js +43 -0
- package/dist/portability/index.js.map +1 -0
- package/dist/{public-envelope-BW6OXORV.js → public-envelope-JHDQCPSX.js} +6 -5
- package/dist/public-envelope-JHDQCPSX.js.map +1 -0
- package/dist/query/index.d.ts +5 -3
- package/dist/query/index.js +21 -13
- package/dist/read-only-facade-5ADRJVTM.js +8 -0
- package/dist/read-only-facade-5ADRJVTM.js.map +1 -0
- package/dist/registry-45RJNWGU.js +9 -0
- package/dist/registry-45RJNWGU.js.map +1 -0
- package/dist/registry-5GRV5VXI.js +13 -0
- package/dist/registry-5GRV5VXI.js.map +1 -0
- package/dist/registry-VW6P6A3J.js +8 -0
- package/dist/registry-VW6P6A3J.js.map +1 -0
- package/dist/registry-WEUCHBO4.js +11 -0
- package/dist/registry-WEUCHBO4.js.map +1 -0
- package/dist/request-withdrawal-GBPH2FDY.js +25 -0
- package/dist/request-withdrawal-GBPH2FDY.js.map +1 -0
- package/dist/revoke-TUFRGI6S.js +18 -0
- package/dist/revoke-TUFRGI6S.js.map +1 -0
- package/dist/sealed-record/index.d.ts +69 -0
- package/dist/sealed-record/index.js +65 -0
- package/dist/sealed-record/index.js.map +1 -0
- package/dist/session/index.d.ts +6 -8
- package/dist/session/index.js +7 -5
- package/dist/session/index.js.map +1 -1
- package/dist/shadow/index.d.ts +5 -7
- package/dist/shadow/index.js +4 -3
- package/dist/shadow/index.js.map +1 -1
- package/dist/{signer-72QAUSVW.js → signer-PNKTBVF7.js} +6 -5
- package/dist/signer-PNKTBVF7.js.map +1 -0
- package/dist/snapshots/index.d.ts +6 -8
- package/dist/snapshots/index.js +13 -11
- package/dist/snapshots/index.js.map +1 -1
- package/dist/{stale-6ZDBTQT7.js → stale-3JWHNDIY.js} +3 -2
- package/dist/stale-3JWHNDIY.js.map +1 -0
- package/dist/store-coordination-provider-3I7XWZZK.js +142 -0
- package/dist/store-coordination-provider-3I7XWZZK.js.map +1 -0
- package/dist/subject-index-O75wKshW.d.ts +362 -0
- package/dist/sync/index.d.ts +4 -6
- package/dist/sync/index.js +7 -6
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.d.ts +5 -7
- package/dist/team/index.js +20 -16
- package/dist/tiers/index.d.ts +5 -0
- package/dist/tiers/index.js +33 -0
- package/dist/tiers/index.js.map +1 -0
- package/dist/to/index.d.ts +5 -0
- package/dist/to/index.js +17 -0
- package/dist/to/index.js.map +1 -0
- package/dist/transition-guard-DqodPNKw.d.ts +165 -0
- package/dist/tx/index.d.ts +23 -9
- package/dist/tx/index.js +13 -8
- package/dist/tx/index.js.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ulid-DRH25k3y.d.ts +66 -0
- package/dist/ulid-PTAIMDG4.js +10 -0
- package/dist/ulid-PTAIMDG4.js.map +1 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.js +7 -2
- package/dist/util/index.js.map +1 -1
- package/dist/vault-diff-BtpiBvic.d.ts +147 -0
- package/dist/with/index.d.ts +38 -0
- package/dist/with/index.js +25 -0
- package/dist/with/index.js.map +1 -0
- package/dist/{with-materialized-view-BLkQxoQN.d.ts → with-materialized-view-DMmWtYfJ.d.ts} +1 -1
- package/dist/{with-overlayed-view-CRsSEwHR.d.ts → with-overlayed-view-D_IB2nkM.d.ts} +2 -3
- package/dist/with-rollup-em2wxoHP.d.ts +47 -0
- package/dist/withdraw-accessible-FFS46EOD.js +22 -0
- package/dist/withdraw-accessible-FFS46EOD.js.map +1 -0
- package/dist/zod-HAJM7LMS.js +14763 -0
- package/dist/zod-HAJM7LMS.js.map +1 -0
- package/package.json +121 -201
- package/dist/aggregate/index.cjs.map +0 -1
- package/dist/aggregate/index.d.cts +0 -38
- package/dist/attestation/index.cjs +0 -305
- package/dist/attestation/index.cjs.map +0 -1
- package/dist/attestation/index.d.cts +0 -52
- package/dist/blobs/index.cjs +0 -1480
- package/dist/blobs/index.cjs.map +0 -1
- package/dist/blobs/index.d.cts +0 -46
- package/dist/bundle/index.cjs +0 -19264
- package/dist/bundle/index.cjs.map +0 -1
- package/dist/bundle/index.d.cts +0 -176
- package/dist/chunk-22DWZL57.js.map +0 -1
- package/dist/chunk-2GLDA55J.js.map +0 -1
- package/dist/chunk-2QR2PQTT.js.map +0 -1
- package/dist/chunk-2WUSG3IT.js.map +0 -1
- package/dist/chunk-3BFJOWSU.js.map +0 -1
- package/dist/chunk-3YPCK6JX.js.map +0 -1
- package/dist/chunk-53XBRIRT.js.map +0 -1
- package/dist/chunk-5T22KDPN.js.map +0 -1
- package/dist/chunk-5XHKQ56N.js +0 -340
- package/dist/chunk-5XHKQ56N.js.map +0 -1
- package/dist/chunk-6CME4UEK.js.map +0 -1
- package/dist/chunk-6STK5TQP.js +0 -139
- package/dist/chunk-6STK5TQP.js.map +0 -1
- package/dist/chunk-A3JMGXPG.js.map +0 -1
- package/dist/chunk-B6PB7JLN.js.map +0 -1
- package/dist/chunk-BVRYKATC.js.map +0 -1
- package/dist/chunk-DE6GKS6G.js.map +0 -1
- package/dist/chunk-DFMLEQZB.js.map +0 -1
- package/dist/chunk-DJHHA6XH.js.map +0 -1
- package/dist/chunk-DL3HWOQ5.js.map +0 -1
- package/dist/chunk-DONMZAD2.js.map +0 -1
- package/dist/chunk-EMIGCR7X.js.map +0 -1
- package/dist/chunk-F3GDRNUT.js.map +0 -1
- package/dist/chunk-FZU343FL.js.map +0 -1
- package/dist/chunk-H2X3ISXG.js.map +0 -1
- package/dist/chunk-HNRHLRDC.js.map +0 -1
- package/dist/chunk-I5FOWO4J.js.map +0 -1
- package/dist/chunk-J66GRPNH.js.map +0 -1
- package/dist/chunk-JWRVQKRZ.js.map +0 -1
- package/dist/chunk-K3DK3NU5.js.map +0 -1
- package/dist/chunk-ML236QKC.js.map +0 -1
- package/dist/chunk-NONAAENK.js.map +0 -1
- package/dist/chunk-O3VZPBZG.js.map +0 -1
- package/dist/chunk-OIF6LZUR.js.map +0 -1
- package/dist/chunk-OQ6PIGHA.js +0 -19
- package/dist/chunk-OQ6PIGHA.js.map +0 -1
- package/dist/chunk-PE2FR4J3.js.map +0 -1
- package/dist/chunk-PP6W64Y3.js +0 -275
- package/dist/chunk-PP6W64Y3.js.map +0 -1
- package/dist/chunk-PX35GA7L.js.map +0 -1
- package/dist/chunk-QEILDE6R.js +0 -793
- package/dist/chunk-QEILDE6R.js.map +0 -1
- package/dist/chunk-QODLLGQ7.js.map +0 -1
- package/dist/chunk-RAZ4OVLL.js +0 -51
- package/dist/chunk-RAZ4OVLL.js.map +0 -1
- package/dist/chunk-SYMWGEET.js.map +0 -1
- package/dist/chunk-T7JEBOGN.js.map +0 -1
- package/dist/chunk-TFBP23BX.js.map +0 -1
- package/dist/chunk-TV3YZ35S.js +0 -90
- package/dist/chunk-TV3YZ35S.js.map +0 -1
- package/dist/chunk-U26HQ6KJ.js.map +0 -1
- package/dist/chunk-UF3BUNQZ.js +0 -1
- package/dist/chunk-UMLVJTYV.js +0 -20
- package/dist/chunk-UMLVJTYV.js.map +0 -1
- package/dist/chunk-VTKGMEPP.js.map +0 -1
- package/dist/chunk-W6EQLGMB.js +0 -100
- package/dist/chunk-W6EQLGMB.js.map +0 -1
- package/dist/chunk-WIRRPTFH.js +0 -17
- package/dist/chunk-WIRRPTFH.js.map +0 -1
- package/dist/chunk-WPLVTJ5D.js.map +0 -1
- package/dist/chunk-XJFLDA7A.js.map +0 -1
- package/dist/chunk-Z6FNBOTC.js.map +0 -1
- package/dist/chunk-ZYWZWG6G.js.map +0 -1
- package/dist/consent/index.cjs +0 -204
- package/dist/consent/index.cjs.map +0 -1
- package/dist/consent/index.d.cts +0 -25
- package/dist/crdt/index.cjs +0 -152
- package/dist/crdt/index.cjs.map +0 -1
- package/dist/crdt/index.d.cts +0 -30
- package/dist/crypto-HTZ4FJOP.js +0 -44
- package/dist/delegation-RI54P6I5.js +0 -17
- package/dist/derivations/index.cjs +0 -351
- package/dist/derivations/index.cjs.map +0 -1
- package/dist/derivations/index.d.cts +0 -72
- package/dist/dev-unlock-BIoEFbwm.d.cts +0 -263
- package/dist/executor-5PNY7LGW.js +0 -8
- package/dist/executor-B4QIYGZX.js +0 -8
- package/dist/executor-BWXXDQ7K.js +0 -11
- package/dist/fanout-sidecar-OKPMMPLG.js.map +0 -1
- package/dist/guards/index.cjs +0 -322
- package/dist/guards/index.cjs.map +0 -1
- package/dist/guards/index.d.cts +0 -31
- package/dist/hash-HJqD14vS.d.ts +0 -63
- package/dist/history/index.cjs +0 -1222
- package/dist/history/index.cjs.map +0 -1
- package/dist/history/index.d.cts +0 -63
- package/dist/i18n/index.cjs +0 -1100
- package/dist/i18n/index.cjs.map +0 -1
- package/dist/i18n/index.d.cts +0 -39
- package/dist/index-4fBVt8j9.d.cts +0 -2387
- package/dist/index-D8I_pyJD.d.ts +0 -2387
- package/dist/index.cjs +0 -24487
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -776
- package/dist/indexing/index.cjs +0 -742
- package/dist/indexing/index.cjs.map +0 -1
- package/dist/indexing/index.d.cts +0 -36
- package/dist/issue-VGDPP4B5.js +0 -12
- package/dist/lazy-builder-7tIpFyWN.d.ts +0 -304
- package/dist/lazy-builder-wY4pMCEe.d.cts +0 -304
- package/dist/materialized-views/index.cjs +0 -854
- package/dist/materialized-views/index.cjs.map +0 -1
- package/dist/materialized-views/index.d.cts +0 -186
- package/dist/mime-magic-CBBSOkjm.d.cts +0 -50
- package/dist/mime-magic-CBBSOkjm.d.ts +0 -50
- package/dist/noydb-G725ZSZG.js +0 -34
- package/dist/overlay-views/index.cjs +0 -359
- package/dist/overlay-views/index.cjs.map +0 -1
- package/dist/overlay-views/index.d.cts +0 -82
- package/dist/periods/index.cjs +0 -1041
- package/dist/periods/index.cjs.map +0 -1
- package/dist/periods/index.d.cts +0 -22
- package/dist/predicate-BSAGEyu5.d.cts +0 -209
- package/dist/predicate-BSAGEyu5.d.ts +0 -209
- package/dist/query/index.cjs +0 -2375
- package/dist/query/index.cjs.map +0 -1
- package/dist/query/index.d.cts +0 -3
- package/dist/read-only-facade-ITU6L7BL.js +0 -7
- package/dist/registry-3QP3YKQS.js +0 -8
- package/dist/registry-DKEXOJVO.js +0 -7
- package/dist/registry-OJIOSBV6.js +0 -10
- package/dist/registry-USRVT6YF.js +0 -8
- package/dist/revoke-JCC7N56X.js +0 -17
- package/dist/session/index.cjs +0 -495
- package/dist/session/index.cjs.map +0 -1
- package/dist/session/index.d.cts +0 -46
- package/dist/shadow/index.cjs +0 -133
- package/dist/shadow/index.cjs.map +0 -1
- package/dist/shadow/index.d.cts +0 -17
- package/dist/snapshots/index.cjs +0 -937
- package/dist/snapshots/index.cjs.map +0 -1
- package/dist/snapshots/index.d.cts +0 -28
- package/dist/store/index.cjs +0 -1083
- package/dist/store/index.cjs.map +0 -1
- package/dist/store/index.d.cts +0 -492
- package/dist/store/index.d.ts +0 -492
- package/dist/store/index.js +0 -37
- package/dist/strategy-BSxFXGzb.d.cts +0 -110
- package/dist/strategy-BSxFXGzb.d.ts +0 -110
- package/dist/strategy-DSTrsZ8t.d.cts +0 -601
- package/dist/strategy-DSTrsZ8t.d.ts +0 -601
- package/dist/sync/index.cjs +0 -1062
- package/dist/sync/index.cjs.map +0 -1
- package/dist/sync/index.d.cts +0 -43
- package/dist/team/index.cjs +0 -2798
- package/dist/team/index.cjs.map +0 -1
- package/dist/team/index.d.cts +0 -118
- package/dist/tx/index.cjs +0 -544
- package/dist/tx/index.cjs.map +0 -1
- package/dist/tx/index.d.cts +0 -21
- package/dist/types-DyXYr8rE.d.cts +0 -12818
- package/dist/ulid-COREQ2RQ.js +0 -9
- package/dist/ulid-Drr7ykr6.d.cts +0 -603
- package/dist/util/index.cjs +0 -230
- package/dist/util/index.cjs.map +0 -1
- package/dist/util/index.d.cts +0 -77
- package/dist/with-derivation-DFnFByiQ.d.ts +0 -13
- package/dist/with-derivation-DU3Sjazm.d.cts +0 -13
- package/dist/with-guard-ByyxmEe7.d.cts +0 -18
- package/dist/with-guard-qube6BMI.d.ts +0 -18
- package/dist/with-materialized-view-BmEToIR1.d.cts +0 -27
- package/dist/with-overlayed-view-BMsQQbWm.d.cts +0 -13
- /package/dist/{store → adapter}/index.js.map +0 -0
- /package/dist/{chunk-UF3BUNQZ.js.map → api-RW3KP7WU.js.map} +0 -0
- /package/dist/{crypto-HTZ4FJOP.js.map → as/index.js.map} +0 -0
- /package/dist/{delegation-RI54P6I5.js.map → at/index.js.map} +0 -0
- /package/dist/{executor-5PNY7LGW.js.map → by/index.js.map} +0 -0
- /package/dist/{executor-B4QIYGZX.js.map → cargo/index.js.map} +0 -0
- /package/dist/{executor-BWXXDQ7K.js.map → chunk-BGIZAFY7.js.map} +0 -0
- /package/dist/{issue-VGDPP4B5.js.map → chunk-CHFZDSE4.js.map} +0 -0
- /package/dist/{ledger-TMRZYNVJ.js.map → chunk-GHVIKOHT.js.map} +0 -0
- /package/dist/{noydb-G725ZSZG.js.map → chunk-K2WCO3NX.js.map} +0 -0
- /package/dist/{public-envelope-BW6OXORV.js.map → chunk-PZ5AY32C.js.map} +0 -0
- /package/dist/{read-only-facade-ITU6L7BL.js.map → collection-facade-GQAOGJP2.js.map} +0 -0
- /package/dist/{registry-3QP3YKQS.js.map → delegation-UL5HKLER.js.map} +0 -0
- /package/dist/{registry-DKEXOJVO.js.map → describe/index.js.map} +0 -0
- /package/dist/{registry-OJIOSBV6.js.map → enclave-UL5LW66V.js.map} +0 -0
- /package/dist/{registry-USRVT6YF.js.map → executor-2FWQRLMG.js.map} +0 -0
- /package/dist/{revoke-JCC7N56X.js.map → executor-QZ7BXICW.js.map} +0 -0
- /package/dist/{signer-72QAUSVW.js.map → executor-Z5ZLJVTV.js.map} +0 -0
- /package/dist/{stale-6ZDBTQT7.js.map → export-accessible-KRV5W4GH.js.map} +0 -0
- /package/dist/{ulid-COREQ2RQ.js.map → extract-partition-GLKBOXFQ.js.map} +0 -0
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NOYDB_FORMAT_VERSION,
|
|
3
|
+
SealedHandle
|
|
4
|
+
} from "./chunk-5TDJ56JE.js";
|
|
5
|
+
import {
|
|
6
|
+
DebugReservedFieldError,
|
|
7
|
+
DecryptionError,
|
|
8
|
+
InvalidKeyError,
|
|
9
|
+
RecordCekNotFoundError,
|
|
10
|
+
SchemaValidationError,
|
|
11
|
+
TamperedError,
|
|
12
|
+
ValidationError
|
|
13
|
+
} from "./chunk-ZYUC53LT.js";
|
|
14
|
+
|
|
15
|
+
// src/kernel/enclave/crypto.ts
|
|
16
|
+
var PBKDF2_ITERATIONS = 6e5;
|
|
17
|
+
var SALT_BYTES = 32;
|
|
18
|
+
var IV_BYTES = 12;
|
|
19
|
+
var KEY_BITS = 256;
|
|
20
|
+
var subtle = globalThis.crypto.subtle;
|
|
21
|
+
async function deriveKey(passphrase, salt) {
|
|
22
|
+
const keyMaterial = await subtle.importKey(
|
|
23
|
+
"raw",
|
|
24
|
+
new TextEncoder().encode(passphrase),
|
|
25
|
+
"PBKDF2",
|
|
26
|
+
false,
|
|
27
|
+
["deriveKey"]
|
|
28
|
+
);
|
|
29
|
+
return subtle.deriveKey(
|
|
30
|
+
{
|
|
31
|
+
name: "PBKDF2",
|
|
32
|
+
salt,
|
|
33
|
+
iterations: PBKDF2_ITERATIONS,
|
|
34
|
+
hash: "SHA-256"
|
|
35
|
+
},
|
|
36
|
+
keyMaterial,
|
|
37
|
+
{ name: "AES-KW", length: KEY_BITS },
|
|
38
|
+
false,
|
|
39
|
+
["wrapKey", "unwrapKey"]
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
async function derivePassphraseKey(passphrase, salt, params) {
|
|
43
|
+
const keyMaterial = await subtle.importKey(
|
|
44
|
+
"raw",
|
|
45
|
+
new TextEncoder().encode(passphrase),
|
|
46
|
+
"PBKDF2",
|
|
47
|
+
false,
|
|
48
|
+
["deriveKey"]
|
|
49
|
+
);
|
|
50
|
+
return params.keyUsage === "aes-gcm" ? subtle.deriveKey(
|
|
51
|
+
{
|
|
52
|
+
name: "PBKDF2",
|
|
53
|
+
salt,
|
|
54
|
+
iterations: params.iterations,
|
|
55
|
+
hash: "SHA-256"
|
|
56
|
+
},
|
|
57
|
+
keyMaterial,
|
|
58
|
+
{ name: "AES-GCM", length: KEY_BITS },
|
|
59
|
+
false,
|
|
60
|
+
["encrypt", "decrypt"]
|
|
61
|
+
) : subtle.deriveKey(
|
|
62
|
+
{
|
|
63
|
+
name: "PBKDF2",
|
|
64
|
+
salt,
|
|
65
|
+
iterations: params.iterations,
|
|
66
|
+
hash: "SHA-256"
|
|
67
|
+
},
|
|
68
|
+
keyMaterial,
|
|
69
|
+
{ name: "AES-KW", length: KEY_BITS },
|
|
70
|
+
false,
|
|
71
|
+
["wrapKey", "unwrapKey"]
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
async function generateDEK() {
|
|
75
|
+
return subtle.generateKey(
|
|
76
|
+
{ name: "AES-GCM", length: KEY_BITS },
|
|
77
|
+
true,
|
|
78
|
+
// extractable — needed for AES-KW wrapping
|
|
79
|
+
["encrypt", "decrypt"]
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
async function wrapKey(dek, kek) {
|
|
83
|
+
const wrapped = await subtle.wrapKey("raw", dek, kek, "AES-KW");
|
|
84
|
+
return bufferToBase64(wrapped);
|
|
85
|
+
}
|
|
86
|
+
async function unwrapKey(wrappedBase64, kek) {
|
|
87
|
+
try {
|
|
88
|
+
return await subtle.unwrapKey(
|
|
89
|
+
"raw",
|
|
90
|
+
base64ToBuffer(wrappedBase64),
|
|
91
|
+
kek,
|
|
92
|
+
"AES-KW",
|
|
93
|
+
{ name: "AES-GCM", length: KEY_BITS },
|
|
94
|
+
true,
|
|
95
|
+
["encrypt", "decrypt"]
|
|
96
|
+
);
|
|
97
|
+
} catch {
|
|
98
|
+
throw new InvalidKeyError();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function asKwKey(dek) {
|
|
102
|
+
const rawDek = await subtle.exportKey("raw", dek);
|
|
103
|
+
const hkdfKey = await subtle.importKey("raw", rawDek, "HKDF", false, ["deriveBits"]);
|
|
104
|
+
const salt = new TextEncoder().encode("noydb-cek-wrap");
|
|
105
|
+
const info = new TextEncoder().encode("v1");
|
|
106
|
+
const bits = await subtle.deriveBits(
|
|
107
|
+
{ name: "HKDF", hash: "SHA-256", salt, info },
|
|
108
|
+
hkdfKey,
|
|
109
|
+
KEY_BITS
|
|
110
|
+
);
|
|
111
|
+
return subtle.importKey("raw", bits, "AES-KW", false, ["wrapKey", "unwrapKey"]);
|
|
112
|
+
}
|
|
113
|
+
async function wrapCek(cek, dek) {
|
|
114
|
+
const kw = await asKwKey(dek);
|
|
115
|
+
const wrapped = await subtle.wrapKey("raw", cek, kw, "AES-KW");
|
|
116
|
+
return bufferToBase64(wrapped);
|
|
117
|
+
}
|
|
118
|
+
async function unwrapCek(wrappedBase64, dek) {
|
|
119
|
+
const kw = await asKwKey(dek);
|
|
120
|
+
try {
|
|
121
|
+
return await subtle.unwrapKey(
|
|
122
|
+
"raw",
|
|
123
|
+
base64ToBuffer(wrappedBase64),
|
|
124
|
+
kw,
|
|
125
|
+
"AES-KW",
|
|
126
|
+
{ name: "AES-GCM", length: KEY_BITS },
|
|
127
|
+
true,
|
|
128
|
+
["encrypt", "decrypt"]
|
|
129
|
+
);
|
|
130
|
+
} catch {
|
|
131
|
+
throw new InvalidKeyError();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function importCek(rawKey) {
|
|
135
|
+
return subtle.importKey("raw", rawKey, { name: "AES-GCM", length: KEY_BITS }, false, ["decrypt"]);
|
|
136
|
+
}
|
|
137
|
+
async function encrypt(plaintext, dek) {
|
|
138
|
+
const iv = generateIV();
|
|
139
|
+
const encoded = new TextEncoder().encode(plaintext);
|
|
140
|
+
const ciphertext = await subtle.encrypt(
|
|
141
|
+
{ name: "AES-GCM", iv },
|
|
142
|
+
dek,
|
|
143
|
+
encoded
|
|
144
|
+
);
|
|
145
|
+
return {
|
|
146
|
+
iv: bufferToBase64(iv),
|
|
147
|
+
data: bufferToBase64(ciphertext)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async function decrypt(ivBase64, dataBase64, dek) {
|
|
151
|
+
const iv = base64ToBuffer(ivBase64);
|
|
152
|
+
const ciphertext = base64ToBuffer(dataBase64);
|
|
153
|
+
try {
|
|
154
|
+
const plaintext = await subtle.decrypt(
|
|
155
|
+
{ name: "AES-GCM", iv },
|
|
156
|
+
dek,
|
|
157
|
+
ciphertext
|
|
158
|
+
);
|
|
159
|
+
return new TextDecoder().decode(plaintext);
|
|
160
|
+
} catch (err) {
|
|
161
|
+
if (err instanceof Error && err.name === "OperationError") {
|
|
162
|
+
throw new TamperedError();
|
|
163
|
+
}
|
|
164
|
+
throw new DecryptionError(
|
|
165
|
+
err instanceof Error ? err.message : "Decryption failed"
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function encryptBytes(data, dek) {
|
|
170
|
+
const iv = generateIV();
|
|
171
|
+
const ciphertext = await subtle.encrypt(
|
|
172
|
+
{ name: "AES-GCM", iv },
|
|
173
|
+
dek,
|
|
174
|
+
data
|
|
175
|
+
);
|
|
176
|
+
return {
|
|
177
|
+
iv: bufferToBase64(iv),
|
|
178
|
+
data: bufferToBase64(ciphertext)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
async function decryptBytes(ivBase64, dataBase64, dek) {
|
|
182
|
+
const iv = base64ToBuffer(ivBase64);
|
|
183
|
+
const ciphertext = base64ToBuffer(dataBase64);
|
|
184
|
+
try {
|
|
185
|
+
const plaintext = await subtle.decrypt(
|
|
186
|
+
{ name: "AES-GCM", iv },
|
|
187
|
+
dek,
|
|
188
|
+
ciphertext
|
|
189
|
+
);
|
|
190
|
+
return new Uint8Array(plaintext);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
if (err instanceof Error && err.name === "OperationError") {
|
|
193
|
+
throw new TamperedError();
|
|
194
|
+
}
|
|
195
|
+
throw new DecryptionError(
|
|
196
|
+
err instanceof Error ? err.message : "Decryption failed"
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async function sha256Hex(data) {
|
|
201
|
+
const hash = await subtle.digest("SHA-256", data);
|
|
202
|
+
return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
203
|
+
}
|
|
204
|
+
async function hmacSha256Hex(key, data) {
|
|
205
|
+
const rawKey = await subtle.exportKey("raw", key);
|
|
206
|
+
const hmacKey = await subtle.importKey(
|
|
207
|
+
"raw",
|
|
208
|
+
rawKey,
|
|
209
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
210
|
+
false,
|
|
211
|
+
["sign"]
|
|
212
|
+
);
|
|
213
|
+
const sig = await subtle.sign("HMAC", hmacKey, data);
|
|
214
|
+
return Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
215
|
+
}
|
|
216
|
+
async function encryptBytesWithAAD(data, dek, aad) {
|
|
217
|
+
const iv = generateIV();
|
|
218
|
+
const ciphertext = await subtle.encrypt(
|
|
219
|
+
{
|
|
220
|
+
name: "AES-GCM",
|
|
221
|
+
iv,
|
|
222
|
+
additionalData: aad
|
|
223
|
+
},
|
|
224
|
+
dek,
|
|
225
|
+
data
|
|
226
|
+
);
|
|
227
|
+
return {
|
|
228
|
+
iv: bufferToBase64(iv),
|
|
229
|
+
data: bufferToBase64(ciphertext)
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
async function decryptBytesWithAAD(ivBase64, dataBase64, dek, aad) {
|
|
233
|
+
const iv = base64ToBuffer(ivBase64);
|
|
234
|
+
const ciphertext = base64ToBuffer(dataBase64);
|
|
235
|
+
try {
|
|
236
|
+
const plaintext = await subtle.decrypt(
|
|
237
|
+
{
|
|
238
|
+
name: "AES-GCM",
|
|
239
|
+
iv,
|
|
240
|
+
additionalData: aad
|
|
241
|
+
},
|
|
242
|
+
dek,
|
|
243
|
+
ciphertext
|
|
244
|
+
);
|
|
245
|
+
return new Uint8Array(plaintext);
|
|
246
|
+
} catch (err) {
|
|
247
|
+
if (err instanceof Error && err.name === "OperationError") {
|
|
248
|
+
throw new TamperedError();
|
|
249
|
+
}
|
|
250
|
+
throw new DecryptionError(
|
|
251
|
+
err instanceof Error ? err.message : "Decryption failed"
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async function derivePresenceKey(dek, collectionName) {
|
|
256
|
+
const rawDek = await subtle.exportKey("raw", dek);
|
|
257
|
+
const hkdfKey = await subtle.importKey(
|
|
258
|
+
"raw",
|
|
259
|
+
rawDek,
|
|
260
|
+
"HKDF",
|
|
261
|
+
false,
|
|
262
|
+
["deriveBits"]
|
|
263
|
+
);
|
|
264
|
+
const salt = new TextEncoder().encode("noydb-presence");
|
|
265
|
+
const info = new TextEncoder().encode(collectionName);
|
|
266
|
+
const bits = await subtle.deriveBits(
|
|
267
|
+
{ name: "HKDF", hash: "SHA-256", salt, info },
|
|
268
|
+
hkdfKey,
|
|
269
|
+
KEY_BITS
|
|
270
|
+
);
|
|
271
|
+
return subtle.importKey(
|
|
272
|
+
"raw",
|
|
273
|
+
bits,
|
|
274
|
+
{ name: "AES-GCM", length: KEY_BITS },
|
|
275
|
+
false,
|
|
276
|
+
["encrypt", "decrypt"]
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
async function deriveSealedFieldKey(dek, collectionName, field) {
|
|
280
|
+
const rawDek = await subtle.exportKey("raw", dek);
|
|
281
|
+
const hkdfKey = await subtle.importKey("raw", rawDek, "HKDF", false, ["deriveBits"]);
|
|
282
|
+
const salt = new TextEncoder().encode("noydb-sealed");
|
|
283
|
+
const info = new TextEncoder().encode(JSON.stringify(["noydb-sealed", collectionName, field]));
|
|
284
|
+
const bits = await subtle.deriveBits(
|
|
285
|
+
{ name: "HKDF", hash: "SHA-256", salt, info },
|
|
286
|
+
hkdfKey,
|
|
287
|
+
KEY_BITS
|
|
288
|
+
);
|
|
289
|
+
return subtle.importKey(
|
|
290
|
+
"raw",
|
|
291
|
+
bits,
|
|
292
|
+
{ name: "AES-GCM", length: KEY_BITS },
|
|
293
|
+
false,
|
|
294
|
+
["encrypt", "decrypt"]
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
async function deriveSealedFieldKeyFromCek(cek, collectionName, field) {
|
|
298
|
+
const rawCek = await subtle.exportKey("raw", cek);
|
|
299
|
+
const hkdfKey = await subtle.importKey("raw", rawCek, "HKDF", false, ["deriveBits"]);
|
|
300
|
+
const salt = new TextEncoder().encode("noydb-sealed-cek");
|
|
301
|
+
const info = new TextEncoder().encode(JSON.stringify(["noydb-sealed-cek", collectionName, field]));
|
|
302
|
+
const bits = await subtle.deriveBits(
|
|
303
|
+
{ name: "HKDF", hash: "SHA-256", salt, info },
|
|
304
|
+
hkdfKey,
|
|
305
|
+
KEY_BITS
|
|
306
|
+
);
|
|
307
|
+
return subtle.importKey(
|
|
308
|
+
"raw",
|
|
309
|
+
bits,
|
|
310
|
+
{ name: "AES-GCM", length: KEY_BITS },
|
|
311
|
+
false,
|
|
312
|
+
["encrypt", "decrypt"]
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
async function deriveDeterministicIV(dek, context, plaintext) {
|
|
316
|
+
const rawDek = await subtle.exportKey("raw", dek);
|
|
317
|
+
const hkdfKey = await subtle.importKey("raw", rawDek, "HKDF", false, ["deriveBits"]);
|
|
318
|
+
const salt = new TextEncoder().encode("noydb-deterministic-v1");
|
|
319
|
+
const info = new TextEncoder().encode(`${context}\0${plaintext}`);
|
|
320
|
+
const bits = await subtle.deriveBits(
|
|
321
|
+
{ name: "HKDF", hash: "SHA-256", salt, info },
|
|
322
|
+
hkdfKey,
|
|
323
|
+
IV_BYTES * 8
|
|
324
|
+
);
|
|
325
|
+
return new Uint8Array(bits);
|
|
326
|
+
}
|
|
327
|
+
async function encryptDeterministic(plaintext, dek, context) {
|
|
328
|
+
const iv = await deriveDeterministicIV(dek, context, plaintext);
|
|
329
|
+
const encoded = new TextEncoder().encode(plaintext);
|
|
330
|
+
const ciphertext = await subtle.encrypt(
|
|
331
|
+
{ name: "AES-GCM", iv },
|
|
332
|
+
dek,
|
|
333
|
+
encoded
|
|
334
|
+
);
|
|
335
|
+
return {
|
|
336
|
+
iv: bufferToBase64(iv),
|
|
337
|
+
data: bufferToBase64(ciphertext)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
async function decryptDeterministic(ivBase64, dataBase64, dek) {
|
|
341
|
+
return decrypt(ivBase64, dataBase64, dek);
|
|
342
|
+
}
|
|
343
|
+
function generateIV() {
|
|
344
|
+
return globalThis.crypto.getRandomValues(new Uint8Array(IV_BYTES));
|
|
345
|
+
}
|
|
346
|
+
function generateSalt() {
|
|
347
|
+
return globalThis.crypto.getRandomValues(new Uint8Array(SALT_BYTES));
|
|
348
|
+
}
|
|
349
|
+
function bufferToBase64(buffer) {
|
|
350
|
+
const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
|
|
351
|
+
let binary = "";
|
|
352
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
353
|
+
binary += String.fromCharCode(bytes[i]);
|
|
354
|
+
}
|
|
355
|
+
return btoa(binary);
|
|
356
|
+
}
|
|
357
|
+
function base64ToBuffer(base64) {
|
|
358
|
+
const binary = atob(base64);
|
|
359
|
+
const bytes = new Uint8Array(binary.length);
|
|
360
|
+
for (let i = 0; i < binary.length; i++) {
|
|
361
|
+
bytes[i] = binary.charCodeAt(i);
|
|
362
|
+
}
|
|
363
|
+
return bytes;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// src/kernel/enclave/record-keys/lifecycle.ts
|
|
367
|
+
async function resolveStableCek(deps, id) {
|
|
368
|
+
const cached = deps.cache?.get(id);
|
|
369
|
+
if (cached) return cached;
|
|
370
|
+
const live = await deps.getLive(id);
|
|
371
|
+
if (live?._cek !== void 0) {
|
|
372
|
+
const cek = await unwrapCek(live._cek, await deps.getDEK());
|
|
373
|
+
deps.cache?.set(id, cek, 1);
|
|
374
|
+
return cek;
|
|
375
|
+
}
|
|
376
|
+
const fresh = await generateDEK();
|
|
377
|
+
deps.cache?.set(id, fresh, 1);
|
|
378
|
+
return fresh;
|
|
379
|
+
}
|
|
380
|
+
async function rewrapBodyToDek(envelope, fromDek, toDek) {
|
|
381
|
+
if (envelope._cek !== void 0) {
|
|
382
|
+
const cek = await unwrapCek(envelope._cek, fromDek);
|
|
383
|
+
const plaintext2 = await decrypt(envelope._iv, envelope._data, cek);
|
|
384
|
+
const { iv: iv2, data: data2 } = await encrypt(plaintext2, cek);
|
|
385
|
+
return { _iv: iv2, _data: data2, _cek: await wrapCek(cek, toDek), cek };
|
|
386
|
+
}
|
|
387
|
+
const plaintext = await decrypt(envelope._iv, envelope._data, fromDek);
|
|
388
|
+
const { iv, data } = await encrypt(plaintext, toDek);
|
|
389
|
+
return { _iv: iv, _data: data, cek: null };
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/kernel/enclave/record-keys/tombstone.ts
|
|
393
|
+
function isTombstone(envelope, encrypted) {
|
|
394
|
+
if (!encrypted) return false;
|
|
395
|
+
return !envelope._data && envelope._cek === void 0;
|
|
396
|
+
}
|
|
397
|
+
function buildTombstone(version, actor) {
|
|
398
|
+
return {
|
|
399
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
400
|
+
_v: version,
|
|
401
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
402
|
+
_iv: "",
|
|
403
|
+
_data: "",
|
|
404
|
+
...actor ? { _by: actor } : {}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/kernel/enclave/record-keys/sealed-slot.ts
|
|
409
|
+
function parseSealedSlot(blob) {
|
|
410
|
+
const sep = blob.indexOf(":");
|
|
411
|
+
return { iv: blob.slice(0, sep), data: blob.slice(sep + 1) };
|
|
412
|
+
}
|
|
413
|
+
async function dualReadSealedSlot(blob, field, collection, cek, dek) {
|
|
414
|
+
const { iv, data } = parseSealedSlot(blob);
|
|
415
|
+
if (cek !== void 0) {
|
|
416
|
+
try {
|
|
417
|
+
const fieldKey2 = await deriveSealedFieldKeyFromCek(cek, collection, field);
|
|
418
|
+
return await decrypt(iv, data, fieldKey2);
|
|
419
|
+
} catch {
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
const fieldKey = await deriveSealedFieldKey(dek, collection, field);
|
|
423
|
+
return decrypt(iv, data, fieldKey);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// src/kernel/schema.ts
|
|
427
|
+
async function validateSchemaInput(schema, value, context) {
|
|
428
|
+
const result = await schema["~standard"].validate(value);
|
|
429
|
+
if (result.issues !== void 0 && result.issues.length > 0) {
|
|
430
|
+
throw new SchemaValidationError(
|
|
431
|
+
`Schema validation failed on ${context}: ${summarizeIssues(result.issues)}`,
|
|
432
|
+
result.issues,
|
|
433
|
+
"input"
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
return result.value;
|
|
437
|
+
}
|
|
438
|
+
async function validateSchemaOutput(schema, value, context) {
|
|
439
|
+
const result = await schema["~standard"].validate(value);
|
|
440
|
+
if (result.issues !== void 0 && result.issues.length > 0) {
|
|
441
|
+
throw new SchemaValidationError(
|
|
442
|
+
`Stored data for ${context} does not match the current schema \u2014 schema drift? ${summarizeIssues(result.issues)}`,
|
|
443
|
+
result.issues,
|
|
444
|
+
"output"
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
return result.value;
|
|
448
|
+
}
|
|
449
|
+
function summarizeIssues(issues) {
|
|
450
|
+
const shown = issues.slice(0, 3).map((issue) => {
|
|
451
|
+
const pathStr = formatPath(issue.path);
|
|
452
|
+
return `${pathStr}: ${issue.message}`;
|
|
453
|
+
});
|
|
454
|
+
const suffix = issues.length > 3 ? ` (+${issues.length - 3} more)` : "";
|
|
455
|
+
return shown.join("; ") + suffix;
|
|
456
|
+
}
|
|
457
|
+
function formatPath(path) {
|
|
458
|
+
if (!path || path.length === 0) return "root";
|
|
459
|
+
return path.map(
|
|
460
|
+
(segment) => typeof segment === "object" && segment !== null ? String(segment.key) : String(segment)
|
|
461
|
+
).join(".");
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// src/kernel/enclave/record-keys/record-codec.ts
|
|
465
|
+
var RecordCodec = class _RecordCodec {
|
|
466
|
+
constructor(ctx) {
|
|
467
|
+
this.ctx = ctx;
|
|
468
|
+
}
|
|
469
|
+
ctx;
|
|
470
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
471
|
+
// Low-level literal builders
|
|
472
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
473
|
+
/**
|
|
474
|
+
* Assemble a canonical ciphertext body envelope literal from already-computed
|
|
475
|
+
* parts. Pure, no crypto. Each call stamps its own `_ts` — provenance carries
|
|
476
|
+
* a SEPARATE timestamp (computed by the caller), so the two never share one.
|
|
477
|
+
*/
|
|
478
|
+
static buildEnvelope(p) {
|
|
479
|
+
return {
|
|
480
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
481
|
+
_v: p.version,
|
|
482
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
483
|
+
_iv: p.iv,
|
|
484
|
+
_data: p.data,
|
|
485
|
+
...p.by !== void 0 ? { _by: p.by } : {},
|
|
486
|
+
...p.cek !== void 0 ? { _cek: p.cek } : {},
|
|
487
|
+
...p.provenance !== void 0 ? { _source: p.provenance.source, _sourceTs: p.provenance.sourceTs } : {},
|
|
488
|
+
...p.extra ?? {}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
/** Plaintext (`_iv:''`) body envelope — the `!storeCiphertext` shape. */
|
|
492
|
+
static buildPlaintextEnvelope(p) {
|
|
493
|
+
return {
|
|
494
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
495
|
+
_v: p.version,
|
|
496
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
497
|
+
_iv: "",
|
|
498
|
+
_data: p.data,
|
|
499
|
+
...p.by !== void 0 ? { _by: p.by } : {},
|
|
500
|
+
...p.provenance !== void 0 ? { _source: p.provenance.source, _sourceTs: p.provenance.sourceTs } : {}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
504
|
+
// Write path
|
|
505
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
506
|
+
/**
|
|
507
|
+
* Build a debug-plaintext envelope: the record's own fields inlined as
|
|
508
|
+
* top-level keys beside the reserved `_`-metadata, with `_debug: 1` and an
|
|
509
|
+
* empty `_data`. Lets native store tooling read the record without
|
|
510
|
+
* unwrapping. Only reached for user collections under `debugPlaintext`
|
|
511
|
+
* (see {@link encryptRecord}). Rejects `_`-prefixed record fields, which
|
|
512
|
+
* would collide with the reserved metadata namespace.
|
|
513
|
+
*/
|
|
514
|
+
buildDebugEnvelope(record, version, source, sourceTs) {
|
|
515
|
+
const rec = record;
|
|
516
|
+
for (const key of Object.keys(rec)) {
|
|
517
|
+
if (key.startsWith("_")) throw new DebugReservedFieldError(this.ctx.name, key);
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
521
|
+
_v: version,
|
|
522
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
523
|
+
_iv: "",
|
|
524
|
+
_data: "",
|
|
525
|
+
_by: this.ctx.actor,
|
|
526
|
+
_debug: NOYDB_FORMAT_VERSION,
|
|
527
|
+
...this.ctx.provenance && source !== void 0 ? { _source: source, _sourceTs: sourceTs ?? (/* @__PURE__ */ new Date()).toISOString() } : {},
|
|
528
|
+
...rec
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Encrypt a JSON body into an envelope.
|
|
533
|
+
*
|
|
534
|
+
* When `cek` is supplied (per-record CEK collections), the body is
|
|
535
|
+
* encrypted under the CEK and the CEK is AES-KW-wrapped under the
|
|
536
|
+
* collection DEK and stamped on `_cek`. When `cek` is omitted, the legacy
|
|
537
|
+
* path encrypts the body directly under the collection DEK — byte-identical
|
|
538
|
+
* to pre-CEK behaviour, so non-adopting collections pay nothing.
|
|
539
|
+
*/
|
|
540
|
+
async encryptJsonString(json, version, cek, source, sourceTs) {
|
|
541
|
+
const by = this.ctx.actor;
|
|
542
|
+
const provenance = this.ctx.provenance && source !== void 0 ? { source, sourceTs: sourceTs ?? (/* @__PURE__ */ new Date()).toISOString() } : void 0;
|
|
543
|
+
if (!this.ctx.storeCiphertext) {
|
|
544
|
+
return _RecordCodec.buildPlaintextEnvelope({ version, data: json, by, provenance });
|
|
545
|
+
}
|
|
546
|
+
const dek = await this.ctx.getDEK();
|
|
547
|
+
if (cek !== void 0) {
|
|
548
|
+
const { iv: iv2, data: data2 } = await encrypt(json, cek);
|
|
549
|
+
const wrapped = await wrapCek(cek, dek);
|
|
550
|
+
return _RecordCodec.buildEnvelope({ version, iv: iv2, data: data2, by, cek: wrapped, provenance });
|
|
551
|
+
}
|
|
552
|
+
const { iv, data } = await encrypt(json, dek);
|
|
553
|
+
return _RecordCodec.buildEnvelope({ version, iv, data, by, provenance });
|
|
554
|
+
}
|
|
555
|
+
async encryptRecord(record, version, cek, source, sourceTs) {
|
|
556
|
+
if (!this.ctx.storeCiphertext && this.ctx.debugPlaintext && !this.ctx.name.startsWith("_")) {
|
|
557
|
+
return this.buildDebugEnvelope(record, version, source, sourceTs);
|
|
558
|
+
}
|
|
559
|
+
let openRecord = record;
|
|
560
|
+
let sealed;
|
|
561
|
+
if (this.ctx.storeCiphertext && this.ctx.sensitiveFields.size > 0) {
|
|
562
|
+
const src = record;
|
|
563
|
+
const dek2 = await this.ctx.getDEK();
|
|
564
|
+
const open = { ...src };
|
|
565
|
+
const slots = {};
|
|
566
|
+
for (const field of this.ctx.sensitiveFields) {
|
|
567
|
+
if (!(field in src)) continue;
|
|
568
|
+
const value = src[field];
|
|
569
|
+
if (value === void 0) continue;
|
|
570
|
+
const fieldKey = cek !== void 0 ? await deriveSealedFieldKeyFromCek(cek, this.ctx.name, field) : await deriveSealedFieldKey(dek2, this.ctx.name, field);
|
|
571
|
+
const { iv, data } = await encrypt(JSON.stringify(value), fieldKey);
|
|
572
|
+
slots[field] = `${iv}:${data}`;
|
|
573
|
+
delete open[field];
|
|
574
|
+
}
|
|
575
|
+
if (Object.keys(slots).length > 0) {
|
|
576
|
+
sealed = slots;
|
|
577
|
+
openRecord = open;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
const base = await this.encryptJsonString(JSON.stringify(openRecord), version, cek, source, sourceTs);
|
|
581
|
+
const withSealed = sealed ? { ...base, _sealed: sealed } : base;
|
|
582
|
+
if (!this.ctx.deterministicFields || !this.ctx.storeCiphertext) return withSealed;
|
|
583
|
+
const dek = await this.ctx.getDEK();
|
|
584
|
+
const rec = record;
|
|
585
|
+
const det = {};
|
|
586
|
+
for (const field of this.ctx.deterministicFields) {
|
|
587
|
+
if (this.ctx.sensitiveFields.has(field)) continue;
|
|
588
|
+
const value = rec[field];
|
|
589
|
+
if (value === void 0 || value === null) continue;
|
|
590
|
+
const plaintext = typeof value === "string" ? value : JSON.stringify(value);
|
|
591
|
+
const { iv, data } = await encryptDeterministic(plaintext, dek, `${this.ctx.name}/${field}`);
|
|
592
|
+
det[field] = `${iv}:${data}`;
|
|
593
|
+
}
|
|
594
|
+
if (Object.keys(det).length === 0) return withSealed;
|
|
595
|
+
return { ...withSealed, _det: det };
|
|
596
|
+
}
|
|
597
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
598
|
+
// Read path
|
|
599
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
600
|
+
/**
|
|
601
|
+
* Resolve the per-record CEK for a stored envelope, or `undefined` for a
|
|
602
|
+
* legacy (`_cek`-absent) envelope. Unwraps `_cek` under the collection DEK and
|
|
603
|
+
* memoises it in the CEK cache under `id` (when supplied) so repeated reads of
|
|
604
|
+
* the same record skip the unwrap. Shared by the body-decrypt path
|
|
605
|
+
* ({@link decryptJsonString}) and the sealed-field path ({@link decryptRecord}
|
|
606
|
+
* / {@link toCacheRecord}) so both agree on the record's key.
|
|
607
|
+
*/
|
|
608
|
+
async resolveEnvelopeCek(envelope, id) {
|
|
609
|
+
if (envelope._cek === void 0) return void 0;
|
|
610
|
+
const cached = id !== void 0 ? this.ctx.cekCache?.get(id) : void 0;
|
|
611
|
+
if (cached !== void 0) return cached;
|
|
612
|
+
const dek = await this.ctx.getDEK();
|
|
613
|
+
const cek = await unwrapCek(envelope._cek, dek);
|
|
614
|
+
if (id !== void 0) this.ctx.cekCache?.set(id, cek, 1);
|
|
615
|
+
return cek;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Low-level: decrypt an envelope and return the raw JSON string.
|
|
619
|
+
*
|
|
620
|
+
* `_cek` presence is the format discriminant (NOT `this.perRecordCek`),
|
|
621
|
+
* so a mixed vault — and a recipient that never opted into
|
|
622
|
+
* `perRecordKeys` — decrypts both legacy and CEK records:
|
|
623
|
+
* - `_cek` present → unwrap the CEK under the collection DEK, decrypt the
|
|
624
|
+
* body under the CEK (cache the unwrapped CEK so repeated reads skip it).
|
|
625
|
+
* - `_cek` absent → legacy path, body decrypts directly under the
|
|
626
|
+
* collection DEK.
|
|
627
|
+
*
|
|
628
|
+
* The optional `id` lets reads populate the CEK cache; it is omitted by
|
|
629
|
+
* callers (history, conflict merge) that have only the envelope.
|
|
630
|
+
*/
|
|
631
|
+
async decryptJsonString(envelope, id) {
|
|
632
|
+
if (isTombstone(envelope, this.ctx.storeCiphertext)) return null;
|
|
633
|
+
if (!this.ctx.storeCiphertext) {
|
|
634
|
+
if (envelope._debug !== void 0) {
|
|
635
|
+
const rec = {};
|
|
636
|
+
for (const [key, value] of Object.entries(envelope)) {
|
|
637
|
+
if (!key.startsWith("_")) rec[key] = value;
|
|
638
|
+
}
|
|
639
|
+
return JSON.stringify(rec);
|
|
640
|
+
}
|
|
641
|
+
return envelope._data;
|
|
642
|
+
}
|
|
643
|
+
const cek = await this.resolveEnvelopeCek(envelope, id);
|
|
644
|
+
if (cek !== void 0) return decrypt(envelope._iv, envelope._data, cek);
|
|
645
|
+
const dek = await this.ctx.getDEK();
|
|
646
|
+
return decrypt(envelope._iv, envelope._data, dek);
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Unseal a single `_sealed[field]` slot to its plaintext value: derive the
|
|
650
|
+
* per-field key off the collection DEK, AES-GCM-decrypt the `iv:data` blob,
|
|
651
|
+
* and JSON-parse the result. Shared by both the inline-decrypt path and a
|
|
652
|
+
* {@link Sealed} handle's `reveal()` — so the on-demand reveal and the eager
|
|
653
|
+
* materialisation always agree byte-for-byte.
|
|
654
|
+
*/
|
|
655
|
+
async unsealField(field, blob, cek) {
|
|
656
|
+
const dek = await this.ctx.getDEK();
|
|
657
|
+
return JSON.parse(await dualReadSealedSlot(blob, field, this.ctx.name, cek, dek));
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Classify a live envelope's `_sealed` slots for crypto-shred completeness.
|
|
661
|
+
* `forget()` drops `_cek`/`_sealed` but
|
|
662
|
+
* RETAINS the collection DEK, so only a slot keyed off the per-record CEK is
|
|
663
|
+
* genuinely shredded by the tombstone; a legacy slot keyed off the
|
|
664
|
+
* collection DEK survives in any synced/backup copy.
|
|
665
|
+
*
|
|
666
|
+
* Mirrors {@link unsealField}'s dual-read split: try the CEK-derived key per
|
|
667
|
+
* slot — success → `shreddable`, AES-GCM auth failure → `dekResidue`. With no
|
|
668
|
+
* `_cek` (pure legacy collection-DEK sealing) ALL slots are residue.
|
|
669
|
+
*/
|
|
670
|
+
async classifySealedShred(live) {
|
|
671
|
+
const shreddable = [];
|
|
672
|
+
const dekResidue = [];
|
|
673
|
+
const sealed = live._sealed;
|
|
674
|
+
if (sealed === void 0) return { shreddable, dekResidue };
|
|
675
|
+
const cek = await this.resolveEnvelopeCek(live);
|
|
676
|
+
for (const [field, blob] of Object.entries(sealed)) {
|
|
677
|
+
if (cek === void 0) {
|
|
678
|
+
dekResidue.push(field);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
const { iv, data } = parseSealedSlot(blob);
|
|
682
|
+
try {
|
|
683
|
+
await decrypt(iv, data, await deriveSealedFieldKeyFromCek(cek, this.ctx.name, field));
|
|
684
|
+
shreddable.push(field);
|
|
685
|
+
} catch {
|
|
686
|
+
dekResidue.push(field);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { shreddable, dekResidue };
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Build a non-leaking {@link Sealed} handle over a sealed field's ciphertext.
|
|
693
|
+
* The handle captures only the ciphertext `blob` and a closure to
|
|
694
|
+
* {@link unsealField}; the plaintext is never stored on it — so the handle
|
|
695
|
+
* may sit in the working-set cache (or be logged/serialised) without
|
|
696
|
+
* exposing the value, which decrypts only on `reveal()`.
|
|
697
|
+
*/
|
|
698
|
+
makeSealedHandle(field, blob, cek) {
|
|
699
|
+
return new SealedHandle(() => this.unsealField(field, blob, cek));
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Replace a record's declared sensitive fields with {@link Sealed} handles
|
|
703
|
+
* built from the just-written envelope's `_sealed` slots, leaving every
|
|
704
|
+
* other field as its plaintext value. Used to populate the cache on the
|
|
705
|
+
* write path without ever materialising sealed plaintext into it. Returns
|
|
706
|
+
* `record` untouched when the collection seals nothing.
|
|
707
|
+
*/
|
|
708
|
+
async toCacheRecord(record, envelope, id) {
|
|
709
|
+
const sealed = envelope._sealed;
|
|
710
|
+
if (sealed === void 0 || !this.ctx.storeCiphertext || this.ctx.sensitiveFields.size === 0) {
|
|
711
|
+
return record;
|
|
712
|
+
}
|
|
713
|
+
const cek = await this.resolveEnvelopeCek(envelope, id);
|
|
714
|
+
const clone = { ...record };
|
|
715
|
+
for (const [field, blob] of Object.entries(sealed)) {
|
|
716
|
+
clone[field] = this.makeSealedHandle(field, blob, cek);
|
|
717
|
+
}
|
|
718
|
+
return clone;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Decrypt an envelope into a record of type `T`.
|
|
722
|
+
*
|
|
723
|
+
* When a schema is attached, the decrypted value is validated before
|
|
724
|
+
* being returned. A divergence between the stored bytes and the
|
|
725
|
+
* current schema throws `SchemaValidationError` with
|
|
726
|
+
* `direction: 'output'` — silently returning drifted data would
|
|
727
|
+
* propagate garbage into the UI and break the whole point of having
|
|
728
|
+
* a schema.
|
|
729
|
+
*
|
|
730
|
+
* `skipValidation` exists for history reads: when calling
|
|
731
|
+
* `getVersion()` the caller is explicitly asking for an old snapshot
|
|
732
|
+
* that may predate a schema change, so validating it would be a
|
|
733
|
+
* false positive. Every non-history read leaves this flag `false`.
|
|
734
|
+
*/
|
|
735
|
+
async decryptRecord(envelope, opts = {}) {
|
|
736
|
+
const json = await this.decryptJsonString(envelope, opts.id);
|
|
737
|
+
if (json === null) return null;
|
|
738
|
+
let parsed = JSON.parse(json);
|
|
739
|
+
if (this.ctx.crdtMode && parsed !== null && typeof parsed === "object" && "_crdt" in parsed) {
|
|
740
|
+
parsed = this.ctx.crdtStrategy.resolveCrdtSnapshot(parsed);
|
|
741
|
+
}
|
|
742
|
+
let record = parsed;
|
|
743
|
+
if (envelope._sealed !== void 0 && this.ctx.storeCiphertext) {
|
|
744
|
+
const sealedCek = await this.resolveEnvelopeCek(envelope, opts.id);
|
|
745
|
+
const target = record;
|
|
746
|
+
for (const [field, blob] of Object.entries(envelope._sealed)) {
|
|
747
|
+
target[field] = opts.sealedAsHandles ? this.makeSealedHandle(field, blob, sealedCek) : await this.unsealField(field, blob, sealedCek);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const sealedAsHandles = opts.sealedAsHandles === true && envelope._sealed !== void 0;
|
|
751
|
+
if (this.ctx.schema !== void 0 && !opts.skipValidation && !sealedAsHandles) {
|
|
752
|
+
record = await validateSchemaOutput(
|
|
753
|
+
this.ctx.schema,
|
|
754
|
+
record,
|
|
755
|
+
`${this.ctx.name}@v${envelope._v}`
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
return record;
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
// src/kernel/enclave/record-keys/sealing.ts
|
|
763
|
+
var subtle2 = globalThis.crypto.subtle;
|
|
764
|
+
var SEALED_CEK_NS = "_sealed_cek";
|
|
765
|
+
async function sealRecordToHost(ctx, collection, id, hostSealer, opts) {
|
|
766
|
+
if (collection.includes("/")) throw new ValidationError(`sealRecordToHost: collection "${collection}" must not contain "/"`);
|
|
767
|
+
if (id.includes("/")) throw new ValidationError(`sealRecordToHost: id "${id}" must not contain "/"`);
|
|
768
|
+
if (Number.isNaN(Date.parse(opts.expiresAt))) {
|
|
769
|
+
throw new ValidationError(`sealRecordToHost: expiresAt "${opts.expiresAt}" is not a valid ISO 8601 timestamp`);
|
|
770
|
+
}
|
|
771
|
+
const live = await ctx.adapter.get(ctx.vault, collection, id);
|
|
772
|
+
if (!live || live._cek === void 0) {
|
|
773
|
+
throw new RecordCekNotFoundError(collection, id);
|
|
774
|
+
}
|
|
775
|
+
const dek = await ctx.getDEK(collection);
|
|
776
|
+
const cek = await unwrapCek(live._cek, dek);
|
|
777
|
+
const rawCek = await subtle2.exportKey("raw", cek);
|
|
778
|
+
const cekB64 = bufferToBase64(rawCek);
|
|
779
|
+
const hint = await hostSealer.publishRecipientHint();
|
|
780
|
+
if (hint.pid.includes("/")) throw new ValidationError(`sealRecordToHost: recipient pid "${hint.pid}" must not contain "/"`);
|
|
781
|
+
const binding = {
|
|
782
|
+
collection,
|
|
783
|
+
id,
|
|
784
|
+
cek: cekB64,
|
|
785
|
+
expiresAt: opts.expiresAt
|
|
786
|
+
};
|
|
787
|
+
const sealed = await hostSealer.sealForRecipient(
|
|
788
|
+
new TextEncoder().encode(JSON.stringify(binding)),
|
|
789
|
+
hint
|
|
790
|
+
);
|
|
791
|
+
const delivery = {
|
|
792
|
+
v: 1,
|
|
793
|
+
_noydb_sealed_cek: 1,
|
|
794
|
+
pid: hint.pid,
|
|
795
|
+
payload: bufferToBase64(sealed),
|
|
796
|
+
expiresAt: opts.expiresAt
|
|
797
|
+
};
|
|
798
|
+
const envelopeKey = `${collection}/${id}/${hint.pid}`;
|
|
799
|
+
const prior = await ctx.adapter.get(ctx.vault, SEALED_CEK_NS, envelopeKey);
|
|
800
|
+
const env = {
|
|
801
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
802
|
+
_v: (prior?._v ?? 0) + 1,
|
|
803
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
804
|
+
// AES-GCM bypassed — the sealing layer is the security boundary, exactly
|
|
805
|
+
// like the managed-passphrase `_meta/sealed-passphrase` envelope.
|
|
806
|
+
_iv: "",
|
|
807
|
+
_data: JSON.stringify(delivery),
|
|
808
|
+
...ctx.actor ? { _by: ctx.actor } : {}
|
|
809
|
+
};
|
|
810
|
+
await ctx.adapter.put(ctx.vault, SEALED_CEK_NS, envelopeKey, env);
|
|
811
|
+
return { pid: hint.pid, envelopeKey };
|
|
812
|
+
}
|
|
813
|
+
async function revokeSealedRecord(ctx, collection, id, pid, opts) {
|
|
814
|
+
if (opts?.hard === true) {
|
|
815
|
+
await rotateRecordCek(ctx, collection, id);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
await ctx.adapter.delete(ctx.vault, SEALED_CEK_NS, `${collection}/${id}/${pid}`);
|
|
819
|
+
}
|
|
820
|
+
async function rotateRecordCek(ctx, collection, id) {
|
|
821
|
+
const live = await ctx.adapter.get(ctx.vault, collection, id);
|
|
822
|
+
if (!live || live._cek === void 0) {
|
|
823
|
+
throw new RecordCekNotFoundError(collection, id);
|
|
824
|
+
}
|
|
825
|
+
const dek = await ctx.getDEK(collection);
|
|
826
|
+
const oldCek = await unwrapCek(live._cek, dek);
|
|
827
|
+
const json = await decrypt(live._iv, live._data, oldCek);
|
|
828
|
+
const newCek = await generateDEK();
|
|
829
|
+
const { iv, data } = await encrypt(json, newCek);
|
|
830
|
+
let sealedOut;
|
|
831
|
+
if (live._sealed !== void 0) {
|
|
832
|
+
const out = {};
|
|
833
|
+
for (const [field, slot] of Object.entries(live._sealed)) {
|
|
834
|
+
const plaintext = await dualReadSealedSlot(slot, field, collection, oldCek, dek);
|
|
835
|
+
const resealed = await encrypt(plaintext, await deriveSealedFieldKeyFromCek(newCek, collection, field));
|
|
836
|
+
out[field] = `${resealed.iv}:${resealed.data}`;
|
|
837
|
+
}
|
|
838
|
+
sealedOut = out;
|
|
839
|
+
}
|
|
840
|
+
const env = {
|
|
841
|
+
_noydb: NOYDB_FORMAT_VERSION,
|
|
842
|
+
_v: live._v + 1,
|
|
843
|
+
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
844
|
+
_iv: iv,
|
|
845
|
+
_data: data,
|
|
846
|
+
_cek: await wrapCek(newCek, dek),
|
|
847
|
+
...ctx.actor ? { _by: ctx.actor } : {},
|
|
848
|
+
...live._tier !== void 0 ? { _tier: live._tier } : {},
|
|
849
|
+
...live._det !== void 0 ? { _det: live._det } : {},
|
|
850
|
+
// Re-encrypt sealed (`sensitive`) fields under the new CEK. Sealed
|
|
851
|
+
// keys derive off the per-record CEK, so the rotated record's old CEK is
|
|
852
|
+
// destroyed here — carrying the old `_sealed` slots forward verbatim would
|
|
853
|
+
// orphan them (unreadable under the new CEK). `sealedOut` holds the slots
|
|
854
|
+
// dual-read from the old CEK (or the legacy DEK) and re-sealed under newCek.
|
|
855
|
+
...sealedOut !== void 0 ? { _sealed: sealedOut } : {}
|
|
856
|
+
};
|
|
857
|
+
await ctx.adapter.put(ctx.vault, collection, id, env);
|
|
858
|
+
await ctx.invalidateRecordCaches(collection, id);
|
|
859
|
+
const prefix = `${collection}/${id}/`;
|
|
860
|
+
const keys = await ctx.adapter.list(ctx.vault, SEALED_CEK_NS);
|
|
861
|
+
for (const key of keys) {
|
|
862
|
+
if (key.startsWith(prefix)) {
|
|
863
|
+
await ctx.adapter.delete(ctx.vault, SEALED_CEK_NS, key);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// src/kernel/enclave/record-keys/deterministic.ts
|
|
869
|
+
async function detTarget(ctx, field, value) {
|
|
870
|
+
const dek = await ctx.getDEK();
|
|
871
|
+
const plaintext = typeof value === "string" ? value : JSON.stringify(value);
|
|
872
|
+
const { iv, data } = await encryptDeterministic(plaintext, dek, `${ctx.name}/${field}`);
|
|
873
|
+
return `${iv}:${data}`;
|
|
874
|
+
}
|
|
875
|
+
function assertDetField(ctx, field, method) {
|
|
876
|
+
if (!ctx.deterministicFields || !ctx.deterministicFields.has(field)) {
|
|
877
|
+
throw new Error(
|
|
878
|
+
`Collection "${ctx.name}": field "${field}" is not declared in deterministicFields`
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
if (!ctx.storeCiphertext) {
|
|
882
|
+
throw new Error(
|
|
883
|
+
`Collection "${ctx.name}": ${method} is only meaningful on encrypted collections`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
async function findByDet(ctx, field, value) {
|
|
888
|
+
assertDetField(ctx, field, "findByDet");
|
|
889
|
+
const target = await detTarget(ctx, field, value);
|
|
890
|
+
const ids = await ctx.adapter.list(ctx.vault, ctx.name);
|
|
891
|
+
for (const id of ids) {
|
|
892
|
+
const env = await ctx.adapter.get(ctx.vault, ctx.name, id);
|
|
893
|
+
if (!env || !env._det) continue;
|
|
894
|
+
if (env._det[field] === target) {
|
|
895
|
+
return ctx.codec.decryptRecord(env);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
async function queryByDet(ctx, field, value) {
|
|
901
|
+
assertDetField(ctx, field, "queryByDet");
|
|
902
|
+
const target = await detTarget(ctx, field, value);
|
|
903
|
+
const ids = await ctx.adapter.list(ctx.vault, ctx.name);
|
|
904
|
+
const matches = [];
|
|
905
|
+
for (const id of ids) {
|
|
906
|
+
const env = await ctx.adapter.get(ctx.vault, ctx.name, id);
|
|
907
|
+
if (!env || !env._det) continue;
|
|
908
|
+
if (env._det[field] === target) {
|
|
909
|
+
const rec = await ctx.codec.decryptRecord(env);
|
|
910
|
+
if (rec !== null) matches.push(rec);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return matches;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// src/kernel/enclave/record-keys/envelope-body.ts
|
|
917
|
+
async function openEnvelopeJson(env, key, opts) {
|
|
918
|
+
if (opts?.encrypted === false) return env._data;
|
|
919
|
+
if (env._cek !== void 0) {
|
|
920
|
+
const cek = await unwrapCek(env._cek, key);
|
|
921
|
+
return decrypt(env._iv, env._data, cek);
|
|
922
|
+
}
|
|
923
|
+
return decrypt(env._iv, env._data, key);
|
|
924
|
+
}
|
|
925
|
+
async function writeEnvelopeBody(json, key, opts) {
|
|
926
|
+
if (opts?.encrypted === false) return { _iv: "", _data: json };
|
|
927
|
+
if (opts?.perRecordKey === true) {
|
|
928
|
+
const cek = await generateDEK();
|
|
929
|
+
const { iv: iv2, data: data2 } = await encrypt(json, cek);
|
|
930
|
+
const wrapped = await wrapCek(cek, key);
|
|
931
|
+
return { _iv: iv2, _data: data2, _cek: wrapped };
|
|
932
|
+
}
|
|
933
|
+
const { iv, data } = await encrypt(json, key);
|
|
934
|
+
return { _iv: iv, _data: data };
|
|
935
|
+
}
|
|
936
|
+
function hasPerRecordKey(env) {
|
|
937
|
+
return env._cek !== void 0;
|
|
938
|
+
}
|
|
939
|
+
function envelopeBodyForHash(env) {
|
|
940
|
+
if (env._sealed === void 0) return env._data;
|
|
941
|
+
const sealedKeys = Object.keys(env._sealed).sort();
|
|
942
|
+
const sealedParts = sealedKeys.map(
|
|
943
|
+
(k) => `${JSON.stringify(k)}:${JSON.stringify(env._sealed[k])}`
|
|
944
|
+
);
|
|
945
|
+
return `{"_data":${JSON.stringify(env._data)},"_sealed":{${sealedParts.join(",")}}}`;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
export {
|
|
949
|
+
deriveKey,
|
|
950
|
+
derivePassphraseKey,
|
|
951
|
+
generateDEK,
|
|
952
|
+
wrapKey,
|
|
953
|
+
unwrapKey,
|
|
954
|
+
wrapCek,
|
|
955
|
+
unwrapCek,
|
|
956
|
+
importCek,
|
|
957
|
+
encrypt,
|
|
958
|
+
decrypt,
|
|
959
|
+
encryptBytes,
|
|
960
|
+
decryptBytes,
|
|
961
|
+
sha256Hex,
|
|
962
|
+
hmacSha256Hex,
|
|
963
|
+
encryptBytesWithAAD,
|
|
964
|
+
decryptBytesWithAAD,
|
|
965
|
+
derivePresenceKey,
|
|
966
|
+
deriveSealedFieldKey,
|
|
967
|
+
deriveSealedFieldKeyFromCek,
|
|
968
|
+
encryptDeterministic,
|
|
969
|
+
decryptDeterministic,
|
|
970
|
+
generateIV,
|
|
971
|
+
generateSalt,
|
|
972
|
+
bufferToBase64,
|
|
973
|
+
base64ToBuffer,
|
|
974
|
+
resolveStableCek,
|
|
975
|
+
rewrapBodyToDek,
|
|
976
|
+
isTombstone,
|
|
977
|
+
buildTombstone,
|
|
978
|
+
validateSchemaInput,
|
|
979
|
+
validateSchemaOutput,
|
|
980
|
+
RecordCodec,
|
|
981
|
+
SEALED_CEK_NS,
|
|
982
|
+
sealRecordToHost,
|
|
983
|
+
revokeSealedRecord,
|
|
984
|
+
rotateRecordCek,
|
|
985
|
+
findByDet,
|
|
986
|
+
queryByDet,
|
|
987
|
+
openEnvelopeJson,
|
|
988
|
+
writeEnvelopeBody,
|
|
989
|
+
hasPerRecordKey,
|
|
990
|
+
envelopeBodyForHash
|
|
991
|
+
};
|
|
992
|
+
//# sourceMappingURL=chunk-5O3AOPDT.js.map
|