@noy-db/hub 0.2.0-pre.2 → 0.2.0-pre.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -0
- package/dist/aggregate/index.cjs +643 -37
- package/dist/aggregate/index.cjs.map +1 -1
- package/dist/aggregate/index.d.cts +3 -2
- package/dist/aggregate/index.d.ts +3 -2
- package/dist/aggregate/index.js +9 -8
- package/dist/aggregate/index.js.map +1 -1
- package/dist/attestation/index.cjs.map +1 -1
- package/dist/attestation/index.d.cts +7 -5
- package/dist/attestation/index.d.ts +7 -5
- package/dist/attestation/index.js +6 -6
- package/dist/blobs/index.cjs +509 -22
- package/dist/blobs/index.cjs.map +1 -1
- package/dist/blobs/index.d.cts +9 -7
- package/dist/blobs/index.d.ts +9 -7
- package/dist/blobs/index.js +11 -6
- package/dist/blobs/index.js.map +1 -1
- package/dist/bundle/index.cjs +7886 -841
- package/dist/bundle/index.cjs.map +1 -1
- package/dist/bundle/index.d.cts +20 -18
- package/dist/bundle/index.d.ts +20 -18
- package/dist/bundle/index.js +24 -13
- package/dist/bundle/index.js.map +1 -1
- package/dist/{chunk-PFSNOPBQ.js → chunk-2XA2ZML4.js} +31 -3
- package/dist/chunk-2XA2ZML4.js.map +1 -0
- package/dist/{chunk-2PAQNPE3.js → chunk-37VGJM3T.js} +37 -2
- package/dist/chunk-37VGJM3T.js.map +1 -0
- package/dist/{chunk-7BRE6EUA.js → chunk-3HNKR65T.js} +4 -4
- package/dist/chunk-3HNKR65T.js.map +1 -0
- package/dist/{chunk-Y2RKOPNC.js → chunk-5YTXYPES.js} +46 -10
- package/dist/chunk-5YTXYPES.js.map +1 -0
- package/dist/{chunk-OVZDFEOR.js → chunk-6QAZ5O6X.js} +2 -2
- package/dist/chunk-6QAZ5O6X.js.map +1 -0
- package/dist/{chunk-RTZVQAJ7.js → chunk-6QE4DUYC.js} +19 -4
- package/dist/chunk-6QE4DUYC.js.map +1 -0
- package/dist/{chunk-7Q5PLD5C.js → chunk-7MRT7EPB.js} +3 -3
- package/dist/{chunk-E535SAN4.js → chunk-7PH4OPBZ.js} +4258 -520
- package/dist/chunk-7PH4OPBZ.js.map +1 -0
- package/dist/{chunk-PEULZC6M.js → chunk-A3JMGXPG.js} +8 -1
- package/dist/chunk-A3JMGXPG.js.map +1 -0
- package/dist/{chunk-UMLVJTYV.js → chunk-ADB7GPM3.js} +7 -4
- package/dist/chunk-ADB7GPM3.js.map +1 -0
- package/dist/{chunk-G6FRSBKK.js → chunk-AI4USDRI.js} +4 -4
- package/dist/chunk-BZW5IL43.js +151 -0
- package/dist/chunk-BZW5IL43.js.map +1 -0
- package/dist/chunk-C2RJVZZL.js +123 -0
- package/dist/chunk-C2RJVZZL.js.map +1 -0
- package/dist/{chunk-UND4XIB6.js → chunk-C6W5KVDV.js} +52 -38
- package/dist/chunk-C6W5KVDV.js.map +1 -0
- package/dist/chunk-CQYEDODS.js +125 -0
- package/dist/chunk-CQYEDODS.js.map +1 -0
- package/dist/{chunk-NWZ3I6R6.js → chunk-EYK72OTL.js} +5 -5
- package/dist/{chunk-7BUTTVMR.js → chunk-F5GWNSE2.js} +2 -2
- package/dist/{chunk-AHPFONIL.js → chunk-F5ILTHMU.js} +5 -5
- package/dist/{chunk-Q6W2CMEJ.js → chunk-FRRJIUSI.js} +18 -5
- package/dist/chunk-FRRJIUSI.js.map +1 -0
- package/dist/{chunk-YMYK7US4.js → chunk-GJTKMME7.js} +2 -2
- package/dist/chunk-GJTKMME7.js.map +1 -0
- package/dist/{chunk-EUYOGYGV.js → chunk-HYJMAV53.js} +6 -6
- package/dist/chunk-HYJMAV53.js.map +1 -0
- package/dist/{chunk-QPEXPHJR.js → chunk-I3IYTUUI.js} +4 -4
- package/dist/{chunk-3QAKZ37R.js → chunk-IVZWHIEK.js} +5 -5
- package/dist/{chunk-PLI5TV7N.js → chunk-IW4L4X65.js} +2 -2
- package/dist/chunk-IW4L4X65.js.map +1 -0
- package/dist/{chunk-3Z2TPHC4.js → chunk-IY24WS2P.js} +69 -5
- package/dist/chunk-IY24WS2P.js.map +1 -0
- package/dist/{chunk-HXJXPZRE.js → chunk-J6RGRZOY.js} +10 -3
- package/dist/chunk-J6RGRZOY.js.map +1 -0
- package/dist/{chunk-3S4BJX25.js → chunk-JBBWALNI.js} +2 -2
- package/dist/chunk-JBBWALNI.js.map +1 -0
- package/dist/{chunk-7Z23ZFLV.js → chunk-JDCPRJVS.js} +5 -5
- package/dist/chunk-JDCPRJVS.js.map +1 -0
- package/dist/{chunk-243PNUA6.js → chunk-JOK73NDT.js} +3 -3
- package/dist/chunk-JTI57WRT.js +164 -0
- package/dist/chunk-JTI57WRT.js.map +1 -0
- package/dist/{chunk-VRBCTEKQ.js → chunk-JYNH4FIM.js} +233 -11
- package/dist/chunk-JYNH4FIM.js.map +1 -0
- package/dist/{chunk-TBKOGSYR.js → chunk-KOAJ3TZM.js} +27 -5
- package/dist/chunk-KOAJ3TZM.js.map +1 -0
- package/dist/{chunk-YTXSFG3C.js → chunk-MBXKRHSS.js} +50 -20
- package/dist/chunk-MBXKRHSS.js.map +1 -0
- package/dist/{chunk-MUWOSVEP.js → chunk-NSXNXLYM.js} +10 -2
- package/dist/chunk-NSXNXLYM.js.map +1 -0
- package/dist/{chunk-J4KLMEUL.js → chunk-NV4IHBZS.js} +664 -51
- package/dist/chunk-NV4IHBZS.js.map +1 -0
- package/dist/{chunk-LRAZDV5X.js → chunk-O5XKZCUD.js} +31 -8
- package/dist/chunk-O5XKZCUD.js.map +1 -0
- package/dist/{chunk-W3XXT26A.js → chunk-OTWT6BAJ.js} +358 -3
- package/dist/chunk-OTWT6BAJ.js.map +1 -0
- package/dist/{chunk-XG3PTSCD.js → chunk-PDVP3C2I.js} +1 -1
- package/dist/chunk-PDVP3C2I.js.map +1 -0
- package/dist/{chunk-GIV6DWBG.js → chunk-S45MDEEF.js} +44 -5
- package/dist/chunk-S45MDEEF.js.map +1 -0
- package/dist/{chunk-VK5EER6C.js → chunk-SQKAECUL.js} +2 -2
- package/dist/{chunk-FAQVNJD4.js → chunk-SQOK5UM6.js} +12 -2
- package/dist/{chunk-FAQVNJD4.js.map → chunk-SQOK5UM6.js.map} +1 -1
- package/dist/chunk-STNPB3UM.js +9 -0
- package/dist/chunk-STNPB3UM.js.map +1 -0
- package/dist/{chunk-YS3POABP.js → chunk-TA6HPKWQ.js} +1 -1
- package/dist/chunk-TA6HPKWQ.js.map +1 -0
- package/dist/{chunk-4HIL6AHQ.js → chunk-TAMRU7A2.js} +4 -4
- package/dist/{chunk-QXQRKXCU.js → chunk-TGIJTNM3.js} +2 -2
- package/dist/chunk-TNH5SLCD.js +361 -0
- package/dist/chunk-TNH5SLCD.js.map +1 -0
- package/dist/{chunk-VPSUZLOJ.js → chunk-TYMDCIQM.js} +31 -5
- package/dist/chunk-TYMDCIQM.js.map +1 -0
- package/dist/chunk-U2XSUCDF.js +524 -0
- package/dist/chunk-U2XSUCDF.js.map +1 -0
- package/dist/{chunk-3Y53S2SA.js → chunk-UU6M64HI.js} +4 -4
- package/dist/{chunk-VCGTOS2A.js → chunk-WE2BUQD2.js} +3 -3
- package/dist/chunk-WE2BUQD2.js.map +1 -0
- package/dist/{chunk-JYQTXEIO.js → chunk-WWVJXBOT.js} +449 -29
- package/dist/chunk-WWVJXBOT.js.map +1 -0
- package/dist/chunk-YPIOFSN3.js +129 -0
- package/dist/chunk-YPIOFSN3.js.map +1 -0
- package/dist/chunk-ZC7J6ZYV.js +7 -0
- package/dist/chunk-ZC7J6ZYV.js.map +1 -0
- package/dist/{chunk-5ZGZ6HIZ.js → chunk-ZONKSLF2.js} +30 -7
- package/dist/chunk-ZONKSLF2.js.map +1 -0
- package/dist/consent/index.cjs.map +1 -1
- package/dist/consent/index.d.cts +8 -6
- package/dist/consent/index.d.ts +8 -6
- package/dist/consent/index.js +3 -3
- package/dist/{crypto-5ZDIY3NG.js → crypto-456N7UVX.js} +7 -3
- package/dist/{delegation-QYXZW25W.js → delegation-DP4COTXB.js} +5 -5
- package/dist/derivations/index.cjs +124 -6
- package/dist/derivations/index.cjs.map +1 -1
- package/dist/derivations/index.d.cts +11 -9
- package/dist/derivations/index.d.ts +11 -9
- package/dist/derivations/index.js +8 -6
- package/dist/{dev-unlock-DQCNDfFp.d.cts → dev-unlock-CY0HIZA0.d.cts} +1 -1
- package/dist/{dev-unlock-utkybTKb.d.ts → dev-unlock-CpKSkl2c.d.ts} +1 -1
- package/dist/discriminant-BN9REW3o.d.cts +60 -0
- package/dist/discriminant-BN9REW3o.d.ts +60 -0
- package/dist/errors-Dkc_fi-S.d.cts +1467 -0
- package/dist/errors-Dkc_fi-S.d.ts +1467 -0
- package/dist/executor-4IEW4KG5.js +8 -0
- package/dist/executor-KYJCJCIN.js +12 -0
- package/dist/executor-W7VIBOBZ.js +8 -0
- package/dist/{fanout-sidecar-VJ52RIEY.js → fanout-sidecar-YXNAEZ33.js} +2 -2
- package/dist/fanout-sidecar-YXNAEZ33.js.map +1 -0
- package/dist/forget/index.cjs +43 -0
- package/dist/forget/index.cjs.map +1 -0
- package/dist/forget/index.d.cts +1 -0
- package/dist/forget/index.d.ts +1 -0
- package/dist/forget/index.js +14 -0
- package/dist/guards/index.cjs +144 -4
- package/dist/guards/index.cjs.map +1 -1
- package/dist/guards/index.d.cts +16 -8
- package/dist/guards/index.d.ts +16 -8
- package/dist/guards/index.js +13 -7
- package/dist/{hash-jDowCrK2.d.cts → hash-BSd0-_L8.d.cts} +1 -1
- package/dist/{hash-DcoYWfJ_.d.ts → hash-BnBQx39y.d.ts} +1 -1
- package/dist/history/index.cjs +28 -5
- package/dist/history/index.cjs.map +1 -1
- package/dist/history/index.d.cts +9 -7
- package/dist/history/index.d.ts +9 -7
- package/dist/history/index.js +9 -7
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.cjs +356 -26
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +8 -6
- package/dist/i18n/index.d.ts +8 -6
- package/dist/i18n/index.js +36 -15
- package/dist/i18n/index.js.map +1 -1
- package/dist/index-BMmajblo.d.cts +362 -0
- package/dist/index-BMmajblo.d.ts +362 -0
- package/dist/{index-BCKdioeh.d.ts → index-Bm9hIY7t.d.ts} +169 -1127
- package/dist/{index-BMjrzNZr.d.cts → index-tZqVB9g5.d.cts} +169 -1127
- package/dist/index.cjs +10286 -2168
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +258 -23
- package/dist/index.d.ts +258 -23
- package/dist/index.js +443 -110
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.cjs +97 -32
- package/dist/indexing/index.cjs.map +1 -1
- package/dist/indexing/index.d.cts +3 -3
- package/dist/indexing/index.d.ts +3 -3
- package/dist/indexing/index.js +4 -4
- package/dist/issue-JXC6T2QR.js +12 -0
- package/dist/{lazy-builder-Rpd-V3jP.d.ts → lazy-builder-ChSqcF5t.d.ts} +2 -2
- package/dist/{lazy-builder-C-rPfWG0.d.cts → lazy-builder-eYZzLEL1.d.cts} +2 -2
- package/dist/{ledger-3IU5GMXA.js → ledger-I7JUYP4L.js} +6 -6
- package/dist/materialized-views/index.cjs +687 -13
- package/dist/materialized-views/index.cjs.map +1 -1
- package/dist/materialized-views/index.d.cts +23 -20
- package/dist/materialized-views/index.d.ts +23 -20
- package/dist/materialized-views/index.js +8 -7
- package/dist/mime-magic-BnJCGJzB.d.cts +103 -0
- package/dist/mime-magic-CjSyakO4.d.ts +103 -0
- package/dist/noydb-ZZCRF6TE.js +38 -0
- package/dist/overlay-views/index.cjs +58 -18
- package/dist/overlay-views/index.cjs.map +1 -1
- package/dist/overlay-views/index.d.cts +32 -12
- package/dist/overlay-views/index.d.ts +32 -12
- package/dist/overlay-views/index.js +6 -6
- package/dist/periods/index.cjs.map +1 -1
- package/dist/periods/index.d.cts +8 -6
- package/dist/periods/index.d.ts +8 -6
- package/dist/periods/index.js +6 -6
- package/dist/{predicate-Dnu81tsS.d.cts → predicate-BmhBSPCH.d.cts} +87 -5
- package/dist/{predicate-Dnu81tsS.d.ts → predicate-BmhBSPCH.d.ts} +87 -5
- package/dist/{public-envelope-U3CMEOMV.js → public-envelope-5XRTUNKF.js} +4 -4
- package/dist/query/index.cjs +1438 -130
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +4 -3
- package/dist/query/index.d.ts +4 -3
- package/dist/query/index.js +13 -6
- package/dist/read-only-facade-EX6WZZBP.js +7 -0
- package/dist/registry-ATRHOG5B.js +8 -0
- package/dist/registry-DKEXOJVO.js +7 -0
- package/dist/registry-LEHB26TY.js +8 -0
- package/dist/{registry-3ALP62P6.js → registry-NWHOLD5M.js} +3 -3
- package/dist/{revoke-KY2GB4KP.js → revoke-5IEK22KT.js} +6 -6
- package/dist/sealed-record/index.cjs +139 -0
- package/dist/sealed-record/index.cjs.map +1 -0
- package/dist/sealed-record/index.d.cts +123 -0
- package/dist/sealed-record/index.d.ts +123 -0
- package/dist/sealed-record/index.js +42 -0
- package/dist/sealed-record/index.js.map +1 -0
- package/dist/session/index.cjs.map +1 -1
- package/dist/session/index.d.cts +9 -7
- package/dist/session/index.d.ts +9 -7
- package/dist/session/index.js +3 -3
- package/dist/shadow/index.cjs.map +1 -1
- package/dist/shadow/index.d.cts +8 -6
- package/dist/shadow/index.d.ts +8 -6
- package/dist/shadow/index.js +2 -2
- package/dist/{signer-GRI5TZKH.js → signer-I6YARZQA.js} +5 -5
- package/dist/snapshots/index.cjs +937 -0
- package/dist/snapshots/index.cjs.map +1 -0
- package/dist/snapshots/index.d.cts +30 -0
- package/dist/snapshots/index.d.ts +30 -0
- package/dist/snapshots/index.js +152 -0
- package/dist/snapshots/index.js.map +1 -0
- package/dist/{stale-OTOF3FH7.js → stale-CPESGAPL.js} +2 -2
- package/dist/stale-CPESGAPL.js.map +1 -0
- package/dist/state-vault-JR3CFGNP.js +14 -0
- package/dist/state-vault-JR3CFGNP.js.map +1 -0
- package/dist/store/index.cjs +8 -0
- package/dist/store/index.cjs.map +1 -1
- package/dist/store/index.d.cts +15 -6
- package/dist/store/index.d.ts +15 -6
- package/dist/store/index.js +2 -2
- package/dist/{strategy-DSTrsZ8t.d.ts → strategy-54eIwox5.d.ts} +456 -7
- package/dist/{strategy-DSTrsZ8t.d.cts → strategy-WtB-jXYv.d.cts} +456 -7
- package/dist/sync/index.cjs.map +1 -1
- package/dist/sync/index.d.cts +7 -5
- package/dist/sync/index.d.ts +7 -5
- package/dist/sync/index.js +4 -4
- package/dist/team/index.cjs +1 -1
- package/dist/team/index.cjs.map +1 -1
- package/dist/team/index.d.cts +8 -6
- package/dist/team/index.d.ts +8 -6
- package/dist/team/index.js +8 -8
- package/dist/transition-guard-D4bfIAiW.d.ts +165 -0
- package/dist/transition-guard-Dmpqzg-_.d.cts +165 -0
- package/dist/tx/index.cjs +155 -5
- package/dist/tx/index.cjs.map +1 -1
- package/dist/tx/index.d.cts +27 -9
- package/dist/tx/index.d.ts +27 -9
- package/dist/tx/index.js +61 -4
- package/dist/tx/index.js.map +1 -1
- package/dist/{types-BoFFiskX.d.ts → types-DLfWFr6U.d.ts} +3997 -1262
- package/dist/{types-DJG8HG6F.d.cts → types-DyOI6XZ_.d.cts} +3997 -1262
- package/dist/{ulid-BmBgooGm.d.ts → ulid-B2L_aqVA.d.ts} +19 -19
- package/dist/{ulid-C7ms9oli.d.cts → ulid-LaxfH2tK.d.cts} +19 -19
- package/dist/util/index.cjs +7 -0
- package/dist/util/index.cjs.map +1 -1
- package/dist/util/index.d.cts +2 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.js +5 -1
- package/dist/util/index.js.map +1 -1
- package/dist/vault-group-BB246VIM.js +804 -0
- package/dist/vault-group-BB246VIM.js.map +1 -0
- package/dist/{with-materialized-view-CqnRwI2S.d.ts → with-materialized-view-CeZYGJVf.d.cts} +2 -2
- package/dist/{with-materialized-view-BbEPFIIJ.d.cts → with-materialized-view-DNULSxoP.d.ts} +2 -2
- package/dist/{with-overlayed-view-Ct1fSJt-.d.ts → with-overlayed-view-C9joG7UZ.d.ts} +2 -2
- package/dist/{with-overlayed-view-bwlmmFjx.d.cts → with-overlayed-view-kdcPGHih.d.cts} +2 -2
- package/dist/with-rollup-DJDbrxjf.d.ts +47 -0
- package/dist/with-rollup-s58XAeWO.d.cts +47 -0
- package/package.json +35 -4
- package/dist/chunk-2PAQNPE3.js.map +0 -1
- package/dist/chunk-3S4BJX25.js.map +0 -1
- package/dist/chunk-3XHOCQK4.js +0 -118
- package/dist/chunk-3XHOCQK4.js.map +0 -1
- package/dist/chunk-3Z2TPHC4.js.map +0 -1
- package/dist/chunk-5ZGZ6HIZ.js.map +0 -1
- package/dist/chunk-7BRE6EUA.js.map +0 -1
- package/dist/chunk-7Z23ZFLV.js.map +0 -1
- package/dist/chunk-CXSCDO5T.js +0 -51
- package/dist/chunk-CXSCDO5T.js.map +0 -1
- package/dist/chunk-E535SAN4.js.map +0 -1
- package/dist/chunk-EUYOGYGV.js.map +0 -1
- package/dist/chunk-GIV6DWBG.js.map +0 -1
- package/dist/chunk-HXJXPZRE.js.map +0 -1
- package/dist/chunk-J4KLMEUL.js.map +0 -1
- package/dist/chunk-JYQTXEIO.js.map +0 -1
- package/dist/chunk-LRAZDV5X.js.map +0 -1
- package/dist/chunk-MRIBLZL3.js +0 -86
- package/dist/chunk-MRIBLZL3.js.map +0 -1
- package/dist/chunk-MUWOSVEP.js.map +0 -1
- package/dist/chunk-OVZDFEOR.js.map +0 -1
- package/dist/chunk-PEULZC6M.js.map +0 -1
- package/dist/chunk-PFSNOPBQ.js.map +0 -1
- package/dist/chunk-PLI5TV7N.js.map +0 -1
- package/dist/chunk-Q6W2CMEJ.js.map +0 -1
- package/dist/chunk-RTZVQAJ7.js.map +0 -1
- package/dist/chunk-TBKOGSYR.js.map +0 -1
- package/dist/chunk-UMLVJTYV.js.map +0 -1
- package/dist/chunk-UND4XIB6.js.map +0 -1
- package/dist/chunk-VCGTOS2A.js.map +0 -1
- package/dist/chunk-VE6YVP32.js +0 -19
- package/dist/chunk-VE6YVP32.js.map +0 -1
- package/dist/chunk-VPSUZLOJ.js.map +0 -1
- package/dist/chunk-VRBCTEKQ.js.map +0 -1
- package/dist/chunk-W3XXT26A.js.map +0 -1
- package/dist/chunk-XG3PTSCD.js.map +0 -1
- package/dist/chunk-Y2RKOPNC.js.map +0 -1
- package/dist/chunk-YMYK7US4.js.map +0 -1
- package/dist/chunk-YS3POABP.js.map +0 -1
- package/dist/chunk-YTXSFG3C.js.map +0 -1
- package/dist/executor-AS2IDHKZ.js +0 -11
- package/dist/executor-HLXFXNFM.js +0 -8
- package/dist/executor-HN6YBHZ5.js +0 -8
- package/dist/fanout-sidecar-VJ52RIEY.js.map +0 -1
- package/dist/issue-ORP37MVW.js +0 -12
- package/dist/mime-magic-CBBSOkjm.d.cts +0 -50
- package/dist/mime-magic-CBBSOkjm.d.ts +0 -50
- package/dist/noydb-5H3C24GG.js +0 -34
- package/dist/read-only-facade-ITU6L7BL.js +0 -7
- package/dist/registry-7HE6VJGC.js +0 -8
- package/dist/registry-PSIPG2QR.js +0 -8
- package/dist/registry-RFGGMVNJ.js +0 -7
- package/dist/with-derivation-BKXXa8Vt.d.ts +0 -13
- package/dist/with-derivation-BjQ7q4NE.d.cts +0 -13
- package/dist/with-guard-C25yNjzd.d.ts +0 -18
- package/dist/with-guard-DQme5DKE.d.cts +0 -18
- /package/dist/{chunk-7Q5PLD5C.js.map → chunk-7MRT7EPB.js.map} +0 -0
- /package/dist/{chunk-G6FRSBKK.js.map → chunk-AI4USDRI.js.map} +0 -0
- /package/dist/{chunk-NWZ3I6R6.js.map → chunk-EYK72OTL.js.map} +0 -0
- /package/dist/{chunk-7BUTTVMR.js.map → chunk-F5GWNSE2.js.map} +0 -0
- /package/dist/{chunk-AHPFONIL.js.map → chunk-F5ILTHMU.js.map} +0 -0
- /package/dist/{chunk-QPEXPHJR.js.map → chunk-I3IYTUUI.js.map} +0 -0
- /package/dist/{chunk-3QAKZ37R.js.map → chunk-IVZWHIEK.js.map} +0 -0
- /package/dist/{chunk-243PNUA6.js.map → chunk-JOK73NDT.js.map} +0 -0
- /package/dist/{chunk-VK5EER6C.js.map → chunk-SQKAECUL.js.map} +0 -0
- /package/dist/{chunk-4HIL6AHQ.js.map → chunk-TAMRU7A2.js.map} +0 -0
- /package/dist/{chunk-QXQRKXCU.js.map → chunk-TGIJTNM3.js.map} +0 -0
- /package/dist/{chunk-3Y53S2SA.js.map → chunk-UU6M64HI.js.map} +0 -0
- /package/dist/{crypto-5ZDIY3NG.js.map → crypto-456N7UVX.js.map} +0 -0
- /package/dist/{delegation-QYXZW25W.js.map → delegation-DP4COTXB.js.map} +0 -0
- /package/dist/{executor-AS2IDHKZ.js.map → executor-4IEW4KG5.js.map} +0 -0
- /package/dist/{executor-HLXFXNFM.js.map → executor-KYJCJCIN.js.map} +0 -0
- /package/dist/{executor-HN6YBHZ5.js.map → executor-W7VIBOBZ.js.map} +0 -0
- /package/dist/{issue-ORP37MVW.js.map → forget/index.js.map} +0 -0
- /package/dist/{ledger-3IU5GMXA.js.map → issue-JXC6T2QR.js.map} +0 -0
- /package/dist/{noydb-5H3C24GG.js.map → ledger-I7JUYP4L.js.map} +0 -0
- /package/dist/{public-envelope-U3CMEOMV.js.map → noydb-ZZCRF6TE.js.map} +0 -0
- /package/dist/{read-only-facade-ITU6L7BL.js.map → public-envelope-5XRTUNKF.js.map} +0 -0
- /package/dist/{registry-3ALP62P6.js.map → read-only-facade-EX6WZZBP.js.map} +0 -0
- /package/dist/{registry-7HE6VJGC.js.map → registry-ATRHOG5B.js.map} +0 -0
- /package/dist/{registry-PSIPG2QR.js.map → registry-DKEXOJVO.js.map} +0 -0
- /package/dist/{registry-RFGGMVNJ.js.map → registry-LEHB26TY.js.map} +0 -0
- /package/dist/{revoke-KY2GB4KP.js.map → registry-NWHOLD5M.js.map} +0 -0
- /package/dist/{signer-GRI5TZKH.js.map → revoke-5IEK22KT.js.map} +0 -0
- /package/dist/{stale-OTOF3FH7.js.map → signer-I6YARZQA.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/persisted-schemas/storage.ts","../src/team/managed-passphrase.ts"],"sourcesContent":["/**\n * Read / write the per-collection persisted-schema envelope. Mirrors the\n * standard noy-db record envelope shape and is **AES-GCM encrypted with\n * the collection's DEK** — the schema body (field names, enum values,\n * constraints) is sensitive metadata, so it gets the same encryption\n * envelope as the records it describes.\n *\n * Storage layout:\n *\n * <vault>/_schemas/<collection> → EncryptedEnvelope\n *\n * The DEK passed to {@link savePersistedSchema} / {@link loadPersistedSchema}\n * is the same key the collection uses for its records.\n *\n * @module\n */\n\nimport { encrypt, decrypt } from '../crypto.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport type { PersistedSchemaEnvelope } from './types.js'\n\n/** Reserved collection name where persisted schemas live. */\nexport const SCHEMAS_COLLECTION = '_schemas' as const\n\n/**\n * Read and decrypt the persisted-schema envelope for one collection.\n * Returns `undefined` when no envelope has been written or when decryption\n * fails (e.g. wrong DEK passed). Tolerates corrupted records — JSON parse\n * failures surface as `undefined`, mirroring `_meta/handle`'s contract.\n */\nexport async function loadPersistedSchema(\n store: NoydbStore,\n vault: string,\n collection: string,\n dek: CryptoKey,\n): Promise<PersistedSchemaEnvelope | undefined> {\n const envelope = await store.get(vault, SCHEMAS_COLLECTION, collection)\n if (!envelope) return undefined\n try {\n const plaintext = await decrypt(envelope._iv, envelope._data, dek)\n const parsed = JSON.parse(plaintext) as PersistedSchemaEnvelope\n if (parsed._noydb_schema !== 1) return undefined\n return parsed\n } catch {\n return undefined\n }\n}\n\n/**\n * Encrypt and persist a schema envelope for one collection. Always\n * overwrites any prior write (callers gate on hash equality before calling\n * to avoid no-op writes).\n */\nexport async function savePersistedSchema(\n store: NoydbStore,\n vault: string,\n collection: string,\n dek: CryptoKey,\n payload: PersistedSchemaEnvelope,\n): Promise<void> {\n const json = JSON.stringify(payload)\n const { iv, data } = await encrypt(json, dek)\n const prior = await store.get(vault, SCHEMAS_COLLECTION, collection)\n const env: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: (prior?._v ?? 0) + 1,\n _ts: new Date().toISOString(),\n _iv: iv,\n _data: data,\n }\n await store.put(vault, SCHEMAS_COLLECTION, collection, env)\n}\n","/**\n * Managed-passphrase mode — rubber-hose-resistant vaults.\n *\n * A vault mode where the passphrase is machine-generated and never\n * exposed to the user, sealed under a developer-provided\n * {@link SealingKeyProvider} (macOS Keychain, Windows Credential\n * Manager, libsecret, AWS KMS, …). The user has no secret to give\n * up to coercion — they can't reveal what they don't know.\n *\n * ## Components in this file\n *\n * - {@link SealingKeyProvider} — the interface concrete providers\n * implement. Provider implementations live OUTSIDE hub (per-\n * platform packages).\n * - {@link MemorySealingKeyProvider} — in-memory test provider; uses\n * a deterministic per-instance \"key\" so two providers with\n * different ids cannot unseal each other's outputs.\n * - {@link RecipientHint} — public material a sender uses to seal\n * plaintext for a specific recipient; published by\n * {@link RecipientSealer.publishRecipientHint} and transported\n * out-of-band to the sender before bundle writes.\n * - {@link RecipientSealer} — interface for asymmetric/granted\n * providers that support recipient-target sealing (RSA-OAEP,\n * cloud-KMS asymmetric, etc.); distinct from self-only\n * {@link SealingKeyProvider} (macOS Keychain, WebAuthn-PRF).\n * - {@link MemoryRecipientSealer} — in-process reference\n * implementation of both `RecipientSealer` and\n * `SealingKeyProvider` using real WebCrypto RSA-OAEP + AES-GCM;\n * safe for tests and same-process sender/recipient scenarios.\n * - {@link loadSealedPassphrase} / {@link saveSealedPassphrase} —\n * plaintext envelope storage at `_meta/sealed-passphrase`.\n * Mirrors the `_meta/handle` and `_meta/public-envelope` AES-\n * GCM-bypassed patterns. The sealing layer (provider's job)\n * is the security boundary; hub doesn't have a key to encrypt\n * with at this layer — that's the whole point of the design.\n * - {@link resolveManagedSecret} — orchestrates the \"generate +\n * seal + persist on first open; unseal on reopen\" flow.\n * Returns the plaintext passphrase string that the rest of the\n * `createNoydb` keyring path consumes.\n *\n * Deferred to follow-ups:\n * - Block `rotate-passphrase` policy gate under managed mode.\n * - Mandatory strong-recovery enforcement.\n * - Recovery flow under managed mode (generates fresh sealed phrase).\n *\n * @see docs/subsystems/session-tiers.md → Managed-passphrase mode\n *\n * @module\n */\n\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\n\n/**\n * The contract concrete providers (per-platform key stores) implement\n * to seal and unseal a hub-generated random passphrase. The plaintext\n * passphrase NEVER leaves hub-controlled memory in unsealed form —\n * the provider receives the bytes, returns opaque sealed bytes, and\n * later reverses the operation. Hub treats the sealed bytes as\n * fully opaque.\n *\n * Implementations live OUTSIDE `@noy-db/hub` (separate packages\n * per the issue's \"Concrete providers (live outside hub)\" note):\n *\n * | Platform | Package (TBD) | Backing |\n * |---|---|---|\n * | macOS | `@noy-db/seal-macos-keychain` | Security.framework |\n * | Windows | `@noy-db/seal-wincred` | Credential Manager |\n * | Linux | `@noy-db/seal-libsecret` | libsecret / secret-service |\n * | Cloud / server | `@noy-db/seal-aws-kms` | AWS KMS Decrypt |\n */\nexport interface SealingKeyProvider {\n /**\n * Non-sensitive identifier disclosed in the persisted envelope.\n * Surfaced to consumers via `loadSealedPassphrase().providerId` so\n * a vault opened with the wrong provider class can detect the\n * mismatch and surface a clear error. NOT secret — fine to log.\n *\n * Suggested format: `<family>:<scope>` — e.g. `macos-keychain:com.acme.app`,\n * `aws-kms:arn:aws:kms:us-east-1:123:key/abc`. The hub never\n * parses this; it's purely audit metadata.\n */\n readonly id: string\n\n /** Seal raw passphrase bytes. Output bytes are opaque to hub. */\n seal(passphrase: Uint8Array): Promise<Uint8Array>\n\n /**\n * Reverse {@link seal}. MUST throw on tamper, wrong-provider, or\n * any other failure — hub treats a thrown error as \"this provider\n * cannot unlock this vault\" and surfaces it to the caller.\n */\n unseal(sealed: Uint8Array): Promise<Uint8Array>\n}\n\n/**\n * In-memory test provider. NOT secure — uses a deterministic\n * per-instance \"key\" (16-byte SHA-256 of `id`) XOR'd over the\n * passphrase plus a 4-byte provider-id fingerprint prefix. The XOR is\n * sufficient to make different `id` values produce mutually-unsealable\n * outputs (the contract tests for that), but offers ZERO real\n * confidentiality — never use outside tests.\n *\n * Replace with a real platform provider in production.\n */\nexport class MemorySealingKeyProvider implements SealingKeyProvider {\n readonly id: string\n private readonly fingerprint: Uint8Array\n private readonly keyBytes: Uint8Array\n\n constructor(opts: { id: string }) {\n this.id = opts.id\n // Deterministic 4-byte fingerprint of the provider id, prepended\n // to every sealed output so we can detect \"wrong provider\" at\n // unseal time without leaking anything sensitive about either\n // provider's actual key material.\n const encoded = new TextEncoder().encode(opts.id)\n let h = 0\n for (let i = 0; i < encoded.length; i++) {\n h = (h * 31 + encoded[i]!) >>> 0\n }\n this.fingerprint = new Uint8Array([\n (h >>> 24) & 0xff, (h >>> 16) & 0xff, (h >>> 8) & 0xff, h & 0xff,\n ])\n // Deterministic 16-byte \"key\" derived from the id by repeating\n // the fingerprint with offsets. Good enough for the XOR-stream\n // test cipher; never confuse this with real key derivation.\n this.keyBytes = new Uint8Array(16)\n for (let i = 0; i < 16; i++) {\n this.keyBytes[i] = this.fingerprint[i % 4]! ^ (i * 17)\n }\n }\n\n async seal(passphrase: Uint8Array): Promise<Uint8Array> {\n const out = new Uint8Array(4 + passphrase.length)\n out.set(this.fingerprint, 0)\n for (let i = 0; i < passphrase.length; i++) {\n out[4 + i] = passphrase[i]! ^ this.keyBytes[i % 16]!\n }\n return out\n }\n\n async unseal(sealed: Uint8Array): Promise<Uint8Array> {\n if (sealed.length < 4) {\n throw new Error('MemorySealingKeyProvider: sealed input too short')\n }\n for (let i = 0; i < 4; i++) {\n if (sealed[i] !== this.fingerprint[i]) {\n throw new Error(\n `MemorySealingKeyProvider(\"${this.id}\"): provider-id mismatch on unseal `\n + '(sealed bytes were produced by a different provider)',\n )\n }\n }\n const body = sealed.subarray(4)\n const out = new Uint8Array(body.length)\n for (let i = 0; i < body.length; i++) {\n out[i] = body[i]! ^ this.keyBytes[i % 16]!\n }\n return out\n }\n}\n\n/**\n * Public material a sender uses to seal-for-this-recipient. Published by\n * a recipient's RecipientSealer; transported to the sender out-of-band\n * (email, S3, in-app message). The sender obtains the hint, supplies it\n * to writeNoydbBundle's sealedCredentials.perUser[userId].hint, and the\n * hub seals each user's credential against it. Per foundation §11.4.\n */\nexport type RecipientHint = {\n readonly v: 1\n /** Recipient's provider id; matches the SealedAutoUnlockEntry.pid they'll unseal under. */\n readonly pid: string\n /** Algorithm the sender uses to produce the seal. Slice 1 ships RSA-OAEP-SHA256 only. */\n readonly alg: 'rsa-oaep-sha256'\n /** Public material — alg-specific. For 'rsa-oaep-sha256': { publicKeyPem: string }. */\n readonly material: Readonly<Record<string, unknown>>\n}\n\n/**\n * Handover-capable provider. Implemented additionally by asymmetric/granted\n * providers (cloud-KMS asymmetric, Azure RSA Key Vault, AWS KMS with grant).\n * Self-only providers (macOS Keychain, env-var, WebAuthn-PRF) do NOT\n * implement this — the §11.2 capability matrix lives in the type system.\n *\n * Per foundation §11.4. A function that requires recipient-target sealing\n * takes `RecipientSealer`, not `SealingKeyProvider` — the compiler rejects\n * passing a self-only provider at the spec site.\n */\nexport interface RecipientSealer {\n readonly id: string\n /** Produce hint material a sender uses to seal-for-this-recipient. */\n publishRecipientHint(): Promise<RecipientHint>\n /**\n * Seal plaintext for the recipient described by `hint`. Returns opaque\n * bytes — same contract as `SealingKeyProvider.seal()`. The bundle\n * layer base64-encodes the bytes into `SealedAutoUnlockEntry.sealed`\n * without inspecting them.\n */\n sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array>\n}\n\n/**\n * Shared RSA-OAEP-SHA256 + AES-GCM seal in the canonical recipient-target\n * TLV wire format. Mints a fresh 32-byte CEK, AES-GCM-encrypts `plaintext`\n * under it, RSA-OAEP-SHA256-wraps the CEK to `publicKeyPem`, and packs:\n *\n * byte 0 : version (0x01)\n * bytes 1..256 : RSA-OAEP-wrapped CEK (fixed 256 bytes at RSA-2048)\n * bytes 257..268: AES-GCM IV (12 bytes)\n * bytes 269.. : AES-GCM ciphertext ‖ 16-byte tag\n *\n * This is the single source of truth for the wire format — both\n * {@link MemoryRecipientSealer} and external sealers (e.g. `@noy-db/at-aws-kms`'s\n * asymmetric-KMS recipient sealer) call it so a blob sealed by one unseals\n * by the other. WebCrypto RSA-OAEP/SHA-256 here is wire-compatible with\n * AWS KMS `RSAES_OAEP_SHA_256` (both RSAES-OAEP, SHA-256 hash, MGF1-SHA256,\n * empty label).\n *\n * @public — re-exported from the hub barrel for external sealer packages.\n */\nexport async function sealRsaOaepTlv(plaintext: Uint8Array, publicKeyPem: string): Promise<Uint8Array> {\n // Parse PEM → SPKI bytes.\n const b64 = publicKeyPem.replace(/-----BEGIN PUBLIC KEY-----/, '').replace(/-----END PUBLIC KEY-----/, '').replace(/\\s+/g, '')\n const spki = base64ToBytes(b64)\n const recipientPub = await crypto.subtle.importKey(\n 'spki', spki as BufferSource,\n { name: 'RSA-OAEP', hash: 'SHA-256' },\n false, ['encrypt'],\n )\n // Mint fresh CEK + IV, AES-GCM encrypt plaintext.\n const cekBytes = crypto.getRandomValues(new Uint8Array(32))\n const cek = await crypto.subtle.importKey('raw', cekBytes as BufferSource, 'AES-GCM', false, ['encrypt'])\n const iv = crypto.getRandomValues(new Uint8Array(12))\n const ct = new Uint8Array(await crypto.subtle.encrypt({ name: 'AES-GCM', iv: iv as BufferSource }, cek, plaintext as BufferSource))\n // RSA-OAEP-wrap the CEK bytes.\n const wrapped = new Uint8Array(await crypto.subtle.encrypt({ name: 'RSA-OAEP' }, recipientPub, cekBytes as BufferSource))\n cekBytes.fill(0)\n if (wrapped.length !== 256) {\n throw new Error(`sealRsaOaepTlv: expected 256-byte RSA-OAEP wrap, got ${wrapped.length}`)\n }\n // TLV layout.\n const out = new Uint8Array(1 + 256 + 12 + ct.length)\n out[0] = 0x01\n out.set(wrapped, 1)\n out.set(iv, 1 + 256)\n out.set(ct, 1 + 256 + 12)\n return out\n}\n\n/**\n * Parse a {@link sealRsaOaepTlv} blob into its three segments without\n * decrypting. The `wrapped` CEK is RSA-OAEP-SHA256 ciphertext over a\n * 32-byte CEK — the unwrap step is pluggable: {@link MemoryRecipientSealer}\n * decrypts it with a local RSA private key; `@noy-db/at-aws-kms` hands it to\n * KMS `Decrypt`. After unwrapping, pass the CEK + `iv` + `ct` to\n * {@link aesGcmOpen}.\n *\n * @public — re-exported from the hub barrel for external sealer packages.\n */\nexport function parseRsaOaepTlv(bytes: Uint8Array): { wrapped: Uint8Array; iv: Uint8Array; ct: Uint8Array } {\n if (bytes.length < 1 + 256 + 12 + 16) {\n throw new Error('parseRsaOaepTlv: sealed input too short')\n }\n if (bytes[0] !== 0x01) {\n throw new Error(`parseRsaOaepTlv: unknown TLV version ${bytes[0]}`)\n }\n return {\n wrapped: bytes.subarray(1, 1 + 256),\n iv: bytes.subarray(1 + 256, 1 + 256 + 12),\n ct: bytes.subarray(1 + 256 + 12),\n }\n}\n\n/**\n * AES-GCM-decrypt the `ct` segment of a {@link parseRsaOaepTlv} result under\n * the unwrapped 32-byte CEK and its `iv`. Throws on a bad tag (tamper) — the\n * same authenticated-decryption guarantee the TLV relies on.\n *\n * @public — re-exported from the hub barrel for external sealer packages.\n */\nexport async function aesGcmOpen(cekBytes: Uint8Array, iv: Uint8Array, ct: Uint8Array): Promise<Uint8Array> {\n const cek = await crypto.subtle.importKey('raw', cekBytes as BufferSource, 'AES-GCM', false, ['decrypt'])\n return new Uint8Array(await crypto.subtle.decrypt({ name: 'AES-GCM', iv: iv as BufferSource }, cek, ct as BufferSource))\n}\n\n/**\n * Reference implementation of `RecipientSealer` + `SealingKeyProvider`.\n * Uses WebCrypto RSA-OAEP-SHA256 (2048-bit) to wrap a fresh 32-byte\n * AES-GCM CEK, AES-GCM-encrypts plaintext under it, and packs the\n * result into a self-describing TLV:\n *\n * byte 0 : version (0x01)\n * bytes 1..256 : RSA-OAEP-wrapped CEK (fixed 256 bytes at RSA-2048)\n * bytes 257..268: AES-GCM IV (12 bytes)\n * bytes 269.. : AES-GCM ciphertext ‖ 16-byte tag\n *\n * Implements BOTH interfaces. `seal(plaintext)` (self-target) is just\n * `sealForRecipient(plaintext, this own hint)` — same TLV. Convenient\n * for tests where one provider plays both ends. Real cloud providers\n * (`at-aws-kms`, etc.) will pick their own internal layouts; the only\n * contract is round-trip identity.\n *\n * SAFE for production within its scope — the cryptography is real\n * (RSA-OAEP + AES-GCM via WebCrypto), but the keypair lives in-process\n * and is regenerated on every construction. Not suitable as a managed\n * keychain; use it for tests and for shipping bundles where the\n * recipient instance lives in the same process as the sender (rare).\n */\nexport class MemoryRecipientSealer implements SealingKeyProvider, RecipientSealer {\n readonly id: string\n private readonly keypair: Promise<CryptoKeyPair>\n\n constructor(opts: { id: string }) {\n this.id = opts.id\n this.keypair = crypto.subtle.generateKey(\n { name: 'RSA-OAEP', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' },\n true,\n ['encrypt', 'decrypt'],\n )\n }\n\n async publishRecipientHint(): Promise<RecipientHint> {\n const { publicKey } = await this.keypair\n const spki = await crypto.subtle.exportKey('spki', publicKey)\n const pem = '-----BEGIN PUBLIC KEY-----\\n'\n + bytesToBase64(new Uint8Array(spki)).match(/.{1,64}/g)!.join('\\n')\n + '\\n-----END PUBLIC KEY-----\\n'\n return { v: 1, pid: this.id, alg: 'rsa-oaep-sha256', material: { publicKeyPem: pem } }\n }\n\n async sealForRecipient(plaintext: Uint8Array, hint: RecipientHint): Promise<Uint8Array> {\n if (hint.v !== 1) {\n throw new Error(`MemoryRecipientSealer.sealForRecipient: unsupported hint.v ${String(hint.v)} (expected 1)`)\n }\n if (hint.alg !== 'rsa-oaep-sha256') {\n throw new Error(`MemoryRecipientSealer.sealForRecipient: unsupported hint.alg '${String(hint.alg)}' (expected 'rsa-oaep-sha256')`)\n }\n const pem = hint.material['publicKeyPem']\n if (typeof pem !== 'string') {\n throw new Error('MemoryRecipientSealer.sealForRecipient: hint.material.publicKeyPem missing or not a string')\n }\n return sealRsaOaepTlv(plaintext, pem)\n }\n\n async seal(plaintext: Uint8Array): Promise<Uint8Array> {\n const hint = await this.publishRecipientHint()\n return this.sealForRecipient(plaintext, hint)\n }\n\n async unseal(bytes: Uint8Array): Promise<Uint8Array> {\n const { wrapped, iv, ct } = parseRsaOaepTlv(bytes)\n const { privateKey } = await this.keypair\n // Local RSA-OAEP-SHA256 unwrap of the CEK — the pluggable step external\n // sealers replace with KMS Decrypt.\n const cekBytes = new Uint8Array(await crypto.subtle.decrypt({ name: 'RSA-OAEP' }, privateKey, wrapped as BufferSource))\n const pt = await aesGcmOpen(cekBytes, iv, ct)\n cekBytes.fill(0)\n return pt\n }\n}\n\n// ─── Persisted envelope ────────────────────────────────────────────────\n\n/** Reserved id for the managed-passphrase envelope under `_meta`. */\nexport const SEALED_PASSPHRASE_RECORD_ID = 'sealed-passphrase' as const\n\n/** Plaintext payload stored inside the `_meta/sealed-passphrase` envelope. */\nexport interface SealedPassphrase {\n readonly _noydb_sealed: 1\n readonly providerId: string\n /** Sealed bytes. Base64-encoded on the wire; decoded on load. */\n readonly sealed: Uint8Array\n}\n\n/**\n * Wire-format envelope persisted at `_meta/sealed-passphrase` for\n * managed-mode vaults. The provider produces raw sealed bytes via\n * {@link SealingKeyProvider.seal}; this wrapper carries the dispatch\n * metadata hub needs to pick the right provider on the unseal path.\n *\n * Stability boundary: once shipped, the wire format only grows by\n * adding optional fields. See the at-* sealing dimension foundation\n * doc, §11.9.1.\n *\n * v1 shape (this release): `{ v: 1, _noydb_sealed: 1, pid, payload }`.\n *\n * Legacy shape (earlier releases): `{ _noydb_sealed: 1, providerId, sealed }`\n * — accepted on read for backwards compatibility; never produced on\n * write going forward.\n */\nexport interface SealedEnvelope {\n /** Envelope schema version. v1 is the current shape. */\n readonly v: 1\n /** Magic marker for forensics + legacy-shape detection. */\n readonly _noydb_sealed: 1\n /** Matches the producing provider's `.id`. Dispatch key on unseal. */\n readonly pid: string\n /** Sealed bytes from the provider, base64-encoded on the wire. */\n readonly payload: string\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n let binary = ''\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]!)\n return btoa(binary)\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n const binary = atob(b64)\n const out = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i)\n return out\n}\n\n/**\n * Parse a `_meta/sealed-passphrase` `_data` JSON string into the\n * in-memory {@link SealedPassphrase} representation. Accepts both:\n *\n * 1. v1 wire format `{ v: 1, _noydb_sealed: 1, pid, payload }` —\n * the current shape.\n * 2. Legacy wire format `{ _noydb_sealed: 1, providerId, sealed }` —\n * read-only; never written\n * going forward.\n *\n * Returns `undefined` for any input that doesn't match either shape,\n * so callers can fall back to \"no managed-mode envelope present.\"\n *\n * @internal — exported only for the migration safety-net test suite.\n */\nexport function parseSealedEnvelope(raw: unknown): SealedPassphrase | undefined {\n if (typeof raw !== 'object' || raw === null) return undefined\n const r = raw as Record<string, unknown>\n if (r._noydb_sealed !== 1) return undefined\n\n // v1 shape — preferred.\n if (\n r.v === 1\n && typeof r.pid === 'string'\n && typeof r.payload === 'string'\n ) {\n return {\n _noydb_sealed: 1,\n providerId: r.pid,\n sealed: base64ToBytes(r.payload),\n }\n }\n\n // Legacy shape — earlier releases. Accept on read for compat.\n if (\n typeof r.providerId === 'string'\n && typeof r.sealed === 'string'\n ) {\n return {\n _noydb_sealed: 1,\n providerId: r.providerId,\n sealed: base64ToBytes(r.sealed),\n }\n }\n\n return undefined\n}\n\nexport async function saveSealedPassphrase(\n store: NoydbStore,\n vault: string,\n payload: { readonly providerId: string; readonly sealed: Uint8Array },\n): Promise<void> {\n const persisted: SealedEnvelope = {\n v: 1,\n _noydb_sealed: 1,\n pid: payload.providerId,\n payload: bytesToBase64(payload.sealed),\n }\n const prior = await store.get(vault, '_meta', SEALED_PASSPHRASE_RECORD_ID)\n const env: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: (prior?._v ?? 0) + 1,\n _ts: new Date().toISOString(),\n // AES-GCM bypassed — the sealing layer is the security boundary.\n _iv: '',\n _data: JSON.stringify(persisted),\n }\n await store.put(vault, '_meta', SEALED_PASSPHRASE_RECORD_ID, env)\n}\n\nexport async function loadSealedPassphrase(\n store: NoydbStore,\n vault: string,\n): Promise<SealedPassphrase | undefined> {\n const envelope = await store.get(vault, '_meta', SEALED_PASSPHRASE_RECORD_ID)\n if (!envelope) return undefined\n try {\n return parseSealedEnvelope(JSON.parse(envelope._data))\n } catch {\n return undefined\n }\n}\n\n// ─── createNoydb orchestration ─────────────────────────────────────────\n\n/**\n * Resolve the effective plaintext passphrase string for a managed-mode\n * vault. Two paths:\n *\n * 1. **First open (no envelope persisted):** generate a 256-bit random\n * via `crypto.getRandomValues`, base64-encode for use as a\n * passphrase string, seal the underlying bytes under the\n * provider, persist `_meta/sealed-passphrase`, return the\n * base64 string.\n *\n * 2. **Reopen (envelope exists):** read + unseal + decode → return.\n * A different provider whose `seal` output disagrees on the\n * stored bytes throws here, surfaced as a clear error.\n *\n * The returned string is the same shape that `secret:` would take in\n * standard mode — the rest of the keyring path consumes it\n * unchanged.\n *\n * @internal — called from `createNoydb` / `getKeyringInternal`.\n */\nexport async function resolveManagedSecret(\n store: NoydbStore,\n vault: string,\n provider: SealingKeyProvider,\n): Promise<string> {\n const existing = await loadSealedPassphrase(store, vault)\n if (existing) {\n if (existing.providerId !== provider.id) {\n throw new Error(\n `Managed-mode vault \"${vault}\" was sealed under provider id `\n + `\"${existing.providerId}\" but the current SealingKeyProvider is `\n + `\"${provider.id}\". Pass the same provider that originally enrolled `\n + 'the vault, or treat this as a fresh enrollment and clear '\n + '`_meta/sealed-passphrase` first.',\n )\n }\n const plaintext = await provider.unseal(existing.sealed)\n return bytesToBase64(plaintext)\n }\n\n // First open: mint a 256-bit random, seal, persist.\n const random = new Uint8Array(32)\n globalThis.crypto.getRandomValues(random)\n const sealed = await provider.seal(random)\n await saveSealedPassphrase(store, vault, { providerId: provider.id, sealed })\n return bytesToBase64(random)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NOYDB_FORMAT_VERSION
|
|
3
|
+
} from "./chunk-TA6HPKWQ.js";
|
|
4
|
+
import {
|
|
5
|
+
decrypt,
|
|
6
|
+
encrypt
|
|
7
|
+
} from "./chunk-37VGJM3T.js";
|
|
8
|
+
|
|
9
|
+
// src/forget/strategy.ts
|
|
10
|
+
var NO_FORGET = { subjects: {} };
|
|
11
|
+
function withForgetCascade(opts) {
|
|
12
|
+
return { subjects: { ...opts.subjects } };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/forget/subject-index.ts
|
|
16
|
+
var SUBJECT_INDEX_COLLECTION = "_subject_index";
|
|
17
|
+
async function sha256HexString(input) {
|
|
18
|
+
const bytes = new TextEncoder().encode(input);
|
|
19
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
|
|
20
|
+
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
21
|
+
}
|
|
22
|
+
async function subjectKey(subjectId) {
|
|
23
|
+
return sha256HexString(subjectId);
|
|
24
|
+
}
|
|
25
|
+
async function readRefs(adapter, vault, getDEK, encrypted, key) {
|
|
26
|
+
const env = await adapter.get(vault, SUBJECT_INDEX_COLLECTION, key);
|
|
27
|
+
if (!env || !env._data) return [];
|
|
28
|
+
if (!encrypted) return JSON.parse(env._data);
|
|
29
|
+
const dek = await getDEK(SUBJECT_INDEX_COLLECTION);
|
|
30
|
+
const json = await decrypt(env._iv, env._data, dek);
|
|
31
|
+
return JSON.parse(json);
|
|
32
|
+
}
|
|
33
|
+
async function writeRefs(adapter, vault, getDEK, encrypted, key, refs) {
|
|
34
|
+
const json = JSON.stringify(refs);
|
|
35
|
+
let env;
|
|
36
|
+
if (!encrypted) {
|
|
37
|
+
env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: "", _data: json };
|
|
38
|
+
} else {
|
|
39
|
+
const dek = await getDEK(SUBJECT_INDEX_COLLECTION);
|
|
40
|
+
const { iv, data } = await encrypt(json, dek);
|
|
41
|
+
env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: (/* @__PURE__ */ new Date()).toISOString(), _iv: iv, _data: data };
|
|
42
|
+
}
|
|
43
|
+
await adapter.put(vault, SUBJECT_INDEX_COLLECTION, key, env);
|
|
44
|
+
}
|
|
45
|
+
async function addSubjectRef(adapter, vault, getDEK, encrypted, subjectId, ref) {
|
|
46
|
+
const key = await subjectKey(subjectId);
|
|
47
|
+
const refs = await readRefs(adapter, vault, getDEK, encrypted, key);
|
|
48
|
+
if (refs.some((r) => r.collection === ref.collection && r.id === ref.id)) return;
|
|
49
|
+
refs.push(ref);
|
|
50
|
+
await writeRefs(adapter, vault, getDEK, encrypted, key, refs);
|
|
51
|
+
}
|
|
52
|
+
async function removeSubjectRef(adapter, vault, getDEK, encrypted, subjectId, ref) {
|
|
53
|
+
const key = await subjectKey(subjectId);
|
|
54
|
+
const refs = await readRefs(adapter, vault, getDEK, encrypted, key);
|
|
55
|
+
const next = refs.filter((r) => !(r.collection === ref.collection && r.id === ref.id));
|
|
56
|
+
if (next.length === refs.length) return;
|
|
57
|
+
if (next.length === 0) {
|
|
58
|
+
await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, key);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
await writeRefs(adapter, vault, getDEK, encrypted, key, next);
|
|
62
|
+
}
|
|
63
|
+
async function lookupSubject(adapter, vault, getDEK, encrypted, subjectId) {
|
|
64
|
+
const key = await subjectKey(subjectId);
|
|
65
|
+
return readRefs(adapter, vault, getDEK, encrypted, key);
|
|
66
|
+
}
|
|
67
|
+
async function rebuildSubjectIndex(adapter, vault, getDEK, encrypted, subjects, decodeRecord) {
|
|
68
|
+
const existing = await adapter.list(vault, SUBJECT_INDEX_COLLECTION);
|
|
69
|
+
for (const k of existing) {
|
|
70
|
+
await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, k);
|
|
71
|
+
}
|
|
72
|
+
const bySubject = /* @__PURE__ */ new Map();
|
|
73
|
+
for (const [collection, field] of Object.entries(subjects)) {
|
|
74
|
+
const ids = await adapter.list(vault, collection);
|
|
75
|
+
for (const id of ids) {
|
|
76
|
+
if (id.startsWith("_")) continue;
|
|
77
|
+
const env = await adapter.get(vault, collection, id);
|
|
78
|
+
if (!env || !env._data) continue;
|
|
79
|
+
const record = await decodeRecord(collection, id, env);
|
|
80
|
+
if (record === null) continue;
|
|
81
|
+
const subjectValue = readDottedPath(record, field);
|
|
82
|
+
if (subjectValue === void 0 || subjectValue === null) continue;
|
|
83
|
+
const subjectId = coerceSubjectId(subjectValue);
|
|
84
|
+
const list = bySubject.get(subjectId) ?? [];
|
|
85
|
+
list.push({ collection, id });
|
|
86
|
+
bySubject.set(subjectId, list);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
let entries = 0;
|
|
90
|
+
for (const [subjectId, refs] of bySubject) {
|
|
91
|
+
const key = await subjectKey(subjectId);
|
|
92
|
+
await writeRefs(adapter, vault, getDEK, encrypted, key, refs);
|
|
93
|
+
entries++;
|
|
94
|
+
}
|
|
95
|
+
return entries;
|
|
96
|
+
}
|
|
97
|
+
function coerceSubjectId(value) {
|
|
98
|
+
if (typeof value === "string") return value;
|
|
99
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
100
|
+
return String(value);
|
|
101
|
+
}
|
|
102
|
+
return JSON.stringify(value);
|
|
103
|
+
}
|
|
104
|
+
function readDottedPath(record, field) {
|
|
105
|
+
if (!field.includes(".")) return record[field];
|
|
106
|
+
let cursor = record;
|
|
107
|
+
for (const segment of field.split(".")) {
|
|
108
|
+
if (cursor === null || cursor === void 0) return void 0;
|
|
109
|
+
cursor = cursor[segment];
|
|
110
|
+
}
|
|
111
|
+
return cursor;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
NO_FORGET,
|
|
116
|
+
withForgetCascade,
|
|
117
|
+
SUBJECT_INDEX_COLLECTION,
|
|
118
|
+
addSubjectRef,
|
|
119
|
+
removeSubjectRef,
|
|
120
|
+
lookupSubject,
|
|
121
|
+
rebuildSubjectIndex,
|
|
122
|
+
coerceSubjectId,
|
|
123
|
+
readDottedPath
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=chunk-CQYEDODS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forget/strategy.ts","../src/forget/subject-index.ts"],"sourcesContent":["/**\n * `withForgetCascade` — declaration surface for GDPR right-to-erasure via\n * per-record CEK crypto-shred (#304, step 2 of the CEK security epic).\n *\n * This file holds only the *declaration* shape and the disabled sentinel.\n * The actual erasure machinery lives in:\n * - `subject-index.ts` — the encrypted `_subject_index` reserved collection\n * - `vault.ts` `forget()` — the per-record tombstone + ledger flow\n * - `collection.ts` `_writeTombstone` — the envelope rewrite\n *\n * A `ForgetStrategy` declares which collections carry erasable subject data\n * and the (dotted-path) field on each record that names the data subject.\n * Declaring a collection here ALSO forces `perRecordKeys: true` for it (a\n * shred can only erase a record whose body is keyed off a per-record CEK),\n * so adopters opt into the CEK foundation transitively.\n *\n * @module\n */\nimport type { LedgerEntry } from '../history/ledger/entry.js'\n\n/**\n * User-supplied declaration passed to {@link withForgetCascade}. Maps a\n * collection name to the record field (dotted path supported, e.g.\n * `'billing.buyerId'`) that identifies the data subject for erasure.\n *\n * ```ts\n * withForgetCascade({ subjects: { invoices: 'buyerId', contacts: 'id' } })\n * ```\n */\nexport interface SubjectDeclaration {\n readonly subjects: Record<string, string>\n}\n\n/**\n * Resolved forget strategy threaded through Noydb → every Vault. Carries\n * the same `subjects` map the user declared. `NO_FORGET` (empty map) is the\n * off-by-default sentinel; `vault.forget()` throws\n * `ForgetStrategyNotConfiguredError` when the map is empty.\n */\nexport interface ForgetStrategy {\n /** Collection → subject-field (dotted path). Empty under `NO_FORGET`. */\n readonly subjects: Readonly<Record<string, string>>\n}\n\n/**\n * Disabled sentinel — no collections declare a subject field. `vault.forget()`\n * refuses with `ForgetStrategyNotConfiguredError`; no write hooks register; no\n * collection is forced into `perRecordKeys`. Non-adopters pay nothing.\n */\nexport const NO_FORGET: ForgetStrategy = { subjects: {} }\n\n/**\n * Declare GDPR crypto-shred for one or more collections.\n *\n * Each declared collection is forced to `perRecordKeys: true` (a shred can\n * only guarantee erasure of a record whose body is keyed off a per-record\n * CEK). On write, Noydb extracts `record[subjectField]` and maintains an\n * encrypted `_subject_index` mapping `subject → [{collection, id}]`, so\n * `vault.forget(subjectId)` can find every record for a subject and rewrite\n * each to a tombstone (body + history permanently undecryptable) while the\n * collection DEK and every other record stay intact.\n *\n * @example\n * ```ts\n * createNoydb({\n * secret, user,\n * forgetStrategy: withForgetCascade({ subjects: { invoices: 'buyerId' } }),\n * })\n * const result = await vault.forget('buyer-123')\n * // → { subject, recordsShredded, historyVersionsShredded, collections, … }\n * ```\n */\nexport function withForgetCascade(opts: SubjectDeclaration): ForgetStrategy {\n return { subjects: { ...opts.subjects } }\n}\n\n/**\n * The outcome of a `vault.forget(subjectId)` call.\n *\n * `unmigratedRecords` lists `collection:id` pairs that were tombstoned but\n * whose body had NOT been migrated to a per-record CEK at shred time (legacy\n * body still under the shared collection DEK). Those records are tombstoned\n * (live envelope + history stripped) but their pre-shred ciphertext, if it\n * leaked into a backup before migration, remains decryptable under the\n * collection DEK — so erasure-completeness is NOT guaranteed for them. Run\n * the per-record-CEK migration pass, then re-forget, to close the gap.\n *\n * Blob attachments (#365): a shredded record's **erasable** blobs (on a\n * `perRecordKeys` collection) are crypto-shredded inline — `blobsShredded`\n * counts those taken to refCount 0 (BlobObject deleted → chunks permanently\n * undecryptable), `blobsRetainedShared` counts those still referenced by\n * another record (shared content legitimately persists for its other owner).\n * `blobResidueCollections` now lists only collections with blobs that could\n * NOT be crypto-shredded: **legacy** blobs (no per-blob `_cek`, chunks under\n * the shared `_blob` DEK — migrate them), or a session without the blob\n * subsystem loaded. An all-erasable subject yields an empty residue list.\n */\nexport interface ForgetResult {\n /** The subject id passed to `forget()`. Echoed for caller convenience. */\n readonly subject: string\n /** Count of live records rewritten to a tombstone. */\n readonly recordsShredded: number\n /** Count of `_history` envelopes tombstoned across all shredded records. */\n readonly historyVersionsShredded: number\n /** Distinct collections that had at least one record shredded. */\n readonly collections: readonly string[]\n /** `collection:id` pairs shredded while still un-migrated (see type docs). */\n readonly unmigratedRecords: readonly string[]\n /** Count of erasable blobs crypto-shredded (refCount → 0, BlobObject deleted). */\n readonly blobsShredded: number\n /** Count of erasable blobs retained because still referenced elsewhere (shared). */\n readonly blobsRetainedShared: number\n /** Collections with blobs that could NOT be crypto-shredded — legacy (no `_cek`) or blobs disabled (see type docs). */\n readonly blobResidueCollections: readonly string[]\n /**\n * Count of persisted `_idx/<field>/<recordId>` index side-cars hard-deleted\n * across the shredded records (#401). These live under the retained\n * collection DEK, so crypto-shred alone would leave the indexed field VALUES\n * readable — `forget()` must delete them.\n */\n readonly indexPostingsPurged: number\n /**\n * `collection:id:field` entries whose persisted `_idx` side-car could NOT be\n * deleted (#401) — index residue that still leaks the indexed value under the\n * retained collection DEK. Non-empty means erasure is INCOMPLETE: retry, or\n * purge the side-car out of band.\n */\n readonly indexResidue: readonly string[]\n /** The single `op:'forget'` ledger entry appended for this erasure. */\n readonly ledgerEntry: LedgerEntry\n}\n","/**\n * The encrypted subject index (#304).\n *\n * GDPR crypto-shred needs to answer \"which records belong to data subject\n * X?\" portably (the index must travel with the vault/bundle) WITHOUT leaking\n * subject-equivalence to the store. The rejected alternative — an unencrypted\n * subject tag in envelope metadata — would let anyone with store access see\n * which records share a subject. Instead we keep a reserved `_subject_index`\n * collection, encrypted under its OWN DEK (`getDEK('_subject_index')`):\n *\n * - record id = `sha256Hex(subjectId)` — the raw subject id never appears\n * as a key, so the store can't correlate index entries to a known subject.\n * - record body = AES-GCM(JSON `[{ collection, id }]`) under the index DEK.\n *\n * ## Concurrency (RISK #3 — known v1 limitation)\n *\n * `addSubjectRef` / `removeSubjectRef` are read-modify-write with no CAS. The\n * design assumes a SINGLE WRITER (the noy-db single-process write model). Two\n * concurrent writers racing on the SAME subject can lose an entry (last-write\n * wins on the ref list). This is documented, not fixed in v1:\n * `rebuildSubjectIndex` performs a full scan to recover a correct index from\n * the canonical records, so a lost ref is recoverable. A CAS-backed index is\n * deferred to a later slice.\n *\n * @module\n */\nimport { encrypt, decrypt } from '../crypto.js'\nimport type { NoydbStore, EncryptedEnvelope } from '../types.js'\nimport { NOYDB_FORMAT_VERSION } from '../types.js'\n\n/** Reserved collection holding the encrypted subject → records index. */\nexport const SUBJECT_INDEX_COLLECTION = '_subject_index'\n\n/** A single record reference held in a subject's index entry. */\nexport interface SubjectRef {\n readonly collection: string\n readonly id: string\n}\n\ntype GetDEK = (collectionName: string) => Promise<CryptoKey>\n\n/** SHA-256 hex of a UTF-8 string. The subject-index record key derivation. */\nasync function sha256HexString(input: string): Promise<string> {\n const bytes = new TextEncoder().encode(input)\n const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes)\n return Array.from(new Uint8Array(digest))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** Stable subject-index record id for a subject id. */\nexport async function subjectKey(subjectId: string): Promise<string> {\n return sha256HexString(subjectId)\n}\n\n/** Read + decrypt the ref list for a subject. Returns `[]` when absent. */\nasync function readRefs(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n key: string,\n): Promise<SubjectRef[]> {\n const env = await adapter.get(vault, SUBJECT_INDEX_COLLECTION, key)\n if (!env || !env._data) return []\n if (!encrypted) return JSON.parse(env._data) as SubjectRef[]\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const json = await decrypt(env._iv, env._data, dek)\n return JSON.parse(json) as SubjectRef[]\n}\n\n/** Encrypt + write a ref list for a subject under its derived key. */\nasync function writeRefs(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n key: string,\n refs: SubjectRef[],\n): Promise<void> {\n const json = JSON.stringify(refs)\n let env: EncryptedEnvelope\n if (!encrypted) {\n env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: '', _data: json }\n } else {\n const dek = await getDEK(SUBJECT_INDEX_COLLECTION)\n const { iv, data } = await encrypt(json, dek)\n env = { _noydb: NOYDB_FORMAT_VERSION, _v: 1, _ts: new Date().toISOString(), _iv: iv, _data: data }\n }\n await adapter.put(vault, SUBJECT_INDEX_COLLECTION, key, env)\n}\n\n/**\n * Add a `{ collection, id }` ref to a subject's index entry (idempotent —\n * a duplicate ref is not appended). Read-modify-write; see the concurrency\n * note in the module docstring.\n */\nexport async function addSubjectRef(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n ref: SubjectRef,\n): Promise<void> {\n const key = await subjectKey(subjectId)\n const refs = await readRefs(adapter, vault, getDEK, encrypted, key)\n if (refs.some((r) => r.collection === ref.collection && r.id === ref.id)) return\n refs.push(ref)\n await writeRefs(adapter, vault, getDEK, encrypted, key, refs)\n}\n\n/**\n * Remove a `{ collection, id }` ref from a subject's index entry. When the\n * last ref is removed the (now empty) entry is deleted so the store holds no\n * residual key for an erased subject.\n */\nexport async function removeSubjectRef(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n ref: SubjectRef,\n): Promise<void> {\n const key = await subjectKey(subjectId)\n const refs = await readRefs(adapter, vault, getDEK, encrypted, key)\n const next = refs.filter((r) => !(r.collection === ref.collection && r.id === ref.id))\n if (next.length === refs.length) return\n if (next.length === 0) {\n await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, key)\n return\n }\n await writeRefs(adapter, vault, getDEK, encrypted, key, next)\n}\n\n/** Look up every record ref for a subject. Returns `[]` when none exist. */\nexport async function lookupSubject(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjectId: string,\n): Promise<SubjectRef[]> {\n const key = await subjectKey(subjectId)\n return readRefs(adapter, vault, getDEK, encrypted, key)\n}\n\n/**\n * Rebuild the entire subject index from the canonical records (the recovery\n * path for the documented read-modify-write race). Scans each declared\n * collection, reads `record[subjectField]` (dotted path), and rewrites the\n * index from scratch. Tombstoned (already-shredded) records contribute no\n * ref — their body is gone, so they cannot be re-indexed.\n *\n * `decodeRecord` decrypts an envelope to a plain object (or returns null for\n * a tombstone / unreadable record); supplied by the caller so this module\n * stays free of Collection internals.\n */\nexport async function rebuildSubjectIndex(\n adapter: NoydbStore,\n vault: string,\n getDEK: GetDEK,\n encrypted: boolean,\n subjects: Readonly<Record<string, string>>,\n decodeRecord: (collection: string, id: string, env: EncryptedEnvelope) => Promise<Record<string, unknown> | null>,\n): Promise<number> {\n // Drop every existing index entry first so removed refs don't linger.\n const existing = await adapter.list(vault, SUBJECT_INDEX_COLLECTION)\n for (const k of existing) {\n await adapter.delete(vault, SUBJECT_INDEX_COLLECTION, k)\n }\n\n // subjectId → refs, accumulated across all declared collections.\n const bySubject = new Map<string, SubjectRef[]>()\n for (const [collection, field] of Object.entries(subjects)) {\n const ids = await adapter.list(vault, collection)\n for (const id of ids) {\n if (id.startsWith('_')) continue\n const env = await adapter.get(vault, collection, id)\n if (!env || !env._data) continue // missing or tombstone\n const record = await decodeRecord(collection, id, env)\n if (record === null) continue\n const subjectValue = readDottedPath(record, field)\n if (subjectValue === undefined || subjectValue === null) continue\n const subjectId = coerceSubjectId(subjectValue)\n const list = bySubject.get(subjectId) ?? []\n list.push({ collection, id })\n bySubject.set(subjectId, list)\n }\n }\n\n let entries = 0\n for (const [subjectId, refs] of bySubject) {\n const key = await subjectKey(subjectId)\n await writeRefs(adapter, vault, getDEK, encrypted, key, refs)\n entries++\n }\n return entries\n}\n\n/**\n * Coerce a read subject-field value to a stable string id. Primitives use\n * their natural string form; objects/arrays are JSON-stringified so structural\n * subjects still get a deterministic key (avoids the `[object Object]` trap).\n */\nexport function coerceSubjectId(value: unknown): string {\n if (typeof value === 'string') return value\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value)\n }\n return JSON.stringify(value)\n}\n\n/** Read a (possibly dotted) field path from a plain record. */\nexport function readDottedPath(record: Record<string, unknown>, field: string): unknown {\n if (!field.includes('.')) return record[field]\n let cursor: unknown = record\n for (const segment of field.split('.')) {\n if (cursor === null || cursor === undefined) return undefined\n cursor = (cursor as Record<string, unknown>)[segment]\n }\n return cursor\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureCollectionDEK
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-FRRJIUSI.js";
|
|
4
4
|
import {
|
|
5
5
|
NOYDB_FORMAT_VERSION
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-TA6HPKWQ.js";
|
|
7
7
|
import {
|
|
8
8
|
decrypt,
|
|
9
9
|
encrypt
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-37VGJM3T.js";
|
|
11
11
|
import {
|
|
12
12
|
PermissionDeniedError
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-OTWT6BAJ.js";
|
|
14
14
|
|
|
15
15
|
// src/team/sync-credentials.ts
|
|
16
16
|
var SYNC_CREDENTIALS_COLLECTION = "_sync_credentials";
|
|
@@ -76,4 +76,4 @@ export {
|
|
|
76
76
|
listCredentials,
|
|
77
77
|
credentialStatus
|
|
78
78
|
};
|
|
79
|
-
//# sourceMappingURL=chunk-
|
|
79
|
+
//# sourceMappingURL=chunk-EYK72OTL.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TierNotGrantedError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OTWT6BAJ.js";
|
|
4
4
|
|
|
5
5
|
// src/team/tiers.ts
|
|
6
6
|
function dekKey(collection, tier) {
|
|
@@ -31,4 +31,4 @@ export {
|
|
|
31
31
|
effectiveClearance,
|
|
32
32
|
assertTierAccess
|
|
33
33
|
};
|
|
34
|
-
//# sourceMappingURL=chunk-
|
|
34
|
+
//# sourceMappingURL=chunk-F5GWNSE2.js.map
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ATTESTATIONS_COLLECTION,
|
|
3
3
|
loadOrCreateSigner
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TAMRU7A2.js";
|
|
5
5
|
import {
|
|
6
6
|
generateULID
|
|
7
7
|
} from "./chunk-FZU343FL.js";
|
|
8
8
|
import {
|
|
9
9
|
NOYDB_FORMAT_VERSION
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TA6HPKWQ.js";
|
|
11
11
|
import {
|
|
12
12
|
encrypt
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-37VGJM3T.js";
|
|
14
14
|
import {
|
|
15
15
|
AttestationError
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-OTWT6BAJ.js";
|
|
17
17
|
|
|
18
18
|
// src/attestation/issue.ts
|
|
19
19
|
import {
|
|
@@ -56,4 +56,4 @@ async function issueAttestationCore(ctx, args) {
|
|
|
56
56
|
export {
|
|
57
57
|
issueAttestationCore
|
|
58
58
|
};
|
|
59
|
-
//# sourceMappingURL=chunk-
|
|
59
|
+
//# sourceMappingURL=chunk-F5ILTHMU.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
NOYDB_FORMAT_VERSION,
|
|
3
3
|
NOYDB_KEYRING_VERSION
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TA6HPKWQ.js";
|
|
5
5
|
import {
|
|
6
6
|
base64ToBuffer,
|
|
7
7
|
bufferToBase64,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
generateSalt,
|
|
13
13
|
unwrapKey,
|
|
14
14
|
wrapKey
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-37VGJM3T.js";
|
|
16
16
|
import {
|
|
17
17
|
ConflictError,
|
|
18
18
|
DirectoryDisabledError,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
PermissionDeniedError,
|
|
25
25
|
PrivilegeEscalationError,
|
|
26
26
|
ValidationError
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-OTWT6BAJ.js";
|
|
28
28
|
|
|
29
29
|
// src/directory/storage.ts
|
|
30
30
|
var META_COLLECTION = "_meta";
|
|
@@ -340,6 +340,18 @@ async function loadKeyring(adapter, vault, userId, passphrase) {
|
|
|
340
340
|
...keyringFile.policy !== void 0 && { policy: keyringFile.policy }
|
|
341
341
|
};
|
|
342
342
|
}
|
|
343
|
+
async function assertKeyringOpenAllowed(store, vault, userId, create) {
|
|
344
|
+
const keyringUsers = await store.list(vault, "_keyring");
|
|
345
|
+
if (keyringUsers.includes(userId)) return;
|
|
346
|
+
if (!create) {
|
|
347
|
+
throw new NoAccessError(`Vault "${vault}" not opened: create disabled and no keyring for "${userId}".`);
|
|
348
|
+
}
|
|
349
|
+
if (keyringUsers.length > 0) {
|
|
350
|
+
throw new NoAccessError(
|
|
351
|
+
`No keyring for user "${userId}" in vault "${vault}" (held by other principals) \u2014 refusing to self-provision.`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
343
355
|
async function createOwnerKeyring(adapter, vault, userId, passphrase, passphraseOpts) {
|
|
344
356
|
if (passphraseOpts?.validate && !passphraseOpts.allowWeakPassphrase) {
|
|
345
357
|
assertStrongPassphrase(passphrase, passphraseOpts);
|
|
@@ -631,7 +643,7 @@ async function changeSecret(adapter, vault, keyring, newPassphrase, passphraseOp
|
|
|
631
643
|
// Tier-2 slots are NOT preserved through `changeSecret` —
|
|
632
644
|
// each slot wraps the OLD KEK, so the new keyring has no
|
|
633
645
|
// authenticator slots until the user re-enrolls. The higher-level
|
|
634
|
-
// `db.rotatePassphrase()`
|
|
646
|
+
// `db.rotatePassphrase()` preserves slots by rewrapping the
|
|
635
647
|
// KEK reference, not the KEK itself.
|
|
636
648
|
authenticators: [],
|
|
637
649
|
...keyring.policy !== void 0 && { policy: keyring.policy }
|
|
@@ -863,6 +875,7 @@ export {
|
|
|
863
875
|
listUserEnvelopeIds,
|
|
864
876
|
mintKeyringCanary,
|
|
865
877
|
loadKeyring,
|
|
878
|
+
assertKeyringOpenAllowed,
|
|
866
879
|
createOwnerKeyring,
|
|
867
880
|
grant,
|
|
868
881
|
revoke,
|
|
@@ -881,4 +894,4 @@ export {
|
|
|
881
894
|
hasImportCapability,
|
|
882
895
|
evaluateImportCapability
|
|
883
896
|
};
|
|
884
|
-
//# sourceMappingURL=chunk-
|
|
897
|
+
//# sourceMappingURL=chunk-FRRJIUSI.js.map
|