@noy-db/hub 0.6.0-pre.9 → 0.7.0-pre.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/CHANGELOG.md +7127 -0
- package/README.md +1 -1
- package/codemods/0.4.0-pre.json +24 -0
- package/codemods/0.6.0-pre.json +229 -0
- package/codemods/0.7.0-pre.json +258 -0
- package/dist/api-EQYRTZO2.js +20 -0
- package/dist/as/index.js +146 -0
- package/dist/as/index.js.map +1 -0
- package/dist/at/index.js +23 -0
- package/dist/attestation/index.js +19 -19
- package/dist/{backup-KULFZ2T4.js → backup-Z7ZBTAQ6.js} +36 -18
- package/dist/backup-Z7ZBTAQ6.js.map +1 -0
- package/dist/blobs/index.js +22 -20
- package/dist/blobs/index.js.map +1 -1
- package/dist/broker/index.js +12 -12
- package/dist/by/index.js +11 -0
- package/dist/cargo/index.js +40 -36
- package/dist/{chunk-YAWK6AWF.js → chunk-225O5CXQ.js} +2 -2
- package/dist/{chunk-MSWYQLMQ.js → chunk-2GXEQUEX.js} +2 -2
- package/dist/chunk-2GXEQUEX.js.map +1 -0
- package/dist/{chunk-3URQC7EH.js → chunk-2LPVI4PX.js} +355 -132
- package/dist/chunk-2LPVI4PX.js.map +1 -0
- package/dist/{chunk-JJNOLSG5.js → chunk-3L2MVE36.js} +15 -16
- package/dist/chunk-3L2MVE36.js.map +1 -0
- package/dist/{chunk-OF3L2SD6.js → chunk-3QARDAYW.js} +2 -2
- package/dist/{chunk-WLZ47GSL.js → chunk-467SKJ65.js} +12 -10
- package/dist/{chunk-WLZ47GSL.js.map → chunk-467SKJ65.js.map} +1 -1
- package/dist/{chunk-W3TVWF6N.js → chunk-4A4TYISP.js} +2 -2
- package/dist/{chunk-ZJADGNYF.js → chunk-4AOERLTV.js} +1 -1
- package/dist/chunk-4AOERLTV.js.map +1 -0
- package/dist/{chunk-CCZJJ4I2.js → chunk-4G25SDQL.js} +6 -6
- package/dist/{chunk-4I72F5DF.js → chunk-4LPF226H.js} +11 -14
- package/dist/chunk-4LPF226H.js.map +1 -0
- package/dist/{chunk-7F4DBNSP.js → chunk-4ZPE6SKS.js} +135 -18
- package/dist/chunk-4ZPE6SKS.js.map +1 -0
- package/dist/{chunk-7TSYBD3E.js → chunk-5263OEM3.js} +2 -2
- package/dist/{chunk-WFUIE565.js → chunk-57JTHWLQ.js} +17 -14
- package/dist/chunk-57JTHWLQ.js.map +1 -0
- package/dist/{chunk-FNSMZQS4.js → chunk-5ACAVJUI.js} +2 -2
- package/dist/{chunk-PDLLCOGD.js → chunk-5AFMP532.js} +20 -17
- package/dist/chunk-5AFMP532.js.map +1 -0
- package/dist/{chunk-JRKZTEZZ.js → chunk-5W7YXFV5.js} +444 -73
- package/dist/chunk-5W7YXFV5.js.map +1 -0
- package/dist/chunk-6EW2UCVF.js +190 -0
- package/dist/chunk-6EW2UCVF.js.map +1 -0
- package/dist/{chunk-4VUPLMZ6.js → chunk-6FOGTKBY.js} +68 -73
- package/dist/chunk-6FOGTKBY.js.map +1 -0
- package/dist/{chunk-VMCCGUQB.js → chunk-7LYUJNU3.js} +2 -2
- package/dist/{chunk-52JPHN5N.js → chunk-A2YW5EMU.js} +27 -149
- package/dist/chunk-A2YW5EMU.js.map +1 -0
- package/dist/{chunk-WLYIROCJ.js → chunk-AB5P32RE.js} +2 -2
- package/dist/{chunk-PL2GHSCM.js → chunk-ALB64CZM.js} +157 -90
- package/dist/chunk-ALB64CZM.js.map +1 -0
- package/dist/{chunk-SUFG5E73.js → chunk-ALUNQEBY.js} +2 -2
- package/dist/{chunk-CADUYBQD.js → chunk-ATL3ZVXT.js} +2 -2
- package/dist/{chunk-BWVKEOPE.js → chunk-ATV637J7.js} +7 -11
- package/dist/chunk-ATV637J7.js.map +1 -0
- package/dist/{chunk-DG27IUVI.js → chunk-CBRS2NEK.js} +2 -11
- package/dist/chunk-CBRS2NEK.js.map +1 -0
- package/dist/{chunk-7MNBICB5.js → chunk-CN6B5PEN.js} +16 -14
- package/dist/chunk-CN6B5PEN.js.map +1 -0
- package/dist/{chunk-6FJWSWFT.js → chunk-DBNYPLWY.js} +3 -3
- package/dist/{chunk-5JY5TSVY.js → chunk-DFAUMLD7.js} +25 -31
- package/dist/chunk-DFAUMLD7.js.map +1 -0
- package/dist/{chunk-FFDNKRCY.js → chunk-DKWPJOYH.js} +2 -2
- package/dist/chunk-DKWPJOYH.js.map +1 -0
- package/dist/{chunk-VJFMXYKP.js → chunk-E6YA6UAP.js} +2 -2
- package/dist/{chunk-WASHZV34.js → chunk-F23WQK3G.js} +65 -27
- package/dist/chunk-F23WQK3G.js.map +1 -0
- package/dist/{chunk-JIUVJ4M7.js → chunk-F7ZFRPGQ.js} +3 -3
- package/dist/{chunk-HXGUIPQB.js → chunk-FOG7YEJP.js} +3 -3
- package/dist/{chunk-QULVCV3M.js → chunk-GK4DRVUD.js} +6 -6
- package/dist/chunk-GK4DRVUD.js.map +1 -0
- package/dist/{chunk-BRVW2GS5.js → chunk-H36KSN6X.js} +87 -34
- package/dist/chunk-H36KSN6X.js.map +1 -0
- package/dist/{chunk-O2ERS4UJ.js → chunk-H4M7ADXY.js} +81 -52
- package/dist/chunk-H4M7ADXY.js.map +1 -0
- package/dist/{chunk-FZ6NYJ7L.js → chunk-H7HP56F6.js} +11 -8
- package/dist/chunk-H7HP56F6.js.map +1 -0
- package/dist/chunk-HK67PX4J.js +27 -0
- package/dist/chunk-HK67PX4J.js.map +1 -0
- package/dist/{chunk-X56RTUK3.js → chunk-HM4JSXX2.js} +3 -3
- package/dist/{chunk-AHLJSVRL.js → chunk-HTGRQZFB.js} +5 -7
- package/dist/chunk-HTGRQZFB.js.map +1 -0
- package/dist/{chunk-GQ3BX5EA.js → chunk-I3ISCYT7.js} +2 -2
- package/dist/{chunk-Q6TEJXBX.js → chunk-IESNYJXZ.js} +31 -49
- package/dist/chunk-IESNYJXZ.js.map +1 -0
- package/dist/{chunk-FUHKW252.js → chunk-J4MN6XIU.js} +2 -2
- package/dist/{chunk-ADW22QSB.js → chunk-J4UJ5FRV.js} +4 -4
- package/dist/{chunk-HNGSV2YC.js → chunk-JBIV4FLR.js} +67 -10
- package/dist/chunk-JBIV4FLR.js.map +1 -0
- package/dist/{chunk-HVQGCKY4.js → chunk-JVLSU7S2.js} +2 -2
- package/dist/{chunk-AR2JDFAE.js → chunk-KDJGJR7F.js} +2 -2
- package/dist/{chunk-NPP5R7FF.js → chunk-KN4W2OQ4.js} +22 -18
- package/dist/chunk-KN4W2OQ4.js.map +1 -0
- package/dist/{chunk-G6S5VVHP.js → chunk-L6GTLZRK.js} +10 -10
- package/dist/chunk-L6GTLZRK.js.map +1 -0
- package/dist/{chunk-2BMRJ6LQ.js → chunk-LF6DMX4C.js} +8 -11
- package/dist/{chunk-2BMRJ6LQ.js.map → chunk-LF6DMX4C.js.map} +1 -1
- package/dist/chunk-LFUID7OW.js +163 -0
- package/dist/chunk-LFUID7OW.js.map +1 -0
- package/dist/{chunk-747LRSOO.js → chunk-LKFKXS5K.js} +2 -2
- package/dist/{chunk-U5O6GIWZ.js → chunk-LR4Z47QD.js} +3 -3
- package/dist/{chunk-VZ3XFUQ7.js → chunk-MTBXJAS4.js} +16 -22
- package/dist/chunk-MTBXJAS4.js.map +1 -0
- package/dist/chunk-N4CNVSUF.js +66 -0
- package/dist/chunk-N4CNVSUF.js.map +1 -0
- package/dist/{chunk-RTAMLDWP.js → chunk-NLKLXXTO.js} +2 -2
- package/dist/{chunk-IGJCYUT5.js → chunk-NSITFOCJ.js} +1 -1
- package/dist/chunk-NSITFOCJ.js.map +1 -0
- package/dist/{chunk-FDX7OWWI.js → chunk-OPXR4PB2.js} +2 -2
- package/dist/{chunk-4GZ6XOVW.js → chunk-OWLVBOWO.js} +44 -42
- package/dist/chunk-OWLVBOWO.js.map +1 -0
- package/dist/{chunk-Z45YRGFO.js → chunk-P6ASQOZR.js} +2 -2
- package/dist/{chunk-5O5HRRTV.js → chunk-PCQDY6QN.js} +7 -7
- package/dist/{chunk-5O5HRRTV.js.map → chunk-PCQDY6QN.js.map} +1 -1
- package/dist/{chunk-BEFVYAR5.js → chunk-PGFKIOR3.js} +4 -4
- package/dist/chunk-PGFKIOR3.js.map +1 -0
- package/dist/{chunk-3FSX6LDG.js → chunk-POS53FG6.js} +18 -3
- package/dist/{chunk-3FSX6LDG.js.map → chunk-POS53FG6.js.map} +1 -1
- package/dist/{chunk-3IXIU6FP.js → chunk-PQ6Z2TAP.js} +7 -10
- package/dist/chunk-PQ6Z2TAP.js.map +1 -0
- package/dist/{chunk-CEJWMBXO.js → chunk-PUJTKLPL.js} +6 -2
- package/dist/chunk-PUJTKLPL.js.map +1 -0
- package/dist/{chunk-GAG7QOVO.js → chunk-QGJ4XIA7.js} +53 -18
- package/dist/chunk-QGJ4XIA7.js.map +1 -0
- package/dist/{chunk-2DHSSLOD.js → chunk-QHJKK77B.js} +13 -15
- package/dist/chunk-QHJKK77B.js.map +1 -0
- package/dist/{chunk-2Z3NCLNX.js → chunk-QI5NWLVW.js} +7 -7
- package/dist/{chunk-6SAFL5YE.js → chunk-RC4BROYR.js} +3 -3
- package/dist/{chunk-NJDQ4YSV.js → chunk-RDHUZ674.js} +4 -4
- package/dist/{chunk-YPYHVOXP.js → chunk-RK7N4OQ5.js} +2 -2
- package/dist/chunk-RK7N4OQ5.js.map +1 -0
- package/dist/{chunk-3737A6EA.js → chunk-S2VG3ABP.js} +127 -31
- package/dist/chunk-S2VG3ABP.js.map +1 -0
- package/dist/{chunk-3JVXVJ7L.js → chunk-S35S77YU.js} +10 -10
- package/dist/{chunk-KUDZVJKQ.js → chunk-SHILAQH5.js} +2 -2
- package/dist/{chunk-4UVDI4XE.js → chunk-T3PHCEN4.js} +4 -4
- package/dist/{chunk-55LKVALY.js → chunk-T7IR4N2S.js} +1 -3
- package/dist/chunk-T7IR4N2S.js.map +1 -0
- package/dist/{chunk-RXYYFSBB.js → chunk-TOV6VMHF.js} +6 -6
- package/dist/chunk-TOV6VMHF.js.map +1 -0
- package/dist/{chunk-W77KZHCW.js → chunk-TVIFAHZR.js} +16 -17
- package/dist/chunk-TVIFAHZR.js.map +1 -0
- package/dist/chunk-UBSFPGVJ.js +1 -0
- package/dist/{chunk-FAXZQ5UF.js → chunk-UCUALOSM.js} +2 -2
- package/dist/{chunk-4HDYQL3J.js → chunk-VAG47QHV.js} +2 -2
- package/dist/{chunk-35EBKWQU.js → chunk-VFIWDPXZ.js} +4 -4
- package/dist/{chunk-5ZKS6ZE2.js → chunk-VTG7327U.js} +4 -4
- package/dist/chunk-VTG7327U.js.map +1 -0
- package/dist/{chunk-VCETL77O.js → chunk-W5LHQXZU.js} +36 -7
- package/dist/chunk-W5LHQXZU.js.map +1 -0
- package/dist/{chunk-6NVPFKN3.js → chunk-WK5FSW7T.js} +92 -35
- package/dist/chunk-WK5FSW7T.js.map +1 -0
- package/dist/{chunk-HNHCQ556.js → chunk-WLLUM35R.js} +2 -2
- package/dist/{chunk-YJ6IONGA.js → chunk-X62MEF2O.js} +13 -5
- package/dist/chunk-X62MEF2O.js.map +1 -0
- package/dist/{chunk-7G6VDWXB.js → chunk-X67FCULK.js} +5 -5
- package/dist/chunk-X67FCULK.js.map +1 -0
- package/dist/{chunk-UHXCLPGC.js → chunk-XDVVBETV.js} +2 -2
- package/dist/chunk-XDVVBETV.js.map +1 -0
- package/dist/chunk-XJ3FEKEY.js +15 -0
- package/dist/chunk-XJ3FEKEY.js.map +1 -0
- package/dist/{chunk-E6427JU2.js → chunk-XSN63FUS.js} +19 -3
- package/dist/chunk-XSN63FUS.js.map +1 -0
- package/dist/{chunk-JRCYZEHU.js → chunk-XVU7J43V.js} +2 -2
- package/dist/{chunk-2GAJGUNM.js → chunk-Y3YLJORD.js} +408 -260
- package/dist/chunk-Y3YLJORD.js.map +1 -0
- package/dist/{chunk-6BU3L4UE.js → chunk-Y6GZ2P4M.js} +7 -10
- package/dist/chunk-Y6GZ2P4M.js.map +1 -0
- package/dist/{chunk-DMQCLZ4Z.js → chunk-YCSM3GZU.js} +2 -2
- package/dist/{chunk-RV3ARMPN.js → chunk-YKPGQJFU.js} +30 -180
- package/dist/chunk-YKPGQJFU.js.map +1 -0
- package/dist/{chunk-FMQJZH4R.js → chunk-YMLQBZFJ.js} +6 -6
- package/dist/{chunk-6HFM7EIT.js → chunk-YPSOHDNC.js} +2 -2
- package/dist/{chunk-CDB2HVQW.js → chunk-YXA2TCTZ.js} +14 -15
- package/dist/chunk-YXA2TCTZ.js.map +1 -0
- package/dist/{chunk-N35NC4JH.js → chunk-Z2PWOSCJ.js} +2 -2
- package/dist/{chunk-XWX7GL56.js → chunk-Z2WK53N6.js} +47 -23
- package/dist/chunk-Z2WK53N6.js.map +1 -0
- package/dist/{chunk-HPLWFNG4.js → chunk-Z7ZXZL6Y.js} +2 -2
- package/dist/{chunk-PNKHAKQ7.js → chunk-ZO252Q5S.js} +17 -10
- package/dist/chunk-ZO252Q5S.js.map +1 -0
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-2KJAXRRO.js → classified-marker-BSQHU4JT.js} +3 -3
- package/dist/collection-facade-LG3NYZVB.js +45 -0
- package/dist/{computed-6G5M3UPH.js → computed-ASBNTLOK.js} +3 -3
- package/dist/consent/index.js +10 -10
- package/dist/cover/index.js +10 -3
- package/dist/custody/index.js +12 -11
- package/dist/{dead-filter-DCB62VWW.js → dead-filter-QKU56XTQ.js} +2 -2
- package/dist/debug/index.js +1 -1
- package/dist/{delegation-MO7YZYJS.js → delegation-XWRPYODW.js} +24 -23
- package/dist/delegation-XWRPYODW.js.map +1 -0
- package/dist/derivations/index.js +12 -12
- package/dist/derive-CXHCY46K.js +22 -0
- package/dist/directory/index.js +10 -10
- package/dist/{dispatch-MJ6SHB3Y.js → dispatch-4AP4PUZU.js} +18 -18
- package/dist/{dispatch-FZEAWKVM.js → dispatch-VR4LRXZ7.js} +9 -6
- package/dist/dispatch-VR4LRXZ7.js.map +1 -0
- package/dist/{enclave-JU4YIEPB.js → enclave-NYPT7WA7.js} +31 -9
- package/dist/executor-3KFRQETS.js +29 -0
- package/dist/executor-AFHQYRAU.js +9 -0
- package/dist/executor-YYDW6XQI.js +9 -0
- package/dist/export-accessible-BX2ILB4C.js +23 -0
- package/dist/extract-partition-UPOVITFI.js +38 -0
- package/dist/{fanout-sidecar-NNAJQXMX.js → fanout-sidecar-IWP3R4RC.js} +18 -27
- package/dist/fanout-sidecar-IWP3R4RC.js.map +1 -0
- package/dist/{fence-watcher-DHQ775JB.js → fence-watcher-ZVSMLKQD.js} +1 -1
- package/dist/fence-watcher-ZVSMLKQD.js.map +1 -0
- package/dist/find-SZBL2HPB.js +11 -0
- package/dist/forget/index.js +9 -9
- package/dist/guards/index.js +4 -4
- package/dist/history/index.js +23 -20
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.js +14 -14
- package/dist/index.d.ts +35 -18
- package/dist/index.js +199 -204
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +4 -4
- package/dist/introspection/index.js +12 -11
- package/dist/issue-G53NB6EW.js +19 -0
- package/dist/kernel/collection-config.d.ts +9 -9
- package/dist/kernel/collection.d.ts +9 -3
- package/dist/kernel/constants.d.ts +36 -0
- package/dist/kernel/enclave/crypto.d.ts +52 -4
- package/dist/kernel/enclave/index.d.ts +10 -3
- package/dist/kernel/enclave/record-aad.d.ts +131 -0
- package/dist/kernel/enclave/record-envelope.d.ts +76 -0
- package/dist/kernel/enclave/record-keys/envelope-body.d.ts +34 -2
- package/dist/kernel/enclave/record-keys/lifecycle.d.ts +22 -5
- package/dist/kernel/enclave/record-keys/record-codec.d.ts +47 -14
- package/dist/kernel/enclave/record-keys/rekey-blob.d.ts +88 -0
- package/dist/kernel/enclave/record-keys/rekey.d.ts +91 -0
- package/dist/kernel/enclave/record-keys/tombstone.d.ts +3 -2
- package/dist/kernel/errors.d.ts +134 -14
- package/dist/kernel/merge-authority.d.ts +25 -0
- package/dist/kernel/noydb.d.ts +33 -10
- package/dist/kernel/query/builder.d.ts +51 -4
- package/dist/kernel/query/join.d.ts +27 -0
- package/dist/kernel/query/live.d.ts +31 -9
- package/dist/kernel/query/predicate.d.ts +9 -0
- package/dist/kernel/refs.d.ts +3 -1
- package/dist/kernel/secret-mode.d.ts +1 -1
- package/dist/kernel/sync-policy.d.ts +0 -2
- package/dist/kernel/tier-visibility.d.ts +34 -0
- package/dist/kernel/types.d.ts +145 -28
- package/dist/kernel/vault.d.ts +16 -7
- package/dist/kernel/via/compose.d.ts +3 -3
- package/dist/kernel/via/index.d.ts +15 -6
- package/dist/kernel/via/pipeline.d.ts +6 -6
- package/dist/kernel/via/reconcile.d.ts +14 -2
- package/dist/kernel/via/taint-binding.d.ts +3 -3
- package/dist/{ledger-AETNF4OM.js → ledger-T5K4P2D7.js} +11 -11
- package/dist/liberate-VEOZRESE.js +25 -0
- package/dist/{link-set-E5HNY67I.js → link-set-76JZ6YB4.js} +11 -11
- package/dist/{managed-secret-CWQ6PND5.js → managed-secret-6YC32VXG.js} +13 -5
- package/dist/materialized-views/index.js +27 -27
- package/dist/migrate-cek-LBRKYUZZ.js +19 -0
- package/dist/migrate-cek-LBRKYUZZ.js.map +1 -0
- package/dist/money/index.js +5 -5
- package/dist/noydb-EDJA4JNJ.js +86 -0
- package/dist/on/index.js +1 -0
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +12 -12
- package/dist/pod/index.js +79 -88
- package/dist/pod-handle-P6DPBM5U.js +41 -0
- package/dist/pod-handle-P6DPBM5U.js.map +1 -0
- package/dist/policy/index.js +11 -4
- package/dist/port/as/active.d.ts +10 -0
- package/dist/port/as/index.d.ts +32 -0
- package/dist/port/as/types.d.ts +113 -0
- package/dist/port/at/index.d.ts +37 -0
- package/dist/port/by/default-provider.d.ts +3 -3
- package/dist/port/by/index.d.ts +32 -10
- package/dist/port/by/types.d.ts +29 -11
- package/dist/port/on/index.d.ts +50 -0
- package/dist/port/to/index.d.ts +4 -4
- package/dist/port/to/locator.d.ts +54 -8
- package/dist/port/with/computed-strategy.d.ts +1 -1
- package/dist/port/with/device-seal-strategy.d.ts +2 -2
- package/dist/port/with/formats-strategy.d.ts +76 -0
- package/dist/port/with/i18n-strategy.d.ts +4 -1
- package/dist/port/with/lookup-strategy.d.ts +4 -1
- package/dist/port/with/merge-authority.d.ts +52 -0
- package/dist/port/with/service-bus.d.ts +0 -4
- package/dist/port/with/strategies.d.ts +13 -5
- package/dist/port/with/team-strategy.d.ts +5 -1
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-2XSSMGEQ.js → post-register-53O7AJRZ.js} +6 -6
- package/dist/query/index.js +6 -6
- package/dist/reduce/index.js +6 -6
- package/dist/register-6TCBR6XD.js +32 -0
- package/dist/registry-DH2L4O2V.js +18 -0
- package/dist/registry-IWFMXCHX.js +9 -0
- package/dist/registry-Q3FG3OAX.js +9 -0
- package/dist/registry-YC3IFG3A.js +19 -0
- package/dist/request-withdrawal-NM5VYR6F.js +31 -0
- package/dist/{reveal-BW36KCOQ.js → reveal-XBIMHAXR.js} +6 -6
- package/dist/revoke-MJNYWO72.js +24 -0
- package/dist/satellites/index.js +1 -1
- package/dist/schema-update/index.js +2 -2
- package/dist/sealed-record/index.js +20 -13
- package/dist/sealed-record/index.js.map +1 -1
- package/dist/search/index.js +4 -4
- package/dist/{seed-GDV5PT4R.js → seed-NSNIGYXE.js} +20 -22
- package/dist/seed-NSNIGYXE.js.map +1 -0
- package/dist/sequence/index.js +9 -9
- package/dist/session/index.js +10 -10
- package/dist/shadow/index.js +2 -2
- package/dist/share-link/index.js +1 -1
- package/dist/signer-6XVS2AU3.js +25 -0
- package/dist/snapshots/index.js +10 -10
- package/dist/snapshots/index.js.map +1 -1
- package/dist/{stale-ARHFVL5U.js → stale-MCS46TFO.js} +10 -10
- package/dist/storage-ZXUT476I.js +23 -0
- package/dist/storage-ZXUT476I.js.map +1 -0
- package/dist/{store-coordination-provider-GFCUOBR6.js → store-coordination-provider-6FM26VQM.js} +22 -15
- package/dist/store-coordination-provider-6FM26VQM.js.map +1 -0
- package/dist/sync/index.js +11 -11
- package/dist/team/index.js +17 -16
- package/dist/tiers/index.js +11 -10
- package/dist/to/index.js +17 -9
- package/dist/to/index.js.map +1 -1
- package/dist/transactions/index.js +4 -4
- package/dist/{ulid-PTAIMDG4.js → ulid-VQW6ERQN.js} +2 -2
- package/dist/ulid-VQW6ERQN.js.map +1 -0
- package/dist/util/index.js +1 -1
- package/dist/vault-diff-FE5J35NJ.js +9 -0
- package/dist/vault-diff-FE5J35NJ.js.map +1 -0
- package/dist/vault-head/index.js +115 -0
- package/dist/vault-head/index.js.map +1 -0
- package/dist/{verify-RO5RTZOJ.js → verify-WYOFLSNA.js} +5 -5
- package/dist/via/blob/binding.d.ts +6 -6
- package/dist/via/blob/index.d.ts +3 -0
- package/dist/via/classified/binding.d.ts +9 -9
- package/dist/via/computed/binding.d.ts +2 -2
- package/dist/via/i18n/binding.d.ts +4 -4
- package/dist/via/lookup/binding.d.ts +5 -5
- package/dist/via/lookup/handle.d.ts +4 -1
- package/dist/via/lookup/index.d.ts +1 -1
- package/dist/via/lookup/snapshot.d.ts +1 -1
- package/dist/via/money/binding.d.ts +3 -3
- package/dist/via/money/normalize.d.ts +1 -1
- package/dist/via/money/where.d.ts +3 -3
- package/dist/walk-JNEOYCAU.js +21 -0
- package/dist/walk-JNEOYCAU.js.map +1 -0
- package/dist/with-audit/periods/periods.d.ts +1 -1
- package/dist/with-audit/periods/strategy.d.ts +1 -1
- package/dist/with-audit/sealed-record/index.d.ts +13 -0
- package/dist/with-audit/tiers/index.d.ts +17 -2
- package/dist/with-cargo/adopt-partition.d.ts +7 -7
- package/dist/with-cargo/extract-partition.d.ts +1 -1
- package/dist/with-cargo/floor.d.ts +1 -1
- package/dist/with-cargo/index.d.ts +2 -2
- package/dist/with-commit/history/history.d.ts +26 -0
- package/dist/with-commit/history/index.d.ts +2 -1
- package/dist/with-commit/history/ledger/store.d.ts +7 -1
- package/dist/with-commit/history/strategy.d.ts +11 -1
- package/dist/with-commit/numbering/index.d.ts +0 -5
- package/dist/with-commit/vault-head/active.d.ts +2 -0
- package/dist/with-commit/vault-head/head.d.ts +28 -0
- package/dist/with-commit/vault-head/index.d.ts +10 -0
- package/dist/with-commit/vault-head/strategy.d.ts +127 -0
- package/dist/with-commit/vault-head/verify.d.ts +90 -0
- package/dist/with-formula/materialized-views/dependency-analyzer.d.ts +4 -0
- package/dist/with-formula/materialized-views/projection-legs.d.ts +59 -0
- package/dist/with-formula/materialized-views/registry.d.ts +50 -0
- package/dist/with-formula/materialized-views/types.d.ts +68 -10
- package/dist/with-party/directory/cover/storage.d.ts +0 -8
- package/dist/with-party/directory/storage.d.ts +0 -11
- package/dist/with-party/directory/visibility.d.ts +0 -21
- package/dist/with-party/policy/storage.d.ts +0 -10
- package/dist/with-party/team/deed.d.ts +4 -4
- package/dist/with-party/team/device-seal.d.ts +5 -5
- package/dist/with-party/team/echo-ceremony.d.ts +2 -2
- package/dist/with-party/team/echo-secret.d.ts +4 -4
- package/dist/with-party/team/index.d.ts +3 -3
- package/dist/with-party/team/keyring.d.ts +141 -4
- package/dist/with-party/team/managed-secret.d.ts +21 -11
- package/dist/with-party/team/noydb-facade.d.ts +7 -3
- package/dist/with-party/team/{shamir-recovery-provider.d.ts → noydb-shamir.d.ts} +1 -1
- package/dist/with-party/team/recovery.d.ts +3 -3
- package/dist/with-party/team/roster-tag.d.ts +101 -0
- package/dist/with-party/team/rotate-recover.d.ts +5 -5
- package/dist/with-party/team/tiers.d.ts +2 -1
- package/dist/with-pod/format.d.ts +11 -13
- package/dist/with-pod/index.d.ts +10 -10
- package/dist/with-pod/open.d.ts +2 -2
- package/dist/with-pod/pod-handle.d.ts +19 -0
- package/dist/with-pod/pod-store.d.ts +0 -8
- package/dist/with-pod/{bundle.d.ts → pod.d.ts} +17 -25
- package/dist/with-pod/ulid.d.ts +1 -1
- package/dist/with-shape/blobs/blob-compaction.d.ts +13 -0
- package/dist/with-shape/blobs/blob-set.d.ts +83 -11
- package/dist/with-shape/blobs/orphan-report.d.ts +62 -0
- package/dist/with-shape/introspection/describe.d.ts +2 -2
- package/dist/with-shape/schema-update/fence-controller.d.ts +4 -4
- package/dist/with-shape/schema-update/fence-watcher.d.ts +4 -4
- package/dist/with-shape/schema-update/store-coordination-provider.d.ts +7 -6
- package/dist/with-sync/engine.d.ts +54 -5
- package/dist/with-sync/strategy.d.ts +3 -0
- package/dist/withdraw-accessible-ILTSXTIE.js +28 -0
- package/dist/withdraw-accessible-ILTSXTIE.js.map +1 -0
- package/package.json +29 -5
- package/dist/api-RXUMEKT3.js +0 -20
- package/dist/backup-KULFZ2T4.js.map +0 -1
- package/dist/bundle-handle-GFNHT52M.js +0 -36
- package/dist/bundle-handle-GFNHT52M.js.map +0 -1
- package/dist/chunk-2DHSSLOD.js.map +0 -1
- package/dist/chunk-2GAJGUNM.js.map +0 -1
- package/dist/chunk-3737A6EA.js.map +0 -1
- package/dist/chunk-3IXIU6FP.js.map +0 -1
- package/dist/chunk-3URQC7EH.js.map +0 -1
- package/dist/chunk-4GZ6XOVW.js.map +0 -1
- package/dist/chunk-4I72F5DF.js.map +0 -1
- package/dist/chunk-4VUPLMZ6.js.map +0 -1
- package/dist/chunk-52JPHN5N.js.map +0 -1
- package/dist/chunk-55LKVALY.js.map +0 -1
- package/dist/chunk-5JY5TSVY.js.map +0 -1
- package/dist/chunk-5ZKS6ZE2.js.map +0 -1
- package/dist/chunk-6BU3L4UE.js.map +0 -1
- package/dist/chunk-6NVPFKN3.js.map +0 -1
- package/dist/chunk-7F4DBNSP.js.map +0 -1
- package/dist/chunk-7G6VDWXB.js.map +0 -1
- package/dist/chunk-7H73RIE3.js +0 -45
- package/dist/chunk-7H73RIE3.js.map +0 -1
- package/dist/chunk-7MNBICB5.js.map +0 -1
- package/dist/chunk-AFHQATY4.js +0 -47
- package/dist/chunk-AFHQATY4.js.map +0 -1
- package/dist/chunk-AHLJSVRL.js.map +0 -1
- package/dist/chunk-BEFVYAR5.js.map +0 -1
- package/dist/chunk-BRVW2GS5.js.map +0 -1
- package/dist/chunk-BWVKEOPE.js.map +0 -1
- package/dist/chunk-CDB2HVQW.js.map +0 -1
- package/dist/chunk-CEJWMBXO.js.map +0 -1
- package/dist/chunk-DG27IUVI.js.map +0 -1
- package/dist/chunk-E6427JU2.js.map +0 -1
- package/dist/chunk-FFDNKRCY.js.map +0 -1
- package/dist/chunk-FZ6NYJ7L.js.map +0 -1
- package/dist/chunk-G6S5VVHP.js.map +0 -1
- package/dist/chunk-GAG7QOVO.js.map +0 -1
- package/dist/chunk-HNGSV2YC.js.map +0 -1
- package/dist/chunk-IGJCYUT5.js.map +0 -1
- package/dist/chunk-JJNOLSG5.js.map +0 -1
- package/dist/chunk-JRKZTEZZ.js.map +0 -1
- package/dist/chunk-MSWYQLMQ.js.map +0 -1
- package/dist/chunk-NPP5R7FF.js.map +0 -1
- package/dist/chunk-O2ERS4UJ.js.map +0 -1
- package/dist/chunk-PDLLCOGD.js.map +0 -1
- package/dist/chunk-PL2GHSCM.js.map +0 -1
- package/dist/chunk-PNKHAKQ7.js.map +0 -1
- package/dist/chunk-Q6TEJXBX.js.map +0 -1
- package/dist/chunk-QULVCV3M.js.map +0 -1
- package/dist/chunk-RV3ARMPN.js.map +0 -1
- package/dist/chunk-RXYYFSBB.js.map +0 -1
- package/dist/chunk-UHXCLPGC.js.map +0 -1
- package/dist/chunk-VCETL77O.js.map +0 -1
- package/dist/chunk-VZ3XFUQ7.js.map +0 -1
- package/dist/chunk-W77KZHCW.js.map +0 -1
- package/dist/chunk-WASHZV34.js.map +0 -1
- package/dist/chunk-WFUIE565.js.map +0 -1
- package/dist/chunk-XWX7GL56.js.map +0 -1
- package/dist/chunk-YJ6IONGA.js.map +0 -1
- package/dist/chunk-YPYHVOXP.js.map +0 -1
- package/dist/chunk-ZJADGNYF.js.map +0 -1
- package/dist/collection-facade-QEJIM6BC.js +0 -45
- package/dist/delegation-MO7YZYJS.js.map +0 -1
- package/dist/derive-IUA6JFLB.js +0 -22
- package/dist/dispatch-FZEAWKVM.js.map +0 -1
- package/dist/executor-CYJQLKKI.js +0 -9
- package/dist/executor-S4QIKEHW.js +0 -9
- package/dist/executor-YIQB2S6N.js +0 -29
- package/dist/export-accessible-4S36PQNR.js +0 -23
- package/dist/extract-partition-IO7FDZ2P.js +0 -38
- package/dist/fanout-sidecar-NNAJQXMX.js.map +0 -1
- package/dist/fence-watcher-DHQ775JB.js.map +0 -1
- package/dist/find-RBBYKXI7.js +0 -11
- package/dist/issue-RIVHSY2S.js +0 -19
- package/dist/liberate-GFN4C7RR.js +0 -24
- package/dist/noydb-ASEXNJS6.js +0 -83
- package/dist/register-4EFJCVYD.js +0 -32
- package/dist/registry-4SDHHOJ7.js +0 -18
- package/dist/registry-CZVINVQH.js +0 -9
- package/dist/registry-IKUR2MXD.js +0 -9
- package/dist/registry-OYTFGWVK.js +0 -19
- package/dist/request-withdrawal-KGXGDPVQ.js +0 -31
- package/dist/revoke-JFN773KF.js +0 -24
- package/dist/seed-GDV5PT4R.js.map +0 -1
- package/dist/signer-TA55QB6R.js +0 -25
- package/dist/storage-V7MTFAQJ.js +0 -23
- package/dist/store-coordination-provider-GFCUOBR6.js.map +0 -1
- package/dist/walk-3ZECBFDA.js +0 -21
- package/dist/with-pod/bundle-handle.d.ts +0 -9
- package/dist/withdraw-accessible-7PG4DYQK.js +0 -28
- /package/dist/{api-RXUMEKT3.js.map → api-EQYRTZO2.js.map} +0 -0
- /package/dist/{collection-facade-QEJIM6BC.js.map → at/index.js.map} +0 -0
- /package/dist/{computed-6G5M3UPH.js.map → by/index.js.map} +0 -0
- /package/dist/{chunk-YAWK6AWF.js.map → chunk-225O5CXQ.js.map} +0 -0
- /package/dist/{chunk-OF3L2SD6.js.map → chunk-3QARDAYW.js.map} +0 -0
- /package/dist/{chunk-W3TVWF6N.js.map → chunk-4A4TYISP.js.map} +0 -0
- /package/dist/{chunk-CCZJJ4I2.js.map → chunk-4G25SDQL.js.map} +0 -0
- /package/dist/{chunk-7TSYBD3E.js.map → chunk-5263OEM3.js.map} +0 -0
- /package/dist/{chunk-FNSMZQS4.js.map → chunk-5ACAVJUI.js.map} +0 -0
- /package/dist/{chunk-VMCCGUQB.js.map → chunk-7LYUJNU3.js.map} +0 -0
- /package/dist/{chunk-WLYIROCJ.js.map → chunk-AB5P32RE.js.map} +0 -0
- /package/dist/{chunk-SUFG5E73.js.map → chunk-ALUNQEBY.js.map} +0 -0
- /package/dist/{chunk-CADUYBQD.js.map → chunk-ATL3ZVXT.js.map} +0 -0
- /package/dist/{chunk-6FJWSWFT.js.map → chunk-DBNYPLWY.js.map} +0 -0
- /package/dist/{chunk-VJFMXYKP.js.map → chunk-E6YA6UAP.js.map} +0 -0
- /package/dist/{chunk-JIUVJ4M7.js.map → chunk-F7ZFRPGQ.js.map} +0 -0
- /package/dist/{chunk-HXGUIPQB.js.map → chunk-FOG7YEJP.js.map} +0 -0
- /package/dist/{chunk-X56RTUK3.js.map → chunk-HM4JSXX2.js.map} +0 -0
- /package/dist/{chunk-GQ3BX5EA.js.map → chunk-I3ISCYT7.js.map} +0 -0
- /package/dist/{chunk-FUHKW252.js.map → chunk-J4MN6XIU.js.map} +0 -0
- /package/dist/{chunk-ADW22QSB.js.map → chunk-J4UJ5FRV.js.map} +0 -0
- /package/dist/{chunk-HVQGCKY4.js.map → chunk-JVLSU7S2.js.map} +0 -0
- /package/dist/{chunk-AR2JDFAE.js.map → chunk-KDJGJR7F.js.map} +0 -0
- /package/dist/{chunk-747LRSOO.js.map → chunk-LKFKXS5K.js.map} +0 -0
- /package/dist/{chunk-U5O6GIWZ.js.map → chunk-LR4Z47QD.js.map} +0 -0
- /package/dist/{chunk-RTAMLDWP.js.map → chunk-NLKLXXTO.js.map} +0 -0
- /package/dist/{chunk-FDX7OWWI.js.map → chunk-OPXR4PB2.js.map} +0 -0
- /package/dist/{chunk-Z45YRGFO.js.map → chunk-P6ASQOZR.js.map} +0 -0
- /package/dist/{chunk-2Z3NCLNX.js.map → chunk-QI5NWLVW.js.map} +0 -0
- /package/dist/{chunk-6SAFL5YE.js.map → chunk-RC4BROYR.js.map} +0 -0
- /package/dist/{chunk-NJDQ4YSV.js.map → chunk-RDHUZ674.js.map} +0 -0
- /package/dist/{chunk-3JVXVJ7L.js.map → chunk-S35S77YU.js.map} +0 -0
- /package/dist/{chunk-KUDZVJKQ.js.map → chunk-SHILAQH5.js.map} +0 -0
- /package/dist/{chunk-4UVDI4XE.js.map → chunk-T3PHCEN4.js.map} +0 -0
- /package/dist/{derive-IUA6JFLB.js.map → chunk-UBSFPGVJ.js.map} +0 -0
- /package/dist/{chunk-FAXZQ5UF.js.map → chunk-UCUALOSM.js.map} +0 -0
- /package/dist/{chunk-4HDYQL3J.js.map → chunk-VAG47QHV.js.map} +0 -0
- /package/dist/{chunk-35EBKWQU.js.map → chunk-VFIWDPXZ.js.map} +0 -0
- /package/dist/{chunk-HNHCQ556.js.map → chunk-WLLUM35R.js.map} +0 -0
- /package/dist/{chunk-JRCYZEHU.js.map → chunk-XVU7J43V.js.map} +0 -0
- /package/dist/{chunk-DMQCLZ4Z.js.map → chunk-YCSM3GZU.js.map} +0 -0
- /package/dist/{chunk-FMQJZH4R.js.map → chunk-YMLQBZFJ.js.map} +0 -0
- /package/dist/{chunk-6HFM7EIT.js.map → chunk-YPSOHDNC.js.map} +0 -0
- /package/dist/{chunk-N35NC4JH.js.map → chunk-Z2PWOSCJ.js.map} +0 -0
- /package/dist/{chunk-HPLWFNG4.js.map → chunk-Z7ZXZL6Y.js.map} +0 -0
- /package/dist/{classified-marker-2KJAXRRO.js.map → classified-marker-BSQHU4JT.js.map} +0 -0
- /package/dist/{enclave-JU4YIEPB.js.map → collection-facade-LG3NYZVB.js.map} +0 -0
- /package/dist/{executor-CYJQLKKI.js.map → computed-ASBNTLOK.js.map} +0 -0
- /package/dist/{dead-filter-DCB62VWW.js.map → dead-filter-QKU56XTQ.js.map} +0 -0
- /package/dist/{executor-S4QIKEHW.js.map → derive-CXHCY46K.js.map} +0 -0
- /package/dist/{dispatch-MJ6SHB3Y.js.map → dispatch-4AP4PUZU.js.map} +0 -0
- /package/dist/{executor-YIQB2S6N.js.map → enclave-NYPT7WA7.js.map} +0 -0
- /package/dist/{export-accessible-4S36PQNR.js.map → executor-3KFRQETS.js.map} +0 -0
- /package/dist/{extract-partition-IO7FDZ2P.js.map → executor-AFHQYRAU.js.map} +0 -0
- /package/dist/{find-RBBYKXI7.js.map → executor-YYDW6XQI.js.map} +0 -0
- /package/dist/{issue-RIVHSY2S.js.map → export-accessible-BX2ILB4C.js.map} +0 -0
- /package/dist/{ledger-AETNF4OM.js.map → extract-partition-UPOVITFI.js.map} +0 -0
- /package/dist/{liberate-GFN4C7RR.js.map → find-SZBL2HPB.js.map} +0 -0
- /package/dist/{link-set-E5HNY67I.js.map → issue-G53NB6EW.js.map} +0 -0
- /package/dist/{managed-secret-CWQ6PND5.js.map → ledger-T5K4P2D7.js.map} +0 -0
- /package/dist/{noydb-ASEXNJS6.js.map → liberate-VEOZRESE.js.map} +0 -0
- /package/dist/{register-4EFJCVYD.js.map → link-set-76JZ6YB4.js.map} +0 -0
- /package/dist/{registry-4SDHHOJ7.js.map → managed-secret-6YC32VXG.js.map} +0 -0
- /package/dist/{registry-CZVINVQH.js.map → noydb-EDJA4JNJ.js.map} +0 -0
- /package/dist/{registry-IKUR2MXD.js.map → on/index.js.map} +0 -0
- /package/dist/{post-register-2XSSMGEQ.js.map → post-register-53O7AJRZ.js.map} +0 -0
- /package/dist/{registry-OYTFGWVK.js.map → register-6TCBR6XD.js.map} +0 -0
- /package/dist/{request-withdrawal-KGXGDPVQ.js.map → registry-DH2L4O2V.js.map} +0 -0
- /package/dist/{revoke-JFN773KF.js.map → registry-IWFMXCHX.js.map} +0 -0
- /package/dist/{signer-TA55QB6R.js.map → registry-Q3FG3OAX.js.map} +0 -0
- /package/dist/{stale-ARHFVL5U.js.map → registry-YC3IFG3A.js.map} +0 -0
- /package/dist/{storage-V7MTFAQJ.js.map → request-withdrawal-NM5VYR6F.js.map} +0 -0
- /package/dist/{reveal-BW36KCOQ.js.map → reveal-XBIMHAXR.js.map} +0 -0
- /package/dist/{ulid-PTAIMDG4.js.map → revoke-MJNYWO72.js.map} +0 -0
- /package/dist/{walk-3ZECBFDA.js.map → signer-6XVS2AU3.js.map} +0 -0
- /package/dist/{withdraw-accessible-7PG4DYQK.js.map → stale-MCS46TFO.js.map} +0 -0
- /package/dist/{verify-RO5RTZOJ.js.map → verify-WYOFLSNA.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/with-audit/attestation/issue.ts"],"sourcesContent":["import type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport { encrypt, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { AttestationError } from '../../kernel/errors.js'\nimport { generateULID } from '../../with-pod/ulid.js'\nimport { loadOrCreateSigner, ATTESTATIONS_COLLECTION } from './signer.js'\nimport {\n computeFieldHashes, signPayloadCore, encodeQr, bytesToB64url,\n type AttestationFieldSchema, type QrPayload,\n} from '@noy-db/attestation'\n\n/** Everything issueAttestationCore needs from the Vault, injected for testability. */\nexport interface IssueContext {\n readonly store: NoydbStore\n readonly vault: string\n readonly role: string\n /** The _attestations collection DEK (AES-KW-wrapped under KEK by the keyring). */\n getDEK(): Promise<EnclaveKey>\n /** Decrypted source record + its envelope version, or null if absent. */\n readRecord(collection: string, id: string): Promise<{ record: Record<string, unknown>; version: number } | null>\n}\n\nexport interface IssueArgs {\n readonly collection: string\n readonly id: string\n readonly fieldSchema: AttestationFieldSchema\n}\nexport interface IssueResult {\n readonly docId: string\n readonly qr: string\n readonly payload: QrPayload\n readonly keyId: string\n readonly publicKeyB64: string\n}\n\nexport async function issueAttestationCore(ctx: IssueContext, args: IssueArgs): Promise<IssueResult> {\n if (ctx.role !== 'owner') {\n throw new AttestationError(`issueAttestation requires the 'owner' role; caller is '${ctx.role}'. Issuing a signed attestation is the firm's identity operation.`)\n }\n const src = await ctx.readRecord(args.collection, args.id)\n if (!src) throw new AttestationError(`issueAttestation: source record '${args.collection}/${args.id}' not found.`)\n\n const dek = await ctx.getDEK()\n // ONE signer implementation, from signer.ts. Lazily minted + persisted.\n const signer = await loadOrCreateSigner(ctx.store, ctx.vault, () => Promise.resolve(dek))\n\n const saltB64 = bytesToB64url(crypto.getRandomValues(new Uint8Array(16)))\n let fieldHashes: string[]\n try {\n fieldHashes = await computeFieldHashes(saltB64, args.fieldSchema, src.record)\n } catch (e) {\n throw new AttestationError(`issueAttestation: ${(e as Error).message}`)\n }\n const docId = generateULID()\n\n const sig = await signPayloadCore({ v: 1, docId, salt: saltB64, keyId: signer.keyId, fieldHashes }, signer.privateKeyPkcs8B64)\n const payload: QrPayload = { v: 1, docId, salt: saltB64, alg: 'ed25519', keyId: signer.keyId, fieldHashes, sig }\n\n const index = {\n docId, issuedAt: new Date().toISOString(), keyId: signer.keyId,\n fieldPaths: args.fieldSchema.fields.map((f) => f.path),\n sourceRefs: [{ collection: args.collection, id: args.id, version: src.version }],\n }\n const { iv, data } = await encrypt(JSON.stringify(index), dek)\n const env: EncryptedEnvelope = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: index.issuedAt, _iv: iv, _data: data }\n await ctx.store.put(ctx.vault, ATTESTATIONS_COLLECTION, docId, env)\n\n return { docId, qr: encodeQr(payload), payload, keyId: signer.keyId, publicKeyB64: signer.publicKeyB64 }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAMA;AAAA,EACE;AAAA,EAAoB;AAAA,EAAiB;AAAA,EAAU;AAAA,OAE1C;AA0BP,eAAsB,qBAAqB,KAAmB,MAAuC;AACnG,MAAI,IAAI,SAAS,SAAS;AACxB,UAAM,IAAI,iBAAiB,0DAA0D,IAAI,IAAI,mEAAmE;AAAA,EAClK;AACA,QAAM,MAAM,MAAM,IAAI,WAAW,KAAK,YAAY,KAAK,EAAE;AACzD,MAAI,CAAC,IAAK,OAAM,IAAI,iBAAiB,oCAAoC,KAAK,UAAU,IAAI,KAAK,EAAE,cAAc;AAEjH,QAAM,MAAM,MAAM,IAAI,OAAO;AAE7B,QAAM,SAAS,MAAM,mBAAmB,IAAI,OAAO,IAAI,OAAO,MAAM,QAAQ,QAAQ,GAAG,CAAC;AAExF,QAAM,UAAU,cAAc,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC,CAAC;AACxE,MAAI;AACJ,MAAI;AACF,kBAAc,MAAM,mBAAmB,SAAS,KAAK,aAAa,IAAI,MAAM;AAAA,EAC9E,SAAS,GAAG;AACV,UAAM,IAAI,iBAAiB,qBAAsB,EAAY,OAAO,EAAE;AAAA,EACxE;AACA,QAAM,QAAQ,aAAa;AAE3B,QAAM,MAAM,MAAM,gBAAgB,EAAE,GAAG,GAAG,OAAO,MAAM,SAAS,OAAO,OAAO,OAAO,YAAY,GAAG,OAAO,kBAAkB;AAC7H,QAAM,UAAqB,EAAE,GAAG,GAAG,OAAO,MAAM,SAAS,KAAK,WAAW,OAAO,OAAO,OAAO,aAAa,IAAI;AAE/G,QAAM,QAAQ;AAAA,IACZ;AAAA,IAAO,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,IAAG,OAAO,OAAO;AAAA,IACzD,YAAY,KAAK,YAAY,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,IACrD,YAAY,CAAC,EAAE,YAAY,KAAK,YAAY,IAAI,KAAK,IAAI,SAAS,IAAI,QAAQ,CAAC;AAAA,EACjF;AACA,QAAM,EAAE,IAAI,KAAK,IAAI,MAAM,QAAQ,KAAK,UAAU,KAAK,GAAG,GAAG;AAC7D,QAAM,MAAyB,EAAE,QAAQ,sBAAsB,IAAI,GAAG,KAAK,MAAM,UAAU,KAAK,IAAI,OAAO,KAAK;AAChH,QAAM,IAAI,MAAM,IAAI,IAAI,OAAO,yBAAyB,OAAO,GAAG;AAElE,SAAO,EAAE,OAAO,IAAI,SAAS,OAAO,GAAG,SAAS,OAAO,OAAO,OAAO,cAAc,OAAO,aAAa;AACzG;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/with-commit/history/ledger/patch.ts","../src/with-commit/history/ledger/store.ts"],"sourcesContent":["/**\n * RFC 6902 JSON Patch — compute + apply.\n *\n * This module is the \"delta history\" primitive: instead of\n * snapshotting the full record on every put (the behavior),\n * `Collection.put` computes a JSON Patch from the previous version to\n * the new version and stores only the patch in the ledger. To\n * reconstruct version N, we walk from the genesis snapshot forward\n * applying patches. Storage scales with **edit size**, not record\n * size — a 10 KB record edited 1000 times costs ~10 KB of deltas\n * instead of ~10 MB of snapshots.\n *\n * ## Why hand-roll instead of using a library?\n *\n * RFC 6902 has good libraries (`fast-json-patch`, `rfc6902`) but every\n * single one of them adds a runtime dependency to `@noy-db/core`. The\n * \"zero runtime dependencies\" promise is one of the core's load-bearing\n * features, and the patch surface we actually need is small enough\n * (~150 LoC) that vendoring is the right call.\n *\n * What we implement:\n * - `add` — insert a value at a path\n * - `remove` — delete the value at a path\n * - `replace` — overwrite the value at a path\n *\n * What we deliberately skip (out of scope for the ledger use):\n * - `move` and `copy` — optimizations; the diff algorithm doesn't\n * emit them, so the apply path doesn't need them\n * - `test` — used for transactional patches; we already have\n * optimistic concurrency via `_v` at the envelope layer\n * - Sophisticated array diffing (LCS, edit distance) — we treat\n * arrays as atomic values and emit a single `replace` op when\n * they differ. The accounting domain has small arrays where this\n * is fine; if we ever need patch-level array diffing we can add\n * it without changing the storage format.\n *\n * ## Path encoding (RFC 6902 §3)\n *\n * Paths look like `/foo/bar/0`. Each path segment is either an object\n * key or a numeric array index. Two characters need escaping inside\n * keys: `~` becomes `~0` and `/` becomes `~1`. We implement both.\n *\n * Empty path (`\"\"`) refers to the root document. Only `replace` makes\n * sense at the root, and our diff function emits it as a top-level\n * `replace` when `prev` and `next` differ in shape (object vs array,\n * primitive vs object, etc.).\n */\n\n/** A single JSON Patch operation. Subset of RFC 6902 — see file docstring. */\nexport type JsonPatchOp =\n | { readonly op: 'add'; readonly path: string; readonly value: unknown }\n | { readonly op: 'remove'; readonly path: string }\n | { readonly op: 'replace'; readonly path: string; readonly value: unknown }\n\n/** A complete JSON Patch document — an array of operations. */\nexport type JsonPatch = readonly JsonPatchOp[]\n\n// ─── Compute (diff) ──────────────────────────────────────────────────\n\n/**\n * Compute a JSON Patch that, when applied to `prev`, produces `next`.\n *\n * The algorithm is a straightforward recursive object walk:\n *\n * 1. If both inputs are plain objects (and not arrays/null):\n * - For each key in `prev`, recurse if `next` has it, else emit `remove`\n * - For each key in `next` not in `prev`, emit `add`\n * 2. If both inputs are arrays AND structurally equal, no-op.\n * Otherwise emit a single `replace` for the whole array.\n * 3. If both inputs are deeply equal primitives, no-op.\n * 4. Otherwise emit a `replace` at the current path.\n *\n * We do not minimize patches across move-like rearrangements — every\n * generated patch is straightforward enough to apply by hand if you\n * had to debug it.\n */\nexport function computePatch(prev: unknown, next: unknown): JsonPatch {\n const ops: JsonPatchOp[] = []\n diff(prev, next, '', ops)\n return ops\n}\n\nfunction diff(\n prev: unknown,\n next: unknown,\n path: string,\n out: JsonPatchOp[],\n): void {\n // Both null / both undefined → no-op (we don't differentiate them\n // in JSON terms; canonicalJson would reject undefined anyway).\n if (prev === next) return\n\n // One side null, the other not → straight replace.\n if (prev === null || next === null) {\n out.push({ op: 'replace', path, value: next })\n return\n }\n\n const prevIsArray = Array.isArray(prev)\n const nextIsArray = Array.isArray(next)\n const prevIsObject = typeof prev === 'object' && !prevIsArray\n const nextIsObject = typeof next === 'object' && !nextIsArray\n\n // Type changed (e.g., object → primitive, array → object). Replace.\n if (prevIsArray !== nextIsArray || prevIsObject !== nextIsObject) {\n out.push({ op: 'replace', path, value: next })\n return\n }\n\n // Both arrays. We don't do clever LCS-based diffing — emit a single\n // replace for the whole array if they differ. See file docstring for\n // the rationale.\n if (prevIsArray && nextIsArray) {\n if (!arrayDeepEqual(prev as unknown[], next as unknown[])) {\n out.push({ op: 'replace', path, value: next })\n }\n return\n }\n\n // Both plain objects. Recurse key by key.\n if (prevIsObject && nextIsObject) {\n const prevObj = prev as Record<string, unknown>\n const nextObj = next as Record<string, unknown>\n const prevKeys = Object.keys(prevObj)\n const nextKeys = Object.keys(nextObj)\n\n // Handle removes and overlapping recursions in one pass over prev.\n for (const key of prevKeys) {\n const childPath = path + '/' + escapePathSegment(key)\n if (!(key in nextObj)) {\n out.push({ op: 'remove', path: childPath })\n } else {\n diff(prevObj[key], nextObj[key], childPath, out)\n }\n }\n // Handle adds.\n for (const key of nextKeys) {\n if (!(key in prevObj)) {\n out.push({\n op: 'add',\n path: path + '/' + escapePathSegment(key),\n value: nextObj[key],\n })\n }\n }\n return\n }\n\n // Two primitives that aren't strictly equal — replace.\n out.push({ op: 'replace', path, value: next })\n}\n\nfunction arrayDeepEqual(a: unknown[], b: unknown[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) {\n if (!deepEqual(a[i], b[i])) return false\n }\n return true\n}\n\nfunction deepEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true\n if (a === null || b === null) return false\n if (typeof a !== typeof b) return false\n if (typeof a !== 'object') return false\n const aArray = Array.isArray(a)\n const bArray = Array.isArray(b)\n if (aArray !== bArray) return false\n if (aArray && bArray) return arrayDeepEqual(a, b as unknown[])\n const aObj = a as Record<string, unknown>\n const bObj = b as Record<string, unknown>\n const aKeys = Object.keys(aObj)\n const bKeys = Object.keys(bObj)\n if (aKeys.length !== bKeys.length) return false\n for (const key of aKeys) {\n if (!(key in bObj)) return false\n if (!deepEqual(aObj[key], bObj[key])) return false\n }\n return true\n}\n\n// ─── Apply ──────────────────────────────────────────────────────────\n\n/**\n * Apply a JSON Patch to a base document and return the result.\n *\n * The base document is **not mutated** — every op clones the parent\n * container before writing to it, so the caller's reference to `base`\n * stays untouched. This costs an extra allocation per op but makes\n * the apply pipeline reorderable and safe to interrupt.\n *\n * Throws on:\n * - Removing a path that doesn't exist\n * - Adding to a path whose parent doesn't exist\n * - A path component that doesn't match the document shape (e.g.,\n * trying to step into a primitive)\n *\n * Throwing is the right behavior for the ledger use case: a failed\n * apply means the chain is corrupted, which should be loud rather\n * than silently producing a wrong reconstruction.\n */\nexport function applyPatch<T = unknown>(base: T, patch: JsonPatch): T {\n let result: unknown = clone(base)\n for (const op of patch) {\n result = applyOp(result, op)\n }\n return result as T\n}\n\nfunction applyOp(doc: unknown, op: JsonPatchOp): unknown {\n // Empty path → operation targets the root. Only `replace` and `add`\n // make sense at the root, but we handle `remove` for completeness\n // (root removal returns null).\n if (op.path === '') {\n if (op.op === 'remove') return null\n return clone(op.value)\n }\n\n const segments = parsePath(op.path)\n return walkAndApply(doc, segments, op)\n}\n\nfunction walkAndApply(\n doc: unknown,\n segments: string[],\n op: JsonPatchOp,\n): unknown {\n if (segments.length === 0) {\n // Should never happen — empty path is handled in applyOp().\n throw new Error('walkAndApply: empty segments (internal error)')\n }\n\n const [head, ...rest] = segments\n if (head === undefined) throw new Error('walkAndApply: undefined segment')\n\n if (rest.length === 0) {\n return applyAtTerminal(doc, head, op)\n }\n\n // Recurse into the child container, then rebuild the parent with\n // the modified child.\n if (Array.isArray(doc)) {\n const idx = parseArrayIndex(head, doc.length)\n const child = doc[idx]\n const newChild = walkAndApply(child, rest, op)\n const next = doc.slice()\n next[idx] = newChild\n return next\n }\n if (doc !== null && typeof doc === 'object') {\n const obj = doc as Record<string, unknown>\n if (!(head in obj)) {\n throw new Error(`applyPatch: path segment \"${head}\" not found in object`)\n }\n const newChild = walkAndApply(obj[head], rest, op)\n return { ...obj, [head]: newChild }\n }\n throw new Error(\n `applyPatch: cannot step into ${typeof doc} at segment \"${head}\"`,\n )\n}\n\nfunction applyAtTerminal(\n doc: unknown,\n segment: string,\n op: JsonPatchOp,\n): unknown {\n if (Array.isArray(doc)) {\n const idx =\n segment === '-' ? doc.length : parseArrayIndex(segment, doc.length + 1)\n const next = doc.slice()\n if (op.op === 'remove') {\n next.splice(idx, 1)\n return next\n }\n if (op.op === 'add') {\n next.splice(idx, 0, clone(op.value))\n return next\n }\n if (op.op === 'replace') {\n if (idx >= doc.length) {\n throw new Error(\n `applyPatch: replace at out-of-bounds array index ${idx}`,\n )\n }\n next[idx] = clone(op.value)\n return next\n }\n }\n if (doc !== null && typeof doc === 'object') {\n const obj = doc as Record<string, unknown>\n if (op.op === 'remove') {\n if (!(segment in obj)) {\n throw new Error(\n `applyPatch: remove on missing key \"${segment}\"`,\n )\n }\n const next = { ...obj }\n delete next[segment]\n return next\n }\n if (op.op === 'add') {\n // RFC 6902: `add` on an existing key replaces it.\n return { ...obj, [segment]: clone(op.value) }\n }\n if (op.op === 'replace') {\n if (!(segment in obj)) {\n throw new Error(\n `applyPatch: replace on missing key \"${segment}\"`,\n )\n }\n return { ...obj, [segment]: clone(op.value) }\n }\n }\n throw new Error(\n `applyPatch: cannot apply ${op.op} at terminal segment \"${segment}\"`,\n )\n}\n\n// ─── Path encoding (RFC 6902 §3) ─────────────────────────────────────\n\n/**\n * Escape a single path segment per RFC 6902 §3:\n * `~` → `~0`\n * `/` → `~1`\n *\n * Order matters: `~` must be escaped first, otherwise the `~1` we\n * just emitted would be re-escaped to `~01`.\n */\nfunction escapePathSegment(segment: string): string {\n return segment.replace(/~/g, '~0').replace(/\\//g, '~1')\n}\n\nfunction unescapePathSegment(segment: string): string {\n return segment.replace(/~1/g, '/').replace(/~0/g, '~')\n}\n\nfunction parsePath(path: string): string[] {\n if (!path.startsWith('/')) {\n throw new Error(`applyPatch: path must start with '/', got \"${path}\"`)\n }\n return path\n .slice(1)\n .split('/')\n .map(unescapePathSegment)\n}\n\nfunction parseArrayIndex(segment: string, max: number): number {\n if (!/^\\d+$/.test(segment)) {\n throw new Error(\n `applyPatch: array index must be a non-negative integer, got \"${segment}\"`,\n )\n }\n const idx = Number.parseInt(segment, 10)\n if (idx < 0 || idx > max) {\n throw new Error(\n `applyPatch: array index ${idx} out of range [0, ${max}]`,\n )\n }\n return idx\n}\n\n// ─── Cheap structural clone ─────────────────────────────────────────\n\n/**\n * Plain-JSON clone via JSON.parse(JSON.stringify(value)).\n *\n * Faster than `structuredClone` for our use because (a) we know our\n * inputs are JSON-compatible (no Dates, Maps, or BigInts — anything\n * else gets rejected by canonicalJson upstream), and (b) `structuredClone`\n * has overhead for handling arbitrary structured data we don't need.\n *\n * For tiny ledger entries (< 1 KB), the JSON round-trip is in the\n * single-digit microsecond range.\n */\nfunction clone<T>(value: T): T {\n if (value === null || value === undefined) return value\n if (typeof value !== 'object') return value\n return JSON.parse(JSON.stringify(value)) as T\n}\n","/**\n * `LedgerStore` — read/write access to a compartment's hash-chained\n * audit log.\n *\n * The store is a thin wrapper around the adapter's `_ledger/` internal\n * collection. Every append:\n *\n * 1. Loads the current head (or treats an empty ledger as head = -1)\n * 2. Computes `prevHash` = sha256(canonicalJson(head))\n * 3. Builds the new entry with `index = head.index + 1`\n * 4. Encrypts the entry with the compartment's ledger DEK\n * 5. Writes the encrypted envelope to `_ledger/<paddedIndex>`\n *\n * `verify()` walks the chain from genesis forward and returns\n * `{ ok: true, head }` on success or `{ ok: false, divergedAt }` on the\n * first broken link.\n *\n * ## Thread / concurrency model\n *\n * For we assume a **single writer per vault**. Two\n * concurrent `append()` calls would race on the \"read head, write\n * head+1\" cycle and could produce a broken chain. The sync engine\n * is the primary concurrent-writer scenario, and it uses\n * optimistic-concurrency via `expectedVersion` on the adapter — but\n * the ledger path has no such guard today. Multi-writer hardening is a\n * follow-up.\n *\n * Single-writer usage IS safe, including across process restarts:\n * `head()` reads the adapter fresh each call, so a crash between the\n * adapter.put of a data record and the ledger append just means the\n * ledger is missing an entry for that record. `verify()` still\n * succeeds; a future `verifyIntegrity()` helper can cross-check the\n * ledger against the data collections to catch the gap.\n *\n * ## Why hide the ledger from `vault.collection()`?\n *\n * The `_ledger` name starts with `_`, matching the existing prefix\n * convention for internal collections (`_keyring`, `_sync`,\n * `_history`). The Vault's public `collection()` method already\n * returns entries for any name, but `loadAll()` filters out\n * underscore-prefixed collections so backups and exports don't leak\n * ledger metadata. We keep the ledger accessible ONLY via\n * `vault.ledger()` to enforce the hash-chain invariants — direct\n * puts via `collection('_ledger')` would bypass the `append()` logic.\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../../kernel/types.js'\nimport { encrypt, openEnvelopeJson, type EnclaveKey } from '../../../kernel/enclave/index.js'\nimport type { ConflictError} from '../../../kernel/errors.js';\nimport { isConflictError, LedgerContentionError } from '../../../kernel/errors.js'\nimport {\n canonicalJson,\n hashEntry,\n paddedIndex,\n sha256Hex,\n type LedgerEntry,\n} from './entry.js'\nimport type { JsonPatch } from './patch.js'\nimport { applyPatch } from './patch.js'\nimport { LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION } from './constants.js'\nimport { envelopePayloadHash } from './hash.js'\n\n/**\n * Maximum optimistic-CAS retries on the ledger head. Each failed\n * attempt invalidates the head cache, re-reads, and retries with a\n * fresh next-index. After N failures we surface\n * `LedgerContentionError` so the caller can decide whether to retry,\n * queue, or alert.\n */\nconst MAX_APPEND_ATTEMPTS = 8\n\n// — re-export the constants + helper so any existing\n// `import { LEDGER_COLLECTION } from '...store.js'` paths keep\n// working. Internal core paths (vault.ts) import from the leaf\n// modules directly to avoid pulling this file's class into the\n// floor bundle.\nexport { LEDGER_COLLECTION, LEDGER_DELTAS_COLLECTION, envelopePayloadHash }\n\n/**\n * Input shape for `LedgerStore.append()`. The caller supplies the\n * operation metadata; the store fills in `index` and `prevHash`.\n */\nexport interface AppendInput {\n op: LedgerEntry['op']\n collection: string\n id: string\n version: number\n actor: string\n payloadHash: string\n /**\n * Optional JSON Patch representing the delta from the previous\n * version to the new version. Present only for `put` operations\n * that had a previous version; omitted for genesis puts and for\n * deletes. When present, `LedgerStore.append` persists the patch\n * in `_ledger_deltas/<paddedIndex>` and records its sha256 hash\n * as the entry's `deltaHash` field.\n */\n delta?: JsonPatch\n /**\n * Present only for `op === 'amendment'` — structured audit\n * payload for multi-record repair operations performed via\n * `withTransactions(...)`. Carried through verbatim to the\n * resulting ledger entry.\n */\n amendment?: LedgerEntry['amendment']\n /**\n * Optional human-readable tag describing why this mutation happened.\n * Threaded from `collection.put(_, _, { reason })`.\n * Carried verbatim onto the resulting ledger entry's `reason` field;\n * omitted from canonical JSON when undefined.\n */\n reason?: string\n}\n\n/**\n * Result of `LedgerStore.verify()`. On success, `head` is the hash of\n * the last entry — the same value that should be published to any\n * external anchoring service (blockchain, OpenTimestamps, etc.). On\n * failure, `divergedAt` is the 0-based index of the first entry whose\n * recorded `prevHash` does not match the recomputed hash of its\n * predecessor. Entries at `divergedAt` and later are untrustworthy;\n * entries before that index are still valid.\n */\nexport type VerifyResult =\n | { readonly ok: true; readonly head: string; readonly length: number }\n | {\n readonly ok: false\n readonly divergedAt: number\n readonly expected: string\n readonly actual: string\n }\n\n/**\n * A LedgerStore is bound to a single vault. Callers obtain one\n * via `vault.ledger()` — there is no public constructor to keep\n * the hash-chain invariants in one place.\n *\n * The class holds no mutable state beyond its dependencies (adapter,\n * vault name, DEK resolver, actor id). Every method reads the\n * adapter fresh so multiple instances against the same vault\n * see each other's writes immediately (at the cost of re-parsing the\n * ledger on every head() / verify() call; acceptable at scale).\n */\nexport class LedgerStore {\n private readonly adapter: NoydbStore\n private readonly vault: string\n private readonly encrypted: boolean\n private readonly getDEK: (collectionName: string) => Promise<EnclaveKey>\n private readonly actor: string\n\n /**\n * In-memory cache of the chain head — the most recently appended\n * entry along with its precomputed hash. Without this, every\n * `append()` would re-load every prior entry to recompute the\n * prevHash, making N puts O(N²) — a 1K-record stress test goes from\n * < 100ms to a multi-second timeout.\n *\n * The cache is populated on first read (`append`, `head`, `verify`)\n * and updated in-place on every successful `append`. Single-writer\n * usage (the assumption) keeps it consistent. A second\n * LedgerStore instance writing to the same vault would not\n * see the first instance's appends in its cached state — that's the\n * concurrency caveat documented at the class level.\n *\n * Sentinel `undefined` means \"not yet loaded\"; an explicit `null`\n * value means \"loaded and confirmed empty\" — distinguishing these\n * matters because an empty ledger is a valid state (genesis prevHash\n * is the empty string), and we don't want to re-scan the adapter\n * just because the chain is freshly initialized.\n */\n private headCache: { entry: LedgerEntry; hash: string } | null | undefined = undefined\n\n constructor(opts: {\n adapter: NoydbStore\n vault: string\n encrypted: boolean\n getDEK: (collectionName: string) => Promise<EnclaveKey>\n actor: string\n }) {\n this.adapter = opts.adapter\n this.vault = opts.vault\n this.encrypted = opts.encrypted\n this.getDEK = opts.getDEK\n this.actor = opts.actor\n }\n\n /**\n * Lazily load (or return cached) the current chain head. The cache\n * sentinel is `undefined` until first access; after the first call,\n * the cache holds either a `{ entry, hash }` for non-empty ledgers\n * or `null` for empty ones.\n */\n private async getCachedHead(): Promise<{ entry: LedgerEntry; hash: string } | null> {\n if (this.headCache !== undefined) return this.headCache\n const entries = await this.loadAllEntries()\n const last = entries[entries.length - 1]\n if (!last) {\n this.headCache = null\n return null\n }\n this.headCache = { entry: last, hash: await hashEntry(last) }\n return this.headCache\n }\n\n /**\n * Append a new entry to the ledger. Returns the full entry that was\n * written (with its assigned index and computed prevHash) so the\n * caller can use the hash for downstream purposes (e.g., embedding\n * in a verifiable backup).\n *\n * This is the **only** way to add entries. Direct adapter writes to\n * `_ledger/` would bypass the chain math and would be caught by the\n * next `verify()` call as a divergence.\n *\n * ## Multi-writer correctness\n *\n * Append is implemented as an optimistic-CAS retry loop. On every\n * attempt:\n *\n * 1. Read fresh head (cache invalidated on retry).\n * 2. Compute `nextIndex = head.index + 1`, `prevHash = hash(head)`.\n * 3. Encrypt delta payload IN MEMORY (no adapter write yet) so we\n * can compute `deltaHash` before claiming the chain slot.\n * 4. Build + encrypt the entry envelope.\n * 5. `adapter.put(_ledger, paddedIndex, envelope, expectedVersion: 0)`\n * — the `expectedVersion: 0` asserts \"this slot must not exist.\"\n * Stores with `casAtomic: true` honor the CAS check; under\n * contention the second writer's put throws `ConflictError`.\n * 6. On `ConflictError`: invalidate the head cache, sleep with\n * bounded backoff + jitter, retry. After `MAX_APPEND_ATTEMPTS`\n * retries throw {@link LedgerContentionError}.\n * 7. On success: write the delta envelope (if any) at the same\n * index. Update the head cache.\n *\n * Entry-first ordering matters: writing the delta first under\n * contention would orphan delta records at indices the writer never\n * actually claimed. The deltaHash is computed off the encrypted\n * envelope's `_data` field, which doesn't require the envelope to\n * be persisted.\n *\n * Stores with `casAtomic: false` (file, s3, r2 by default) silently\n * accept the `expectedVersion: 0` argument and proceed without a\n * CAS check. Concurrent appends against those stores remain\n * best-effort — pair them with an advisory lock or with sync\n * single-writer discipline.\n */\n async append(input: AppendInput): Promise<LedgerEntry> {\n let lastConflict: ConflictError | undefined\n for (let attempt = 0; attempt < MAX_APPEND_ATTEMPTS; attempt++) {\n // Force a fresh head read on every retry. The first attempt may\n // hit the cache; subsequent attempts must re-scan the adapter\n // because the prior conflict means our cached state is stale.\n if (attempt > 0) {\n this.headCache = undefined\n }\n try {\n return await this.appendOnce(input)\n } catch (err) {\n if (isConflictError(err)) {\n lastConflict = err\n if (attempt < MAX_APPEND_ATTEMPTS - 1) {\n await sleepBackoff(attempt)\n }\n continue\n }\n throw err\n }\n }\n void lastConflict\n throw new LedgerContentionError(MAX_APPEND_ATTEMPTS)\n }\n\n /**\n * One attempt at the append cycle. Throws `ConflictError` when the\n * CAS check on the entry put fails — `append()` catches that and\n * retries. Any other error propagates to the caller.\n */\n private async appendOnce(input: AppendInput): Promise<LedgerEntry> {\n const cached = await this.getCachedHead()\n const lastEntry = cached?.entry\n const prevHash = cached?.hash ?? ''\n const nextIndex = lastEntry ? lastEntry.index + 1 : 0\n\n // Encrypt the delta in memory so we can compute deltaHash WITHOUT\n // claiming the deltas slot yet — entry-put is the chain claim.\n let deltaEnvelope: EncryptedEnvelope | undefined\n let deltaHash: string | undefined\n if (input.delta !== undefined) {\n deltaEnvelope = await this.encryptDelta(input.delta)\n deltaHash = await sha256Hex(deltaEnvelope._data)\n }\n\n // Build the entry. Conditionally include `deltaHash` so\n // canonicalJson (which rejects undefined) never sees it when\n // there's no delta.\n const entryBase = {\n index: nextIndex,\n prevHash,\n op: input.op,\n collection: input.collection,\n id: input.id,\n version: input.version,\n ts: new Date().toISOString(),\n actor: input.actor === '' ? this.actor : input.actor,\n payloadHash: input.payloadHash,\n } as const\n const entry: LedgerEntry = {\n ...entryBase,\n ...(deltaHash !== undefined ? { deltaHash } : {}),\n ...(input.amendment !== undefined ? { amendment: input.amendment } : {}),\n ...(input.reason !== undefined ? { reason: input.reason } : {}),\n }\n\n const envelope = await this.encryptEntry(entry)\n // expectedVersion: 0 ≡ \"the slot must not yet exist.\" Honored by\n // casAtomic stores; silently passed through by non-CAS stores.\n await this.adapter.put(\n this.vault,\n LEDGER_COLLECTION,\n paddedIndex(entry.index),\n envelope,\n 0,\n )\n\n // Chain slot claimed. Now write the delta record (if any).\n if (deltaEnvelope) {\n await this.adapter.put(\n this.vault,\n LEDGER_DELTAS_COLLECTION,\n paddedIndex(entry.index),\n deltaEnvelope,\n 0,\n )\n }\n\n // Update the head cache so the next append() doesn't re-scan the\n // adapter.\n this.headCache = { entry, hash: await hashEntry(entry) }\n return entry\n }\n\n /**\n * Load a delta payload by its entry index. Returns `null` if the\n * entry at that index doesn't reference a delta (genesis puts and\n * deletes leave the slot empty) or if the delta row is missing\n * (possible after a `pruneHistory` fold).\n *\n * The caller is responsible for deciding what to do with a missing\n * delta — `ledger.reconstruct()` uses it as a \"stop walking\n * backward\" signal and falls back to the on-disk current value.\n */\n async loadDelta(index: number): Promise<JsonPatch | null> {\n const envelope = await this.adapter.get(\n this.vault,\n LEDGER_DELTAS_COLLECTION,\n paddedIndex(index),\n )\n if (!envelope) return null\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as JsonPatch\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const json = await openEnvelopeJson(envelope, dek)\n return JSON.parse(json) as JsonPatch\n }\n\n /**\n * Purge a record's tier-0-era plaintext deltas (#729). Deletes each\n * `_ledger_deltas/<paddedIndex>` row whose entry matches\n * `(collection, id)` and carries a delta, then returns the count\n * deleted.\n *\n * Chain-safe by construction: `verify()` recomputes the tamper-chain\n * from each `_ledger` entry's own canonical fields — including the\n * stored `deltaHash` field, which lives on the entry, not on the\n * `_ledger_deltas` row — and never re-reads `_ledger_deltas`.\n * Deleting a delta row therefore cannot break `verify()`.\n * `reconstruct()` already treats a missing delta (`loadDelta` →\n * `null`) as a pruned stop, so a purge just makes the pre-purge\n * plaintext unreachable rather than corrupting anything.\n *\n * No `_ledger` entry is touched and nothing is re-encrypted — this\n * is irreversible (the deleted plaintext cannot be recovered) but\n * leaves the entry metadata (that the record was mutated, at which\n * version/timestamp/actor) fully intact for audit purposes.\n */\n async purgeRecordDeltas(collection: string, id: string): Promise<number> {\n const entries = await this.loadAllEntries()\n let purged = 0\n for (const entry of entries) {\n if (entry.collection !== collection || entry.id !== id) continue\n if (entry.deltaHash === undefined) continue\n const key = paddedIndex(entry.index)\n // adapter.delete() is a void no-op on an already-missing key, so\n // check for the row's presence first — the count must reflect\n // rows actually removed (idempotent repeat purges return 0), not\n // the number of delta-bearing entries (which never changes).\n const existing = await this.adapter.get(this.vault, LEDGER_DELTAS_COLLECTION, key)\n if (!existing) continue\n await this.adapter.delete(this.vault, LEDGER_DELTAS_COLLECTION, key)\n purged++\n }\n return purged\n }\n\n /** Encrypt a JSON Patch into an envelope for storage. Mirrors encryptEntry. */\n private async encryptDelta(patch: JsonPatch): Promise<EncryptedEnvelope> {\n const json = JSON.stringify(patch)\n if (!this.encrypted) {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: json,\n _by: this.actor,\n }\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: iv,\n _data: data,\n _by: this.actor,\n }\n }\n\n /**\n * Read all entries in ascending-index order. Used internally by\n * `append()`, `head()`, `verify()`, and `entries()`. Decryption is\n * serial because the entries are tiny and the overhead of a Promise\n * pool would dominate at realistic chain lengths (< 100K entries).\n */\n async loadAllEntries(): Promise<LedgerEntry[]> {\n const keys = await this.adapter.list(this.vault, LEDGER_COLLECTION)\n // Sort lexicographically, which matches numeric order because\n // keys are zero-padded to 10 digits.\n keys.sort()\n const entries: LedgerEntry[] = []\n for (const key of keys) {\n const envelope = await this.adapter.get(\n this.vault,\n LEDGER_COLLECTION,\n key,\n )\n if (!envelope) continue\n entries.push(await this.decryptEntry(envelope))\n }\n return entries\n }\n\n /**\n * Return the current head of the ledger: the last entry, its hash,\n * and the total chain length. `null` on an empty ledger so callers\n * can distinguish \"no history yet\" from \"empty history\".\n */\n async head(): Promise<\n | { readonly entry: LedgerEntry; readonly hash: string; readonly length: number }\n | null\n > {\n const cached = await this.getCachedHead()\n if (!cached) return null\n // `length` is `entry.index + 1` because indices are zero-based and\n // contiguous. We don't need to re-scan the adapter to compute it.\n return {\n entry: cached.entry,\n hash: cached.hash,\n length: cached.entry.index + 1,\n }\n }\n\n /**\n * Return entries in the requested half-open range `[from, to)`.\n * Defaults: `from = 0`, `to = length`. The indices are clipped to\n * the valid range; no error is thrown for out-of-range queries.\n */\n async entries(opts: { from?: number; to?: number } = {}): Promise<LedgerEntry[]> {\n const all = await this.loadAllEntries()\n const from = Math.max(0, opts.from ?? 0)\n const to = Math.min(all.length, opts.to ?? all.length)\n return all.slice(from, to)\n }\n\n /**\n * Reconstruct a record's state at a given historical version by\n * walking the ledger's delta chain backward from the current state.\n *\n * ## Algorithm\n *\n * Ledger deltas are stored in **reverse** form — each entry's\n * patch describes how to undo that put, transforming the new\n * record back into the previous one. `reconstruct` exploits this\n * by:\n *\n * 1. Finding every ledger entry for `(collection, id)` in the\n * chain, sorted by index ascending.\n * 2. Starting from `current` (the present value of the record,\n * as held by the caller — typically fetched via\n * `Collection.get()`).\n * 3. Walking entries in **descending** index order and applying\n * each entry's reverse patch, stopping when we reach the\n * entry whose version equals `atVersion`.\n *\n * The result is the record as it existed immediately AFTER the\n * put at `atVersion`. To get the state at the genesis put\n * (version 1), the walk runs all the way back through every put\n * after the first.\n *\n * ## Caveats\n *\n * - **Delete entries** break the walk: once we see a delete, the\n * record didn't exist before that point, so there's nothing to\n * reconstruct. We return `null` in that case.\n * - **Missing deltas** (e.g., after `pruneHistory` folds old\n * entries into a base snapshot) also stop the walk. does\n * not ship pruneHistory, so today this only happens if an entry\n * was deleted out-of-band.\n * - The caller MUST pass the correct current value. Passing a\n * mutated object would corrupt the reconstruction — the patch\n * chain is only valid against the exact state that was in\n * effect when the most recent put happened.\n *\n * For, `reconstruct` is the only way to read a historical\n * version via deltas. The legacy `_history` collection still\n * holds full snapshots and `Collection.getVersion()` still reads\n * from there — the two paths coexist until pruneHistory lands in\n * a follow-up and delta becomes the default.\n */\n async reconstruct<T>(\n collection: string,\n id: string,\n current: T,\n atVersion: number,\n ): Promise<T | null> {\n const all = await this.loadAllEntries()\n // Filter to entries for this (collection, id), in ascending index.\n const matching = all.filter(\n (e) => e.collection === collection && e.id === id,\n )\n if (matching.length === 0) {\n // No ledger history at all; the current state IS version 1\n // (or there's nothing), so the only valid atVersion is the\n // current record's version. We can't verify that here, so\n // return current if atVersion is plausible, null otherwise.\n return null\n }\n\n // Walk entries in descending index order, applying each reverse\n // delta until we reach the target version.\n let state: T | null = current\n for (let i = matching.length - 1; i >= 0; i--) {\n const entry = matching[i]\n if (!entry) continue\n\n // Defensive: skip every non-put/non-delete op variant. The\n // outer filter on `e.collection === collection && e.id === id`\n // already excludes `amendment` entries (their collection/id are\n // empty strings), but a top-of-loop guard keeps the walker\n // robust if a future op variant slips through the filter.\n if (entry.op !== 'put' && entry.op !== 'delete') continue\n\n // Match check FIRST — before applying this entry's reverse\n // patch. `state` at this point is the record state immediately\n // after this entry's put (or before this entry's delete), so\n // if the caller asked for this exact version, we're done.\n if (entry.version === atVersion && entry.op !== 'delete') {\n return state\n }\n\n if (entry.op === 'delete') {\n // A delete erases the live state. If the caller asks for a\n // version older than the delete we should continue walking\n // (state becomes null and the next put resets it). But we\n // can't reconstruct that pre-delete state from the current\n // in-memory `state` — the delete has no reverse patch. So\n // anything past this point is unreachable; return null.\n return null\n }\n\n if (entry.deltaHash === undefined) {\n // Genesis put — the earliest state for this lifecycle. We\n // can't walk further back. If the caller asked for exactly\n // this version, return the current state (we already failed\n // the match check above because a fresh genesis after a\n // delete can have version === atVersion). Otherwise the\n // target is unreachable from here.\n if (entry.version === atVersion) return state\n return null\n }\n\n const patch = await this.loadDelta(entry.index)\n if (!patch) {\n // Delta row is missing (probably pruned). Stop walking.\n return null\n }\n\n if (state === null) {\n // We're trying to walk back across a delete range and there's\n // nothing to apply a reverse patch to. Bail.\n return null\n }\n\n state = applyPatch(state, patch)\n }\n\n // Ran off the end of the walk without matching. The target\n // version doesn't exist in this record's chain.\n return null\n }\n\n /**\n * Walk the chain from genesis forward and verify every link.\n *\n * Returns `{ ok: true, head, length }` if every entry's `prevHash`\n * matches the recomputed hash of its predecessor (and the genesis\n * entry's `prevHash` is the empty string).\n *\n * Returns `{ ok: false, divergedAt, expected, actual }` on the first\n * mismatch. `divergedAt` is the 0-based index of the BROKEN entry\n * — entries before that index still verify cleanly; entries at and\n * after `divergedAt` are untrustworthy.\n *\n * This method detects:\n * - Mutated entry content (fields changed)\n * - Reordered entries (if any adjacent pair swaps, the prevHash\n * of the second no longer matches)\n * - Inserted entries (the inserted entry's prevHash likely fails,\n * and the following entry's prevHash definitely fails)\n * - Deleted entries (the entry after the deletion sees a wrong\n * prevHash)\n *\n * It does NOT detect:\n * - Tampering with the DATA collections that bypassed the ledger\n * entirely (e.g., an attacker who modifies records without\n * appending matching ledger entries — this is why we also\n * plan a `verifyIntegrity()` helper in a follow-up)\n * - Truncation of the chain at the tail (dropping the last N\n * entries leaves a shorter but still consistent chain). External\n * anchoring of `head.hash` to a trusted service is the defense\n * against this.\n */\n async verify(): Promise<VerifyResult> {\n const entries = await this.loadAllEntries()\n let expectedPrevHash = ''\n for (let i = 0; i < entries.length; i++) {\n const entry = entries[i]\n if (!entry) continue\n if (entry.prevHash !== expectedPrevHash) {\n return {\n ok: false,\n divergedAt: i,\n expected: expectedPrevHash,\n actual: entry.prevHash,\n }\n }\n if (entry.index !== i) {\n // An entry whose stored index doesn't match its position in\n // the sorted list means someone rewrote the adapter keys.\n // Treat as divergence.\n return {\n ok: false,\n divergedAt: i,\n expected: `index=${i}`,\n actual: `index=${entry.index}`,\n }\n }\n expectedPrevHash = await hashEntry(entry)\n }\n return {\n ok: true,\n head: expectedPrevHash,\n length: entries.length,\n }\n }\n\n // ─── Encryption plumbing ─────────────────────────────────────────\n\n /**\n * Serialize + encrypt a ledger entry into an EncryptedEnvelope. The\n * envelope's `_v` field is set to `entry.index + 1` so the usual\n * optimistic-concurrency machinery has a reasonable version number\n * to compare against (the ledger is append-only, so concurrent\n * writes should always bump the index).\n */\n private async encryptEntry(entry: LedgerEntry): Promise<EncryptedEnvelope> {\n const json = canonicalJson(entry)\n if (!this.encrypted) {\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: entry.index + 1,\n _ts: entry.ts,\n _iv: '',\n _data: json,\n _by: entry.actor,\n }\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n return {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: entry.index + 1,\n _ts: entry.ts,\n _iv: iv,\n _data: data,\n _by: entry.actor,\n }\n }\n\n /** Decrypt an envelope into a LedgerEntry. Throws on bad key / tamper. */\n private async decryptEntry(envelope: EncryptedEnvelope): Promise<LedgerEntry> {\n if (!this.encrypted) {\n return JSON.parse(envelope._data) as LedgerEntry\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const json = await openEnvelopeJson(envelope, dek)\n return JSON.parse(json) as LedgerEntry\n }\n}\n\n// `envelopePayloadHash` lives in `./hash.ts` so it can be\n// imported by core code without dragging this file's `LedgerStore`\n// class into the floor bundle. The re-export at the top of this\n// file keeps the `import { envelopePayloadHash } from '.../store.js'`\n// path working.\n\n/**\n * Exponential backoff with jitter for the append CAS retry loop.\n * Attempt 0 → ~5–10 ms, attempt 7 → ~640–1280 ms. Jitter avoids the\n * thundering-herd problem when multiple writers collide repeatedly.\n */\nfunction sleepBackoff(attempt: number): Promise<void> {\n const base = 5 * Math.pow(2, attempt)\n const jitter = Math.random() * base\n return new Promise((resolve) => setTimeout(resolve, base + jitter))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/via/classified/descriptor.ts","../src/via/classified/errors.ts","../src/via/classified/resolve.ts","../src/via/classified/write.ts","../src/via/classified/guards.ts","../src/via/classified/binding.ts","../src/port/with/classified-strategy.ts"],"sourcesContent":["/**\n * Classified fields — behavioral sensitive-field descriptors (stage 1).\n * Design: design-history/2026-07-04-classified-fields-design.md\n * Law: open on write, declarative on read — read-side behavior is this\n * closed vocabulary; there are no read-side callbacks.\n * @module\n */\n\nexport type ClassifiedStorage = 'recoverable' | 'never' | 'digest-only'\n\nexport type ClassifiedList =\n | { readonly kind: 'omit' }\n | { readonly kind: 'mask'; readonly pattern: string }\n | { readonly kind: 'rider'; readonly rider: string }\n\nexport type ClassifiedRider = (value: unknown) => unknown\n\nexport interface ClassifiedFieldSpec {\n readonly _noydbClassified: true\n /**\n * Via port brand marker — lets a `ClassifiedFieldSpec` satisfy the\n * kernel's opaque `ViaDescriptor` (#629 Task 5). Optional (unlike\n * `MoneyDescriptor._viaBrand`/`I18nTextDescriptor._viaBrand`, which are\n * required): every `classified.*()` preset factory stamps it, but the\n * pre-existing test suite's hand-rolled `ClassifiedFieldSpec` fixtures\n * (built without going through a preset) predate this field and would\n * fail to compile if it were mandatory.\n */\n readonly _viaBrand?: 'classified'\n readonly preset: string\n readonly storage: ClassifiedStorage\n readonly list: ClassifiedList\n readonly sensitivity: 'pii' | 'secret'\n /** Write-time safe projections; companion field name is `<field>_<rider>`. */\n readonly riders?: Record<string, ClassifiedRider>\n /** Write-time validator: error message, or null when valid. */\n readonly validate?: (value: unknown) => string | null\n /** Digest-only verify policy (stage 2). Mode both sides normalize under. */\n readonly verifyNormalize?: 'password' | 'secret-answer'\n /** Decorate ok:true verdicts with mustRotate after this many days (I1). */\n readonly rotateDays?: number\n /** Refuse reuse of the last N values on rotate (cap 8, spec Q4). */\n readonly notLastN?: number\n /** Member of the collection's matchGroup (secretAnswer preset). */\n readonly verifyGroupMember?: true\n /**\n * Opt into a store-visible blind-index (`_bidx`) tag so equal values become\n * queryable/joinable while staying digest-only. Digest-only knob (R7); gated\n * behind the collection's `acknowledgeEquatableRisk` door (R8). Equal values\n * produce equal tags — a partition leak a DEK holder can offline-dictionary\n * at the PBKDF2 floor. Absent/false ⇒ refused-by-default.\n */\n readonly equatable?: true\n}\n\nexport interface ClassifiedGroup {\n readonly _noydbClassifiedGroup: true\n /** Via port brand marker — see {@link ClassifiedFieldSpec._viaBrand}. */\n readonly _viaBrand?: 'classified'\n readonly preset: string\n /** member record-field name -> spec (differential per-member policy). */\n readonly members: Record<string, ClassifiedFieldSpec>\n}\n\nexport type ClassifiedEntry = ClassifiedFieldSpec | ClassifiedGroup\n\nexport function isClassifiedFieldSpec(x: unknown): x is ClassifiedFieldSpec {\n return typeof x === 'object' && x !== null\n && (x as { _noydbClassified?: unknown })._noydbClassified === true\n}\n\nexport function isClassifiedGroup(x: unknown): x is ClassifiedGroup {\n return typeof x === 'object' && x !== null\n && (x as { _noydbClassifiedGroup?: unknown })._noydbClassifiedGroup === true\n}\n","/**\n * Configuration/write errors for classified fields. @module\n *\n * `ClassifiedConfigError` / `ClassifiedRevealError` moved to `kernel/errors.ts`\n * (stage 2) so `kernel/enclave/classify/*` can throw them without importing\n * with-*. Re-exported here under the same names so existing import paths\n * keep working.\n */\nexport { ClassifiedConfigError, ClassifiedRevealError, ClassifiedVerifyError, ClassifiedRotationError } from '../../kernel/errors.js'\n\nexport class ClassifiedNeverStoredError extends Error {\n constructor(public readonly collection: string, public readonly field: string) {\n super(`Field \"${field}\" in collection \"${collection}\" is classified storage:'never' `\n + `(e.g. a CVC) and must not be persisted. Validate it at capture and drop it before put().`)\n this.name = 'ClassifiedNeverStoredError'\n }\n}\n\nexport class ClassifiedValidationError extends Error {\n constructor(public readonly collection: string, public readonly field: string, detail: string) {\n super(`Classified field \"${field}\" in collection \"${collection}\" failed validation: ${detail}`)\n this.name = 'ClassifiedValidationError'\n }\n}\n","/** Flatten a classifiedFields config into a per-field map + rider computed entries. @module */\n\nimport { isClassifiedGroup, type ClassifiedEntry, type ClassifiedFieldSpec } from './descriptor.js'\nimport { ClassifiedConfigError } from './errors.js'\n\nexport type { ClassifiedEntry, ClassifiedFieldSpec, ClassifiedGroup, ClassifiedList } from './descriptor.js'\nexport { ClassifiedConfigError } from './errors.js'\n\nexport interface ResolvedClassified {\n readonly byField: Record<string, ClassifiedFieldSpec>\n readonly riderComputed: Record<string, (record: Record<string, unknown>) => unknown>\n}\n\nexport function resolveClassifiedFields(\n collection: string,\n config: Record<string, ClassifiedEntry>,\n): ResolvedClassified {\n const byField: Record<string, ClassifiedFieldSpec> = {}\n const claim = (field: string, spec: ClassifiedFieldSpec): void => {\n if (byField[field] !== undefined) {\n throw new ClassifiedConfigError(collection,\n `field \"${field}\" is claimed twice — storage forms are mutually exclusive per field (R5): ` +\n `a field is digest-only OR recoverable OR never, exactly one`)\n }\n byField[field] = spec\n }\n for (const [key, entry] of Object.entries(config)) {\n if (isClassifiedGroup(entry)) {\n for (const [field, spec] of Object.entries(entry.members)) claim(field, spec)\n } else {\n claim(key, entry)\n }\n }\n const riderComputed: Record<string, (record: Record<string, unknown>) => unknown> = {}\n for (const [field, spec] of Object.entries(byField)) {\n for (const [name, rider] of Object.entries(spec.riders ?? {})) {\n const companion = `${field}_${name}`\n if (byField[companion] !== undefined || riderComputed[companion] !== undefined) {\n throw new ClassifiedConfigError(collection, `rider companion \"${companion}\" collides with a declared field`)\n }\n riderComputed[companion] = (record) =>\n record[field] === undefined ? undefined : rider(record[field])\n }\n }\n return { byField, riderComputed }\n}\n","/** Write-path enforcement: storage:'never' rejection + preset validators.\n * Runs BEFORE riders/computed and BEFORE schema validation. Pure. @module */\n\nimport type { ClassifiedFieldSpec } from './descriptor.js'\nimport { ClassifiedNeverStoredError, ClassifiedValidationError } from './errors.js'\n\nexport function enforceClassifiedWrite(\n record: Record<string, unknown>,\n byField: Record<string, ClassifiedFieldSpec>,\n collection: string,\n): void {\n for (const [field, spec] of Object.entries(byField)) {\n const value = record[field]\n if (value === undefined) continue\n if (value === null && spec.storage === 'digest-only') continue // explicit clear (C6 branch 3)\n if (spec.storage === 'never') throw new ClassifiedNeverStoredError(collection, field)\n const problem = spec.validate?.(value) ?? null\n if (problem !== null) throw new ClassifiedValidationError(collection, field, problem)\n }\n}\n","/**\n * Refusal matrix R1-R5 (spec) — ONE guard, run at BOTH doors:\n * `collection()` config resolution AND `_applyClassifiedFields` (the reconcile\n * seam), because crdt/conflictPolicy/perRecordKeys are construction-only\n * while classifiedFields can attach later (C5's lesson). @module\n */\nimport type { ClassifiedFieldSpec } from './descriptor.js'\nimport { ClassifiedConfigError } from '../../kernel/errors.js'\n\nexport interface ClassifiedGuardCtx {\n readonly perRecordKeys: boolean\n readonly crdt: boolean\n readonly hasConflictPolicy: boolean\n /** False on plaintext (`encrypt: false`) collections — no vdig/seal path exists. */\n readonly storeCiphertext: boolean\n readonly deterministicFields: ReadonlySet<string> | null\n readonly indexedFields: ReadonlySet<string>\n readonly textIndexFields: ReadonlySet<string>\n readonly vectorSourceFields: ReadonlySet<string>\n readonly subjectKeyField: string | undefined\n readonly bareSensitiveFields: ReadonlySet<string>\n /** Collection-level gate for the `equatable` knob (R8 double door). */\n readonly acknowledgeEquatableRisk: boolean\n}\n\nexport function guardClassifiedCompat(\n collection: string,\n byField: Record<string, ClassifiedFieldSpec>,\n ctx: ClassifiedGuardCtx,\n): void {\n const digestOnly = Object.entries(byField)\n .filter(([, s]) => s.storage === 'digest-only')\n const protectedForms = Object.values(byField)\n .filter((s) => s.storage === 'digest-only' || s.storage === 'recoverable')\n\n // R2 — merge resolvers bypass the write pipeline entirely (C5): a merge\n // could carry stale/foreign _vdig or resurrect plaintext (the CRDT put\n // branch persists via encryptJsonString with no vdig backstop). Fail-loud.\n if (protectedForms.length > 0 && (ctx.crdt || ctx.hasConflictPolicy)) {\n throw new ClassifiedConfigError(collection,\n 'digest-only/recoverable classified fields cannot combine with a crdt mode or a ' +\n 'conflictPolicy resolver — merge paths bypass write enforcement (R2)')\n }\n // R7 — `equatable` is a digest-only knob: it drives the `_bidx` blind-index\n // slot, which only exists on the digest-only write path. A recoverable/never\n // field carrying it would silently no-op (recoverable equality is `_det`'s\n // job). Refuse rather than accept-and-ignore. Checked over ALL specs, before\n // the digest-only early-return below, precisely to catch the non-digest case.\n for (const [f, spec] of Object.entries(byField)) {\n if (spec.equatable === true && spec.storage !== 'digest-only') {\n throw new ClassifiedConfigError(collection,\n `field \"${f}\" declares equatable but storage is '${spec.storage}' — equatable is a ` +\n `digest-only knob; recoverable equality is _det's job (R7)`)\n }\n }\n\n // R8 — the equatable double door (mirrors deterministicFields ×\n // acknowledgeDeterministicRisk): any field opting into `equatable` requires\n // the collection to acknowledge the partition-leak risk. One-directional —\n // `acknowledgeEquatableRisk: true` with ZERO equatable members is a silent\n // no-op (never throws here), so the ack can sit on a collection config\n // ahead of the field landing.\n const hasEquatable = Object.values(byField).some((s) => s.equatable === true)\n if (hasEquatable && ctx.acknowledgeEquatableRisk !== true) {\n throw new ClassifiedConfigError(collection,\n `classified equatable fields require \\`acknowledgeEquatableRisk: true\\` — equal values ` +\n `produce equal store-visible index tags (a partition leak a DEK holder can offline-` +\n `dictionary at the PBKDF2 floor); the door is the real control for low-entropy fields (R8)`)\n }\n\n if (digestOnly.length === 0) return\n\n // Digest-only on a plaintext collection: the codec's vdig block is gated on\n // storeCiphertext, so the secret would persist as raw plaintext. Refuse.\n if (!ctx.storeCiphertext) {\n throw new ClassifiedConfigError(collection,\n `storage:'digest-only' requires an encrypted collection — with encrypt: false the ` +\n `write path skips the digest slot entirely and the secret would persist as raw plaintext`)\n }\n // R1 — vdig slot keys are CEK-only (I3); without perRecordKeys a digest\n // would survive forget() in backups as offline-crackable material.\n if (!ctx.perRecordKeys) {\n throw new ClassifiedConfigError(collection,\n `storage:'digest-only' requires perRecordKeys: true — vdig keys derive from the ` +\n `per-record CEK so forget() shreds them (R1)`)\n }\n for (const [f, spec] of digestOnly) {\n // notLastN cap: presets clamp this at construction, but a raw spec object\n // bypasses the preset — enforce the same 0..8 cap in the policy projection.\n const n = spec.notLastN ?? 0\n if (!Number.isInteger(n) || n < 0 || n > 8) {\n throw new ClassifiedConfigError(collection,\n `digest-only field \"${f}\" declares notLastN ${n} — must be an integer 0..8 ` +\n `(write cost is n × 600K PBKDF2; ring blast radius is documented)`)\n }\n if (ctx.bareSensitiveFields.has(f)) {\n // R5 — the sealed block runs before the vdig block in the codec, so the\n // overlap would seal the value recoverably (silent downgrade + I4\n // double-slot). Storage forms are mutually exclusive per field.\n throw new ClassifiedConfigError(collection,\n `field \"${f}\" is digest-only AND listed in sensitive[] — storage forms are mutually exclusive per field (R5)`)\n }\n if (ctx.deterministicFields?.has(f)) {\n throw new ClassifiedConfigError(collection,\n `digest-only field \"${f}\" cannot be deterministic — equality-correlatable ciphertext defeats per-write salts (R3)`)\n }\n if (ctx.indexedFields.has(f) || ctx.textIndexFields.has(f)\n || ctx.vectorSourceFields.has(f) || ctx.subjectKeyField === f) {\n throw new ClassifiedConfigError(collection,\n `digest-only field \"${f}\" cannot be indexed, text-indexed, embedded, or a forget-subject key (R4)`)\n }\n }\n}\n","/**\n * The classified `ViaBinding` (#629 Task 5, LIVE since Task 6) — wires\n * classified-fields declaration/write-enforcement/at-rest sealing/erasure\n * into the kernel's generic Via port. Mirrors `via/money/binding.ts`'s\n * inline declaration-time validation and `via/i18n/binding.ts`'s\n * `linkXVia` static-link pattern — except the link itself is EAGER\n * (`port/with/classified-strategy.ts` calls `linkClassifiedVia()` at module\n * load, not lazily from a `classified.*()` preset call): several fixtures\n * build a raw `ClassifiedFieldSpec` literal without ever calling a preset,\n * and the binder must be installed before `compileViaBindings` needs it\n * regardless.\n *\n * `compileViaBindings` (`kernel/collection-config.ts`) compiles this binding\n * in whenever a collection declares `classifiedFields` — money then i18n\n * then classified, order pinned. `kernel/collection.ts`'s `_putInternal`\n * runs `enforceClassifiedWrite`'s effect via the pipeline's `enforceWrite`\n * phase; the codec's `encodeAtRest`/`decodeAtRest` hooks (Task 3's boundary)\n * replace the inline `sensitiveFields` seal path for any collection this\n * binding is compiled into.\n *\n * `declare` — `classifiedBinding(cfg)` runs `resolveClassifiedFields` +\n * `guardClassifiedCompat` at CONSTRUCTION time (the same #553 pattern\n * `moneyBinding`'s `validateMoneyFieldPaths(moneyFields)` call uses) —\n * mirrors today's \"door 1\" (`collection-config.ts`'s\n * `resolveCollectionConfig`). Both may throw `ClassifiedConfigError`.\n */\nimport type { ViaBinding, ViaWriteCtx, ViaCryptoCtx, SealedSlotRef, ViaEraseCtx, ViaEraseReport } from '../../kernel/via/index.js'\nimport { installViaBinder } from '../../kernel/via/index.js'\nimport { SealedHandle } from '../../kernel/types.js'\nimport { resolveClassifiedFields, type ClassifiedEntry } from './resolve.js'\nimport { guardClassifiedCompat, type ClassifiedGuardCtx } from './guards.js'\nimport { enforceClassifiedWrite } from './write.js'\nimport type { ClassifiedFieldSpec } from './descriptor.js'\n\n/**\n * One classified per-slot shred/residue verdict — mirrors\n * `RecordCodec.SealedShredSlot` (`kernel/enclave/record-keys/record-codec.ts`)\n * byte-for-byte. Duplicated here (not imported) so this binding never\n * reaches into `kernel/enclave/` (`via-enclave-isolation`, #629) — the\n * closure that PRODUCES real values of this shape is codec-owned and\n * injected via {@link ClassifiedViaConfig.classifySealedShred}.\n */\nexport interface ClassifiedShredSlot {\n readonly field: string\n readonly class: 'shreddable' | 'dekResidue' | 'live-shreddable+dekResidue-in-backups'\n}\n\n/**\n * Config a classified collection's declarations resolve to — the binding's\n * construction input. `entries`/`guardCtx` are the raw \"door 1\" inputs\n * (`resolveClassifiedFields`/`guardClassifiedCompat`'s own parameters);\n * `classifySealedShred`/`purgeSealedCekEnvelopes` are OPTIONAL codec/vault-\n * provided closures for the `erase` hook — undefined in this dormant task\n * (no caller wires them yet), wired for real in #629 Task 10 (\"posture\n * enforcement — forget + erase hooks live\").\n */\nexport interface ClassifiedViaConfig {\n readonly entries: Record<string, ClassifiedEntry>\n readonly collectionName: string\n readonly guardCtx: ClassifiedGuardCtx\n /**\n * Codec-provided closure mirroring `RecordCodec.classifySealedShred` —\n * classifies a live envelope's `_sealed` slots for crypto-shred\n * completeness (see that method's doc comment for the shreddable/\n * dekResidue/both-class semantics). `live` is `ViaEraseCtx.live`'s opaque\n * envelope, passed straight through. NOT `readonly` (#629 Task 10):\n * `compileViaBindings` builds this cfg before the owning `Collection`'s\n * `RecordCodec` exists, so `Collection`'s constructor mutates this field\n * in place once `this.codec` is available — the closure `eraseClassified`\n * reads is looked up at CALL time, not at binding-construction time, so\n * the late assignment is picked up correctly.\n */\n classifySealedShred?: (live: unknown) => Promise<{ readonly slots: readonly ClassifiedShredSlot[] }>\n /**\n * Vault-provided closure that purges every `_sealed_cek/<collection>/<id>/*`\n * host-delivery envelope for one record (mirrors `rotateRecordCek`'s own\n * prefix-delete) and returns the count purged. Deliberately left UNWIRED\n * by `compileViaBindings` (#629 Task 10) — `forget-sealed-erasure.test.ts`\n * proves this purge is UNCONDITIONAL on `sealRecordToHost` usage alone,\n * independent of whether `classifiedFields` is declared (a bare\n * `sensitive: [...]` collection exercises it with no classified binding\n * at all) — vault.ts keeps owning it directly so that case stays correct.\n */\n readonly purgeSealedCekEnvelopes?: (id: string) => Promise<number>\n}\n\n/** Fields declared `storage: 'recoverable'` (digest-only stays codec-inline; `'never'` is rejected by `enforceWrite` before either hook runs). */\nfunction recoverableFields(byField: Record<string, ClassifiedFieldSpec>): string[] {\n return Object.entries(byField).filter(([, spec]) => spec.storage === 'recoverable').map(([field]) => field)\n}\n\n/**\n * The full set this binding's `encodeAtRest`/`decodeAtRest` seal: declared\n * recoverable classified fields UNION bare `sensitive[]` fields — mirrors\n * `collection-config.ts`'s pre-cutover \"recoverable classified fields are\n * unioned into sensitiveFields... zero new crypto code\" semantics. Once this\n * binding is compiled into a collection's pipeline, `hasAtRestHooks` is true\n * and the codec's inline `sensitiveFields` seal path (record-codec.ts) is\n * DEAD for that collection — mutually exclusive with the hook path (#629\n * Task 3) — so bare `sensitive[]` fields MUST seal through here too, or\n * they'd silently stop being sealed at all (#629 Task 6 reconciliation).\n * `guardCtx.bareSensitiveFields` is the exact same `Set` the collection's\n * refusal-matrix guard already carries (`ClassifiedGuardCtx`).\n */\nfunction sealFieldNames(byField: Record<string, ClassifiedFieldSpec>, guardCtx: ClassifiedGuardCtx): string[] {\n return [...recoverableFields(byField), ...guardCtx.bareSensitiveFields]\n}\n\n/**\n * Seal every named field present with a defined value into its own sealed\n * slot via `crypto.sealedSlots`, peeling it out of the record.\n */\nasync function encodeClassifiedAtRest(\n record: Record<string, unknown>,\n crypto: ViaCryptoCtx,\n fields: readonly string[],\n): Promise<{ record: Record<string, unknown>; sealed?: Record<string, SealedSlotRef> }> {\n let open = record\n let sealed: Record<string, SealedSlotRef> | undefined\n for (const field of fields) {\n if (!(field in record)) continue\n const value = record[field]\n if (value === undefined) continue\n const ref = await crypto.sealedSlots.seal(field, value)\n if (open === record) open = { ...record }\n delete open[field]\n sealed ??= {}\n sealed[field] = ref\n }\n return sealed ? { record: open, sealed } : { record }\n}\n\n/**\n * Restore every named field's sealed slot back onto the record: an opaque\n * `SealedHandle` under `asHandles` (never materialises plaintext into the\n * cache — `SealedHandle.toJSON()` returns `'[sealed]'`, the export-redaction\n * guarantee `reveal()` alone can lift), the plaintext value otherwise.\n */\nasync function decodeClassifiedAtRest(\n record: Record<string, unknown>,\n sealed: Record<string, SealedSlotRef>,\n crypto: ViaCryptoCtx,\n opts: { asHandles: boolean },\n fields: readonly string[],\n): Promise<Record<string, unknown>> {\n let out = record\n for (const field of fields) {\n const ref = sealed[field]\n if (ref === undefined) continue\n if (out === record) out = { ...record }\n out[field] = opts.asHandles\n ? new SealedHandle(() => crypto.sealedSlots.unseal(field, ref))\n : await crypto.sealedSlots.unseal(field, ref)\n }\n return out\n}\n\n/**\n * `forget()`'s per-ref erasure participation: classify the live envelope's\n * sealed slots (via the codec-provided closure) and mark each shreddable\n * one deleted on this call's sealed-slots capability, then purge the\n * record's sealed-CEK host-delivery envelopes (via the vault-provided\n * closure). Both closures are optional — undefined (this dormant task's\n * only caller, its own unit tests) reports a zero-shredded, zero-residue\n * no-op rather than throwing.\n */\nasync function eraseClassified(\n ctx: ViaEraseCtx,\n cfg: ClassifiedViaConfig,\n): Promise<ViaEraseReport> {\n let shredded = 0\n const residue: unknown[] = []\n\n if (cfg.classifySealedShred) {\n const { slots } = await cfg.classifySealedShred(ctx.live)\n for (const slot of slots) {\n if (slot.class === 'shreddable' || slot.class === 'live-shreddable+dekResidue-in-backups') {\n shredded += 1\n await ctx.crypto.sealedSlots.delete(slot.field)\n }\n if (slot.class === 'dekResidue' || slot.class === 'live-shreddable+dekResidue-in-backups') {\n residue.push({ kind: 'classified-sealed-dek-residue', field: slot.field })\n }\n }\n }\n\n if (cfg.purgeSealedCekEnvelopes) {\n shredded += await cfg.purgeSealedCekEnvelopes(ctx.id)\n }\n\n return { shredded, residue }\n}\n\nfunction buildClassifiedDescribeFragment(byField: Record<string, ClassifiedFieldSpec>): Record<string, unknown> {\n return {\n classifiedFields: Object.fromEntries(\n Object.entries(byField).map(([field, spec]) => [field, { storage: spec.storage, sensitivity: spec.sensitivity }]),\n ),\n }\n}\n\nexport function classifiedBinding(cfg: ClassifiedViaConfig): ViaBinding {\n // declare — door 1: resolve + guard, exactly like collection-config.ts's\n // resolveCollectionConfig does today. Throws ClassifiedConfigError on any\n // R1-R8 refusal-matrix violation or field-collision.\n const resolved = resolveClassifiedFields(cfg.collectionName, cfg.entries)\n guardClassifiedCompat(cfg.collectionName, resolved.byField, cfg.guardCtx)\n const byField = resolved.byField\n const sealFields = sealFieldNames(byField, cfg.guardCtx)\n\n return {\n brand: 'classified',\n posture: { encryptedAtRest: 'sealed', queryable: 'det-exact', exportable: false, forgettable: true },\n covers: (field) => field in byField,\n // async (rather than a bare passthrough) so a refusal is always a\n // rejected Promise for direct callers, matching every other async-stack\n // hook's calling convention — `enforceClassifiedWrite` itself stays sync.\n enforceWrite: async (record: Record<string, unknown>, _ctx: ViaWriteCtx) =>\n enforceClassifiedWrite(record, byField, cfg.collectionName),\n encodeAtRest: (record, crypto) => encodeClassifiedAtRest(record, crypto, sealFields),\n decodeAtRest: (record, sealed, crypto, opts) => decodeClassifiedAtRest(record, sealed, crypto, opts, sealFields),\n erase: (ctx) => eraseClassified(ctx, cfg),\n describeFragment: () => buildClassifiedDescribeFragment(byField),\n }\n}\n\nexport function linkClassifiedVia(): void {\n installViaBinder('classified', (c) => classifiedBinding(c as ClassifiedViaConfig))\n}\n","/**\n * Classified strategy seam (#629 Task 5 — moved out of\n * `via/classified/strategy.ts`, precedent: `port/with/i18n-strategy.ts`).\n * Lives on the `/with` port (the one seam the kernel spine may import\n * statically) so `Collection`/`Vault` can hold the `NO_CLASSIFIED` default\n * without a spine→service static import.\n *\n * This file (unlike `via/classified/**`) is NOT subject to the\n * `via-enclave-isolation` architecture guard — it may import `EnclaveKey`\n * from the enclave barrel freely, the same way `port/with/i18n-strategy.ts`\n * did before #629 Task 4's DictionaryHandle cutover.\n *\n * The ② capability seam for classified read-egress ops (stage 1: reveal;\n * stage 2: verify).\n *\n * @internal\n */\nimport type { ClassifiedFieldSpec } from '../../via/classified/descriptor.js'\nimport { ClassifiedNotEnabledError } from '../../kernel/errors.js'\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\nimport { linkClassifiedVia } from '../../via/classified/binding.js'\n\n// #629 Task 10 — re-export the classified binding's erase-cfg TYPE (not\n// `via/classified/binding.js` directly) so the kernel spine can name\n// it for the post-construction `classifySealedShred` wiring, same rationale\n// as `resolveClassifiedFields`/`guardClassifiedCompat` above.\nexport type { ClassifiedViaConfig } from '../../via/classified/binding.js'\n\nexport type { ClassifiedVerdict } from '../../kernel/types.js'\nimport type { ClassifiedVerdict } from '../../kernel/types.js'\n\n// #629 Task 6 — the classified binding's construction-time resolve/guard,\n// re-exported here (not `via/classified/{resolve,guards}.js` directly)\n// so the kernel spine keeps importing only this one `/with` port seam, same\n// rationale as `NO_CLASSIFIED` below.\nexport { resolveClassifiedFields, type ClassifiedEntry, type ResolvedClassified } from '../../via/classified/resolve.js'\nexport { guardClassifiedCompat, type ClassifiedGuardCtx } from '../../via/classified/guards.js'\nexport type { ClassifiedFieldSpec }\n\n// Install the classified Via binder EAGERLY — unlike money()/i18nText(),\n// several classified fixtures (both hub tests and consumer code) build a raw\n// `ClassifiedFieldSpec` object literal without ever calling a\n// `classified.*()` preset (bypassing `via/classified/presets.ts`\n// entirely — an intentionally supported pattern, see that module's Task 5\n// doc comment). Linking lazily at preset-call time (the money/i18n pattern)\n// would leave `viaBinder('classified')` unlinked for those declarations.\n// This port module is already unconditionally imported by the kernel spine\n// (for the `NO_CLASSIFIED` strategy default below), so linking here\n// guarantees the binder is always installed before `compileViaBindings`\n// ever needs it, regardless of how a collection's classifiedFields were\n// constructed.\nlinkClassifiedVia()\n\nexport interface ClassifiedRevealCtx {\n readonly collection: string\n readonly spec: ClassifiedFieldSpec\n /** True on an encrypted collection — false selects the plaintext-body read. */\n readonly encrypted: boolean\n readonly getEnvelope: (id: string) => Promise<EncryptedEnvelope | null>\n readonly resolveCek: (env: EncryptedEnvelope) => Promise<EnclaveKey | undefined>\n readonly getDEK: () => Promise<EnclaveKey>\n readonly onAccess?: ((op: 'reveal', id: string) => Promise<void>) | undefined\n}\n\nexport interface ClassifiedVerifyCtx {\n readonly collection: string\n readonly spec: ClassifiedFieldSpec\n readonly getEnvelope: (id: string) => Promise<EncryptedEnvelope | null> // raw envelope, NOT a decrypted view\n readonly resolveCek: (env: EncryptedEnvelope) => Promise<EnclaveKey | undefined>\n readonly getDEK: () => Promise<EnclaveKey>\n readonly now: () => number // injected (Q7)\n /** Group members resolved by the collection (matchGroup only). */\n readonly groupMembers?: ReadonlyArray<{ readonly field: string; readonly spec: ClassifiedFieldSpec }>\n readonly onAccess?: ((op: 'verify' | 'find', id: string) => Promise<void>) | undefined\n}\n\nexport interface ClassifiedStrategy {\n reveal(ctx: ClassifiedRevealCtx, id: string, field: string): Promise<unknown>\n verify(ctx: ClassifiedVerifyCtx, id: string, field: string, candidate: string): Promise<ClassifiedVerdict>\n verifyText(ctx: ClassifiedVerifyCtx, id: string, field: string, candidate: string): Promise<ClassifiedVerdict>\n matchGroup(ctx: ClassifiedVerifyCtx, id: string, answers: Record<string, string>,\n opts: { readonly min: number }): Promise<{ readonly passed: boolean }>\n computeTarget(ctx: ClassifiedVerifyCtx, field: string, candidate: string, costByte?: number): Promise<string | null>\n}\n\nexport const NO_CLASSIFIED: ClassifiedStrategy = {\n async reveal() { throw new ClassifiedNotEnabledError() },\n async verify() { throw new ClassifiedNotEnabledError() },\n async verifyText() { throw new ClassifiedNotEnabledError() },\n async matchGroup() { throw new ClassifiedNotEnabledError() },\n async computeTarget() { throw new ClassifiedNotEnabledError() },\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/with-cargo/vault-diff.ts","../src/with-cargo/active.ts","../src/with-cargo/describe-extraction.ts","../src/with-cargo/adopt-partition.ts","../src/with-cargo/decrypt-partition.ts"],"sourcesContent":["/**\n * Vault-level diff orchestrator.\n *\n * Compares a live `Vault`'s plaintext state against a candidate state\n * (another vault, a plain `{ collection: records[] }` map, or a vault\n * dump JSON) and returns a structured `VaultDiff` plan listing the\n * records that would be added, modified, or deleted to bring the live\n * vault into the candidate's shape.\n *\n * Builds on two existing record-level helpers:\n *\n * 1. `diff(a, b)` from `./history/diff.ts` — emits dot-pathed\n * `DiffEntry[]` with `type: 'added' | 'removed' | 'changed'` for\n * each changed field of two records. Used here for the\n * `fieldDiffs` of every `modified` entry, and (with empty result)\n * as the default deep-equal check.\n *\n * 2. `Vault.exportStream()` from `./vault.ts` — the canonical\n * decrypt-and-stream-records iterator. Used to walk both sides\n * when the candidate is itself a `Vault`. ACL-scoped: collections\n * the caller can't read silently drop out, the same way every\n * other plaintext-emitting export pipeline filters them.\n *\n * The new orchestration is the **vault-level** enumeration: bucket\n * each record id into added (only in candidate), deleted (only in\n * vault), or modified (in both with field changes); leave the\n * field-level granularity to the existing `diff()`.\n *\n * Use cases:\n *\n * - Import preview (`@noy-db/as-*` `fromString` returns a plan\n * whose body is a `VaultDiff`).\n * - Backup verification (\"does this `.noydb` bundle from yesterday\n * match the current vault?\").\n * - Two-vault reconciliation (\"what's different between Office A\n * and Office B before we sync?\").\n * - Test assertions (golden-file testing with one-liner\n * `expect(plan.summary).toEqual(...)`).\n *\n * @module\n */\n\nimport type { Vault } from '../kernel/vault.js'\nimport { diff as fieldDiff, type DiffEntry as FieldDiffEntry } from '../with-commit/history/diff.js'\n\n// ─── Public types ──────────────────────────────────────────────────────\n\n/** Per-record entry shape — added and deleted records carry only the record value. */\nexport interface VaultDiffEntry<T = unknown> {\n readonly collection: string\n readonly id: string\n readonly record: T\n /**\n * Unencrypted envelope metadata for the RECEIVER-side record.\n * Populated only when `diffVault` is called with `{includeMetadata: true}`.\n * Default: `undefined` (zero extra reads, no behavior change).\n *\n * Populated for `modified` (the CURRENT receiver state, before the candidate\n * is applied) and `deleted` (the receiver envelope of the record being\n * removed). `added` entries have no receiver envelope at diff time, so their\n * `metadata` is always absent.\n */\n readonly metadata?: {\n readonly version: number\n readonly timestamp: string\n readonly by?: string\n readonly source?: string\n readonly sourceTs?: string\n }\n}\n\n/** Modified records carry both halves of the diff plus the field-level breakdown. */\nexport interface VaultDiffModifiedEntry<T = unknown> extends VaultDiffEntry<T> {\n /** The record as it stands in the live vault. */\n readonly before: T\n /** Top-level keys whose values differ between `before` and `record`. */\n readonly fieldsChanged: readonly string[]\n /**\n * Field-level diff entries from `diff(before, record)`. Reuses the\n * existing per-record diff helper so consumers can render git-style\n * `path: from → to` rows without re-walking the records.\n */\n readonly fieldDiffs: readonly FieldDiffEntry[]\n}\n\nexport interface VaultDiff<T = unknown> {\n readonly added: readonly VaultDiffEntry<T>[]\n readonly modified: readonly VaultDiffModifiedEntry<T>[]\n readonly deleted: readonly VaultDiffEntry<T>[]\n /** Only populated when `options.includeUnchanged: true`. */\n readonly unchanged: readonly VaultDiffEntry<T>[] | undefined\n readonly summary: {\n readonly add: number\n readonly modify: number\n readonly delete: number\n readonly total: number\n }\n /**\n * Format the diff as a human-readable string.\n *\n * - `'count'` — one line, just the numbers (`12 added · 3 modified · 0 deleted`)\n * - `'one-line'` — count plus a single overview line\n * - `'full'` — count + one row per added/modified/deleted record (default)\n */\n format(opts?: { detail?: 'count' | 'one-line' | 'full' }): string\n}\n\nexport interface DiffOptions {\n /** Restrict the diff to a subset of collections. */\n readonly collections?: readonly string[]\n /** Field on each record that carries its id. Defaults to `'id'`. */\n readonly idKey?: string\n /** Override the default deep-equal check for \"modified vs unchanged\". */\n readonly compareFn?: (a: unknown, b: unknown) => boolean\n /** If true, include unchanged records in the diff (off by default to save memory). */\n readonly includeUnchanged?: boolean\n /**\n * When `true`, each entry in `added`, `modified`, and `deleted` will carry a\n * `metadata` field with the RECEIVER-side envelope metadata\n * (`version`, `timestamp`, `by?`, `source?`, `sourceTs?`).\n *\n * Off by default — no extra adapter reads, no behavior change for existing callers.\n */\n readonly includeMetadata?: boolean\n}\n\n/**\n * Candidate state to diff the vault against:\n *\n * - A `Vault` instance — both sides are walked via `exportStream()`.\n * - A `Record<collection, records[]>` map — same shape `as-json.toObject()`\n * produces. Useful for diffing parsed file content against the live vault.\n * - A `VaultDump` (output of `vault.dump()`) — a JSON string carrying the\n * full vault state. Parsed and reduced to the map shape above.\n */\nexport type DiffCandidate<T = unknown> =\n | Vault\n | Record<string, readonly T[]>\n | string\n\n// ─── Implementation ────────────────────────────────────────────────────\n\n/**\n * Compute the diff between a live vault and a candidate state.\n *\n * Ungated shared import/merge infrastructure: the `as-*` exporters call this\n * internally from their `fromString`/import merge logic, so it is always\n * available (not behind `withCargo()`). Only `extractPartition` is a gated\n * cargo capability.\n *\n * Returns a fully buffered `VaultDiff` — no streaming. Memory cost is\n * O(n + m) in the row count of vault + candidate. For documented\n * 1K-50K-record vaults this is fine; a streaming variant lands as a\n * follow-up if a > 100K-record consumer arrives.\n */\nexport async function diffVault<T = unknown>(\n vault: Vault,\n candidate: DiffCandidate<T>,\n options: DiffOptions = {},\n): Promise<VaultDiff<T>> {\n const idKey = options.idKey ?? 'id'\n const filter = options.collections ? new Set(options.collections) : null\n const compareFn =\n options.compareFn ?? ((a: unknown, b: unknown) => fieldDiff(a, b).length === 0)\n\n // Side A — walk the live vault via exportStream(). Each chunk arrives\n // already decrypted and ACL-scoped, so collections the caller can't\n // read silently drop out. exportStream's records are typed `unknown[]`\n // — diffVault is the type-erasure boundary; the caller asserts the\n // record shape via the function's `<T>` generic.\n const live = new Map<string, Map<string, T>>()\n for await (const chunk of vault.exportStream({ granularity: 'collection' })) {\n if (filter && !filter.has(chunk.collection)) continue\n const collection = live.get(chunk.collection) ?? new Map<string, T>()\n for (const record of chunk.records) {\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record as T)\n }\n live.set(chunk.collection, collection)\n }\n\n // Side B — normalise the candidate into the same shape.\n const cand = await normaliseCandidate<T>(candidate, idKey, filter)\n\n // Walk every (collection, id) on either side and bucket.\n const added: VaultDiffEntry<T>[] = []\n const modified: VaultDiffModifiedEntry<T>[] = []\n const deleted: VaultDiffEntry<T>[] = []\n const unchanged: VaultDiffEntry<T>[] | undefined = options.includeUnchanged ? [] : undefined\n\n const collectionNames = new Set([...live.keys(), ...cand.keys()])\n for (const collection of [...collectionNames].sort()) {\n const liveColl = live.get(collection) ?? new Map<string, T>()\n const candColl = cand.get(collection) ?? new Map<string, T>()\n const allIds = new Set([...liveColl.keys(), ...candColl.keys()])\n\n for (const id of [...allIds].sort()) {\n const before = liveColl.get(id)\n const after = candColl.get(id)\n\n if (before === undefined && after !== undefined) {\n // added: record only in candidate — no receiver envelope\n added.push({ collection, id, record: after })\n } else if (before !== undefined && after === undefined) {\n // deleted: record only in receiver — attach metadata if requested\n const metadata = options.includeMetadata\n ? (await vault.collection(collection).getMetadata(id)) ?? undefined\n : undefined\n deleted.push({ collection, id, record: before, ...(metadata !== undefined ? { metadata } : {}) })\n } else if (before !== undefined && after !== undefined) {\n if (compareFn(before, after)) {\n unchanged?.push({ collection, id, record: after })\n } else {\n // modified: record in both — attach receiver-side metadata if requested\n const metadata = options.includeMetadata\n ? (await vault.collection(collection).getMetadata(id)) ?? undefined\n : undefined\n const fieldDiffs = fieldDiff(before, after)\n const fieldsChanged = uniqueTopLevelKeys(fieldDiffs)\n modified.push({\n collection,\n id,\n record: after,\n before,\n fieldsChanged,\n fieldDiffs,\n ...(metadata !== undefined ? { metadata } : {}),\n })\n }\n }\n }\n }\n\n const summary = {\n add: added.length,\n modify: modified.length,\n delete: deleted.length,\n total: added.length + modified.length + deleted.length,\n }\n\n return {\n added,\n modified,\n deleted,\n unchanged,\n summary,\n format(opts) {\n return formatDiff(opts?.detail ?? 'full', { added, modified, deleted, summary })\n },\n }\n}\n\n// ─── Internals ─────────────────────────────────────────────────────────\n\nasync function normaliseCandidate<T>(\n candidate: DiffCandidate<T>,\n idKey: string,\n filter: Set<string> | null,\n): Promise<Map<string, Map<string, T>>> {\n const out = new Map<string, Map<string, T>>()\n\n // Vault instance — duck-type via the exportStream method (matches\n // vault.ts's structural shape without forcing a runtime instanceof check\n // that would import the class and risk circular deps).\n if (\n typeof candidate === 'object' &&\n candidate !== null &&\n 'exportStream' in candidate &&\n typeof (candidate as Vault).exportStream === 'function'\n ) {\n for await (const chunk of (candidate as Vault).exportStream({ granularity: 'collection' })) {\n if (filter && !filter.has(chunk.collection)) continue\n const collection = out.get(chunk.collection) ?? new Map<string, T>()\n for (const record of chunk.records) {\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record as T)\n }\n out.set(chunk.collection, collection)\n }\n return out\n }\n\n // String — assume a vault.dump() JSON string. Parse and reduce to the map shape.\n if (typeof candidate === 'string') {\n let parsed: unknown\n try {\n parsed = JSON.parse(candidate)\n } catch (err) {\n throw new Error(\n `diffVault: candidate string is not valid JSON (${(err as Error).message})`,\n )\n }\n return collectionsFromObject<T>(parsed, idKey, filter)\n }\n\n // Plain object — `Record<collection, records[]>` (same shape as-json.toObject() returns).\n return collectionsFromObject<T>(candidate, idKey, filter)\n}\n\nfunction collectionsFromObject<T>(\n raw: unknown,\n idKey: string,\n filter: Set<string> | null,\n): Map<string, Map<string, T>> {\n const out = new Map<string, Map<string, T>>()\n if (raw === null || typeof raw !== 'object') {\n throw new Error('diffVault: candidate must be a Vault, an object, or a JSON string')\n }\n // A vault dump JSON has a top-level shape like { _compartment, _keyring, <coll>: <records[]> }.\n // We accept both: keys starting with `_` are skipped (they're metadata), the rest are collections.\n for (const [key, value] of Object.entries(raw)) {\n if (key.startsWith('_')) continue\n if (filter && !filter.has(key)) continue\n if (!Array.isArray(value)) continue\n const collection = new Map<string, T>()\n for (const record of value as readonly T[]) {\n if (record === null || typeof record !== 'object') continue\n const id = readIdField(record, idKey)\n if (!id) continue\n collection.set(id, record)\n }\n out.set(key, collection)\n }\n return out\n}\n\nfunction uniqueTopLevelKeys(diffs: readonly FieldDiffEntry[]): readonly string[] {\n const keys = new Set<string>()\n for (const d of diffs) {\n // path is dot-separated; the top-level key is everything before the\n // first `.` or `[`. (`a.b.c` → `a`, `tags[0]` → `tags`, `(root)` → `(root)`).\n const m = /^[^.[]+/.exec(d.path)\n if (m) keys.add(m[0])\n }\n return [...keys]\n}\n\n/**\n * Pull the id field off a record without going through `String(obj)`,\n * which would emit `[object Object]` for nested objects and silently\n * collapse rows that share the same parent. Only string and number ids\n * are accepted; anything else returns the empty string and the record\n * is skipped at the call site.\n */\nfunction readIdField(record: unknown, idKey: string): string {\n if (record === null || typeof record !== 'object') return ''\n const v = (record as Record<string, unknown>)[idKey]\n if (typeof v === 'string') return v\n if (typeof v === 'number' && Number.isFinite(v)) return String(v)\n return ''\n}\n\ninterface FormatBuckets<T> {\n readonly added: readonly VaultDiffEntry<T>[]\n readonly modified: readonly VaultDiffModifiedEntry<T>[]\n readonly deleted: readonly VaultDiffEntry<T>[]\n readonly summary: VaultDiff<T>['summary']\n}\n\nfunction formatDiff<T>(\n detail: 'count' | 'one-line' | 'full',\n b: FormatBuckets<T>,\n): string {\n const head = `${b.summary.add} added · ${b.summary.modify} modified · ${b.summary.delete} deleted`\n if (detail === 'count') return head\n if (b.summary.total === 0) return head + '\\n(no changes)'\n if (detail === 'one-line') return head\n\n const rows: string[] = [head, '']\n for (const e of b.added) rows.push(`${e.collection}/${e.id}\\tadded`)\n for (const e of b.modified) {\n const fields = e.fieldDiffs\n .map((f) => `${f.path}: ${shortJSON(f.from)} → ${shortJSON(f.to)}`)\n .join(', ')\n rows.push(`${e.collection}/${e.id}\\tmodified\\t${fields}`)\n }\n for (const e of b.deleted) rows.push(`${e.collection}/${e.id}\\tdeleted`)\n return rows.join('\\n')\n}\n\nfunction shortJSON(value: unknown): string {\n if (value === undefined) return 'undefined'\n const s = JSON.stringify(value)\n // JSON.stringify returns string for any JSON value except `undefined`\n // (handled above), `function`, and `symbol`. Fall back to a static\n // tag for those — never let an arbitrary object hit the default\n // stringifier (which the lint rule explicitly bans).\n if (typeof s !== 'string') return '<unrepresentable>'\n return s.length > 60 ? s.slice(0, 57) + '...' : s\n}\n","/**\n * Enable the cargo (partition extraction) capability.\n * Pass to `createNoydb({ cargoStrategy: withCargo() })` to make the source-side\n * `extractPartition(vault, ...)` free function live. The extraction crypto\n * engine is dynamically imported here, so it stays out of the floor bundle\n * until opted in. The recipient-side `adoptPartition` /\n * `decryptExtractedPartition` free functions — and `diffVault` (shared\n * import/merge infra) — are ungated and unaffected.\n */\nimport type { CargoStrategy } from './strategy.js'\n\nexport function withCargo(): CargoStrategy {\n return {\n async extractPartition(vault, opts) {\n const { extractPartitionCore } = await import('./extract-partition.js')\n return extractPartitionCore(vault, opts)\n },\n }\n}\n","/**\n * Partition-extraction dry-run. Read-only preview of what an\n * `extractPartition` would move: record counts, byte totals, and the\n * timestamp span per collection — computed from raw encrypted\n * envelopes WITHOUT decrypting them. Writes nothing, mutates nothing.\n *\n * @module\n */\nimport type { Vault } from '../kernel/vault.js'\nimport { walkClosure, type WalkClosureOptions, type DanglingRefNotice } from './walk-closure.js'\n\nexport interface ExtractionPreview {\n readonly totalRecords: number\n /** Sum of serialized encrypted-envelope sizes (bytes). */\n readonly totalBytes: number\n readonly byCollection: ReadonlyArray<{\n readonly name: string\n readonly recordCount: number\n readonly bytes: number\n /** Earliest envelope `_ts` in this collection (lexicographic). */\n readonly oldestTs?: string\n readonly newestTs?: string\n }>\n readonly graph: { readonly depth: number; readonly cyclesDetected: boolean }\n /** Records the walk reached but whose envelope couldn't be read. */\n readonly inaccessible: ReadonlyArray<{ readonly collection: string; readonly id: string }>\n /**\n * #772: outbound FK edges whose referenced parent was excluded from the\n * closure (missing, or tier-elevated and therefore invisible) — mirrors\n * `ExtractPartitionResult.danglingRefs` (#759) so a preview/extract pair\n * gives the SAME signal. A parent excluded this way never appears in\n * `byCollection` at all; this is the only preview-time indicator.\n */\n readonly danglingRefs: ReadonlyArray<DanglingRefNotice>\n}\n\nexport async function describeExtraction(\n vault: Vault,\n opts: WalkClosureOptions,\n): Promise<ExtractionPreview> {\n const { closure, graph, danglingRefs } = await walkClosure(vault, opts)\n\n const { name: vaultName, adapter } = vault._introspectState()\n const encoder = new TextEncoder()\n\n const byCollection: Array<{\n name: string; recordCount: number; bytes: number; oldestTs?: string; newestTs?: string\n }> = []\n const inaccessible: Array<{ collection: string; id: string }> = []\n let totalBytes = 0\n let totalRecords = 0\n\n for (const [collectionName, ids] of closure) {\n let bytes = 0\n let oldestTs: string | undefined\n let newestTs: string | undefined\n let recordCount = 0\n\n for (const id of ids) {\n const env = await adapter.get(vaultName, collectionName, id)\n if (!env) {\n // Walk reached it (via decrypted list) but the raw store read\n // returned nothing — surface rather than miscount.\n inaccessible.push({ collection: collectionName, id })\n continue\n }\n recordCount++\n bytes += encoder.encode(JSON.stringify(env)).length\n const ts = env._ts\n if (oldestTs === undefined || ts < oldestTs) oldestTs = ts\n if (newestTs === undefined || ts > newestTs) newestTs = ts\n }\n\n byCollection.push({\n name: collectionName,\n recordCount,\n bytes,\n // Spread conditionally — exactOptionalPropertyTypes forbids an\n // explicit `undefined` on an optional property.\n ...(oldestTs !== undefined ? { oldestTs } : {}),\n ...(newestTs !== undefined ? { newestTs } : {}),\n })\n totalBytes += bytes\n totalRecords += recordCount\n }\n\n byCollection.sort((a, b) => a.name.localeCompare(b.name))\n\n return Object.freeze({\n totalRecords,\n totalBytes,\n byCollection,\n graph,\n inaccessible,\n danglingRefs,\n })\n}\n","/**\n * Partition adoption. Recipient side: verify an extracted bundle,\n * validate the transfer key, import the re-keyed collections into a\n * destination store, and record an `_meta/adoption` marker. The bundle\n * stays UNOWNED after adoption — `createOwnerOnAdoptedPartition`\n * mints the owner; the transfer seal is then destroyed.\n *\n * @module\n */\nimport { base64ToBuffer, wrapKey, type EnclaveKey } from '../kernel/enclave/index.js'\nimport { TransferSealError, AdoptionStateError, ValidationError } from '../kernel/errors.js'\nimport type { NoydbStore, VaultSnapshot, KeyringFile } from '../kernel/types.js'\nimport { createOwnerKeyring } from '../with-party/team/keyring.js'\nimport { resolveManagedSecret } from '../with-party/team/managed-secret.js'\nimport type { SealingKeyProvider } from '../with-party/team/managed-secret.js'\nimport type { ShamirRecoveryProvider } from '../with-party/team/shamir-recovery-provider.js'\nimport type { RecoveryEnrollmentInput } from '../with-party/team/rotate-recover.js'\nimport { LedgerStore } from '../with-commit/history/ledger/store.js'\nimport { LEDGER_COLLECTION } from '../with-commit/history/ledger/constants.js'\nimport type { TransferSealPayload } from '../with-pod/bundle.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/bundle.js'\n\n/**\n * Reverse of `sealDeks`. Imports the transfer key, decrypts the\n * sealed `{ collection: base64(rawDEK) }` map (layout iv(12)‖ct‖tag), and\n * re-imports each DEK as an AES-GCM key. Throws `TransferSealError` on a\n * wrong key (AES-GCM auth-tag failure) or malformed payload.\n */\nexport async function unsealDeks(\n seal: TransferSealPayload,\n transferKey: Uint8Array,\n): Promise<Map<string, EnclaveKey>> {\n if (transferKey.byteLength !== 32) {\n throw new TransferSealError(\n `transfer key must be 32 bytes, got ${transferKey.byteLength}.`,\n )\n }\n const key = await crypto.subtle.importKey('raw', transferKey as BufferSource, 'AES-GCM', false, ['decrypt'])\n const raw = base64ToBuffer(seal.payload)\n let plaintext: ArrayBuffer\n try {\n plaintext = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: raw.slice(0, 12) as BufferSource },\n key,\n raw.slice(12) as BufferSource,\n )\n } catch {\n throw new TransferSealError(\n 'transfer seal could not be opened — wrong transfer key (AES-GCM authentication failed).',\n )\n }\n let dekMap: Record<string, string>\n try {\n dekMap = JSON.parse(new TextDecoder().decode(plaintext)) as Record<string, string>\n } catch {\n throw new TransferSealError('transfer seal payload is not valid JSON after decryption.')\n }\n const deks = new Map<string, EnclaveKey>()\n for (const [collection, b64] of Object.entries(dekMap)) {\n // Extractable: the recipient must be able to re-wrap these under their\n // own KEK (AES-KW) at owner-creation. Matches generateDEK.\n const dek = await crypto.subtle.importKey('raw', base64ToBuffer(b64) as BufferSource, 'AES-GCM', true, ['encrypt', 'decrypt'])\n deks.set(collection, dek)\n }\n return deks\n}\n\nexport interface AdoptPartitionOptions {\n readonly transferKey: Uint8Array\n readonly destinationStore: NoydbStore\n readonly vaultName: string\n}\n\nexport interface AdoptPartitionResult {\n readonly vaultName: string\n readonly needsOwner: true\n readonly sealId: string\n}\n\nexport async function adoptPartition(\n bundleBytes: Uint8Array,\n opts: AdoptPartitionOptions,\n): Promise<AdoptPartitionResult> {\n const { transferKey, destinationStore, vaultName } = opts\n\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new ValidationError(\n 'adoptPartition requires an extracted-partition bundle with a transfer seal. '\n + 'For ordinary backups use readPod + vault.load.',\n )\n }\n\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n\n // Validate the transfer key by unsealing in memory; throws\n // TransferSealError on mismatch. DEKs are discarded here — they stay\n // sealed at rest (in _meta/adoption) until owner-creation wraps them under the\n // recipient's KEK.\n await unsealDeks(seal, transferKey)\n\n // Single-occupancy per vaultName: an `_meta/adoption` marker already present\n // means this slot holds a partition (adopted-and-unowned, or already owned).\n // saveAll below would overwrite its data and replace the marker, stranding the\n // prior adoption's transfer seal. Refuse regardless of sealId — re-adopting the\n // SAME bundle is a redundant call, and adopting a DIFFERENT bundle here would\n // clobber the existing partition. Either way, pick a fresh vaultName.\n const existing = await destinationStore.get(vaultName, '_meta', 'adoption')\n if (existing) {\n const prior = JSON.parse(existing._data) as { sealId?: string }\n if (prior.sealId === seal.sealId) {\n throw new AdoptionStateError(\n `partition (sealId ${seal.sealId}) is already adopted into vault \"${vaultName}\".`,\n )\n }\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds an adopted partition (sealId ${prior.sealId}); `\n + `adopting a different partition (sealId ${seal.sealId}) here would overwrite it. `\n + `Adopt into a fresh vaultName instead.`,\n )\n }\n\n // The marker-only check above misses a worse case: a vaultName already in use\n // by an ORDINARY vault (createNoydb + openVault) carries no `_meta/adoption`,\n // yet `saveAll` below is destructive on SQL adapters (`DELETE FROM ... WHERE\n // vault = ?` followed by upsert) and would wipe the legitimate keyring +\n // data. Refuse adoption into ANY occupied slot — a fresh vaultName is the\n // documented precondition.\n const existingKeyring = await destinationStore.list(vaultName, '_keyring')\n if (existingKeyring.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already holds a keyring (an unrelated owner exists at this slot); `\n + `adoptPartition requires a fresh vaultName to avoid destructive saveAll on SQL adapters.`,\n )\n }\n\n const backup = JSON.parse(dump) as { collections: VaultSnapshot; _internal?: VaultSnapshot }\n await destinationStore.saveAll(vaultName, backup.collections)\n\n // Import carried internal collections (e.g. _schemas from carrySchemas).\n // saveAll only writes data collections; _internal is written per-record.\n if (backup._internal) {\n for (const [collection, records] of Object.entries(backup._internal)) {\n for (const [id, envelope] of Object.entries(records)) {\n await destinationStore.put(vaultName, collection, id, envelope)\n }\n }\n }\n\n const adoptedAt = new Date().toISOString()\n const adoption = { sealId: seal.sealId, adoptedAt, needsOwner: true as const, transferSeal: seal }\n await destinationStore.put(vaultName, '_meta', 'adoption', {\n _noydb: 1, _v: 1, _ts: adoptedAt, _iv: '', _data: JSON.stringify(adoption),\n })\n\n return { vaultName, needsOwner: true, sealId: seal.sealId }\n}\n\nexport interface CreateOwnerResult {\n readonly vaultName: string\n readonly userId: string\n}\n\n/** Standard-mode owner: recipient supplies the secret. */\nexport interface CreateOwnerStandardOptions {\n readonly userId: string\n readonly secret: string\n readonly transferKey: Uint8Array\n}\n\n/**\n * Managed-mode owner: the secret is minted + sealed under\n * a `SealingKeyProvider` (e.g. an `at-*` OS keychain) so the partition\n * auto-unlocks on the recipient's device. Managed mode mandates a strong\n * (Shamir) recovery profile at creation, which needs the\n * `shamirRecovery` provider injected.\n */\nexport interface CreateOwnerManagedOptions {\n readonly userId: string\n readonly secretMode: 'managed'\n readonly sealingKey: SealingKeyProvider\n readonly recovery: ReadonlyArray<RecoveryEnrollmentInput>\n readonly shamirRecovery: ShamirRecoveryProvider\n readonly transferKey: Uint8Array\n}\n\nexport type CreateOwnerOptions = CreateOwnerStandardOptions | CreateOwnerManagedOptions\n\nfunction isManaged(o: CreateOwnerOptions): o is CreateOwnerManagedOptions {\n return 'secretMode' in o && o.secretMode === 'managed'\n}\n\n/**\n * Mint the first owner keyring on an adopted-but-unowned partition,\n * then destroy the transfer seal.\n *\n * Standard mode: the recipient supplies a secret. Managed mode: the\n * secret is minted + sealed under a `SealingKeyProvider` and a strong\n * (Shamir) recovery profile is enrolled — orchestrated via the existing\n * `openVaultAndEnrollRecovery` ceremony.\n *\n * Either way, reuses `createOwnerKeyring` to derive the KEK + write the base\n * keyring, then wraps the partition's DEKs (recovered from the seal) under that\n * KEK and re-persists the merged keyring file.\n *\n * Idempotent under retry: the seal is destroyed LAST (Stage D), after the\n * keyring (Stage A), the ledger transition (Stage B), and — in managed mode —\n * strong-recovery enrollment (Stage C). A failure in the fallible enrollment\n * step leaves the seal intact, and re-running with the same `userId` +\n * `transferKey` resumes from the first incomplete stage. (Multi-profile recovery\n * arrays may re-enroll an already-enrolled profile on retry; managed mode's\n * mandated single Shamir profile does not.)\n */\nexport async function createOwnerOnAdoptedPartition(\n store: NoydbStore,\n vaultName: string,\n opts: CreateOwnerOptions,\n): Promise<CreateOwnerResult> {\n const { userId, transferKey } = opts\n\n // Managed mode requires a strong (Shamir) recovery profile, validated BEFORE\n // any disk write — same gate as createNoydb.\n if (isManaged(opts) && !opts.recovery.some((r) => r.profile === 'shamir')) {\n throw new AdoptionStateError(\n 'managed-mode adoption requires at least one strong (shamir) recovery profile in '\n + '`recovery` — paper alone is not strong when there is no user secret to fall back on.',\n )\n }\n\n // 1. Verify adopted-unowned state.\n const adoptionEnv = await store.get(vaultName, '_meta', 'adoption')\n if (!adoptionEnv) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" is not an adopted partition (no _meta/adoption). `\n + `createOwnerOnAdoptedPartition only applies to vaults created via adoptPartition.`,\n )\n }\n const adoption = JSON.parse(adoptionEnv._data) as {\n sealId: string; adoptedAt: string; needsOwner?: boolean\n consumedAt?: string; transferSeal?: TransferSealPayload\n }\n if (adoption.consumedAt !== undefined || adoption.transferSeal === undefined) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has an owner (transfer seal consumed at ${adoption.consumedAt}).`,\n )\n }\n\n // 2. Recover the partition DEKs from the seal (throws on wrong key) BEFORE\n // writing any keyring, so a bad transfer key leaves no trace. Always\n // validated, including when resuming a partial prior call.\n const partitionDeks = await unsealDeks(adoption.transferSeal, transferKey)\n\n // The ceremony below is split into stages so a failure in the fallible\n // managed-enrollment step (network/provider outage) leaves the call RETRYABLE\n // — the seal is destroyed only once everything durable is in place. Each stage\n // detects its own prior completion rather than relying on a single resume bit.\n\n // A keyring present for a DIFFERENT user (with the seal still unconsumed) is a\n // genuine second-owner attempt — refuse it. A same-user keyring is a resumed\n // partial call and is handled by the stage checks below.\n const existingKeyring = await store.get(vaultName, '_keyring', userId)\n const otherOwners = (await store.list(vaultName, '_keyring')).filter((u) => u !== userId)\n if (otherOwners.length > 0) {\n throw new AdoptionStateError(\n `vault \"${vaultName}\" already has a keyring for a different owner; cannot create owner \"${userId}\".`,\n )\n }\n\n // Stage A — mint the owner keyring + merge the partition DEKs. Considered done\n // only when the keyring already holds every partition DEK. createOwnerKeyring\n // overwrites (fresh KEK + fresh _users DEK), so re-running is safe ONLY while\n // no recovery has been enrolled yet — guaranteed here because enrollment\n // (Stage C) runs strictly after Stage A completes.\n const partitionCollections = [...partitionDeks.keys()]\n const priorDeks = existingKeyring ? (JSON.parse(existingKeyring._data) as KeyringFile).deks : {}\n const ownerMinted = existingKeyring !== null && partitionCollections.every((c) => c in priorDeks)\n if (!ownerMinted) {\n // Resolve the owner secret. Managed mode mints a random secret, seals\n // it under the provider, and persists _meta/sealed-secret (so the\n // partition auto-unlocks on the recipient's device); standard mode uses the\n // caller's secret. Idempotent under retry — resolveManagedSecret's reopen\n // arm reuses an already-sealed secret.\n const secret = isManaged(opts)\n ? await resolveManagedSecret(store, vaultName, opts.sealingKey)\n : opts.secret\n\n // Mint the owner keyring (KEK + _users DEK + canary, written to disk).\n const unlocked = await createOwnerKeyring(store, vaultName, { userId: userId, secret: secret })\n\n // Merge the partition DEKs (wrapped under the new KEK) into the keyring.\n const env = await store.get(vaultName, '_keyring', userId)\n if (!env) throw new AdoptionStateError(`keyring write for \"${userId}\" did not persist`)\n const keyringFile = JSON.parse(env._data) as KeyringFile\n const kek = unlocked.kek\n if (!kek) throw new AdoptionStateError(`owner keyring for \"${userId}\" has no KEK to wrap partition DEKs under`)\n const mergedDeks: Record<string, string> = { ...keyringFile.deks }\n for (const [collection, dek] of partitionDeks) {\n mergedDeks[collection] = await wrapKey(dek, kek)\n }\n const mergedFile: KeyringFile = { ...keyringFile, deks: mergedDeks }\n await store.put(vaultName, '_keyring', userId, { ...env, _data: JSON.stringify(mergedFile) })\n }\n\n // Stage B — record the ownership transition on the carried\n // audit chain (carryLedger sealed the _ledger DEK). No-op without that DEK.\n // Idempotent: appended only if the closing `transfer-seal-consumed` entry is\n // absent, so a retry does not duplicate the pair.\n const ledgerDek = partitionDeks.get(LEDGER_COLLECTION)\n if (ledgerDek) {\n const ledger = new LedgerStore({\n adapter: store,\n vault: vaultName,\n encrypted: true,\n getDEK: async () => ledgerDek,\n actor: userId,\n })\n const creationReason = `creation-of-new-owner:${userId}`\n const consumedReason = `transfer-seal-consumed:${adoption.sealId}`\n // Gate each append on its own presence — a crash or store error strictly\n // between the two adjacent puts would otherwise re-append the first one\n // on retry. The pair is the audit record, not a single transaction.\n const recordedReasons = new Set((await ledger.loadAllEntries()).map((e) => e.reason))\n if (!recordedReasons.has(creationReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: creationReason })\n }\n if (!recordedReasons.has(consumedReason)) {\n await ledger.append({ op: 'lifecycle', collection: '', id: '', version: 0, actor: '', payloadHash: '', reason: consumedReason })\n }\n }\n\n // Stage C — Managed mode: enroll the mandatory strong recovery\n // by orchestrating the existing public ceremony. The partition is\n // now a managed-mode vault on disk (sealed secret + keyring), so we\n // open it as a normal client and let openVaultAndEnrollRecovery do the\n // gate-bypass + enroll + re-assert. Dynamic import keeps the Noydb class\n // out of the @noy-db/hub/cargo static graph. Runs BEFORE seal destruction\n // so a failure here leaves the seal intact and the call retryable.\n if (isManaged(opts)) {\n const { createNoydb } = await import('../kernel/noydb.js')\n const db = await createNoydb({\n store,\n user: userId,\n secretMode: 'managed',\n sealingKey: opts.sealingKey,\n shamirRecovery: opts.shamirRecovery,\n })\n await db.team.openVaultAndEnrollRecovery(vaultName, { recovery: opts.recovery })\n }\n\n // Stage D — Destroy the transfer seal LAST — the commit point. Everything\n // above is either idempotent or resumable, so the seal is only consumed\n // once the owner keyring (and, in managed mode, strong recovery) is\n // durably in place. Retain sealId + consumedAt for audit.\n const consumed = { sealId: adoption.sealId, adoptedAt: adoption.adoptedAt, consumedAt: new Date().toISOString() }\n await store.put(vaultName, '_meta', 'adoption', { ...adoptionEnv, _data: JSON.stringify(consumed) })\n\n return { vaultName, userId }\n}\n","/**\n * Read-side counterpart to `extractPartition`: decrypt an\n * extracted-partition bundle's records to plaintext using its transfer\n * key, WITHOUT adopting it into a vault. Used by an outward\n * orchestration layer's reconcile/merge and field-authority flows to\n * compare incoming records against a receiver. The transfer key validates the bundle\n * (wrong key throws).\n * @module\n */\nimport type { EncryptedEnvelope } from '../kernel/types.js'\nimport { openEnvelopeJson } from '../kernel/enclave/index.js'\nimport { readPodHeader, readPod, parseExtractedPartitionBody } from '../with-pod/bundle.js'\nimport { unsealDeks } from './adopt-partition.js'\n\n/** One decrypted record from an extracted-partition compartment. */\nexport interface DecryptedRecord {\n readonly id: string\n readonly record: Record<string, unknown>\n /** Source envelope write timestamp (ISO) — for last-write-wins merges. */\n readonly ts: string\n /** Source envelope version. */\n readonly version: number\n /** Provenance source id (FR-5). Present only when the source collection had provenance:true and a source was supplied on put. */\n readonly source?: string\n /** ISO-8601 timestamp the provenance source was recorded (FR-5). */\n readonly sourceTs?: string\n}\n\n/**\n * Decrypt every record of an extracted-partition bundle to plaintext,\n * grouped by collection. Throws if the bundle isn't an\n * extracted-partition or the transfer key is wrong.\n */\nexport async function decryptExtractedPartition(\n bundleBytes: Uint8Array,\n transferKey: Uint8Array,\n): Promise<Record<string, DecryptedRecord[]>> {\n const header = readPodHeader(bundleBytes)\n if (header.bundleKind !== 'extracted-partition' || header.transferSeal === undefined) {\n throw new Error('decryptExtractedPartition: bundle is not an extracted-partition.')\n }\n const { dumpJson } = await readPod(bundleBytes)\n const { dump, seal } = parseExtractedPartitionBody(dumpJson)\n const deks = await unsealDeks(seal, transferKey) // throws TransferSealError on wrong key\n const backup = JSON.parse(dump) as { collections: Record<string, Record<string, EncryptedEnvelope>> }\n const out: Record<string, DecryptedRecord[]> = {}\n for (const [collection, byId] of Object.entries(backup.collections)) {\n const dek = deks.get(collection)\n if (dek === undefined) continue // no DEK sealed for this collection — skip\n const recs: DecryptedRecord[] = []\n for (const [id, env] of Object.entries(byId)) {\n const plaintext = await openEnvelopeJson(env, dek)\n const body = JSON.parse(plaintext) as Record<string, unknown>\n recs.push({\n id,\n record: { ...body, id },\n ts: env._ts,\n version: env._v,\n ...(env._source !== undefined ? { source: env._source } : {}),\n ...(env._sourceTs !== undefined ? { sourceTs: env._sourceTs } : {}),\n })\n }\n out[collection] = recs\n }\n return out\n}\n"],"mappings":"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","names":[]}
|