@noy-db/hub 0.6.0-pre.9 → 0.6.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 +7079 -0
- package/codemods/0.6.0-pre.json +211 -0
- package/dist/api-6X2VLZ7J.js +20 -0
- package/dist/attestation/index.js +19 -19
- package/dist/{backup-KULFZ2T4.js → backup-VL4GRIEW.js} +36 -18
- package/dist/backup-VL4GRIEW.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/cargo/index.js +35 -35
- package/dist/{chunk-KUDZVJKQ.js → chunk-22GR3O4J.js} +2 -2
- package/dist/{chunk-CCZJJ4I2.js → chunk-2CBFYXJX.js} +6 -6
- package/dist/{chunk-JJNOLSG5.js → chunk-2J5IT37K.js} +15 -16
- package/dist/chunk-2J5IT37K.js.map +1 -0
- package/dist/{chunk-PNKHAKQ7.js → chunk-2ZCWHCLF.js} +17 -10
- package/dist/chunk-2ZCWHCLF.js.map +1 -0
- package/dist/{chunk-2BMRJ6LQ.js → chunk-2ZT4INZ4.js} +8 -11
- package/dist/{chunk-2BMRJ6LQ.js.map → chunk-2ZT4INZ4.js.map} +1 -1
- package/dist/{chunk-FAXZQ5UF.js → chunk-323MN67Z.js} +2 -2
- package/dist/{chunk-VMCCGUQB.js → chunk-36CEG6CM.js} +2 -2
- package/dist/{chunk-PDLLCOGD.js → chunk-3OE2QNSM.js} +20 -17
- package/dist/chunk-3OE2QNSM.js.map +1 -0
- package/dist/{chunk-AHLJSVRL.js → chunk-3YGUHUJA.js} +5 -7
- package/dist/chunk-3YGUHUJA.js.map +1 -0
- package/dist/{chunk-ZJADGNYF.js → chunk-4AOERLTV.js} +1 -1
- package/dist/chunk-4AOERLTV.js.map +1 -0
- package/dist/{chunk-BRVW2GS5.js → chunk-4ATF4BLF.js} +87 -34
- package/dist/chunk-4ATF4BLF.js.map +1 -0
- package/dist/{chunk-747LRSOO.js → chunk-4K6BIMLF.js} +2 -2
- package/dist/{chunk-5O5HRRTV.js → chunk-5ATC6UT4.js} +7 -7
- package/dist/{chunk-5O5HRRTV.js.map → chunk-5ATC6UT4.js.map} +1 -1
- package/dist/{chunk-4GZ6XOVW.js → chunk-5F7BZAGV.js} +44 -42
- package/dist/chunk-5F7BZAGV.js.map +1 -0
- package/dist/{chunk-FNSMZQS4.js → chunk-5IHGJHWC.js} +2 -2
- package/dist/{chunk-4I72F5DF.js → chunk-5LZZQMIC.js} +11 -14
- package/dist/chunk-5LZZQMIC.js.map +1 -0
- package/dist/{chunk-HPLWFNG4.js → chunk-5O5ZSW7N.js} +2 -2
- package/dist/{chunk-CEJWMBXO.js → chunk-5OT4G5N5.js} +6 -2
- package/dist/chunk-5OT4G5N5.js.map +1 -0
- package/dist/{chunk-GAG7QOVO.js → chunk-64WHKBMP.js} +53 -18
- package/dist/chunk-64WHKBMP.js.map +1 -0
- package/dist/{chunk-7TSYBD3E.js → chunk-65JXA63J.js} +2 -2
- package/dist/{chunk-Z45YRGFO.js → chunk-6NR36UNB.js} +2 -2
- package/dist/{chunk-W77KZHCW.js → chunk-6RH7PRTL.js} +16 -17
- package/dist/chunk-6RH7PRTL.js.map +1 -0
- package/dist/{chunk-3JVXVJ7L.js → chunk-7OBIBPCV.js} +10 -10
- package/dist/{chunk-AR2JDFAE.js → chunk-7Q3RHOA5.js} +2 -2
- package/dist/chunk-7ZGPQOFV.js +190 -0
- package/dist/chunk-7ZGPQOFV.js.map +1 -0
- package/dist/{chunk-QULVCV3M.js → chunk-AJGGBXWA.js} +4 -4
- package/dist/{chunk-CADUYBQD.js → chunk-ATL3ZVXT.js} +2 -2
- package/dist/{chunk-DMQCLZ4Z.js → chunk-ATY3ZASK.js} +2 -2
- package/dist/{chunk-4HDYQL3J.js → chunk-B4CRUZPR.js} +2 -2
- package/dist/{chunk-RV3ARMPN.js → chunk-BMU2DS3S.js} +30 -21
- package/dist/chunk-BMU2DS3S.js.map +1 -0
- package/dist/{chunk-6SAFL5YE.js → chunk-BZ3WMOYX.js} +3 -3
- package/dist/{chunk-UHXCLPGC.js → chunk-C3XWJCG7.js} +2 -2
- package/dist/chunk-C3XWJCG7.js.map +1 -0
- package/dist/{chunk-HXGUIPQB.js → chunk-CKKN25P2.js} +3 -3
- package/dist/{chunk-3URQC7EH.js → chunk-CXPOG3ZY.js} +355 -132
- package/dist/chunk-CXPOG3ZY.js.map +1 -0
- package/dist/{chunk-3FSX6LDG.js → chunk-D2YFQ7XG.js} +18 -3
- package/dist/{chunk-3FSX6LDG.js.map → chunk-D2YFQ7XG.js.map} +1 -1
- package/dist/{chunk-52JPHN5N.js → chunk-D6SXRMZZ.js} +27 -149
- package/dist/chunk-D6SXRMZZ.js.map +1 -0
- package/dist/{chunk-2DHSSLOD.js → chunk-DECNV4QK.js} +13 -15
- package/dist/chunk-DECNV4QK.js.map +1 -0
- package/dist/{chunk-6BU3L4UE.js → chunk-DNKVKO7K.js} +7 -10
- package/dist/chunk-DNKVKO7K.js.map +1 -0
- package/dist/{chunk-6HFM7EIT.js → chunk-EHRIDNZX.js} +2 -2
- package/dist/{chunk-3737A6EA.js → chunk-EHW7NLGD.js} +127 -31
- package/dist/chunk-EHW7NLGD.js.map +1 -0
- package/dist/{chunk-BEFVYAR5.js → chunk-EQOTJL76.js} +4 -4
- package/dist/chunk-EQOTJL76.js.map +1 -0
- package/dist/{chunk-E6427JU2.js → chunk-EV2TDHQZ.js} +19 -3
- package/dist/chunk-EV2TDHQZ.js.map +1 -0
- package/dist/{chunk-WFUIE565.js → chunk-FNYCT27B.js} +17 -14
- package/dist/chunk-FNYCT27B.js.map +1 -0
- package/dist/{chunk-RTAMLDWP.js → chunk-FU5VYEYK.js} +2 -2
- package/dist/chunk-FX3UIDCS.js +66 -0
- package/dist/chunk-FX3UIDCS.js.map +1 -0
- package/dist/{chunk-7F4DBNSP.js → chunk-GANFIM2M.js} +135 -18
- package/dist/chunk-GANFIM2M.js.map +1 -0
- package/dist/{chunk-4VUPLMZ6.js → chunk-GCU42IG7.js} +66 -71
- package/dist/chunk-GCU42IG7.js.map +1 -0
- package/dist/{chunk-RXYYFSBB.js → chunk-GKTW3KLV.js} +4 -4
- package/dist/chunk-HK67PX4J.js +27 -0
- package/dist/chunk-HK67PX4J.js.map +1 -0
- package/dist/{chunk-OF3L2SD6.js → chunk-HOPNIR5R.js} +2 -2
- package/dist/{chunk-WLZ47GSL.js → chunk-HSJBJIB2.js} +12 -10
- package/dist/{chunk-WLZ47GSL.js.map → chunk-HSJBJIB2.js.map} +1 -1
- package/dist/{chunk-ADW22QSB.js → chunk-HWI7GHJZ.js} +4 -4
- package/dist/{chunk-GQ3BX5EA.js → chunk-I3ISCYT7.js} +2 -2
- package/dist/{chunk-YJ6IONGA.js → chunk-IWKCJCLP.js} +11 -3
- package/dist/{chunk-YJ6IONGA.js.map → chunk-IWKCJCLP.js.map} +1 -1
- package/dist/{chunk-NJDQ4YSV.js → chunk-IWQPYT6S.js} +4 -4
- package/dist/{chunk-JRKZTEZZ.js → chunk-J6I5VR6P.js} +444 -73
- package/dist/chunk-J6I5VR6P.js.map +1 -0
- package/dist/{chunk-2Z3NCLNX.js → chunk-JHYKSMVX.js} +7 -7
- package/dist/{chunk-6FJWSWFT.js → chunk-JIHWBKMQ.js} +3 -3
- package/dist/{chunk-YAWK6AWF.js → chunk-JKATP6OE.js} +2 -2
- package/dist/{chunk-PL2GHSCM.js → chunk-JMJSCI7U.js} +157 -90
- package/dist/chunk-JMJSCI7U.js.map +1 -0
- package/dist/{chunk-Q6TEJXBX.js → chunk-JXHNUYQO.js} +31 -49
- package/dist/chunk-JXHNUYQO.js.map +1 -0
- package/dist/{chunk-2GAJGUNM.js → chunk-JYPRZWCZ.js} +343 -229
- package/dist/chunk-JYPRZWCZ.js.map +1 -0
- package/dist/{chunk-5ZKS6ZE2.js → chunk-K6CPIK3R.js} +4 -4
- package/dist/chunk-K6CPIK3R.js.map +1 -0
- package/dist/{chunk-CDB2HVQW.js → chunk-KM6ZXG7U.js} +14 -15
- package/dist/chunk-KM6ZXG7U.js.map +1 -0
- package/dist/{chunk-DG27IUVI.js → chunk-LC6NE4IQ.js} +2 -2
- package/dist/{chunk-WASHZV34.js → chunk-LIR2DJWP.js} +63 -25
- package/dist/chunk-LIR2DJWP.js.map +1 -0
- package/dist/{chunk-VZ3XFUQ7.js → chunk-LUYPYCHY.js} +16 -22
- package/dist/chunk-LUYPYCHY.js.map +1 -0
- package/dist/{chunk-G6S5VVHP.js → chunk-M7WBE3OR.js} +10 -10
- package/dist/chunk-M7WBE3OR.js.map +1 -0
- package/dist/{chunk-VJFMXYKP.js → chunk-MPKP6L5E.js} +2 -2
- package/dist/{chunk-IGJCYUT5.js → chunk-NSITFOCJ.js} +1 -1
- package/dist/chunk-NSITFOCJ.js.map +1 -0
- package/dist/{chunk-FUHKW252.js → chunk-OCJ3JA4O.js} +2 -2
- package/dist/{chunk-VCETL77O.js → chunk-ONPVHX2Y.js} +36 -7
- package/dist/chunk-ONPVHX2Y.js.map +1 -0
- package/dist/{chunk-WLYIROCJ.js → chunk-OQMCFHUX.js} +2 -2
- package/dist/{chunk-JRCYZEHU.js → chunk-OR2FYRYR.js} +2 -2
- package/dist/{chunk-HNHCQ556.js → chunk-PFWE7FJG.js} +2 -2
- package/dist/{chunk-NPP5R7FF.js → chunk-PJX72ICF.js} +22 -18
- package/dist/chunk-PJX72ICF.js.map +1 -0
- package/dist/{chunk-U5O6GIWZ.js → chunk-POFYIGQ2.js} +3 -3
- package/dist/{chunk-35EBKWQU.js → chunk-QH47SQLD.js} +4 -4
- package/dist/{chunk-FZ6NYJ7L.js → chunk-QQRRKO7W.js} +11 -8
- package/dist/chunk-QQRRKO7W.js.map +1 -0
- package/dist/{chunk-HVQGCKY4.js → chunk-QVXZFJBU.js} +2 -2
- package/dist/{chunk-FMQJZH4R.js → chunk-RDB66EKD.js} +6 -6
- package/dist/{chunk-X56RTUK3.js → chunk-RJZQ2PVD.js} +3 -3
- package/dist/{chunk-SUFG5E73.js → chunk-RPRASGEY.js} +2 -2
- package/dist/{chunk-XWX7GL56.js → chunk-S2I5MYSA.js} +7 -10
- package/dist/chunk-S2I5MYSA.js.map +1 -0
- package/dist/{chunk-FFDNKRCY.js → chunk-S6PJ6ZKE.js} +2 -2
- package/dist/chunk-S6PJ6ZKE.js.map +1 -0
- package/dist/{chunk-JIUVJ4M7.js → chunk-S7PM7Z4K.js} +3 -3
- package/dist/{chunk-55LKVALY.js → chunk-T7IR4N2S.js} +1 -3
- package/dist/chunk-T7IR4N2S.js.map +1 -0
- package/dist/{chunk-N35NC4JH.js → chunk-TKBB62DN.js} +2 -2
- package/dist/{chunk-YPYHVOXP.js → chunk-UJ4Z67UQ.js} +2 -2
- package/dist/chunk-UJ4Z67UQ.js.map +1 -0
- package/dist/{chunk-7MNBICB5.js → chunk-W3HAIN6U.js} +16 -14
- package/dist/chunk-W3HAIN6U.js.map +1 -0
- package/dist/{chunk-4UVDI4XE.js → chunk-WPZ5FVAS.js} +4 -4
- package/dist/{chunk-MSWYQLMQ.js → chunk-XINT4VJA.js} +2 -2
- package/dist/{chunk-7G6VDWXB.js → chunk-XPEPMK4Y.js} +3 -3
- package/dist/chunk-XPEPMK4Y.js.map +1 -0
- package/dist/{chunk-W3TVWF6N.js → chunk-YCGLAANM.js} +2 -2
- package/dist/{chunk-O2ERS4UJ.js → chunk-YTUFV7U7.js} +80 -51
- package/dist/chunk-YTUFV7U7.js.map +1 -0
- package/dist/{chunk-FDX7OWWI.js → chunk-YYV53EHO.js} +2 -2
- package/dist/{chunk-HNGSV2YC.js → chunk-Z3BZXPRO.js} +67 -10
- package/dist/chunk-Z3BZXPRO.js.map +1 -0
- package/dist/{chunk-6NVPFKN3.js → chunk-ZIMQDXTQ.js} +92 -35
- package/dist/chunk-ZIMQDXTQ.js.map +1 -0
- package/dist/{chunk-5JY5TSVY.js → chunk-ZJUZOJVW.js} +25 -31
- package/dist/chunk-ZJUZOJVW.js.map +1 -0
- package/dist/{chunk-3IXIU6FP.js → chunk-ZY6P7M52.js} +7 -10
- package/dist/chunk-ZY6P7M52.js.map +1 -0
- package/dist/{chunk-BWVKEOPE.js → chunk-ZYZJLYNS.js} +7 -11
- package/dist/chunk-ZYZJLYNS.js.map +1 -0
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-2KJAXRRO.js → classified-marker-AZDSDG6V.js} +3 -3
- package/dist/collection-facade-WSUAYFJ4.js +45 -0
- package/dist/{computed-6G5M3UPH.js → computed-VG2PCM4L.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-JNQNB3TA.js} +2 -2
- package/dist/debug/index.js +1 -1
- package/dist/{delegation-MO7YZYJS.js → delegation-5F36DRDH.js} +24 -23
- package/dist/delegation-5F36DRDH.js.map +1 -0
- package/dist/derivations/index.js +12 -12
- package/dist/derive-MPOGZ4NR.js +22 -0
- package/dist/directory/index.js +10 -10
- package/dist/{dispatch-MJ6SHB3Y.js → dispatch-3ZA733EY.js} +18 -18
- package/dist/{dispatch-FZEAWKVM.js → dispatch-TNM5CRLZ.js} +9 -6
- package/dist/dispatch-TNM5CRLZ.js.map +1 -0
- package/dist/{enclave-JU4YIEPB.js → enclave-Y2ALNDRD.js} +31 -9
- package/dist/executor-BBEEUFO3.js +9 -0
- package/dist/executor-G6G3YEWR.js +9 -0
- package/dist/executor-ZUTUMTPA.js +29 -0
- package/dist/export-accessible-S3KEB763.js +23 -0
- package/dist/extract-partition-KVCTSUVE.js +38 -0
- package/dist/{fanout-sidecar-NNAJQXMX.js → fanout-sidecar-3QZU7ZZX.js} +18 -27
- package/dist/fanout-sidecar-3QZU7ZZX.js.map +1 -0
- package/dist/find-V4FN5ITT.js +11 -0
- package/dist/forget/index.js +9 -9
- package/dist/guards/index.js +7 -7
- 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 +30 -13
- package/dist/index.js +180 -188
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +4 -4
- package/dist/introspection/index.js +11 -11
- package/dist/issue-5CFBWBRX.js +19 -0
- package/dist/kernel/collection.d.ts +8 -2
- 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 +132 -12
- package/dist/kernel/merge-authority.d.ts +25 -0
- package/dist/kernel/noydb.d.ts +27 -4
- 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/sync-policy.d.ts +0 -2
- package/dist/kernel/tier-visibility.d.ts +34 -0
- package/dist/kernel/types.d.ts +111 -13
- package/dist/kernel/vault.d.ts +6 -6
- package/dist/kernel/via/index.d.ts +11 -2
- package/dist/kernel/via/reconcile.d.ts +12 -0
- package/dist/{ledger-AETNF4OM.js → ledger-MQIIOTZB.js} +11 -11
- package/dist/liberate-LW7OKVBH.js +25 -0
- package/dist/{link-set-E5HNY67I.js → link-set-5PJBBR63.js} +11 -11
- package/dist/{managed-secret-CWQ6PND5.js → managed-secret-ETDG6AAE.js} +11 -3
- package/dist/materialized-views/index.js +24 -24
- package/dist/migrate-cek-V3VDRV24.js +19 -0
- package/dist/migrate-cek-V3VDRV24.js.map +1 -0
- package/dist/money/index.js +5 -5
- package/dist/noydb-LFDRMYZ5.js +85 -0
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +12 -12
- package/dist/pod/index.js +79 -89
- package/dist/pod-handle-AZ3CXY5Y.js +41 -0
- package/dist/pod-handle-AZ3CXY5Y.js.map +1 -0
- package/dist/policy/index.js +11 -4
- package/dist/port/to/index.d.ts +4 -4
- package/dist/port/to/locator.d.ts +54 -8
- 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 +2 -0
- 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-QCJYRMIK.js} +6 -6
- package/dist/query/index.js +6 -6
- package/dist/reduce/index.js +6 -6
- package/dist/register-JBOEHTVX.js +32 -0
- package/dist/registry-5DYHQFJS.js +9 -0
- package/dist/registry-EANV44W2.js +9 -0
- package/dist/registry-J25H6VB4.js +19 -0
- package/dist/registry-R23SQUQH.js +18 -0
- package/dist/request-withdrawal-KQ4W4UT5.js +31 -0
- package/dist/{reveal-BW36KCOQ.js → reveal-W7BKZSUP.js} +6 -6
- package/dist/revoke-H3VSTAIE.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-55FPOMYP.js} +20 -22
- package/dist/seed-55FPOMYP.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-53AQ3BU2.js +25 -0
- package/dist/snapshots/index.js +10 -10
- package/dist/snapshots/index.js.map +1 -1
- package/dist/{stale-ARHFVL5U.js → stale-TH7SSVRV.js} +10 -10
- package/dist/storage-WSF7ZKTL.js +23 -0
- package/dist/{store-coordination-provider-GFCUOBR6.js → store-coordination-provider-R27VRD6K.js} +18 -12
- package/dist/store-coordination-provider-R27VRD6K.js.map +1 -0
- package/dist/sync/index.js +11 -11
- package/dist/team/index.js +15 -14
- 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/util/index.js +1 -1
- package/dist/vault-head/index.js +115 -0
- package/dist/vault-head/index.js.map +1 -0
- package/dist/{verify-RO5RTZOJ.js → verify-M5LQH43C.js} +5 -5
- package/dist/via/blob/index.d.ts +3 -0
- package/dist/via/lookup/handle.d.ts +4 -1
- package/dist/walk-NABCHDZN.js +21 -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 +1 -1
- package/dist/with-cargo/extract-partition.d.ts +1 -1
- package/dist/with-cargo/index.d.ts +1 -1
- 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/index.d.ts +1 -1
- package/dist/with-party/team/keyring.d.ts +139 -2
- package/dist/with-party/team/noydb-facade.d.ts +5 -1
- package/dist/with-party/team/roster-tag.d.ts +101 -0
- package/dist/with-party/team/tiers.d.ts +2 -1
- package/dist/with-pod/format.d.ts +10 -12
- 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} +11 -19
- 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-sync/engine.d.ts +54 -5
- package/dist/with-sync/strategy.d.ts +3 -0
- package/dist/withdraw-accessible-VCINIG6V.js +28 -0
- package/package.json +10 -4
- 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-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-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-RV3ARMPN.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-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/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-6X2VLZ7J.js.map} +0 -0
- /package/dist/{chunk-KUDZVJKQ.js.map → chunk-22GR3O4J.js.map} +0 -0
- /package/dist/{chunk-CCZJJ4I2.js.map → chunk-2CBFYXJX.js.map} +0 -0
- /package/dist/{chunk-FAXZQ5UF.js.map → chunk-323MN67Z.js.map} +0 -0
- /package/dist/{chunk-VMCCGUQB.js.map → chunk-36CEG6CM.js.map} +0 -0
- /package/dist/{chunk-747LRSOO.js.map → chunk-4K6BIMLF.js.map} +0 -0
- /package/dist/{chunk-FNSMZQS4.js.map → chunk-5IHGJHWC.js.map} +0 -0
- /package/dist/{chunk-HPLWFNG4.js.map → chunk-5O5ZSW7N.js.map} +0 -0
- /package/dist/{chunk-7TSYBD3E.js.map → chunk-65JXA63J.js.map} +0 -0
- /package/dist/{chunk-Z45YRGFO.js.map → chunk-6NR36UNB.js.map} +0 -0
- /package/dist/{chunk-3JVXVJ7L.js.map → chunk-7OBIBPCV.js.map} +0 -0
- /package/dist/{chunk-AR2JDFAE.js.map → chunk-7Q3RHOA5.js.map} +0 -0
- /package/dist/{chunk-QULVCV3M.js.map → chunk-AJGGBXWA.js.map} +0 -0
- /package/dist/{chunk-CADUYBQD.js.map → chunk-ATL3ZVXT.js.map} +0 -0
- /package/dist/{chunk-DMQCLZ4Z.js.map → chunk-ATY3ZASK.js.map} +0 -0
- /package/dist/{chunk-4HDYQL3J.js.map → chunk-B4CRUZPR.js.map} +0 -0
- /package/dist/{chunk-6SAFL5YE.js.map → chunk-BZ3WMOYX.js.map} +0 -0
- /package/dist/{chunk-HXGUIPQB.js.map → chunk-CKKN25P2.js.map} +0 -0
- /package/dist/{chunk-6HFM7EIT.js.map → chunk-EHRIDNZX.js.map} +0 -0
- /package/dist/{chunk-RTAMLDWP.js.map → chunk-FU5VYEYK.js.map} +0 -0
- /package/dist/{chunk-RXYYFSBB.js.map → chunk-GKTW3KLV.js.map} +0 -0
- /package/dist/{chunk-OF3L2SD6.js.map → chunk-HOPNIR5R.js.map} +0 -0
- /package/dist/{chunk-ADW22QSB.js.map → chunk-HWI7GHJZ.js.map} +0 -0
- /package/dist/{chunk-GQ3BX5EA.js.map → chunk-I3ISCYT7.js.map} +0 -0
- /package/dist/{chunk-NJDQ4YSV.js.map → chunk-IWQPYT6S.js.map} +0 -0
- /package/dist/{chunk-2Z3NCLNX.js.map → chunk-JHYKSMVX.js.map} +0 -0
- /package/dist/{chunk-6FJWSWFT.js.map → chunk-JIHWBKMQ.js.map} +0 -0
- /package/dist/{chunk-YAWK6AWF.js.map → chunk-JKATP6OE.js.map} +0 -0
- /package/dist/{chunk-DG27IUVI.js.map → chunk-LC6NE4IQ.js.map} +0 -0
- /package/dist/{chunk-VJFMXYKP.js.map → chunk-MPKP6L5E.js.map} +0 -0
- /package/dist/{chunk-FUHKW252.js.map → chunk-OCJ3JA4O.js.map} +0 -0
- /package/dist/{chunk-WLYIROCJ.js.map → chunk-OQMCFHUX.js.map} +0 -0
- /package/dist/{chunk-JRCYZEHU.js.map → chunk-OR2FYRYR.js.map} +0 -0
- /package/dist/{chunk-HNHCQ556.js.map → chunk-PFWE7FJG.js.map} +0 -0
- /package/dist/{chunk-U5O6GIWZ.js.map → chunk-POFYIGQ2.js.map} +0 -0
- /package/dist/{chunk-35EBKWQU.js.map → chunk-QH47SQLD.js.map} +0 -0
- /package/dist/{chunk-HVQGCKY4.js.map → chunk-QVXZFJBU.js.map} +0 -0
- /package/dist/{chunk-FMQJZH4R.js.map → chunk-RDB66EKD.js.map} +0 -0
- /package/dist/{chunk-X56RTUK3.js.map → chunk-RJZQ2PVD.js.map} +0 -0
- /package/dist/{chunk-SUFG5E73.js.map → chunk-RPRASGEY.js.map} +0 -0
- /package/dist/{chunk-JIUVJ4M7.js.map → chunk-S7PM7Z4K.js.map} +0 -0
- /package/dist/{chunk-N35NC4JH.js.map → chunk-TKBB62DN.js.map} +0 -0
- /package/dist/{chunk-4UVDI4XE.js.map → chunk-WPZ5FVAS.js.map} +0 -0
- /package/dist/{chunk-MSWYQLMQ.js.map → chunk-XINT4VJA.js.map} +0 -0
- /package/dist/{chunk-W3TVWF6N.js.map → chunk-YCGLAANM.js.map} +0 -0
- /package/dist/{chunk-FDX7OWWI.js.map → chunk-YYV53EHO.js.map} +0 -0
- /package/dist/{classified-marker-2KJAXRRO.js.map → classified-marker-AZDSDG6V.js.map} +0 -0
- /package/dist/{collection-facade-QEJIM6BC.js.map → collection-facade-WSUAYFJ4.js.map} +0 -0
- /package/dist/{computed-6G5M3UPH.js.map → computed-VG2PCM4L.js.map} +0 -0
- /package/dist/{dead-filter-DCB62VWW.js.map → dead-filter-JNQNB3TA.js.map} +0 -0
- /package/dist/{derive-IUA6JFLB.js.map → derive-MPOGZ4NR.js.map} +0 -0
- /package/dist/{dispatch-MJ6SHB3Y.js.map → dispatch-3ZA733EY.js.map} +0 -0
- /package/dist/{enclave-JU4YIEPB.js.map → enclave-Y2ALNDRD.js.map} +0 -0
- /package/dist/{executor-CYJQLKKI.js.map → executor-BBEEUFO3.js.map} +0 -0
- /package/dist/{executor-S4QIKEHW.js.map → executor-G6G3YEWR.js.map} +0 -0
- /package/dist/{executor-YIQB2S6N.js.map → executor-ZUTUMTPA.js.map} +0 -0
- /package/dist/{export-accessible-4S36PQNR.js.map → export-accessible-S3KEB763.js.map} +0 -0
- /package/dist/{extract-partition-IO7FDZ2P.js.map → extract-partition-KVCTSUVE.js.map} +0 -0
- /package/dist/{find-RBBYKXI7.js.map → find-V4FN5ITT.js.map} +0 -0
- /package/dist/{issue-RIVHSY2S.js.map → issue-5CFBWBRX.js.map} +0 -0
- /package/dist/{ledger-AETNF4OM.js.map → ledger-MQIIOTZB.js.map} +0 -0
- /package/dist/{liberate-GFN4C7RR.js.map → liberate-LW7OKVBH.js.map} +0 -0
- /package/dist/{link-set-E5HNY67I.js.map → link-set-5PJBBR63.js.map} +0 -0
- /package/dist/{managed-secret-CWQ6PND5.js.map → managed-secret-ETDG6AAE.js.map} +0 -0
- /package/dist/{noydb-ASEXNJS6.js.map → noydb-LFDRMYZ5.js.map} +0 -0
- /package/dist/{post-register-2XSSMGEQ.js.map → post-register-QCJYRMIK.js.map} +0 -0
- /package/dist/{register-4EFJCVYD.js.map → register-JBOEHTVX.js.map} +0 -0
- /package/dist/{registry-4SDHHOJ7.js.map → registry-5DYHQFJS.js.map} +0 -0
- /package/dist/{registry-CZVINVQH.js.map → registry-EANV44W2.js.map} +0 -0
- /package/dist/{registry-IKUR2MXD.js.map → registry-J25H6VB4.js.map} +0 -0
- /package/dist/{registry-OYTFGWVK.js.map → registry-R23SQUQH.js.map} +0 -0
- /package/dist/{request-withdrawal-KGXGDPVQ.js.map → request-withdrawal-KQ4W4UT5.js.map} +0 -0
- /package/dist/{reveal-BW36KCOQ.js.map → reveal-W7BKZSUP.js.map} +0 -0
- /package/dist/{revoke-JFN773KF.js.map → revoke-H3VSTAIE.js.map} +0 -0
- /package/dist/{signer-TA55QB6R.js.map → signer-53AQ3BU2.js.map} +0 -0
- /package/dist/{stale-ARHFVL5U.js.map → stale-TH7SSVRV.js.map} +0 -0
- /package/dist/{storage-V7MTFAQJ.js.map → storage-WSF7ZKTL.js.map} +0 -0
- /package/dist/{ulid-PTAIMDG4.js.map → ulid-VQW6ERQN.js.map} +0 -0
- /package/dist/{verify-RO5RTZOJ.js.map → verify-M5LQH43C.js.map} +0 -0
- /package/dist/{walk-3ZECBFDA.js.map → walk-NABCHDZN.js.map} +0 -0
- /package/dist/{withdraw-accessible-7PG4DYQK.js.map → withdraw-accessible-VCINIG6V.js.map} +0 -0
|
@@ -5,7 +5,18 @@ import {
|
|
|
5
5
|
readDirectoryConfig,
|
|
6
6
|
readUserVisibility,
|
|
7
7
|
saveUserEnvelope
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZJUZOJVW.js";
|
|
9
|
+
import {
|
|
10
|
+
parseDekKey
|
|
11
|
+
} from "./chunk-D2YFQ7XG.js";
|
|
12
|
+
import {
|
|
13
|
+
envelopeOpensUnderAny,
|
|
14
|
+
rekeyBlobSet,
|
|
15
|
+
rekeyEnvelopeIfNeeded
|
|
16
|
+
} from "./chunk-CXPOG3ZY.js";
|
|
17
|
+
import {
|
|
18
|
+
buildRecordEnvelope
|
|
19
|
+
} from "./chunk-FX3UIDCS.js";
|
|
9
20
|
import {
|
|
10
21
|
base64ToBuffer,
|
|
11
22
|
bufferToBase64,
|
|
@@ -20,25 +31,104 @@ import {
|
|
|
20
31
|
generateSalt,
|
|
21
32
|
unwrapKey,
|
|
22
33
|
wrapKey
|
|
23
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-ONPVHX2Y.js";
|
|
24
35
|
import {
|
|
25
|
-
NOYDB_FORMAT_VERSION,
|
|
26
36
|
NOYDB_KEYRING_VERSION
|
|
27
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-UJ4Z67UQ.js";
|
|
28
38
|
import {
|
|
39
|
+
BLOB_ADDRESS_KEY_ID,
|
|
29
40
|
DirectoryDisabledError,
|
|
30
41
|
EchoCeremonyRequiredError,
|
|
31
42
|
InvalidKeyError,
|
|
32
43
|
KeyringCorruptError,
|
|
33
44
|
KeyringExpiredError,
|
|
45
|
+
KeyringTamperedError,
|
|
34
46
|
NoAccessError,
|
|
35
47
|
NoydbError,
|
|
36
48
|
PermissionDeniedError,
|
|
37
49
|
PrivilegeEscalationError,
|
|
50
|
+
ROSTER_KEY_ID,
|
|
38
51
|
USER_ENVELOPE_COLLECTION,
|
|
39
52
|
ValidationError,
|
|
40
53
|
WrongPromptError
|
|
41
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-Z3BZXPRO.js";
|
|
55
|
+
|
|
56
|
+
// src/with-party/team/roster-tag.ts
|
|
57
|
+
function stable(value) {
|
|
58
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
59
|
+
if (value !== null && typeof value === "object") {
|
|
60
|
+
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${stable(v)}`);
|
|
61
|
+
return `{${entries.join(",")}}`;
|
|
62
|
+
}
|
|
63
|
+
return JSON.stringify(value) ?? "null";
|
|
64
|
+
}
|
|
65
|
+
function rosterCanonical(file) {
|
|
66
|
+
return stable({
|
|
67
|
+
user_id: file.user_id,
|
|
68
|
+
role: file.role,
|
|
69
|
+
permissions: file.permissions,
|
|
70
|
+
granted_by: file.granted_by,
|
|
71
|
+
// #1115 — WHICH collections this keyring holds a key for, authenticated.
|
|
72
|
+
//
|
|
73
|
+
// `revoke` derives its rotation scope from `Object.keys(target.deks)`, so
|
|
74
|
+
// while this set was unauthenticated a store could strip entries from the
|
|
75
|
+
// target's file and have those collections silently skipped by the
|
|
76
|
+
// rotation — leaving a revoked member, colluding with that store, holding
|
|
77
|
+
// live DEKs for exactly the collections it removed. That directly
|
|
78
|
+
// contradicts `SECURITY.md`'s "the rotation cannot be skipped".
|
|
79
|
+
//
|
|
80
|
+
// NAMES ONLY, deliberately. The wrapped values are AES-KW, which is
|
|
81
|
+
// self-authenticating: a tampered wrap fails to unwrap. What was
|
|
82
|
+
// unprotected is the SHAPE of the map, not its contents.
|
|
83
|
+
//
|
|
84
|
+
// `pending_deks` is bound for the same reason one field over: stripping it
|
|
85
|
+
// makes an interrupted rotation mint a fresh DEK instead of resuming,
|
|
86
|
+
// permanently orphaning every record already rewritten under the pending
|
|
87
|
+
// key (#1074). Leaving it out would recreate the identical hole.
|
|
88
|
+
dek_slots: Object.keys(file.deks ?? {}).sort(),
|
|
89
|
+
pending_dek_slots: Object.keys(file.pending_deks ?? {}).sort(),
|
|
90
|
+
expires_at: file.expires_at ?? null,
|
|
91
|
+
export_capability: file.export_capability ?? null,
|
|
92
|
+
import_capability: file.import_capability ?? null
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
async function mintRosterTag(file, rosterKey) {
|
|
96
|
+
return encrypt(rosterCanonical(file), rosterKey);
|
|
97
|
+
}
|
|
98
|
+
async function verifyRosterTag(file, tag, rosterKey) {
|
|
99
|
+
if (tag == null) return false;
|
|
100
|
+
try {
|
|
101
|
+
return await decrypt(tag.iv, tag.data, rosterKey) === rosterCanonical(file);
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function assertRosterAuthenticated(file, deks, userId) {
|
|
107
|
+
const rosterKey = deks.get(ROSTER_KEY_ID);
|
|
108
|
+
if (rosterKey === void 0) {
|
|
109
|
+
throw new KeyringTamperedError({ userId, reason: "roster-key-missing" });
|
|
110
|
+
}
|
|
111
|
+
await assertRosterTagValid(file, rosterKey, userId);
|
|
112
|
+
}
|
|
113
|
+
async function assertRosterTagValid(file, rosterKey, userId) {
|
|
114
|
+
if (!await verifyRosterTag(file, file.roster_tag, rosterKey)) {
|
|
115
|
+
const reason = mismatchReason(file);
|
|
116
|
+
throw new KeyringTamperedError({
|
|
117
|
+
userId,
|
|
118
|
+
reason,
|
|
119
|
+
// Only on the format branch: elsewhere the declared version matches, so
|
|
120
|
+
// reporting a transition would invent one. `from` is what the FILE says
|
|
121
|
+
// — untrusted, and safe to surface for the same reason `mismatchReason`
|
|
122
|
+
// is safe to compute from it: it selects wording, never a decision.
|
|
123
|
+
...reason === "format-superseded" ? { format: { from: Number(file._noydb_keyring), to: NOYDB_KEYRING_VERSION } } : {}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function mismatchReason(file) {
|
|
128
|
+
if (file.roster_tag == null) return "roster-tag-missing";
|
|
129
|
+
if (file._noydb_keyring !== NOYDB_KEYRING_VERSION) return "format-superseded";
|
|
130
|
+
return "roster-tag-mismatch";
|
|
131
|
+
}
|
|
42
132
|
|
|
43
133
|
// src/kernel/validation.ts
|
|
44
134
|
var WeakSecretError = class extends NoydbError {
|
|
@@ -294,6 +384,18 @@ async function verifyKeyringCanary(wrappedCanary, kek) {
|
|
|
294
384
|
return false;
|
|
295
385
|
}
|
|
296
386
|
}
|
|
387
|
+
function rosterKeyOf(keyring) {
|
|
388
|
+
return keyring.deks.get(ROSTER_KEY_ID) ?? null;
|
|
389
|
+
}
|
|
390
|
+
function requireRosterKey(keyring, fn) {
|
|
391
|
+
const rosterKey = rosterKeyOf(keyring);
|
|
392
|
+
if (!rosterKey) {
|
|
393
|
+
throw new ValidationError(
|
|
394
|
+
`${fn}: caller keyring has no vault roster key \u2014 cannot stamp a roster tag. This typically means the keyring was opened via tier-3 PIN resume, session restore, or a wrap-DEKs tier-2 unlock. Re-authenticate at tier 1 (secret) before writing a keyring.`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
return rosterKey;
|
|
398
|
+
}
|
|
297
399
|
async function deriveKekForKeyring(file, secret, salt) {
|
|
298
400
|
if (file.echo !== void 0) {
|
|
299
401
|
if (typeof secret === "string") throw new EchoCeremonyRequiredError();
|
|
@@ -327,10 +429,12 @@ async function loadKeyring(store, vault, opts) {
|
|
|
327
429
|
throw new NoAccessError(`No keyring found for user "${userId}" in vault "${vault}"`);
|
|
328
430
|
}
|
|
329
431
|
const { file: keyringFile } = found;
|
|
330
|
-
assertKeyringNotExpired(keyringFile);
|
|
331
432
|
const salt = base64ToBuffer(keyringFile.salt);
|
|
332
433
|
const kek = await deriveKekForKeyring(keyringFile, secret, salt);
|
|
333
|
-
|
|
434
|
+
if (keyringFile.canary === void 0) {
|
|
435
|
+
throw new KeyringTamperedError({ userId, reason: "canary-missing" });
|
|
436
|
+
}
|
|
437
|
+
const canaryOk = await verifyKeyringCanary(keyringFile.canary, kek);
|
|
334
438
|
const deks = /* @__PURE__ */ new Map();
|
|
335
439
|
const failedCollections = [];
|
|
336
440
|
let firstUnwrapError = null;
|
|
@@ -343,11 +447,18 @@ async function loadKeyring(store, vault, opts) {
|
|
|
343
447
|
if (firstUnwrapError === null) firstUnwrapError = err;
|
|
344
448
|
}
|
|
345
449
|
}
|
|
346
|
-
|
|
450
|
+
const pendingDeks = /* @__PURE__ */ new Map();
|
|
451
|
+
for (const [collName, wrapped] of Object.entries(keyringFile.pending_deks ?? {})) {
|
|
452
|
+
try {
|
|
453
|
+
pendingDeks.set(collName, await unwrapKey(wrapped, kek));
|
|
454
|
+
} catch {
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (canaryOk) {
|
|
347
458
|
if (failedCollections.length > 0) {
|
|
348
459
|
throw new KeyringCorruptError({ failedCollections, intactCount: deks.size });
|
|
349
460
|
}
|
|
350
|
-
} else
|
|
461
|
+
} else {
|
|
351
462
|
if (deks.size > 0) {
|
|
352
463
|
throw new KeyringCorruptError({
|
|
353
464
|
failedCollections: [...failedCollections, "_canary"],
|
|
@@ -355,20 +466,16 @@ async function loadKeyring(store, vault, opts) {
|
|
|
355
466
|
});
|
|
356
467
|
}
|
|
357
468
|
throw firstUnwrapError instanceof Error ? firstUnwrapError : new InvalidKeyError();
|
|
358
|
-
} else {
|
|
359
|
-
if (failedCollections.length > 0) {
|
|
360
|
-
if (deks.size > 0) {
|
|
361
|
-
throw new KeyringCorruptError({ failedCollections, intactCount: deks.size });
|
|
362
|
-
}
|
|
363
|
-
throw firstUnwrapError instanceof Error ? firstUnwrapError : new InvalidKeyError();
|
|
364
|
-
}
|
|
365
469
|
}
|
|
470
|
+
await assertRosterAuthenticated(keyringFile, deks, userId);
|
|
471
|
+
assertKeyringNotExpired(keyringFile);
|
|
366
472
|
return {
|
|
367
473
|
userId: keyringFile.user_id,
|
|
368
474
|
displayName: keyringFile.display_name,
|
|
369
475
|
role: keyringFile.role,
|
|
370
476
|
permissions: keyringFile.permissions,
|
|
371
477
|
deks,
|
|
478
|
+
pendingDeks,
|
|
372
479
|
kek,
|
|
373
480
|
salt,
|
|
374
481
|
authenticators: keyringFile.authenticators ?? [],
|
|
@@ -404,18 +511,33 @@ async function createOwnerKeyring(store, vault, opts) {
|
|
|
404
511
|
const kek = typeof secret === "string" ? await deriveKey(secret, salt) : await deriveEchoKey(secret, salt);
|
|
405
512
|
const userEnvelopeDek = await generateDEK();
|
|
406
513
|
const wrappedUserEnvelopeDek = await wrapKey(userEnvelopeDek, kek);
|
|
514
|
+
const rosterKey = await generateDEK();
|
|
515
|
+
const wrappedRosterKey = await wrapKey(rosterKey, kek);
|
|
516
|
+
const blobAddressKey = await generateDEK();
|
|
517
|
+
const wrappedBlobAddressKey = await wrapKey(blobAddressKey, kek);
|
|
407
518
|
const canary = await mintKeyringCanary(kek);
|
|
408
|
-
const
|
|
409
|
-
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
519
|
+
const authority = {
|
|
410
520
|
user_id: userId,
|
|
411
521
|
display_name: userId,
|
|
412
522
|
role: "owner",
|
|
413
523
|
permissions: {},
|
|
414
|
-
|
|
524
|
+
granted_by: userId
|
|
525
|
+
};
|
|
526
|
+
const authorityWithDeks = {
|
|
527
|
+
...authority,
|
|
528
|
+
deks: {
|
|
529
|
+
[USER_ENVELOPE_COLLECTION]: wrappedUserEnvelopeDek,
|
|
530
|
+
[ROSTER_KEY_ID]: wrappedRosterKey,
|
|
531
|
+
[BLOB_ADDRESS_KEY_ID]: wrappedBlobAddressKey
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
const keyringFile = {
|
|
535
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
536
|
+
...authorityWithDeks,
|
|
415
537
|
salt: bufferToBase64(salt),
|
|
416
538
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
417
|
-
granted_by: userId,
|
|
418
539
|
canary,
|
|
540
|
+
roster_tag: await mintRosterTag(authorityWithDeks, rosterKey),
|
|
419
541
|
// The presence of this block is what makes the keyring an echo keyring —
|
|
420
542
|
// `deriveKekForKeyring` reads it to refuse a single-string unlock (AG-1).
|
|
421
543
|
...typeof secret !== "string" ? {
|
|
@@ -432,7 +554,10 @@ async function createOwnerKeyring(store, vault, opts) {
|
|
|
432
554
|
displayName: userId,
|
|
433
555
|
role: "owner",
|
|
434
556
|
permissions: {},
|
|
435
|
-
deks: /* @__PURE__ */ new Map([
|
|
557
|
+
deks: /* @__PURE__ */ new Map([
|
|
558
|
+
[USER_ENVELOPE_COLLECTION, userEnvelopeDek],
|
|
559
|
+
[ROSTER_KEY_ID, rosterKey]
|
|
560
|
+
]),
|
|
436
561
|
kek,
|
|
437
562
|
salt,
|
|
438
563
|
authenticators: []
|
|
@@ -444,6 +569,7 @@ async function grant(store, vault, callerKeyring, options) {
|
|
|
444
569
|
"grant: caller keyring has no KEK \u2014 tier-2 wrap-DEKs and tier-3 PIN-resume sessions cannot grant access to other users. Re-authenticate at tier 1 (secret) before granting."
|
|
445
570
|
);
|
|
446
571
|
}
|
|
572
|
+
const callerRosterKey = requireRosterKey(callerKeyring, "grant");
|
|
447
573
|
if (!canGrant(callerKeyring.role, options.role)) {
|
|
448
574
|
throw new PermissionDeniedError(
|
|
449
575
|
`Role "${callerKeyring.role}" cannot grant role "${options.role}"`
|
|
@@ -472,6 +598,8 @@ async function grant(store, vault, callerKeyring, options) {
|
|
|
472
598
|
mintedForGrant = true;
|
|
473
599
|
}
|
|
474
600
|
if (mintedForGrant) await persistKeyring(store, vault, callerKeyring);
|
|
601
|
+
const previousGrantFound = await readKeyringFile(store, vault, options.userId);
|
|
602
|
+
const previousDekNames = previousGrantFound ? new Set(Object.keys(previousGrantFound.file.deks)) : null;
|
|
475
603
|
const wrappedDeks = {};
|
|
476
604
|
for (const collName of Object.keys(permissions)) {
|
|
477
605
|
if (collName === PERMISSION_WILDCARD) continue;
|
|
@@ -499,21 +627,35 @@ async function grant(store, vault, callerKeyring, options) {
|
|
|
499
627
|
}
|
|
500
628
|
}
|
|
501
629
|
const canary = await mintKeyringCanary(newKek);
|
|
502
|
-
const
|
|
503
|
-
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
630
|
+
const authority = {
|
|
504
631
|
user_id: options.userId,
|
|
505
632
|
display_name: options.displayName,
|
|
506
633
|
role: options.role,
|
|
507
634
|
permissions,
|
|
508
|
-
deks: wrappedDeks,
|
|
509
|
-
salt: bufferToBase64(newSalt),
|
|
510
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
511
635
|
granted_by: callerKeyring.userId,
|
|
512
|
-
canary,
|
|
513
636
|
...options.exportCapability !== void 0 && { export_capability: options.exportCapability },
|
|
514
637
|
...options.importCapability !== void 0 && { import_capability: options.importCapability }
|
|
515
638
|
};
|
|
639
|
+
const authorityWithDeks = { ...authority, deks: wrappedDeks };
|
|
640
|
+
const keyringFile = {
|
|
641
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
642
|
+
...authorityWithDeks,
|
|
643
|
+
salt: bufferToBase64(newSalt),
|
|
644
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
645
|
+
canary,
|
|
646
|
+
// The grantee's own copy of the roster key rode in via the `_`-prefix
|
|
647
|
+
// propagation loop above, so they can verify this tag on first unlock.
|
|
648
|
+
roster_tag: await mintRosterTag(authorityWithDeks, callerRosterKey)
|
|
649
|
+
};
|
|
516
650
|
await writeKeyringFile(store, vault, options.userId, keyringFile);
|
|
651
|
+
if (previousDekNames !== null && options.userId !== callerKeyring.userId) {
|
|
652
|
+
const dropped = [...previousDekNames].filter(
|
|
653
|
+
(name) => !name.startsWith("_") && !(name in wrappedDeks)
|
|
654
|
+
);
|
|
655
|
+
if (dropped.length > 0) {
|
|
656
|
+
await rotateKeys(store, vault, callerKeyring, { collections: dropped });
|
|
657
|
+
}
|
|
658
|
+
}
|
|
517
659
|
const userEnvelopeDek = callerKeyring.deks.get(USER_ENVELOPE_COLLECTION);
|
|
518
660
|
if (userEnvelopeDek) {
|
|
519
661
|
const initialPayload = options.initialProfile ?? {};
|
|
@@ -526,13 +668,14 @@ async function grant(store, vault, callerKeyring, options) {
|
|
|
526
668
|
);
|
|
527
669
|
}
|
|
528
670
|
}
|
|
529
|
-
async function findAdminDescendants(store, vault, rootUserId) {
|
|
671
|
+
async function findAdminDescendants(store, vault, rootUserId, rosterKey) {
|
|
530
672
|
const allUserIds = await store.list(vault, "_keyring");
|
|
531
673
|
const childrenByParent = /* @__PURE__ */ new Map();
|
|
532
674
|
for (const userId of allUserIds) {
|
|
533
675
|
const found = await readKeyringFile(store, vault, userId);
|
|
534
676
|
if (!found) continue;
|
|
535
677
|
const kf = found.file;
|
|
678
|
+
await assertRosterTagValid(kf, rosterKey, userId);
|
|
536
679
|
if (kf.role !== "admin") continue;
|
|
537
680
|
if (kf.user_id === rootUserId) continue;
|
|
538
681
|
const list = childrenByParent.get(kf.granted_by) ?? [];
|
|
@@ -556,9 +699,16 @@ async function findAdminDescendants(store, vault, rootUserId) {
|
|
|
556
699
|
async function revoke(store, vault, callerKeyring, options) {
|
|
557
700
|
const targetFound = await readKeyringFile(store, vault, options.userId);
|
|
558
701
|
if (!targetFound) {
|
|
702
|
+
const pending = [...callerKeyring.pendingDeks?.keys() ?? []];
|
|
703
|
+
if (pending.length > 0) {
|
|
704
|
+
await rotateKeys(store, vault, callerKeyring, { collections: pending });
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
559
707
|
throw new NoAccessError(`User "${options.userId}" has no keyring in vault "${vault}"`);
|
|
560
708
|
}
|
|
561
709
|
const targetKeyring = targetFound.file;
|
|
710
|
+
const callerRosterKey = requireRosterKey(callerKeyring, "revoke");
|
|
711
|
+
await assertRosterTagValid(targetKeyring, callerRosterKey, options.userId);
|
|
562
712
|
if (!canRevoke(callerKeyring.role, targetKeyring.role)) {
|
|
563
713
|
throw new PermissionDeniedError(
|
|
564
714
|
`Role "${callerKeyring.role}" cannot revoke role "${targetKeyring.role}"`
|
|
@@ -568,7 +718,7 @@ async function revoke(store, vault, callerKeyring, options) {
|
|
|
568
718
|
const usersToRevoke = [options.userId];
|
|
569
719
|
const affectedCollections = new Set(Object.keys(targetKeyring.deks));
|
|
570
720
|
if (targetKeyring.role === "admin") {
|
|
571
|
-
const descendants = await findAdminDescendants(store, vault, options.userId);
|
|
721
|
+
const descendants = await findAdminDescendants(store, vault, options.userId, callerRosterKey);
|
|
572
722
|
if (descendants.length > 0) {
|
|
573
723
|
if (cascadeMode === "warn") {
|
|
574
724
|
console.warn(
|
|
@@ -578,6 +728,7 @@ async function revoke(store, vault, callerKeyring, options) {
|
|
|
578
728
|
for (const userId of descendants) {
|
|
579
729
|
const descFound = await readKeyringFile(store, vault, userId);
|
|
580
730
|
if (!descFound) continue;
|
|
731
|
+
await assertRosterTagValid(descFound.file, callerRosterKey, userId);
|
|
581
732
|
usersToRevoke.push(userId);
|
|
582
733
|
for (const c of Object.keys(descFound.file.deks)) affectedCollections.add(c);
|
|
583
734
|
}
|
|
@@ -589,10 +740,105 @@ async function revoke(store, vault, callerKeyring, options) {
|
|
|
589
740
|
await deleteUserEnvelope(store, vault, userId);
|
|
590
741
|
await deleteUserVisibility(store, vault, userId);
|
|
591
742
|
}
|
|
592
|
-
|
|
593
|
-
|
|
743
|
+
affectedCollections.delete(ROSTER_KEY_ID);
|
|
744
|
+
affectedCollections.delete(BLOB_ADDRESS_KEY_ID);
|
|
745
|
+
if (affectedCollections.size > 0) {
|
|
746
|
+
const { unverified } = await rotateKeys(store, vault, callerKeyring, {
|
|
747
|
+
collections: [...affectedCollections]
|
|
748
|
+
});
|
|
749
|
+
if (unverified.length > 0) {
|
|
750
|
+
console.warn(
|
|
751
|
+
`[noy-db] revoke(${options.userId}): ${unverified.length} member(s) were SKIPPED by the rotation because their keyring failed roster authentication: ${unverified.map((u) => `${u.userId} (${u.reason})`).join(", ")}. The revocation itself completed. Those members cannot open the vault and did not receive the rotated keys; their keyring files need repair or removal (noy-db#1114).`
|
|
752
|
+
);
|
|
753
|
+
}
|
|
594
754
|
}
|
|
595
755
|
}
|
|
756
|
+
async function readKeyringForAudit(store, vault, userId) {
|
|
757
|
+
const envelope = await store.get(vault, "_keyring", userId);
|
|
758
|
+
if (!envelope) return void 0;
|
|
759
|
+
try {
|
|
760
|
+
return { file: parseKeyringEnvelope(envelope) };
|
|
761
|
+
} catch {
|
|
762
|
+
return { unparseable: true };
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
async function verifyRoster(store, vault, callerKeyring) {
|
|
766
|
+
const rosterKey = requireRosterKey(callerKeyring, "verifyRoster");
|
|
767
|
+
const unverified = [];
|
|
768
|
+
let checked = 0;
|
|
769
|
+
for (const userId of await store.list(vault, "_keyring")) {
|
|
770
|
+
const found = await readKeyringForAudit(store, vault, userId);
|
|
771
|
+
if (!found) continue;
|
|
772
|
+
checked += 1;
|
|
773
|
+
if ("unparseable" in found) {
|
|
774
|
+
unverified.push({ userId, reason: "unparseable" });
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
await assertRosterTagValid(found.file, rosterKey, userId);
|
|
779
|
+
} catch (err) {
|
|
780
|
+
if (!(err instanceof KeyringTamperedError)) throw err;
|
|
781
|
+
unverified.push({ userId, reason: err.details.reason });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return { checked, unverified };
|
|
785
|
+
}
|
|
786
|
+
async function quarantineKeyring(store, vault, callerKeyring, userId) {
|
|
787
|
+
if (callerKeyring.role !== "owner") {
|
|
788
|
+
throw new PermissionDeniedError(
|
|
789
|
+
`Role "${callerKeyring.role}" cannot quarantine a keyring \u2014 quarantine is owner-only, because it removes a file whose own authority claims cannot be trusted.`
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
if (userId === callerKeyring.userId) {
|
|
793
|
+
throw new ValidationError(
|
|
794
|
+
"quarantineKeyring: refusing to quarantine the calling keyring. Removing your own keyring and rotating behind it would leave the vault with no reachable owner."
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
const rosterKey = requireRosterKey(callerKeyring, "quarantineKeyring");
|
|
798
|
+
const found = await readKeyringForAudit(store, vault, userId);
|
|
799
|
+
if (!found) {
|
|
800
|
+
const pending = [...callerKeyring.pendingDeks?.keys() ?? []];
|
|
801
|
+
if (pending.length > 0) {
|
|
802
|
+
const resumed = await rotateKeys(store, vault, callerKeyring, { collections: pending });
|
|
803
|
+
return {
|
|
804
|
+
userId,
|
|
805
|
+
reason: "roster-tag-mismatch",
|
|
806
|
+
rotated: pending,
|
|
807
|
+
needsRegrant: resumed.needsRegrant,
|
|
808
|
+
alsoUnverified: resumed.unverified
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
throw new NoAccessError(`No keyring found for user "${userId}" in vault "${vault}"`);
|
|
812
|
+
}
|
|
813
|
+
let reason = null;
|
|
814
|
+
if ("unparseable" in found) {
|
|
815
|
+
reason = "unparseable";
|
|
816
|
+
} else {
|
|
817
|
+
try {
|
|
818
|
+
await assertRosterTagValid(found.file, rosterKey, userId);
|
|
819
|
+
} catch (err) {
|
|
820
|
+
if (!(err instanceof KeyringTamperedError)) throw err;
|
|
821
|
+
reason = err.details.reason;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
if (reason === null) {
|
|
825
|
+
throw new ValidationError(
|
|
826
|
+
`quarantineKeyring: the keyring for "${userId}" authenticates correctly, so it is not a quarantine case \u2014 use revoke() instead. Quarantine bypasses the role checks revoke performs, so it is restricted to files that genuinely fail authentication.`
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
await store.delete(vault, "_keyring", userId);
|
|
830
|
+
await deleteUserEnvelope(store, vault, userId);
|
|
831
|
+
await deleteUserVisibility(store, vault, userId);
|
|
832
|
+
const rotated = [...callerKeyring.deks.keys()].filter((c) => c !== ROSTER_KEY_ID);
|
|
833
|
+
const result = rotated.length > 0 ? await rotateKeys(store, vault, callerKeyring, { collections: rotated }) : { needsRegrant: [], unverified: [] };
|
|
834
|
+
return {
|
|
835
|
+
userId,
|
|
836
|
+
reason,
|
|
837
|
+
rotated,
|
|
838
|
+
needsRegrant: result.needsRegrant,
|
|
839
|
+
alsoUnverified: result.unverified
|
|
840
|
+
};
|
|
841
|
+
}
|
|
596
842
|
async function updateKeyringIdentity(store, vault, callerKeyring, options) {
|
|
597
843
|
if (options.role === void 0 && options.displayName === void 0 && options.permissions === void 0) {
|
|
598
844
|
throw new ValidationError(
|
|
@@ -606,6 +852,8 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
|
|
|
606
852
|
);
|
|
607
853
|
}
|
|
608
854
|
const target = found.file;
|
|
855
|
+
const rosterKey = requireRosterKey(callerKeyring, "updateKeyringIdentity");
|
|
856
|
+
await assertRosterTagValid(target, rosterKey, options.userId);
|
|
609
857
|
if (!canUpdateRole(callerKeyring.role, target.role)) {
|
|
610
858
|
throw new PermissionDeniedError(
|
|
611
859
|
`Role "${callerKeyring.role}" cannot update a keyring with role "${target.role}"`
|
|
@@ -616,7 +864,7 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
|
|
|
616
864
|
`Role "${callerKeyring.role}" cannot promote target to role "${options.role}"`
|
|
617
865
|
);
|
|
618
866
|
}
|
|
619
|
-
const
|
|
867
|
+
const edited = {
|
|
620
868
|
...target,
|
|
621
869
|
...options.role !== void 0 && { role: options.role },
|
|
622
870
|
...options.displayName !== void 0 && {
|
|
@@ -625,50 +873,130 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
|
|
|
625
873
|
},
|
|
626
874
|
...options.permissions !== void 0 && { permissions: options.permissions }
|
|
627
875
|
};
|
|
876
|
+
const next = { ...edited, roster_tag: await mintRosterTag(edited, rosterKey) };
|
|
628
877
|
await writeKeyringFile(store, vault, options.userId, next);
|
|
629
878
|
}
|
|
879
|
+
var HISTORY_COLLECTION = "_history";
|
|
880
|
+
var LEDGER_COLLECTION = "_ledger";
|
|
881
|
+
var LEDGER_DELTAS_COLLECTION = "_ledger_deltas";
|
|
882
|
+
var BLOB_COLLECTION = "_blob";
|
|
883
|
+
var BLOB_SLOTS_PREFIX = "_blob_slots_";
|
|
884
|
+
var BLOB_VERSIONS_PREFIX = "_blob_versions_";
|
|
885
|
+
var BLOB_INTENT_COLLECTION = "_blob_intent";
|
|
886
|
+
async function listOrEmpty(store, vault, collection) {
|
|
887
|
+
try {
|
|
888
|
+
return await store.list(vault, collection);
|
|
889
|
+
} catch {
|
|
890
|
+
return [];
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
async function derivedRefsFor(store, vault, rotated) {
|
|
894
|
+
const out = [];
|
|
895
|
+
if (!rotated.startsWith("_")) {
|
|
896
|
+
for (const id of await listOrEmpty(store, vault, HISTORY_COLLECTION)) {
|
|
897
|
+
if (id.startsWith(`${rotated}:`)) out.push({ collection: HISTORY_COLLECTION, id });
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
if (rotated === LEDGER_COLLECTION) {
|
|
901
|
+
for (const id of await listOrEmpty(store, vault, LEDGER_DELTAS_COLLECTION)) {
|
|
902
|
+
out.push({ collection: LEDGER_DELTAS_COLLECTION, id });
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
if (!rotated.startsWith("_")) {
|
|
906
|
+
for (const id of await listOrEmpty(store, vault, `${BLOB_SLOTS_PREFIX}${rotated}`)) {
|
|
907
|
+
out.push({ collection: `${BLOB_SLOTS_PREFIX}${rotated}`, id });
|
|
908
|
+
}
|
|
909
|
+
for (const id of await listOrEmpty(store, vault, `${BLOB_VERSIONS_PREFIX}${rotated}`)) {
|
|
910
|
+
out.push({ collection: `${BLOB_VERSIONS_PREFIX}${rotated}`, id });
|
|
911
|
+
}
|
|
912
|
+
for (const id of await listOrEmpty(store, vault, BLOB_INTENT_COLLECTION)) {
|
|
913
|
+
if (id.startsWith(`${rotated}::`)) out.push({ collection: BLOB_INTENT_COLLECTION, id });
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
return out;
|
|
917
|
+
}
|
|
630
918
|
async function rotateKeys(store, vault, callerKeyring, opts) {
|
|
631
919
|
const { collections } = opts;
|
|
920
|
+
if (collections.includes(BLOB_ADDRESS_KEY_ID)) {
|
|
921
|
+
throw new ValidationError(
|
|
922
|
+
`rotateKeys: "${BLOB_ADDRESS_KEY_ID}" is the vault's blob content-addressing root, not a collection, and cannot be rotated \u2014 every blob eTag is derived from it, so rotating it would invalidate every stored address at once. Remove it from \`collections\`.`
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
if (collections.includes(ROSTER_KEY_ID)) {
|
|
926
|
+
throw new ValidationError(
|
|
927
|
+
`rotateKeys: "${ROSTER_KEY_ID}" is the vault roster key, not a collection, and cannot be rotated \u2014 doing so would strip it from every other member and leave the vault unopenable. Remove it from \`collections\`.`
|
|
928
|
+
);
|
|
929
|
+
}
|
|
632
930
|
if (callerKeyring.role === "custodian") {
|
|
633
931
|
throw new PermissionDeniedError(
|
|
634
932
|
"custodian cannot rotate keys (FR-6: re-key is an owner-only meta-capability; use the Deed owner)"
|
|
635
933
|
);
|
|
636
934
|
}
|
|
935
|
+
const callerRosterKey = requireRosterKey(callerKeyring, "rotateKeys");
|
|
637
936
|
const newDeks = /* @__PURE__ */ new Map();
|
|
638
937
|
for (const collName of collections) {
|
|
639
|
-
newDeks.set(collName, await generateDEK());
|
|
938
|
+
newDeks.set(collName, callerKeyring.pendingDeks?.get(collName) ?? await generateDEK());
|
|
640
939
|
}
|
|
940
|
+
const pending = callerKeyring.pendingDeks ?? /* @__PURE__ */ new Map();
|
|
941
|
+
callerKeyring.pendingDeks = pending;
|
|
942
|
+
for (const [collName, newDek] of newDeks) {
|
|
943
|
+
pending.set(collName, newDek);
|
|
944
|
+
}
|
|
945
|
+
await persistKeyring(store, vault, callerKeyring);
|
|
641
946
|
for (const collName of collections) {
|
|
642
947
|
const oldDek = callerKeyring.deks.get(collName);
|
|
643
948
|
const newDek = newDeks.get(collName);
|
|
644
949
|
if (!oldDek) continue;
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
950
|
+
if (collName === BLOB_COLLECTION || collName.startsWith(`${BLOB_COLLECTION}#`)) {
|
|
951
|
+
const others = [];
|
|
952
|
+
for (const [name, dek] of callerKeyring.deks) if (name !== collName) others.push(dek);
|
|
953
|
+
for (const [name, dek] of newDeks) if (name !== collName) others.push(dek);
|
|
954
|
+
await rekeyBlobSet(store, vault, oldDek, newDek, others);
|
|
955
|
+
}
|
|
956
|
+
const { collection: baseColl } = parseDekKey(collName);
|
|
957
|
+
const siblingSlotDeks = [];
|
|
958
|
+
for (const [name, dek] of callerKeyring.deks) if (name !== collName) siblingSlotDeks.push(dek);
|
|
959
|
+
for (const [name, dek] of newDeks) if (name !== collName) siblingSlotDeks.push(dek);
|
|
960
|
+
const refs = [
|
|
961
|
+
...(await store.list(vault, baseColl)).map((id) => ({ collection: baseColl, id })),
|
|
962
|
+
...await derivedRefsFor(store, vault, baseColl)
|
|
963
|
+
];
|
|
964
|
+
for (const ref of refs) {
|
|
965
|
+
const { collection: refColl, id } = ref;
|
|
966
|
+
const envelope = await store.get(vault, refColl, id);
|
|
648
967
|
if (!envelope || !envelope._iv) continue;
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
}
|
|
658
|
-
await store.put(vault,
|
|
968
|
+
let newEnvelope;
|
|
969
|
+
try {
|
|
970
|
+
newEnvelope = await rekeyEnvelopeIfNeeded({ collection: refColl, id }, envelope, oldDek, newDek);
|
|
971
|
+
} catch (err) {
|
|
972
|
+
if (await envelopeOpensUnderAny({ collection: refColl, id }, envelope, siblingSlotDeks)) {
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
throw err;
|
|
976
|
+
}
|
|
977
|
+
if (newEnvelope !== null) await store.put(vault, refColl, id, newEnvelope);
|
|
659
978
|
}
|
|
660
979
|
}
|
|
661
980
|
for (const [collName, newDek] of newDeks) {
|
|
662
981
|
callerKeyring.deks.set(collName, newDek);
|
|
982
|
+
callerKeyring.pendingDeks?.delete(collName);
|
|
663
983
|
}
|
|
664
984
|
await persistKeyring(store, vault, callerKeyring);
|
|
665
985
|
const needsRegrant = [];
|
|
986
|
+
const unverified = [];
|
|
666
987
|
const userIds = await store.list(vault, "_keyring");
|
|
667
988
|
for (const userId of userIds) {
|
|
668
989
|
if (userId === callerKeyring.userId) continue;
|
|
669
990
|
const userFound = await readKeyringFile(store, vault, userId);
|
|
670
991
|
if (!userFound) continue;
|
|
671
992
|
const userKeyringFile = userFound.file;
|
|
993
|
+
try {
|
|
994
|
+
await assertRosterTagValid(userKeyringFile, callerRosterKey, userId);
|
|
995
|
+
} catch (err) {
|
|
996
|
+
if (!(err instanceof KeyringTamperedError)) throw err;
|
|
997
|
+
unverified.push({ userId, reason: err.details.reason });
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
672
1000
|
const updatedDeks = { ...userKeyringFile.deks };
|
|
673
1001
|
const updatedPermissions = { ...userKeyringFile.permissions };
|
|
674
1002
|
for (const collName of collections) {
|
|
@@ -678,17 +1006,24 @@ async function rotateKeys(store, vault, callerKeyring, opts) {
|
|
|
678
1006
|
delete updatedDeks[collName];
|
|
679
1007
|
delete updatedPermissions[collName];
|
|
680
1008
|
}
|
|
681
|
-
const
|
|
1009
|
+
const edited = {
|
|
682
1010
|
...userKeyringFile,
|
|
683
1011
|
deks: updatedDeks,
|
|
684
1012
|
permissions: updatedPermissions
|
|
685
1013
|
};
|
|
1014
|
+
const updatedKeyring = {
|
|
1015
|
+
...edited,
|
|
1016
|
+
roster_tag: await mintRosterTag(edited, callerRosterKey)
|
|
1017
|
+
};
|
|
686
1018
|
await writeKeyringFile(store, vault, userId, updatedKeyring);
|
|
687
1019
|
}
|
|
688
|
-
return { needsRegrant };
|
|
1020
|
+
return { needsRegrant, unverified };
|
|
689
1021
|
}
|
|
690
1022
|
async function changeSecret(store, vault, keyring, opts) {
|
|
691
1023
|
const existingFound = await readKeyringFile(store, vault, keyring.userId);
|
|
1024
|
+
if (existingFound) {
|
|
1025
|
+
await assertRosterTagValid(existingFound.file, requireRosterKey(keyring, "changeSecret"), keyring.userId);
|
|
1026
|
+
}
|
|
692
1027
|
if (existingFound && existingFound.file.echo !== void 0) throw new EchoCeremonyRequiredError();
|
|
693
1028
|
const { newSecret } = opts;
|
|
694
1029
|
if (!opts.allowWeakSecret) {
|
|
@@ -700,18 +1035,28 @@ async function changeSecret(store, vault, keyring, opts) {
|
|
|
700
1035
|
for (const [collName, dek] of keyring.deks) {
|
|
701
1036
|
wrappedDeks[collName] = await wrapKey(dek, newKek);
|
|
702
1037
|
}
|
|
1038
|
+
const rosterKey = requireRosterKey(keyring, "changeSecret");
|
|
703
1039
|
const canary = await mintKeyringCanary(newKek);
|
|
704
|
-
const
|
|
705
|
-
|
|
1040
|
+
const existingAuthority = existingFound?.file;
|
|
1041
|
+
const keptGrantedBy = existingAuthority?.granted_by ?? keyring.userId;
|
|
1042
|
+
const authority = {
|
|
706
1043
|
user_id: keyring.userId,
|
|
707
1044
|
display_name: keyring.displayName,
|
|
708
1045
|
role: keyring.role,
|
|
709
1046
|
permissions: keyring.permissions,
|
|
710
|
-
|
|
1047
|
+
granted_by: keptGrantedBy,
|
|
1048
|
+
...existingAuthority?.expires_at !== void 0 && { expires_at: existingAuthority.expires_at },
|
|
1049
|
+
...existingAuthority?.export_capability !== void 0 && { export_capability: existingAuthority.export_capability },
|
|
1050
|
+
...existingAuthority?.import_capability !== void 0 && { import_capability: existingAuthority.import_capability }
|
|
1051
|
+
};
|
|
1052
|
+
const authorityWithDeks = { ...authority, deks: wrappedDeks };
|
|
1053
|
+
const keyringFile = {
|
|
1054
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1055
|
+
...authorityWithDeks,
|
|
711
1056
|
salt: bufferToBase64(newSalt),
|
|
712
1057
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
713
|
-
|
|
714
|
-
|
|
1058
|
+
canary,
|
|
1059
|
+
roster_tag: await mintRosterTag(authorityWithDeks, rosterKey)
|
|
715
1060
|
};
|
|
716
1061
|
await writeKeyringFile(store, vault, keyring.userId, keyringFile);
|
|
717
1062
|
return {
|
|
@@ -738,6 +1083,7 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
|
|
|
738
1083
|
"buildRecipientKeyringFile: caller keyring has no KEK \u2014 tier-2 wrap-DEKs and tier-3 PIN-resume sessions cannot create bundle recipients. Re-authenticate at tier 1 (secret) before building a bundle."
|
|
739
1084
|
);
|
|
740
1085
|
}
|
|
1086
|
+
const rosterKey = requireRosterKey(callerKeyring, "buildRecipientKeyringFile");
|
|
741
1087
|
const role = recipient.role ?? "viewer";
|
|
742
1088
|
const permissions = resolvePermissions(role, recipient.permissions);
|
|
743
1089
|
const newSalt = generateSalt();
|
|
@@ -767,20 +1113,26 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
|
|
|
767
1113
|
}
|
|
768
1114
|
}
|
|
769
1115
|
const canary = await mintKeyringCanary(newKek);
|
|
770
|
-
|
|
771
|
-
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1116
|
+
const authority = {
|
|
772
1117
|
user_id: recipient.id,
|
|
773
1118
|
display_name: recipient.displayName ?? recipient.id,
|
|
774
1119
|
role,
|
|
775
1120
|
permissions,
|
|
776
|
-
deks: wrappedDeks,
|
|
777
|
-
salt: bufferToBase64(newSalt),
|
|
778
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
779
1121
|
granted_by: callerKeyring.userId,
|
|
780
|
-
canary,
|
|
781
1122
|
...recipient.exportCapability !== void 0 ? { export_capability: recipient.exportCapability } : {},
|
|
782
1123
|
...recipient.importCapability !== void 0 ? { import_capability: recipient.importCapability } : {},
|
|
783
|
-
...recipient.expiresAt !== void 0 ? { expires_at: recipient.expiresAt } : {}
|
|
1124
|
+
...recipient.expiresAt !== void 0 ? { expires_at: recipient.expiresAt } : {}
|
|
1125
|
+
};
|
|
1126
|
+
const authorityWithDeks = { ...authority, deks: wrappedDeks };
|
|
1127
|
+
return {
|
|
1128
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1129
|
+
...authorityWithDeks,
|
|
1130
|
+
salt: bufferToBase64(newSalt),
|
|
1131
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1132
|
+
canary,
|
|
1133
|
+
// The recipient's copy of the roster key rode in via the `_`-prefix
|
|
1134
|
+
// propagation loop above, so the slot verifies on first unlock.
|
|
1135
|
+
roster_tag: await mintRosterTag(authorityWithDeks, rosterKey),
|
|
784
1136
|
// The presence of this block is what makes the recipient's slot an
|
|
785
1137
|
// echo keyring — mirrors `createOwnerKeyring`'s same dance so the
|
|
786
1138
|
// ceremony (prompt → echo → key) travels with the pod, not just a
|
|
@@ -908,28 +1260,44 @@ async function persistKeyring(store, vault, keyring) {
|
|
|
908
1260
|
"persistKeyring: keyring.kek is null \u2014 cannot wrap DEKs without the KEK. This typically means the keyring was opened via tier-3 PIN resume, session restore, or a wrap-DEKs tier-2 unlock. Re-authenticate at tier 1 (secret) before persisting."
|
|
909
1261
|
);
|
|
910
1262
|
}
|
|
1263
|
+
const rosterKey = requireRosterKey(keyring, "persistKeyring");
|
|
911
1264
|
const existingFound = await readKeyringFile(store, vault, keyring.userId);
|
|
1265
|
+
if (existingFound) await assertRosterTagValid(existingFound.file, rosterKey, keyring.userId);
|
|
912
1266
|
const existingEcho = existingFound?.file.echo;
|
|
913
1267
|
const existingGrantedBy = existingFound?.file.granted_by;
|
|
914
1268
|
const existingCreatedAt = existingFound?.file.created_at;
|
|
1269
|
+
const existingExpiresAt = existingFound?.file.expires_at;
|
|
915
1270
|
const wrappedDeks = {};
|
|
916
1271
|
for (const [collName, dek] of keyring.deks) {
|
|
917
1272
|
wrappedDeks[collName] = await wrapKey(dek, keyring.kek);
|
|
918
1273
|
}
|
|
1274
|
+
const wrappedPending = {};
|
|
1275
|
+
for (const [collName, dek] of keyring.pendingDeks ?? []) {
|
|
1276
|
+
wrappedPending[collName] = await wrapKey(dek, keyring.kek);
|
|
1277
|
+
}
|
|
919
1278
|
const canary = await mintKeyringCanary(keyring.kek);
|
|
920
|
-
const
|
|
921
|
-
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1279
|
+
const authority = {
|
|
922
1280
|
user_id: keyring.userId,
|
|
923
1281
|
display_name: keyring.displayName,
|
|
924
1282
|
role: keyring.role,
|
|
925
1283
|
permissions: keyring.permissions,
|
|
1284
|
+
granted_by: existingGrantedBy ?? keyring.userId,
|
|
1285
|
+
...existingExpiresAt !== void 0 && { expires_at: existingExpiresAt },
|
|
1286
|
+
...keyring.exportCapability !== void 0 && { export_capability: keyring.exportCapability },
|
|
1287
|
+
...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability }
|
|
1288
|
+
};
|
|
1289
|
+
const authorityWithDeks = {
|
|
1290
|
+
...authority,
|
|
926
1291
|
deks: wrappedDeks,
|
|
1292
|
+
...Object.keys(wrappedPending).length > 0 ? { pending_deks: wrappedPending } : {}
|
|
1293
|
+
};
|
|
1294
|
+
const keyringFile = {
|
|
1295
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1296
|
+
...authorityWithDeks,
|
|
927
1297
|
salt: bufferToBase64(keyring.salt),
|
|
928
1298
|
created_at: existingCreatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
929
|
-
granted_by: existingGrantedBy ?? keyring.userId,
|
|
930
1299
|
canary,
|
|
931
|
-
|
|
932
|
-
...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability },
|
|
1300
|
+
roster_tag: await mintRosterTag(authorityWithDeks, rosterKey),
|
|
933
1301
|
...keyring.authenticators.length > 0 && { authenticators: keyring.authenticators },
|
|
934
1302
|
...keyring.policy !== void 0 && { policy: keyring.policy },
|
|
935
1303
|
...existingEcho !== void 0 && { echo: existingEcho }
|
|
@@ -974,17 +1342,17 @@ function resolvePermissions(role, explicit) {
|
|
|
974
1342
|
return explicit ?? {};
|
|
975
1343
|
}
|
|
976
1344
|
async function writeKeyringFile(store, vault, userId, keyringFile) {
|
|
977
|
-
const envelope =
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
_iv: "",
|
|
982
|
-
_data: JSON.stringify(keyringFile)
|
|
983
|
-
};
|
|
1345
|
+
const envelope = buildRecordEnvelope(
|
|
1346
|
+
{ collection: "_keyring", id: userId, version: 1 },
|
|
1347
|
+
{ iv: "", data: JSON.stringify(keyringFile) }
|
|
1348
|
+
);
|
|
984
1349
|
await store.put(vault, "_keyring", userId, envelope);
|
|
985
1350
|
}
|
|
986
1351
|
|
|
987
1352
|
export {
|
|
1353
|
+
mintRosterTag,
|
|
1354
|
+
assertRosterAuthenticated,
|
|
1355
|
+
assertRosterTagValid,
|
|
988
1356
|
WeakSecretError,
|
|
989
1357
|
validateSecret,
|
|
990
1358
|
assertStrongSecret,
|
|
@@ -1001,6 +1369,7 @@ export {
|
|
|
1001
1369
|
BROKER_COLLECTION,
|
|
1002
1370
|
isSecretBearingReservedCollection,
|
|
1003
1371
|
mintKeyringCanary,
|
|
1372
|
+
requireRosterKey,
|
|
1004
1373
|
deriveKekForKeyring,
|
|
1005
1374
|
readKeyringFile,
|
|
1006
1375
|
assertKeyringNotExpired,
|
|
@@ -1009,6 +1378,8 @@ export {
|
|
|
1009
1378
|
createOwnerKeyring,
|
|
1010
1379
|
grant,
|
|
1011
1380
|
revoke,
|
|
1381
|
+
verifyRoster,
|
|
1382
|
+
quarantineKeyring,
|
|
1012
1383
|
updateKeyringIdentity,
|
|
1013
1384
|
rotateKeys,
|
|
1014
1385
|
changeSecret,
|
|
@@ -1024,4 +1395,4 @@ export {
|
|
|
1024
1395
|
hasImportCapability,
|
|
1025
1396
|
evaluateImportCapability
|
|
1026
1397
|
};
|
|
1027
|
-
//# sourceMappingURL=chunk-
|
|
1398
|
+
//# sourceMappingURL=chunk-J6I5VR6P.js.map
|