@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-audit/portability/withdraw-accessible.ts"],"sourcesContent":["/**\n * `unilateralWithdrawal`: a non-owner extracts their accessible scope\n * AND disposes of the source copy. Destructive; gated by the default-off\n * fail-closed built-in `client-unilateral-withdraw` policy (checked in the\n * UserApi wrapper).\n *\n * Two source dispositions:\n * - 'delete' — delete-closure: the records leave the source vault entirely.\n * - 'freeze' — the firm retains a cryptographically-frozen, read-only, write-once\n * snapshot (hash-pinned in the tamper-evident ledger) while the live\n * records are removed.\n *\n * Ordering guarantees no data loss: the client's re-keyed export bundle (and the\n * freeze snapshot) are produced BEFORE anything is deleted.\n *\n * The `freezeAndDeleteClosure` core is shared with the two-party approval path\n * (`request-withdrawal.ts`).\n */\nimport type { Vault } from '../../kernel/vault.js'\nimport { buildRecordEnvelope, sha256Hex } from '../../kernel/enclave/index.js'\nimport { ReadOnlyError } from '../../kernel/errors.js'\nimport { resolveAccessibleCollections, buildAccessibleBundle } from './export-accessible.js'\n\nexport const FROZEN_SNAPSHOTS_COLLECTION = '_frozen_snapshots'\nconst ENC = new TextEncoder()\n\n/** 24-hex-char random id (used for withdrawal ids + request ids). */\nexport function randomId(): string {\n const b = globalThis.crypto.getRandomValues(new Uint8Array(12))\n return Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('')\n}\n\nexport interface FrozenSnapshotRef {\n readonly withdrawalId: string\n readonly sha256: string\n readonly recordCount: number\n readonly frozenAt: string\n}\n\nexport interface WithdrawAccessibleOptions {\n /** Legal/contractual basis recorded in the audit (e.g. 'gdpr-art-17'). */\n readonly legalBasis: string\n /** Re-key the exported bundle to a new owner secret. */\n readonly reKey?: { readonly secret: string }\n /** Source disposition. Default 'delete'. */\n readonly disposition?: 'delete' | 'freeze'\n readonly scope?: { readonly collections?: readonly string[] }\n /** Stable id for idempotent resume (default random). */\n readonly withdrawalId?: string\n}\n\nexport interface WithdrawResult {\n readonly bundle: Uint8Array\n readonly snapshot?: FrozenSnapshotRef\n}\n\n/**\n * Freeze a write-once, hash-pinned snapshot of the current ciphertext for\n * `collections` WITHOUT touching the live records. Returns the snapshot ref.\n *\n * This is the non-destructive core shared by two callers:\n * - `freezeAndDeleteClosure` (withdrawal, disposition `'freeze'`) — which\n * pins the snapshot and THEN delete-closures the live records.\n * - FR-6 `liberateVault` (custody) — which pins a PRE-liberation evidence\n * snapshot but PRESERVES the live data for the new owner (operational\n * continuity; liberation transfers, it does not erase).\n *\n * The snapshot put is write-once (CAS expectedVersion 0) and the ledger pin is\n * appended on success, so a crashed run re-run with the same `withdrawalId` is\n * safe. The snapshot is taken under the vault's own firm-owned DEKs — no re-key.\n */\nexport async function freezeSnapshotOnly(\n vault: Vault,\n collections: readonly string[],\n opts: { actorUserId: string; withdrawalId?: string },\n): Promise<FrozenSnapshotRef> {\n const { name: vaultName, adapter } = vault._introspectState()\n\n // Enumerate the closure (record ids per collection).\n const closure: Array<{ collection: string; id: string }> = []\n for (const c of collections) {\n for (const id of await adapter.list(vaultName, c)) closure.push({ collection: c, id })\n }\n\n const withdrawalId = opts.withdrawalId ?? `wd-${randomId()}`\n const snap: Record<string, Record<string, unknown>> = {}\n for (const { collection, id } of closure) {\n const env = await adapter.get(vaultName, collection, id)\n if (env) (snap[collection] ??= {})[id] = env\n }\n const frozenAt = new Date().toISOString()\n const body = JSON.stringify({ withdrawalId, frozenAt, by: opts.actorUserId, collections: snap })\n const sha = await sha256Hex(ENC.encode(body))\n // Write-once: expectedVersion 0 rejects an overwrite (idempotent resume).\n await adapter.put(\n vaultName,\n FROZEN_SNAPSHOTS_COLLECTION,\n withdrawalId,\n buildRecordEnvelope(\n { collection: FROZEN_SNAPSHOTS_COLLECTION, id: withdrawalId, by: opts.actorUserId, version: 1 },\n { ts: frozenAt, iv: '', data: body},\n ),\n 0,\n )\n // Hash-pin into the tamper-evident ledger — makes the snapshot provably unaltered.\n await vault._getLedgerOrNull()?.append({\n op: 'lifecycle', collection: '', id: '', version: 0, actor: opts.actorUserId, payloadHash: '',\n reason: `withdrawal-frozen-snapshot:${withdrawalId}:${sha}`,\n })\n return { withdrawalId, sha256: sha, recordCount: closure.length, frozenAt }\n}\n\n/**\n * Dispose of the source records for `collections`: optionally freeze a\n * write-once hash-pinned snapshot of the original ciphertext, then\n * delete-closure the live records. Returns the snapshot ref on freeze.\n *\n * Caller MUST have produced the portable bundle FIRST — this is destructive.\n * The delete is best-effort + idempotent (re-deleting an absent record is a\n * no-op) and the snapshot put is write-once (CAS expectedVersion 0), so a\n * crashed run re-run with the same `withdrawalId` is safe.\n */\nexport async function freezeAndDeleteClosure(\n vault: Vault,\n collections: readonly string[],\n opts: { disposition: 'delete' | 'freeze'; actorUserId: string; withdrawalId?: string },\n): Promise<FrozenSnapshotRef | undefined> {\n // freeze: pin a write-once, hash-pinned snapshot BEFORE any delete. The\n // snapshot-only core is shared with FR-6 liberation (which never deletes).\n const snapshot = opts.disposition === 'freeze'\n ? await freezeSnapshotOnly(vault, collections, {\n actorUserId: opts.actorUserId,\n ...(opts.withdrawalId ? { withdrawalId: opts.withdrawalId } : {}),\n })\n : undefined\n\n // delete-closure — only after the snapshot is durable.\n const { name: vaultName, adapter } = vault._introspectState()\n for (const c of collections) {\n for (const id of await adapter.list(vaultName, c)) {\n await vault.collection(c).delete(id)\n }\n }\n\n return snapshot\n}\n\nexport async function withdrawAccessibleData(\n vault: Vault,\n opts: WithdrawAccessibleOptions,\n): Promise<WithdrawResult> {\n const { keyring } = vault._introspectState()\n const disposition = opts.disposition ?? 'delete'\n\n // Owner-class roles hold blanket authority — they use extractPartition.\n if (keyring.role === 'owner' || keyring.role === 'admin') {\n throw new ReadOnlyError(\n 'unilateralWithdrawal is the scoped self-service path; an owner/admin should use extractPartition',\n )\n }\n // FR-6: a custodian must NOT destructively SEVER the vault. It holds the DEKs\n // and operates fully, but ownership is claimed only through the audited\n // Liberate ceremony — never by a one-sided delete/freeze withdrawal. Fires\n // BEFORE the operator rw-scope path below (where a custodian would otherwise\n // be rejected with the wrong \"requires rw access\" message).\n if (keyring.role === 'custodian') {\n throw new ReadOnlyError(\n 'a custodian cannot destructively withdraw/sever; use vault.custody.liberate for an audited ownership claim',\n )\n }\n // client/viewer are read-only by construction (see hasWritePermission): a\n // destructive withdrawal they cannot self-serve — they use the two-party\n // requestWithdrawal flow where firm authority executes the delete-closure.\n if (keyring.role === 'client' || keyring.role === 'viewer') {\n throw new ReadOnlyError(\n 'read-only role cannot self-serve a destructive withdrawal — use requestWithdrawal (two-party)',\n )\n }\n // Operator path: destructive ops need rw on the whole scope.\n const collections = resolveAccessibleCollections(keyring, opts.scope, true)\n if (!collections || collections.length === 0) {\n throw new ReadOnlyError(\n 'unilateralWithdrawal requires rw access on the withdrawn collections — use requestWithdrawal for read-only scope',\n )\n }\n\n // 1. Produce the client's re-keyed portable copy FIRST (nothing destroyed yet).\n const bundle = await buildAccessibleBundle(vault, collections, opts.reKey)\n\n // 2. + 3. freeze (optional) then delete-closure.\n const snapshot = await freezeAndDeleteClosure(vault, collections, {\n disposition, actorUserId: keyring.userId, ...(opts.withdrawalId ? { withdrawalId: opts.withdrawalId } : {}),\n })\n\n // 4. audit\n await vault._getLedgerOrNull()?.append({\n op: 'lifecycle', collection: '', id: '', version: 0, actor: keyring.userId, payloadHash: '',\n reason: `user-unilateral-withdrawal:${keyring.userId}:${disposition}:${opts.legalBasis}`,\n })\n\n return snapshot ? { bundle, snapshot } : { bundle }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/with-commit/vault-head/strategy.ts
|
|
2
|
+
var VAULT_HEAD_COLLECTION = "_head";
|
|
3
|
+
var DEFAULT_HEAD_BUCKETS = 256;
|
|
4
|
+
function notEnabled(op) {
|
|
5
|
+
return new Error(
|
|
6
|
+
`${op} requires the vault-head capability. Pass \`vaultHeadStrategy: withVaultHead()\` from "@noy-db/hub/vault-head" to createNoydb().`
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
var NO_VAULT_HEAD = {
|
|
10
|
+
enabled: false,
|
|
11
|
+
bucketFor: () => "",
|
|
12
|
+
async note() {
|
|
13
|
+
},
|
|
14
|
+
async expected() {
|
|
15
|
+
throw notEnabled("vault.verifyHead()");
|
|
16
|
+
},
|
|
17
|
+
async knownIn() {
|
|
18
|
+
throw notEnabled("vault.verifyHead()");
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
VAULT_HEAD_COLLECTION,
|
|
24
|
+
DEFAULT_HEAD_BUCKETS,
|
|
25
|
+
NO_VAULT_HEAD
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=chunk-HK67PX4J.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-commit/vault-head/strategy.ts"],"sourcesContent":["/**\n * `withVaultHead()` — the opt-in detector for **omission** (#1044).\n *\n * ## What it is for, and what it deliberately is not\n *\n * #1041 made every envelope self-authenticating at its identity, and #1042\n * made the merge reject one that is not. What neither can catch is **absence**:\n * a store that serves a genuine, unmodified `v1` when `v7` exists, or serves\n * nothing at all. An authentic old envelope is indistinguishable from an\n * authentic current one without external knowledge of what *should* be there.\n *\n * The head is that external knowledge: an authenticated `{id → version}`\n * manifest the client writes and the store cannot forge. It is **not** a Merkle\n * chain over content — content integrity is already handled, so the head needs\n * only versions.\n *\n * ## Why it is opt-in when AAD is not\n *\n * AAD costs nothing to coordinate and closes *alteration*, so it is kernel. The\n * head costs a write per commit and needs anti-entropy; on a single-device\n * offline vault it defends against nothing. Making it kernel would tax every\n * user for a multi-writer property. ADR 0003, Decision 3.\n *\n * `SECURITY.md` states the split exactly: a store cannot alter, relocate,\n * re-tier, re-author or rewind a record; **without `withVaultHead()` it can\n * still withhold or omit.**\n *\n * ## Bucketed, and why that is not a compromise\n *\n * ADR 0003 left head granularity open and asked for it to be sized early.\n * Measured, at the documented 50K-record vault ceiling:\n *\n * | shape | write cost per commit | detection |\n * |---|---|---|\n * | one per-vault manifest | **1.1 MiB** | per-record |\n * | one per collection | the same problem, renamed | per-record |\n * | **bucketed, 256 buckets** | **~4.4 KiB** | per-record |\n *\n * Bucketing changes *only* write amplification. Each bucket still lists every\n * one of its records' versions, so detection stays per-record — which is why\n * this is not a trade-off between cost and strength. Verifying one pulled\n * record reads one bucket rather than a megabyte.\n *\n * ## Wired as a bus OBSERVER, not a call in `collection.ts`\n *\n * That is what `kernel-surface` means by \"register on the ServiceBus instead of\n * growing this file\", and it keeps the hot write path untouched for anyone who\n * has not opted in: nothing is registered at all, so `hasHandlers` stays false\n * and the dispatch never happens.\n *\n * Both `afterPut` AND `afterDelete`, because a delete is a version bump like\n * any other. A head that stopped tracking deleted records would let a store\n * resurrect one by withholding the tombstone — precisely the omission this\n * exists to catch.\n *\n * ⚠️ The registration must be UNCONDITIONAL on anything but `enabled`. Its first\n * draft sat inside `#registerForgetHooks()`, which returns early when no forget\n * subjects are declared, so the head silently recorded nothing unless the vault\n * happened to use an unrelated feature. The tests caught it only because they\n * asserted a COUNT rather than \"no error\" — a head that records nothing raises\n * no errors and sweeps perfectly clean.\n *\n * @packageDocumentation\n */\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\n\n/** Reserved collection the head's buckets live in. */\nexport const VAULT_HEAD_COLLECTION = '_head' as const\n\n/** Default bucket count — see the sizing table above. */\nexport const DEFAULT_HEAD_BUCKETS = 256\n\nexport interface WithVaultHeadOptions {\n /**\n * How many buckets the manifest is split across. Higher means smaller writes\n * and more reads to sweep the whole vault; lower means the opposite.\n * Changing it on an existing vault re-homes every entry, so treat it as a\n * layout decision rather than a tuning knob.\n */\n readonly buckets?: number\n}\n\n/** One record's expected state, as the client last committed it. */\nexport interface HeadEntry {\n readonly collection: string\n readonly id: string\n readonly version: number\n}\n\nexport interface VaultHeadStrategy {\n /**\n * Is the head actually keeping a manifest?\n *\n * An explicit flag rather than an identity check against the stub: the kernel\n * decides whether to register the write-path observer at all, and\n * `port-layering` forbids it importing `NO_VAULT_HEAD` to compare. Without\n * this, every write in a vault that never opted in would still pay a cache\n * lookup and a no-op call.\n */\n readonly enabled: boolean\n\n /** Bucket a record id lands in — pure, so callers can pre-group a batch. */\n bucketFor(collection: string, id: string): string\n\n /**\n * Record `entry` as the client's expectation for that record.\n *\n * Called on every committed write, INSIDE the same logical operation, so\n * there is no window where the record exists and the head does not know it.\n */\n note(\n store: NoydbStore,\n vault: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n entry: HeadEntry,\n ): Promise<void>\n\n /**\n * What the client expects for `collection/id`, or `null` if the head has\n * never seen it.\n */\n expected(\n store: NoydbStore,\n vault: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n collection: string,\n id: string,\n ): Promise<number | null>\n\n /**\n * Every record the head knows about in `collection` — the set a sweep\n * compares against what the store actually serves, which is how DELETION\n * SUPPRESSION is caught. A withheld record is absent from the store but\n * present here.\n */\n knownIn(\n store: NoydbStore,\n vault: string,\n getDEK: (collection: string) => Promise<EnclaveKey>,\n collection: string,\n ): Promise<ReadonlyMap<string, number>>\n}\n\nfunction notEnabled(op: string): Error {\n return new Error(\n `${op} requires the vault-head capability. Pass \\`vaultHeadStrategy: withVaultHead()\\` ` +\n 'from \"@noy-db/hub/vault-head\" to createNoydb().',\n )\n}\n\n/**\n * The un-opted-in stub. `note` is a NO-OP rather than a throw: the write path\n * calls it unconditionally, and a vault without the head must simply not keep\n * one. The READ side throws, because asking what the head expects when there is\n * no head is a caller error, not a silent \"nothing to report\" — the latter\n * would look exactly like a clean sweep.\n */\nexport const NO_VAULT_HEAD: VaultHeadStrategy = {\n enabled: false,\n bucketFor: () => '',\n async note() {},\n async expected() { throw notEnabled('vault.verifyHead()') },\n async knownIn() { throw notEnabled('vault.verifyHead()') },\n}\n\nexport type { EncryptedEnvelope }\n"],"mappings":";AAoEO,IAAM,wBAAwB;AAG9B,IAAM,uBAAuB;AAyEpC,SAAS,WAAW,IAAmB;AACrC,SAAO,IAAI;AAAA,IACT,GAAG,EAAE;AAAA,EAEP;AACF;AASO,IAAM,gBAAmC;AAAA,EAC9C,SAAS;AAAA,EACT,WAAW,MAAM;AAAA,EACjB,MAAM,OAAO;AAAA,EAAC;AAAA,EACd,MAAM,WAAW;AAAE,UAAM,WAAW,oBAAoB;AAAA,EAAE;AAAA,EAC1D,MAAM,UAAU;AAAE,UAAM,WAAW,oBAAoB;AAAA,EAAE;AAC3D;","names":[]}
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
} from "./chunk-KHKKD62N.js";
|
|
5
5
|
import {
|
|
6
6
|
derivePersistedSchema
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-P6ASQOZR.js";
|
|
8
8
|
import {
|
|
9
9
|
loadPersistedSchema
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-YXA2TCTZ.js";
|
|
11
11
|
|
|
12
12
|
// src/with-shape/introspection/walk.ts
|
|
13
13
|
var INTERNAL_PREFIX = "_";
|
|
@@ -238,4 +238,4 @@ function summariseAggregateOp(value) {
|
|
|
238
238
|
export {
|
|
239
239
|
dumpVaultSchema
|
|
240
240
|
};
|
|
241
|
-
//# sourceMappingURL=chunk-
|
|
241
|
+
//# sourceMappingURL=chunk-HM4JSXX2.js.map
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
dekKey
|
|
3
|
+
} from "./chunk-POS53FG6.js";
|
|
1
4
|
import {
|
|
2
5
|
TierNotGrantedError
|
|
3
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JBIV4FLR.js";
|
|
4
7
|
|
|
5
8
|
// src/with-party/team/tiers.ts
|
|
6
|
-
function dekKey(collection, tier) {
|
|
7
|
-
if (tier <= 0) return collection;
|
|
8
|
-
return `${collection}#${tier}`;
|
|
9
|
-
}
|
|
10
9
|
function assertTierAccess(keyring, collection, tier) {
|
|
11
10
|
if (tier <= 0) return;
|
|
12
11
|
if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian") return;
|
|
@@ -16,7 +15,6 @@ function assertTierAccess(keyring, collection, tier) {
|
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
export {
|
|
19
|
-
dekKey,
|
|
20
18
|
assertTierAccess
|
|
21
19
|
};
|
|
22
|
-
//# sourceMappingURL=chunk-
|
|
20
|
+
//# sourceMappingURL=chunk-HTGRQZFB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-party/team/tiers.ts"],"sourcesContent":["/**\n * Hierarchical access — tier-aware keyring helpers.\n *\n * The keyring's existing `deks: Map<string, CryptoKey>` is keyed by\n * collection name. extends the key space:\n *\n * `'invoices'` — tier-0 DEK (unchanged from v0.x)\n * `'invoices#1'` — tier-1 DEK\n * `'invoices#2'` — tier-2 DEK\n *\n * Tier 0 keeps the bare collection name so any keyring written\n * before tiers existed loads without migration. Tiers ≥ 1 use `#N`\n * suffixes that\n * would be invalid as user-supplied collection names (see\n * `ReservedCollectionNameError` — `#` is reserved).\n *\n * @module\n */\n\nimport type { UnlockedKeyring } from './keyring.js'\nimport { TierNotGrantedError } from '../../kernel/errors.js'\n\n/** Canonical DEK key for a given collection + tier. Tier 0 → bare name. */\n// Moved to `kernel/tier-visibility.ts` (#1042): the kernel needs it to resolve\n// a pulled envelope's DEK, and `port-layering` forbids the spine importing a\n// `with-*` service. Re-exported so every existing importer is unchanged.\nimport { dekKey } from '../../kernel/tier-visibility.js'\nexport { dekKey }\n\n/**\n * Returns the user's effective clearance for a given collection: the\n * maximum tier for which their keyring holds a DEK. Falls back to 0\n * when the user has only the tier-0 DEK (or none — the getDEK caller\n * will raise separately).\n */\nexport function effectiveClearance(keyring: UnlockedKeyring, collection: string): number {\n let max = 0\n const prefix = `${collection}#`\n for (const key of keyring.deks.keys()) {\n if (!key.startsWith(prefix)) continue\n const suffix = key.slice(prefix.length)\n const n = Number.parseInt(suffix, 10)\n if (Number.isFinite(n) && n > max) max = n\n }\n return max\n}\n\n/**\n * Assert the caller is cleared for the requested tier. Owners and\n * admins always pass (they can mint any new tier DEK on demand);\n * other roles must already hold the tier DEK — via a prior grant or\n * an active delegation — otherwise this throws `TierNotGrantedError`.\n *\n * This gate runs BEFORE `getDEK()` on the mutation path so a\n * non-cleared operator never has the opportunity to silently\n * auto-create a tier DEK they shouldn't have.\n */\nexport function assertTierAccess(\n keyring: UnlockedKeyring,\n collection: string,\n tier: number,\n): void {\n if (tier <= 0) return\n // FR-6: custodian operates every collection at every tier (admin-level\n // operational authority), so it may mint a tier DEK on demand like admin.\n if (keyring.role === 'owner' || keyring.role === 'admin' || keyring.role === 'custodian') return\n if (!keyring.deks.has(dekKey(collection, tier))) {\n throw new TierNotGrantedError(collection, tier)\n }\n}\n"],"mappings":";;;;;;;;AAyDO,SAAS,iBACd,SACA,YACA,MACM;AACN,MAAI,QAAQ,EAAG;AAGf,MAAI,QAAQ,SAAS,WAAW,QAAQ,SAAS,WAAW,QAAQ,SAAS,YAAa;AAC1F,MAAI,CAAC,QAAQ,KAAK,IAAI,OAAO,YAAY,IAAI,CAAC,GAAG;AAC/C,UAAM,IAAI,oBAAoB,YAAY,IAAI;AAAA,EAChD;AACF;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
readPath
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NSITFOCJ.js";
|
|
4
4
|
|
|
5
5
|
// src/with-lookup/indexing/eager-indexes.ts
|
|
6
6
|
function normalizeIndexDefs(defs) {
|
|
@@ -156,4 +156,4 @@ export {
|
|
|
156
156
|
normalizeIndexDefs,
|
|
157
157
|
CollectionIndexes
|
|
158
158
|
};
|
|
159
|
-
//# sourceMappingURL=chunk-
|
|
159
|
+
//# sourceMappingURL=chunk-I3ISCYT7.js.map
|
|
@@ -7,20 +7,21 @@ import {
|
|
|
7
7
|
hashEntry,
|
|
8
8
|
paddedIndex,
|
|
9
9
|
sha256Hex
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-RC4BROYR.js";
|
|
11
11
|
import {
|
|
12
|
+
buildRecordAad,
|
|
12
13
|
openEnvelopeJson
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-2LPVI4PX.js";
|
|
14
15
|
import {
|
|
15
|
-
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
buildRecordEnvelope
|
|
17
|
+
} from "./chunk-N4CNVSUF.js";
|
|
17
18
|
import {
|
|
18
|
-
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
encrypt
|
|
20
|
+
} from "./chunk-W5LHQXZU.js";
|
|
20
21
|
import {
|
|
21
22
|
LedgerContentionError,
|
|
22
23
|
isConflictError
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-JBIV4FLR.js";
|
|
24
25
|
|
|
25
26
|
// src/with-commit/history/ledger/patch.ts
|
|
26
27
|
function computePatch(prev, next) {
|
|
@@ -357,7 +358,7 @@ var LedgerStore = class {
|
|
|
357
358
|
let deltaEnvelope;
|
|
358
359
|
let deltaHash;
|
|
359
360
|
if (input.delta !== void 0) {
|
|
360
|
-
deltaEnvelope = await this.encryptDelta(input.delta);
|
|
361
|
+
deltaEnvelope = await this.encryptDelta(input.delta, nextIndex);
|
|
361
362
|
deltaHash = await sha256Hex(deltaEnvelope._data);
|
|
362
363
|
}
|
|
363
364
|
const entryBase = {
|
|
@@ -418,7 +419,7 @@ var LedgerStore = class {
|
|
|
418
419
|
return JSON.parse(envelope._data);
|
|
419
420
|
}
|
|
420
421
|
const dek = await this.getDEK(LEDGER_COLLECTION);
|
|
421
|
-
const json = await openEnvelopeJson(envelope, dek);
|
|
422
|
+
const json = await openEnvelopeJson({ collection: LEDGER_DELTAS_COLLECTION, id: paddedIndex(index) }, envelope, dek);
|
|
422
423
|
return JSON.parse(json);
|
|
423
424
|
}
|
|
424
425
|
/**
|
|
@@ -455,29 +456,22 @@ var LedgerStore = class {
|
|
|
455
456
|
}
|
|
456
457
|
return purged;
|
|
457
458
|
}
|
|
458
|
-
/**
|
|
459
|
-
|
|
459
|
+
/**
|
|
460
|
+
* Encrypt a JSON Patch into an envelope for storage. Mirrors encryptEntry.
|
|
461
|
+
*
|
|
462
|
+
* `index` is the ledger index this delta will be stored under — the caller
|
|
463
|
+
* knows it before the entry exists, and it is what makes the envelope's
|
|
464
|
+
* identity bindable (#1051).
|
|
465
|
+
*/
|
|
466
|
+
async encryptDelta(patch, index) {
|
|
467
|
+
const identity = { collection: LEDGER_DELTAS_COLLECTION, id: paddedIndex(index), by: this.actor, version: 1 };
|
|
460
468
|
const json = JSON.stringify(patch);
|
|
461
469
|
if (!this.encrypted) {
|
|
462
|
-
return {
|
|
463
|
-
_noydb: NOYDB_FORMAT_VERSION,
|
|
464
|
-
_v: 1,
|
|
465
|
-
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
466
|
-
_iv: "",
|
|
467
|
-
_data: json,
|
|
468
|
-
_by: this.actor
|
|
469
|
-
};
|
|
470
|
+
return buildRecordEnvelope(identity, { iv: "", data: json });
|
|
470
471
|
}
|
|
471
472
|
const dek = await this.getDEK(LEDGER_COLLECTION);
|
|
472
|
-
const { iv, data } = await encrypt(json, dek);
|
|
473
|
-
return {
|
|
474
|
-
_noydb: NOYDB_FORMAT_VERSION,
|
|
475
|
-
_v: 1,
|
|
476
|
-
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
477
|
-
_iv: iv,
|
|
478
|
-
_data: data,
|
|
479
|
-
_by: this.actor
|
|
480
|
-
};
|
|
473
|
+
const { iv, data } = await encrypt(json, dek, buildRecordAad(identity));
|
|
474
|
+
return buildRecordEnvelope(identity, { iv, data });
|
|
481
475
|
}
|
|
482
476
|
/**
|
|
483
477
|
* Read all entries in ascending-index order. Used internally by
|
|
@@ -496,7 +490,7 @@ var LedgerStore = class {
|
|
|
496
490
|
key
|
|
497
491
|
);
|
|
498
492
|
if (!envelope) continue;
|
|
499
|
-
entries.push(await this.decryptEntry(envelope));
|
|
493
|
+
entries.push(await this.decryptEntry(key, envelope));
|
|
500
494
|
}
|
|
501
495
|
return entries;
|
|
502
496
|
}
|
|
@@ -674,35 +668,23 @@ var LedgerStore = class {
|
|
|
674
668
|
* writes should always bump the index).
|
|
675
669
|
*/
|
|
676
670
|
async encryptEntry(entry) {
|
|
671
|
+
const identity = { collection: LEDGER_COLLECTION, id: paddedIndex(entry.index), by: entry.actor, version: entry.index + 1 };
|
|
672
|
+
const body = { ts: entry.ts };
|
|
677
673
|
const json = canonicalJson(entry);
|
|
678
674
|
if (!this.encrypted) {
|
|
679
|
-
return {
|
|
680
|
-
_noydb: NOYDB_FORMAT_VERSION,
|
|
681
|
-
_v: entry.index + 1,
|
|
682
|
-
_ts: entry.ts,
|
|
683
|
-
_iv: "",
|
|
684
|
-
_data: json,
|
|
685
|
-
_by: entry.actor
|
|
686
|
-
};
|
|
675
|
+
return buildRecordEnvelope(identity, { ...body, iv: "", data: json });
|
|
687
676
|
}
|
|
688
677
|
const dek = await this.getDEK(LEDGER_COLLECTION);
|
|
689
|
-
const { iv, data } = await encrypt(json, dek);
|
|
690
|
-
return {
|
|
691
|
-
_noydb: NOYDB_FORMAT_VERSION,
|
|
692
|
-
_v: entry.index + 1,
|
|
693
|
-
_ts: entry.ts,
|
|
694
|
-
_iv: iv,
|
|
695
|
-
_data: data,
|
|
696
|
-
_by: entry.actor
|
|
697
|
-
};
|
|
678
|
+
const { iv, data } = await encrypt(json, dek, buildRecordAad(identity));
|
|
679
|
+
return buildRecordEnvelope(identity, { ...body, iv, data });
|
|
698
680
|
}
|
|
699
681
|
/** Decrypt an envelope into a LedgerEntry. Throws on bad key / tamper. */
|
|
700
|
-
async decryptEntry(envelope) {
|
|
682
|
+
async decryptEntry(key, envelope) {
|
|
701
683
|
if (!this.encrypted) {
|
|
702
684
|
return JSON.parse(envelope._data);
|
|
703
685
|
}
|
|
704
686
|
const dek = await this.getDEK(LEDGER_COLLECTION);
|
|
705
|
-
const json = await openEnvelopeJson(envelope, dek);
|
|
687
|
+
const json = await openEnvelopeJson({ collection: LEDGER_COLLECTION, id: key }, envelope, dek);
|
|
706
688
|
return JSON.parse(json);
|
|
707
689
|
}
|
|
708
690
|
};
|
|
@@ -717,4 +699,4 @@ export {
|
|
|
717
699
|
applyPatch,
|
|
718
700
|
LedgerStore
|
|
719
701
|
};
|
|
720
|
-
//# sourceMappingURL=chunk-
|
|
702
|
+
//# sourceMappingURL=chunk-IESNYJXZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 { buildRecordAad, buildRecordEnvelope, 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, nextIndex)\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({ collection: LEDGER_DELTAS_COLLECTION, id: paddedIndex(index) }, 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 /**\n * Encrypt a JSON Patch into an envelope for storage. Mirrors encryptEntry.\n *\n * `index` is the ledger index this delta will be stored under — the caller\n * knows it before the entry exists, and it is what makes the envelope's\n * identity bindable (#1051).\n */\n private async encryptDelta(patch: JsonPatch, index: number): Promise<EncryptedEnvelope> {\n const identity = { collection: LEDGER_DELTAS_COLLECTION, id: paddedIndex(index), by: this.actor, version: 1 }\n const json = JSON.stringify(patch)\n if (!this.encrypted) {\n return buildRecordEnvelope(identity, { iv: '', data: json})\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n return buildRecordEnvelope(identity, { iv, data})\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(key, 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 identity = { collection: LEDGER_COLLECTION, id: paddedIndex(entry.index), by: entry.actor, version: entry.index + 1 }\n const body = { ts: entry.ts }\n const json = canonicalJson(entry)\n if (!this.encrypted) {\n return buildRecordEnvelope(identity, { ...body, iv: '', data: json })\n }\n const dek = await this.getDEK(LEDGER_COLLECTION)\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n return buildRecordEnvelope(identity, { ...body, iv, data })\n }\n\n /** Decrypt an envelope into a LedgerEntry. Throws on bad key / tamper. */\n private async decryptEntry(key: string, 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({ collection: LEDGER_COLLECTION, id: key }, 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,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PortabilityNotEnabledError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-JBIV4FLR.js";
|
|
4
4
|
|
|
5
5
|
// src/with-audit/portability/strategy.ts
|
|
6
6
|
var NO_PORTABILITY = {
|
|
@@ -27,4 +27,4 @@ var NO_PORTABILITY = {
|
|
|
27
27
|
export {
|
|
28
28
|
NO_PORTABILITY
|
|
29
29
|
};
|
|
30
|
-
//# sourceMappingURL=chunk-
|
|
30
|
+
//# sourceMappingURL=chunk-J4MN6XIU.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
grant,
|
|
3
3
|
revoke
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-5W7YXFV5.js";
|
|
5
5
|
import {
|
|
6
6
|
CustodyNotEnabledError
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JBIV4FLR.js";
|
|
8
8
|
|
|
9
9
|
// src/with-party/custody/active.ts
|
|
10
10
|
function withCustody() {
|
|
@@ -16,7 +16,7 @@ function withCustody() {
|
|
|
16
16
|
return host._revokeCustodianImpl(revoke, vault, options, factors);
|
|
17
17
|
},
|
|
18
18
|
async liberate(vault, opts) {
|
|
19
|
-
const { liberateVault } = await import("./liberate-
|
|
19
|
+
const { liberateVault } = await import("./liberate-VEOZRESE.js");
|
|
20
20
|
return liberateVault(vault, opts);
|
|
21
21
|
}
|
|
22
22
|
};
|
|
@@ -74,4 +74,4 @@ export {
|
|
|
74
74
|
withCustody,
|
|
75
75
|
CustodyApi
|
|
76
76
|
};
|
|
77
|
-
//# sourceMappingURL=chunk-
|
|
77
|
+
//# sourceMappingURL=chunk-J4UJ5FRV.js.map
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
var STATE_VAULT_NAME = "__noydb_state__";
|
|
3
3
|
var USER_ENVELOPE_MAX_BYTES = 64 * 1024;
|
|
4
4
|
var USER_ENVELOPE_COLLECTION = "_users";
|
|
5
|
+
var ROSTER_KEY_ID = "_roster";
|
|
6
|
+
var BLOB_ADDRESS_KEY_ID = "_blob_addr";
|
|
5
7
|
|
|
6
8
|
// src/kernel/errors.ts
|
|
7
9
|
var NoydbError = class extends Error {
|
|
@@ -35,9 +37,26 @@ var DecryptionError = class extends NoydbError {
|
|
|
35
37
|
}
|
|
36
38
|
};
|
|
37
39
|
var TamperedError = class extends NoydbError {
|
|
38
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Why the tag check failed, when the enclave could tell (#1103).
|
|
42
|
+
*
|
|
43
|
+
* `'unbound-legacy-format'` — the body opens under an EMPTY AAD, so it was
|
|
44
|
+
* sealed before identity binding (#1041) and this is a format transition, not
|
|
45
|
+
* an attack. Positive evidence, not a guess: producing a body that decrypts
|
|
46
|
+
* under this DEK requires the DEK, which an untrusted store does not hold.
|
|
47
|
+
*
|
|
48
|
+
* Absent — no benign explanation was found. Treat as the security alert this
|
|
49
|
+
* error has always been.
|
|
50
|
+
*
|
|
51
|
+
* The field is additive and the throw is unchanged, so
|
|
52
|
+
* `catch (e) { if (e instanceof TamperedError) … }` keeps working; callers
|
|
53
|
+
* that want to tell a migration from a breach can now read this.
|
|
54
|
+
*/
|
|
55
|
+
reason;
|
|
56
|
+
constructor(message = "Data integrity check failed \u2014 record may have been tampered with", reason) {
|
|
39
57
|
super("TAMPERED", message);
|
|
40
58
|
this.name = "TamperedError";
|
|
59
|
+
if (reason !== void 0) this.reason = reason;
|
|
41
60
|
}
|
|
42
61
|
};
|
|
43
62
|
var InvalidKeyError = class extends NoydbError {
|
|
@@ -59,6 +78,30 @@ var KeyringCorruptError = class extends NoydbError {
|
|
|
59
78
|
this.intactCount = opts.intactCount;
|
|
60
79
|
}
|
|
61
80
|
};
|
|
81
|
+
var RE_SEED = " To re-seed: REMOVE THE VAULT FROM THIS DEVICE FIRST, then import the new bundle. Importing over a vault that is still present does not heal it \u2014 the stale keyring is loaded during setup, before the import can replace it, so the same error is raised again.";
|
|
82
|
+
function keyringTamperedMessage(userId, reason, format) {
|
|
83
|
+
const head = `Keyring for "${userId}" failed roster authentication (${reason}). `;
|
|
84
|
+
switch (reason) {
|
|
85
|
+
case "canary-missing":
|
|
86
|
+
case "roster-key-missing":
|
|
87
|
+
case "roster-tag-missing":
|
|
88
|
+
return head + "This vault was most likely written before 0.6.0-pre.21, which is when the roster became authenticated \u2014 that format change ships without a migration, so an existing vault must be re-seeded. The same state is what an untrusted store would produce by deleting the field to escape verification, and the two are indistinguishable from here, so access is refused either way. If this vault has been opened by 0.6.0-pre.21 or later before, treat it as the second case." + RE_SEED;
|
|
89
|
+
case "unparseable":
|
|
90
|
+
return head + "The file did not parse \u2014 truncation or corruption in transit or at rest is the likeliest cause, and a store serving deliberate garbage is indistinguishable from it. `verifyRoster()` reports which members are affected; `quarantineKeyring()` removes one.";
|
|
91
|
+
case "format-superseded":
|
|
92
|
+
return head + `This keyring declares ${format ? `keyring format ${format.from}` : "an older keyring format"}; this build requires ${format ? String(format.to) : "a newer one"}. The roster tag now protects a wider set of fields (the DEK key set joined it), so a genuine older tag cannot verify here. That format change ships without a migration, so an existing vault must be re-seeded.` + RE_SEED + " Access is refused either way, and the version field this branch reads selects only the wording \u2014 never the decision \u2014 so a store cannot use it to weaken anything.";
|
|
93
|
+
case "roster-tag-mismatch":
|
|
94
|
+
return head + "The roster tag is present but does not match the authority fields it protects, so those fields were altered after they were signed. Unlike a missing tag this is not a format-transition state \u2014 a same-format mismatch is one no released version ever wrote (an older FORMAT reports `format-superseded` instead) \u2014 so the store serving this vault, or something between you and it, has changed a member's role, permissions or expiry.";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
var KeyringTamperedError = class extends NoydbError {
|
|
98
|
+
details;
|
|
99
|
+
constructor(details) {
|
|
100
|
+
super("KEYRING_TAMPERED", keyringTamperedMessage(details.userId, details.reason, details.format));
|
|
101
|
+
this.name = "KeyringTamperedError";
|
|
102
|
+
this.details = details;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
62
105
|
var NoAccessError = class extends NoydbError {
|
|
63
106
|
constructor(message = "No access \u2014 user does not have a key for this collection") {
|
|
64
107
|
super("NO_ACCESS", message);
|
|
@@ -502,7 +545,6 @@ var PodVersionConflictError = class extends NoydbError {
|
|
|
502
545
|
this.remoteVersion = remoteVersion;
|
|
503
546
|
}
|
|
504
547
|
};
|
|
505
|
-
var BundleVersionConflictError = PodVersionConflictError;
|
|
506
548
|
var NetworkError = class extends NoydbError {
|
|
507
549
|
constructor(message = "Network error") {
|
|
508
550
|
super("NETWORK_ERROR", message);
|
|
@@ -664,10 +706,10 @@ var FieldNotQueryableError = class extends NoydbError {
|
|
|
664
706
|
this.field = field;
|
|
665
707
|
}
|
|
666
708
|
};
|
|
667
|
-
var
|
|
709
|
+
var PodIntegrityError = class extends NoydbError {
|
|
668
710
|
constructor(message) {
|
|
669
711
|
super("BUNDLE_INTEGRITY", `.noydb bundle integrity check failed: ${message}`);
|
|
670
|
-
this.name = "
|
|
712
|
+
this.name = "PodIntegrityError";
|
|
671
713
|
}
|
|
672
714
|
};
|
|
673
715
|
var PodHeaderVerificationError = class extends NoydbError {
|
|
@@ -679,7 +721,7 @@ var PodHeaderVerificationError = class extends NoydbError {
|
|
|
679
721
|
this.name = "PodHeaderVerificationError";
|
|
680
722
|
}
|
|
681
723
|
};
|
|
682
|
-
var
|
|
724
|
+
var PodSealMismatchError = class extends NoydbError {
|
|
683
725
|
userId;
|
|
684
726
|
pid;
|
|
685
727
|
constructor(userId, pid) {
|
|
@@ -694,7 +736,7 @@ Resolutions:
|
|
|
694
736
|
3. Inspect the bundle without unsealing \u2014 pass no \`sealingProviders\`
|
|
695
737
|
to receive the sealed entries unmodified for offline analysis.`
|
|
696
738
|
);
|
|
697
|
-
this.name = "
|
|
739
|
+
this.name = "PodSealMismatchError";
|
|
698
740
|
this.userId = userId;
|
|
699
741
|
this.pid = pid;
|
|
700
742
|
}
|
|
@@ -1079,6 +1121,18 @@ var CrossJoinTooLargeError = class extends NoydbError {
|
|
|
1079
1121
|
this.limit = opts.limit;
|
|
1080
1122
|
}
|
|
1081
1123
|
};
|
|
1124
|
+
var RefNotDeclaredError = class extends NoydbError {
|
|
1125
|
+
/** Collection the join was issued from. */
|
|
1126
|
+
collection;
|
|
1127
|
+
/** Field the join tried to follow. */
|
|
1128
|
+
field;
|
|
1129
|
+
constructor(opts) {
|
|
1130
|
+
super("REF_NOT_DECLARED", opts.message);
|
|
1131
|
+
this.name = "RefNotDeclaredError";
|
|
1132
|
+
this.collection = opts.collection;
|
|
1133
|
+
this.field = opts.field;
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1082
1136
|
var CrossJoinSourceUnknownError = class extends NoydbError {
|
|
1083
1137
|
target;
|
|
1084
1138
|
leftCollection;
|
|
@@ -1551,6 +1605,8 @@ export {
|
|
|
1551
1605
|
STATE_VAULT_NAME,
|
|
1552
1606
|
USER_ENVELOPE_MAX_BYTES,
|
|
1553
1607
|
USER_ENVELOPE_COLLECTION,
|
|
1608
|
+
ROSTER_KEY_ID,
|
|
1609
|
+
BLOB_ADDRESS_KEY_ID,
|
|
1554
1610
|
NoydbError,
|
|
1555
1611
|
DebugPlaintextError,
|
|
1556
1612
|
DebugReservedFieldError,
|
|
@@ -1558,6 +1614,7 @@ export {
|
|
|
1558
1614
|
TamperedError,
|
|
1559
1615
|
InvalidKeyError,
|
|
1560
1616
|
KeyringCorruptError,
|
|
1617
|
+
KeyringTamperedError,
|
|
1561
1618
|
NoAccessError,
|
|
1562
1619
|
ReadOnlyError,
|
|
1563
1620
|
ReadOnlyAtInstantError,
|
|
@@ -1599,7 +1656,6 @@ export {
|
|
|
1599
1656
|
SequenceNotEnabledError,
|
|
1600
1657
|
NumberingUncertaintyError,
|
|
1601
1658
|
PodVersionConflictError,
|
|
1602
|
-
BundleVersionConflictError,
|
|
1603
1659
|
NetworkError,
|
|
1604
1660
|
NotFoundError,
|
|
1605
1661
|
ValidationError,
|
|
@@ -1617,9 +1673,9 @@ export {
|
|
|
1617
1673
|
IndexWriteFailureError,
|
|
1618
1674
|
PersistedIndexCompensationError,
|
|
1619
1675
|
FieldNotQueryableError,
|
|
1620
|
-
|
|
1676
|
+
PodIntegrityError,
|
|
1621
1677
|
PodHeaderVerificationError,
|
|
1622
|
-
|
|
1678
|
+
PodSealMismatchError,
|
|
1623
1679
|
RedirectDepthExceededError,
|
|
1624
1680
|
RedirectLoopError,
|
|
1625
1681
|
RedirectBadSignatureError,
|
|
@@ -1658,6 +1714,7 @@ export {
|
|
|
1658
1714
|
SessionPolicyError,
|
|
1659
1715
|
JoinTooLargeError,
|
|
1660
1716
|
CrossJoinTooLargeError,
|
|
1717
|
+
RefNotDeclaredError,
|
|
1661
1718
|
CrossJoinSourceUnknownError,
|
|
1662
1719
|
DanglingReferenceError,
|
|
1663
1720
|
FilenameSanitizationError,
|
|
@@ -1700,4 +1757,4 @@ export {
|
|
|
1700
1757
|
ClassifiedRotationError,
|
|
1701
1758
|
SatelliteConfigError
|
|
1702
1759
|
};
|
|
1703
|
-
//# sourceMappingURL=chunk-
|
|
1760
|
+
//# sourceMappingURL=chunk-JBIV4FLR.js.map
|