@noy-db/hub 0.6.0-pre.2 → 0.6.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/CHANGELOG.md +5234 -0
- package/codemods/0.4.0-pre.json +699 -0
- package/codemods/0.6.0-pre.json +211 -0
- package/dist/api-EYNT6KGS.js +20 -0
- package/dist/attestation/index.js +19 -19
- package/dist/{backup-YYBE5RPC.js → backup-FSC36VWC.js} +36 -18
- package/dist/backup-FSC36VWC.js.map +1 -0
- package/dist/blobs/index.js +13 -13
- package/dist/broker/index.js +12 -12
- package/dist/cargo/index.js +33 -33
- package/dist/{chunk-35EBKWQU.js → chunk-2LFOMUEA.js} +4 -4
- package/dist/{chunk-OF3L2SD6.js → chunk-32B7VCKU.js} +2 -2
- package/dist/{chunk-MSWYQLMQ.js → chunk-3B6S6RSI.js} +2 -2
- package/dist/{chunk-PJNTR2NV.js → chunk-3ENNPCIW.js} +7 -10
- package/dist/chunk-3ENNPCIW.js.map +1 -0
- package/dist/{chunk-7F4DBNSP.js → chunk-3O72ZD2E.js} +3 -3
- package/dist/{chunk-FNSMZQS4.js → chunk-3PMFMUFX.js} +2 -2
- package/dist/{chunk-WQDTCJ6W.js → chunk-45YHJ3GV.js} +11 -3
- package/dist/{chunk-WQDTCJ6W.js.map → chunk-45YHJ3GV.js.map} +1 -1
- package/dist/{chunk-NIVEDYJZ.js → chunk-4A34KWOU.js} +31 -49
- package/dist/chunk-4A34KWOU.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-EDXAQ6JI.js → chunk-4N5Q23KV.js} +87 -34
- package/dist/chunk-4N5Q23KV.js.map +1 -0
- package/dist/{chunk-VV4NSB7P.js → chunk-4OQZNXQG.js} +15 -24
- package/dist/chunk-4OQZNXQG.js.map +1 -0
- package/dist/{chunk-FAXZQ5UF.js → chunk-4RAS6HN6.js} +2 -2
- package/dist/{chunk-C2OOEM57.js → chunk-4YL2LMXK.js} +92 -35
- package/dist/chunk-4YL2LMXK.js.map +1 -0
- package/dist/{chunk-EP7VPR7Y.js → chunk-5Q7AJ7DN.js} +11 -8
- package/dist/chunk-5Q7AJ7DN.js.map +1 -0
- package/dist/{chunk-VMCCGUQB.js → chunk-6JPGVL7X.js} +2 -2
- package/dist/{chunk-PRDRGL73.js → chunk-6KTVJ5ZZ.js} +17 -14
- package/dist/chunk-6KTVJ5ZZ.js.map +1 -0
- package/dist/{chunk-4MC5JRRX.js → chunk-6WO62AJW.js} +14 -15
- package/dist/chunk-6WO62AJW.js.map +1 -0
- package/dist/{chunk-WLYIROCJ.js → chunk-6YD5SYQS.js} +2 -2
- package/dist/{chunk-YAWK6AWF.js → chunk-7KAE7CQ2.js} +2 -2
- package/dist/{chunk-MWKDOHEF.js → chunk-7LNE2N2V.js} +17 -10
- package/dist/chunk-7LNE2N2V.js.map +1 -0
- package/dist/{chunk-5PR33AC4.js → chunk-7PJ3BKHS.js} +20 -17
- package/dist/chunk-7PJ3BKHS.js.map +1 -0
- package/dist/{chunk-JXIDGFKS.js → chunk-A4TOGAUW.js} +4 -2
- package/dist/chunk-A4TOGAUW.js.map +1 -0
- package/dist/{chunk-VCETL77O.js → chunk-AU3AUJIZ.js} +27 -7
- package/dist/chunk-AU3AUJIZ.js.map +1 -0
- package/dist/{chunk-NKH7GSIH.js → chunk-AUPSEJ7O.js} +4 -4
- package/dist/{chunk-CCZJJ4I2.js → chunk-B6IYATME.js} +4 -4
- package/dist/{chunk-WYZ5MYUA.js → chunk-BAHOV5I5.js} +44 -42
- package/dist/chunk-BAHOV5I5.js.map +1 -0
- package/dist/{chunk-G5APARUV.js → chunk-BEZDLIDG.js} +13 -15
- package/dist/chunk-BEZDLIDG.js.map +1 -0
- package/dist/{chunk-ERB6NDDW.js → chunk-CAGI4LLJ.js} +427 -72
- package/dist/chunk-CAGI4LLJ.js.map +1 -0
- package/dist/{chunk-4UVDI4XE.js → chunk-CLAHBPOI.js} +4 -4
- package/dist/{chunk-HNGSV2YC.js → chunk-CWM4GQD2.js} +39 -10
- package/dist/chunk-CWM4GQD2.js.map +1 -0
- package/dist/{chunk-HVQGCKY4.js → chunk-D3R7Q4QK.js} +2 -2
- package/dist/{chunk-I4F6PKUK.js → chunk-DIZ6HM7Q.js} +16 -17
- package/dist/chunk-DIZ6HM7Q.js.map +1 -0
- package/dist/{chunk-ZNACADFG.js → chunk-DVRZYAGT.js} +4 -4
- package/dist/chunk-DVRZYAGT.js.map +1 -0
- package/dist/{chunk-P7OG2ETF.js → chunk-DXKLLKME.js} +3 -3
- package/dist/{chunk-TDJPS3T2.js → chunk-E5SC6ENM.js} +24 -19
- package/dist/chunk-E5SC6ENM.js.map +1 -0
- package/dist/{chunk-26JMGSSC.js → chunk-EES37S2V.js} +4 -4
- package/dist/chunk-EES37S2V.js.map +1 -0
- package/dist/{chunk-JIUVJ4M7.js → chunk-EKEIEJL6.js} +2 -2
- package/dist/{chunk-GASWQ3XG.js → chunk-ESKIAXO2.js} +15 -16
- package/dist/chunk-ESKIAXO2.js.map +1 -0
- package/dist/{chunk-TOAIYQJR.js → chunk-FFSQFH6D.js} +7 -10
- package/dist/chunk-FFSQFH6D.js.map +1 -0
- package/dist/{chunk-DG27IUVI.js → chunk-FS7NTGCJ.js} +2 -2
- package/dist/{chunk-V5KYPBUR.js → chunk-FW7NGYLT.js} +11 -14
- package/dist/chunk-FW7NGYLT.js.map +1 -0
- package/dist/{chunk-52QYGZHH.js → chunk-GBBDFE6C.js} +2 -2
- package/dist/chunk-GBBDFE6C.js.map +1 -0
- package/dist/{chunk-HXGUIPQB.js → chunk-GQXFOAYZ.js} +3 -3
- package/dist/{chunk-BTVYJWSN.js → chunk-GTHVPVAR.js} +10 -10
- package/dist/{chunk-DMQCLZ4Z.js → chunk-GWO2IDAB.js} +2 -2
- package/dist/{chunk-6HFM7EIT.js → chunk-H3G4I3PN.js} +2 -2
- package/dist/chunk-HK67PX4J.js +27 -0
- package/dist/chunk-HK67PX4J.js.map +1 -0
- package/dist/{chunk-E7OBCBXS.js → chunk-HQSPF5IY.js} +22 -18
- package/dist/chunk-HQSPF5IY.js.map +1 -0
- package/dist/{chunk-VJFMXYKP.js → chunk-HR5R5JOM.js} +2 -2
- package/dist/{chunk-FDX7OWWI.js → chunk-HYYVT2KZ.js} +2 -2
- package/dist/{chunk-JRCYZEHU.js → chunk-I6QKB7MT.js} +2 -2
- package/dist/{chunk-MZ5ZFO2D.js → chunk-ICDHUUMQ.js} +84 -112
- package/dist/chunk-ICDHUUMQ.js.map +1 -0
- package/dist/{chunk-W5W6KHN2.js → chunk-IGNJOJEP.js} +1 -1
- package/dist/chunk-IGNJOJEP.js.map +1 -0
- package/dist/{chunk-KUDZVJKQ.js → chunk-IJ46VCWD.js} +2 -2
- package/dist/{chunk-4HDYQL3J.js → chunk-INMHEMXX.js} +2 -2
- package/dist/{chunk-66W5OOCX.js → chunk-JLYQQNY4.js} +52 -65
- package/dist/chunk-JLYQQNY4.js.map +1 -0
- package/dist/{chunk-UHXCLPGC.js → chunk-JNMTN5D5.js} +2 -2
- package/dist/chunk-JNMTN5D5.js.map +1 -0
- package/dist/{chunk-I4ZAO7F7.js → chunk-JWMBG5S3.js} +7 -11
- package/dist/chunk-JWMBG5S3.js.map +1 -0
- package/dist/chunk-KRVS7YWY.js +123 -0
- package/dist/chunk-KRVS7YWY.js.map +1 -0
- package/dist/{chunk-AR2JDFAE.js → chunk-KV6JM2IA.js} +2 -2
- package/dist/{chunk-PVU7LW5L.js → chunk-KX57HODD.js} +6 -6
- package/dist/{chunk-PY7CI6L5.js → chunk-LGFTOU27.js} +553 -275
- package/dist/chunk-LGFTOU27.js.map +1 -0
- package/dist/chunk-LN7P6MZB.js +190 -0
- package/dist/chunk-LN7P6MZB.js.map +1 -0
- package/dist/{chunk-UIBGXRDD.js → chunk-LX46B56Q.js} +25 -31
- package/dist/chunk-LX46B56Q.js.map +1 -0
- package/dist/{chunk-CEJWMBXO.js → chunk-LXECF2ZR.js} +2 -2
- package/dist/{chunk-7VHY5E3X.js → chunk-MSLPDFK2.js} +7 -7
- package/dist/{chunk-I6SDX7WX.js → chunk-MZTUTEHP.js} +16 -22
- package/dist/chunk-MZTUTEHP.js.map +1 -0
- package/dist/{chunk-VRA4Q7DA.js → chunk-NCCEDL2K.js} +55 -48
- package/dist/chunk-NCCEDL2K.js.map +1 -0
- package/dist/{chunk-HNHCQ556.js → chunk-NR2SUJ3H.js} +2 -2
- package/dist/{chunk-AHLJSVRL.js → chunk-NW4TY6VC.js} +5 -7
- package/dist/chunk-NW4TY6VC.js.map +1 -0
- package/dist/{chunk-RKKORO4M.js → chunk-O742QBZZ.js} +10 -10
- package/dist/chunk-O742QBZZ.js.map +1 -0
- package/dist/{chunk-VN45YLWM.js → chunk-OHOKBMTM.js} +343 -132
- package/dist/chunk-OHOKBMTM.js.map +1 -0
- package/dist/{chunk-W3TVWF6N.js → chunk-OIBEJN5H.js} +2 -2
- package/dist/{chunk-QD724WDJ.js → chunk-OMYCSTXL.js} +4 -4
- package/dist/{chunk-Z45YRGFO.js → chunk-ONVD6GBY.js} +2 -2
- package/dist/{chunk-TAGFGKHP.js → chunk-OTHHO3KI.js} +7 -10
- package/dist/chunk-OTHHO3KI.js.map +1 -0
- package/dist/{chunk-3737A6EA.js → chunk-P2MR5UNE.js} +75 -18
- package/dist/chunk-P2MR5UNE.js.map +1 -0
- package/dist/chunk-PENPHA45.js +66 -0
- package/dist/chunk-PENPHA45.js.map +1 -0
- package/dist/{chunk-3FSX6LDG.js → chunk-PPCOKVFA.js} +8 -3
- package/dist/{chunk-3FSX6LDG.js.map → chunk-PPCOKVFA.js.map} +1 -1
- package/dist/{chunk-VOQW5LAM.js → chunk-QSIEL6TL.js} +102 -71
- package/dist/chunk-QSIEL6TL.js.map +1 -0
- package/dist/chunk-RFO4UXKQ.js +201 -0
- package/dist/chunk-RFO4UXKQ.js.map +1 -0
- package/dist/{chunk-KCQX65FU.js → chunk-RKCRPPNB.js} +3 -3
- package/dist/{chunk-7TSYBD3E.js → chunk-RSXQNFGO.js} +2 -2
- package/dist/{chunk-SZ2K3MHL.js → chunk-S56VYR4Q.js} +8 -11
- package/dist/{chunk-SZ2K3MHL.js.map → chunk-S56VYR4Q.js.map} +1 -1
- package/dist/{chunk-7NXHMW6Y.js → chunk-SYS46BL7.js} +2 -2
- package/dist/{chunk-55LKVALY.js → chunk-T7IR4N2S.js} +1 -3
- package/dist/chunk-T7IR4N2S.js.map +1 -0
- package/dist/{chunk-A67356IR.js → chunk-T7JGWTES.js} +33 -8
- package/dist/chunk-T7JGWTES.js.map +1 -0
- package/dist/{chunk-WHJKIZS7.js → chunk-TDSN2O2V.js} +27 -149
- package/dist/chunk-TDSN2O2V.js.map +1 -0
- package/dist/{chunk-747LRSOO.js → chunk-TMBSPLUG.js} +2 -2
- package/dist/{chunk-LWSJG3LI.js → chunk-TQPASLY4.js} +7 -3
- package/dist/chunk-TQPASLY4.js.map +1 -0
- package/dist/{chunk-WKPJXWKR.js → chunk-TX5MKAHA.js} +3 -3
- package/dist/{chunk-HFQ6LGIO.js → chunk-U27Y4DJZ.js} +12 -10
- package/dist/{chunk-HFQ6LGIO.js.map → chunk-U27Y4DJZ.js.map} +1 -1
- package/dist/{chunk-E6427JU2.js → chunk-VVSOPRUE.js} +18 -2
- package/dist/chunk-VVSOPRUE.js.map +1 -0
- package/dist/{chunk-7G6VDWXB.js → chunk-WVE6YKMX.js} +3 -3
- package/dist/chunk-WVE6YKMX.js.map +1 -0
- package/dist/{chunk-GLKDWWNO.js → chunk-YALD3SST.js} +16 -14
- package/dist/chunk-YALD3SST.js.map +1 -0
- package/dist/{chunk-NJDQ4YSV.js → chunk-YKXZHUOW.js} +4 -4
- package/dist/{chunk-VW6ZZGCG.js → chunk-ZLHHE5XZ.js} +3 -3
- package/dist/{chunk-FUHKW252.js → chunk-ZZXLB6AS.js} +2 -2
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-FTOX7Q5L.js → classified-marker-NA6HUV33.js} +3 -3
- package/dist/collection-facade-VBM35WAH.js +45 -0
- package/dist/{computed-6G5M3UPH.js → computed-GSTLWSY5.js} +3 -3
- package/dist/consent/index.js +10 -10
- package/dist/cover/index.js +10 -3
- package/dist/custody/index.js +11 -11
- package/dist/{dead-filter-ENRIOQIE.js → dead-filter-UALP5DJX.js} +2 -2
- package/dist/debug/index.js +1 -1
- package/dist/{delegation-SSEPLXXO.js → delegation-JLNXM7KH.js} +24 -23
- package/dist/delegation-JLNXM7KH.js.map +1 -0
- package/dist/derivations/index.js +12 -12
- package/dist/derive-YUO3TQYN.js +22 -0
- package/dist/directory/index.js +10 -10
- package/dist/{dispatch-RE74SH6L.js → dispatch-4ZHLSFZQ.js} +5 -5
- package/dist/{dispatch-BDVGYC6X.js → dispatch-77CB75KB.js} +18 -18
- package/dist/{enclave-52VUXNRL.js → enclave-O4NOROCJ.js} +27 -9
- package/dist/executor-CZDXBXKE.js +29 -0
- package/dist/executor-DVJAWN3B.js +9 -0
- package/dist/executor-VRLG4TQJ.js +9 -0
- package/dist/export-accessible-UXSMG7AB.js +23 -0
- package/dist/extract-partition-TVY7AMKH.js +38 -0
- package/dist/{fanout-sidecar-DTBIDOWF.js → fanout-sidecar-NLQ7QAYW.js} +18 -27
- package/dist/fanout-sidecar-NLQ7QAYW.js.map +1 -0
- package/dist/find-WSARMJBW.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 +33 -15
- package/dist/index.js +187 -191
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +2 -2
- package/dist/introspection/index.js +11 -11
- package/dist/issue-3JNJNPT3.js +19 -0
- package/dist/kernel/constants.d.ts +18 -0
- package/dist/kernel/enclave/crypto.d.ts +24 -4
- package/dist/kernel/enclave/index.d.ts +9 -2
- 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 +68 -0
- package/dist/kernel/enclave/record-keys/tombstone.d.ts +3 -2
- package/dist/kernel/errors.d.ts +71 -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 +15 -2
- package/dist/kernel/query/join.d.ts +27 -0
- package/dist/kernel/sync-policy.d.ts +0 -2
- package/dist/kernel/tier-visibility.d.ts +15 -0
- package/dist/kernel/types.d.ts +145 -12
- package/dist/kernel/vault.d.ts +22 -18
- package/dist/kernel/via/index.d.ts +11 -2
- package/dist/{ledger-4WJL66BW.js → ledger-MVPNG5OS.js} +11 -11
- package/dist/liberate-4LOJOLDT.js +24 -0
- package/dist/{link-set-N4ZPV6E6.js → link-set-3POMQR3I.js} +11 -11
- package/dist/{managed-secret-OTHS64IF.js → managed-secret-IARIJJ7Q.js} +11 -3
- package/dist/materialized-views/index.js +22 -21
- package/dist/migrate-cek-XM2ETTZW.js +19 -0
- package/dist/migrate-cek-XM2ETTZW.js.map +1 -0
- package/dist/money/index.js +12 -8
- package/dist/noydb-IRILUDED.js +84 -0
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +22 -12
- package/dist/pod/index.js +75 -86
- package/dist/pod-handle-MELERCKU.js +41 -0
- package/dist/pod-handle-MELERCKU.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-ZAG2CAIY.js → post-register-Z5T7FY45.js} +6 -6
- package/dist/query/index.js +3 -3
- package/dist/reduce/index.js +4 -4
- package/dist/register-UZ37QPDB.js +32 -0
- package/dist/registry-ETJNA7PO.js +9 -0
- package/dist/registry-F7U6KSLX.js +9 -0
- package/dist/registry-L4ELKX6C.js +18 -0
- package/dist/registry-SDT5LIGO.js +19 -0
- package/dist/request-withdrawal-PVFEYX57.js +31 -0
- package/dist/{reveal-CMOJY4VO.js → reveal-DQ2E3QAZ.js} +6 -6
- package/dist/revoke-YXY6SHUH.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-UK4QRIUN.js → seed-DZ4ORU5N.js} +19 -22
- package/dist/seed-DZ4ORU5N.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-Q7XQBMPA.js +25 -0
- package/dist/snapshots/index.js +10 -10
- package/dist/snapshots/index.js.map +1 -1
- package/dist/{stale-ZWXSOWJI.js → stale-NI7SAXAZ.js} +10 -10
- package/dist/storage-3VNGSHXS.js +23 -0
- package/dist/{store-coordination-provider-PB6QZ57W.js → store-coordination-provider-TYKNR3OU.js} +18 -12
- package/dist/store-coordination-provider-TYKNR3OU.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-6J2YLKSO.js} +5 -5
- package/dist/via/lookup/handle.d.ts +4 -1
- package/dist/via/money/exact.d.ts +44 -0
- package/dist/via/money/index.d.ts +2 -0
- package/dist/via/money/normalize.d.ts +20 -0
- package/dist/walk-WRA7RAPY.js +21 -0
- package/dist/with-audit/guards/immutable-guard.d.ts +14 -0
- package/dist/with-audit/periods/active.d.ts +28 -1
- package/dist/with-audit/periods/index.d.ts +4 -2
- package/dist/with-audit/periods/periods.d.ts +161 -6
- package/dist/with-audit/periods/strategy.d.ts +14 -5
- package/dist/with-audit/periods/vault-facade.d.ts +89 -10
- package/dist/with-audit/periods/window.d.ts +10 -0
- 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 +2 -2
- package/dist/with-commit/history/history.d.ts +26 -0
- package/dist/with-commit/history/index.d.ts +2 -1
- package/dist/with-commit/history/ledger/store.d.ts +7 -1
- package/dist/with-commit/history/strategy.d.ts +11 -1
- package/dist/with-commit/numbering/index.d.ts +0 -5
- package/dist/with-commit/vault-head/active.d.ts +2 -0
- package/dist/with-commit/vault-head/head.d.ts +28 -0
- package/dist/with-commit/vault-head/index.d.ts +10 -0
- package/dist/with-commit/vault-head/strategy.d.ts +127 -0
- package/dist/with-commit/vault-head/verify.d.ts +90 -0
- package/dist/with-formula/materialized-views/types.d.ts +52 -0
- 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 +149 -3
- 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} +26 -19
- package/dist/with-pod/ulid.d.ts +1 -1
- package/dist/with-shape/blobs/blob-set.d.ts +2 -2
- package/dist/with-shape/introspection/index.d.ts +1 -0
- package/dist/with-sync/engine.d.ts +54 -5
- package/dist/with-sync/strategy.d.ts +3 -0
- package/dist/withdraw-accessible-4JTDFRFZ.js +28 -0
- package/package.json +13 -5
- package/dist/api-WVY3H3YH.js +0 -20
- package/dist/backup-YYBE5RPC.js.map +0 -1
- package/dist/bundle-handle-JEUWU2SJ.js +0 -36
- package/dist/bundle-handle-JEUWU2SJ.js.map +0 -1
- package/dist/chunk-26JMGSSC.js.map +0 -1
- package/dist/chunk-3737A6EA.js.map +0 -1
- package/dist/chunk-4MC5JRRX.js.map +0 -1
- package/dist/chunk-52QYGZHH.js.map +0 -1
- package/dist/chunk-55LKVALY.js.map +0 -1
- package/dist/chunk-5PR33AC4.js.map +0 -1
- package/dist/chunk-66W5OOCX.js.map +0 -1
- package/dist/chunk-7G6VDWXB.js.map +0 -1
- package/dist/chunk-A67356IR.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-C2OOEM57.js.map +0 -1
- package/dist/chunk-DIMKRG2E.js +0 -131
- package/dist/chunk-DIMKRG2E.js.map +0 -1
- package/dist/chunk-E6427JU2.js.map +0 -1
- package/dist/chunk-E7OBCBXS.js.map +0 -1
- package/dist/chunk-EDXAQ6JI.js.map +0 -1
- package/dist/chunk-EP7VPR7Y.js.map +0 -1
- package/dist/chunk-ERB6NDDW.js.map +0 -1
- package/dist/chunk-G5APARUV.js.map +0 -1
- package/dist/chunk-GASWQ3XG.js.map +0 -1
- package/dist/chunk-GLKDWWNO.js.map +0 -1
- package/dist/chunk-HNGSV2YC.js.map +0 -1
- package/dist/chunk-I4F6PKUK.js.map +0 -1
- package/dist/chunk-I4ZAO7F7.js.map +0 -1
- package/dist/chunk-I6SDX7WX.js.map +0 -1
- package/dist/chunk-JXIDGFKS.js.map +0 -1
- package/dist/chunk-LWSJG3LI.js.map +0 -1
- package/dist/chunk-MWKDOHEF.js.map +0 -1
- package/dist/chunk-MZ5ZFO2D.js.map +0 -1
- package/dist/chunk-NIVEDYJZ.js.map +0 -1
- package/dist/chunk-NWCGDAV6.js +0 -45
- package/dist/chunk-NWCGDAV6.js.map +0 -1
- package/dist/chunk-PJNTR2NV.js.map +0 -1
- package/dist/chunk-PRDRGL73.js.map +0 -1
- package/dist/chunk-PY7CI6L5.js.map +0 -1
- package/dist/chunk-RKKORO4M.js.map +0 -1
- package/dist/chunk-TAGFGKHP.js.map +0 -1
- package/dist/chunk-TDJPS3T2.js.map +0 -1
- package/dist/chunk-TOAIYQJR.js.map +0 -1
- package/dist/chunk-UHXCLPGC.js.map +0 -1
- package/dist/chunk-UIBGXRDD.js.map +0 -1
- package/dist/chunk-V5KYPBUR.js.map +0 -1
- package/dist/chunk-VCETL77O.js.map +0 -1
- package/dist/chunk-VN45YLWM.js.map +0 -1
- package/dist/chunk-VOQW5LAM.js.map +0 -1
- package/dist/chunk-VRA4Q7DA.js.map +0 -1
- package/dist/chunk-VV4NSB7P.js.map +0 -1
- package/dist/chunk-W5W6KHN2.js.map +0 -1
- package/dist/chunk-WHJKIZS7.js.map +0 -1
- package/dist/chunk-WYZ5MYUA.js.map +0 -1
- package/dist/chunk-ZJADGNYF.js.map +0 -1
- package/dist/chunk-ZNACADFG.js.map +0 -1
- package/dist/collection-facade-QN5ULWYC.js +0 -45
- package/dist/delegation-SSEPLXXO.js.map +0 -1
- package/dist/derive-6V3PIZF7.js +0 -22
- package/dist/executor-CYJQLKKI.js +0 -9
- package/dist/executor-FHHLXO4Y.js +0 -28
- package/dist/executor-S4QIKEHW.js +0 -9
- package/dist/export-accessible-PE5AYKZR.js +0 -23
- package/dist/extract-partition-X2L26O6A.js +0 -38
- package/dist/fanout-sidecar-DTBIDOWF.js.map +0 -1
- package/dist/find-RBBYKXI7.js +0 -11
- package/dist/issue-RNU55YOF.js +0 -19
- package/dist/liberate-PX4CAT5V.js +0 -24
- package/dist/noydb-YPSEGK32.js +0 -83
- package/dist/register-TMD6YRCX.js +0 -32
- package/dist/registry-CZVINVQH.js +0 -9
- package/dist/registry-FVLDWQKT.js +0 -18
- package/dist/registry-IKUR2MXD.js +0 -9
- package/dist/registry-JET4HBLY.js +0 -19
- package/dist/request-withdrawal-RH55LNZL.js +0 -31
- package/dist/revoke-R5TBYVA6.js +0 -24
- package/dist/seed-UK4QRIUN.js.map +0 -1
- package/dist/signer-AGALOXOO.js +0 -25
- package/dist/storage-N7LB275C.js +0 -23
- package/dist/store-coordination-provider-PB6QZ57W.js.map +0 -1
- package/dist/walk-OCUHCYJ6.js +0 -21
- package/dist/with-pod/bundle-handle.d.ts +0 -9
- package/dist/withdraw-accessible-QQNWM5BO.js +0 -28
- /package/dist/{api-WVY3H3YH.js.map → api-EYNT6KGS.js.map} +0 -0
- /package/dist/{chunk-35EBKWQU.js.map → chunk-2LFOMUEA.js.map} +0 -0
- /package/dist/{chunk-OF3L2SD6.js.map → chunk-32B7VCKU.js.map} +0 -0
- /package/dist/{chunk-MSWYQLMQ.js.map → chunk-3B6S6RSI.js.map} +0 -0
- /package/dist/{chunk-7F4DBNSP.js.map → chunk-3O72ZD2E.js.map} +0 -0
- /package/dist/{chunk-FNSMZQS4.js.map → chunk-3PMFMUFX.js.map} +0 -0
- /package/dist/{chunk-FAXZQ5UF.js.map → chunk-4RAS6HN6.js.map} +0 -0
- /package/dist/{chunk-VMCCGUQB.js.map → chunk-6JPGVL7X.js.map} +0 -0
- /package/dist/{chunk-WLYIROCJ.js.map → chunk-6YD5SYQS.js.map} +0 -0
- /package/dist/{chunk-YAWK6AWF.js.map → chunk-7KAE7CQ2.js.map} +0 -0
- /package/dist/{chunk-NKH7GSIH.js.map → chunk-AUPSEJ7O.js.map} +0 -0
- /package/dist/{chunk-CCZJJ4I2.js.map → chunk-B6IYATME.js.map} +0 -0
- /package/dist/{chunk-4UVDI4XE.js.map → chunk-CLAHBPOI.js.map} +0 -0
- /package/dist/{chunk-HVQGCKY4.js.map → chunk-D3R7Q4QK.js.map} +0 -0
- /package/dist/{chunk-P7OG2ETF.js.map → chunk-DXKLLKME.js.map} +0 -0
- /package/dist/{chunk-JIUVJ4M7.js.map → chunk-EKEIEJL6.js.map} +0 -0
- /package/dist/{chunk-DG27IUVI.js.map → chunk-FS7NTGCJ.js.map} +0 -0
- /package/dist/{chunk-HXGUIPQB.js.map → chunk-GQXFOAYZ.js.map} +0 -0
- /package/dist/{chunk-BTVYJWSN.js.map → chunk-GTHVPVAR.js.map} +0 -0
- /package/dist/{chunk-DMQCLZ4Z.js.map → chunk-GWO2IDAB.js.map} +0 -0
- /package/dist/{chunk-6HFM7EIT.js.map → chunk-H3G4I3PN.js.map} +0 -0
- /package/dist/{chunk-VJFMXYKP.js.map → chunk-HR5R5JOM.js.map} +0 -0
- /package/dist/{chunk-FDX7OWWI.js.map → chunk-HYYVT2KZ.js.map} +0 -0
- /package/dist/{chunk-JRCYZEHU.js.map → chunk-I6QKB7MT.js.map} +0 -0
- /package/dist/{chunk-KUDZVJKQ.js.map → chunk-IJ46VCWD.js.map} +0 -0
- /package/dist/{chunk-4HDYQL3J.js.map → chunk-INMHEMXX.js.map} +0 -0
- /package/dist/{chunk-AR2JDFAE.js.map → chunk-KV6JM2IA.js.map} +0 -0
- /package/dist/{chunk-PVU7LW5L.js.map → chunk-KX57HODD.js.map} +0 -0
- /package/dist/{chunk-CEJWMBXO.js.map → chunk-LXECF2ZR.js.map} +0 -0
- /package/dist/{chunk-7VHY5E3X.js.map → chunk-MSLPDFK2.js.map} +0 -0
- /package/dist/{chunk-HNHCQ556.js.map → chunk-NR2SUJ3H.js.map} +0 -0
- /package/dist/{chunk-W3TVWF6N.js.map → chunk-OIBEJN5H.js.map} +0 -0
- /package/dist/{chunk-QD724WDJ.js.map → chunk-OMYCSTXL.js.map} +0 -0
- /package/dist/{chunk-Z45YRGFO.js.map → chunk-ONVD6GBY.js.map} +0 -0
- /package/dist/{chunk-KCQX65FU.js.map → chunk-RKCRPPNB.js.map} +0 -0
- /package/dist/{chunk-7TSYBD3E.js.map → chunk-RSXQNFGO.js.map} +0 -0
- /package/dist/{chunk-7NXHMW6Y.js.map → chunk-SYS46BL7.js.map} +0 -0
- /package/dist/{chunk-747LRSOO.js.map → chunk-TMBSPLUG.js.map} +0 -0
- /package/dist/{chunk-WKPJXWKR.js.map → chunk-TX5MKAHA.js.map} +0 -0
- /package/dist/{chunk-NJDQ4YSV.js.map → chunk-YKXZHUOW.js.map} +0 -0
- /package/dist/{chunk-VW6ZZGCG.js.map → chunk-ZLHHE5XZ.js.map} +0 -0
- /package/dist/{chunk-FUHKW252.js.map → chunk-ZZXLB6AS.js.map} +0 -0
- /package/dist/{classified-marker-FTOX7Q5L.js.map → classified-marker-NA6HUV33.js.map} +0 -0
- /package/dist/{collection-facade-QN5ULWYC.js.map → collection-facade-VBM35WAH.js.map} +0 -0
- /package/dist/{computed-6G5M3UPH.js.map → computed-GSTLWSY5.js.map} +0 -0
- /package/dist/{dead-filter-ENRIOQIE.js.map → dead-filter-UALP5DJX.js.map} +0 -0
- /package/dist/{derive-6V3PIZF7.js.map → derive-YUO3TQYN.js.map} +0 -0
- /package/dist/{dispatch-RE74SH6L.js.map → dispatch-4ZHLSFZQ.js.map} +0 -0
- /package/dist/{dispatch-BDVGYC6X.js.map → dispatch-77CB75KB.js.map} +0 -0
- /package/dist/{enclave-52VUXNRL.js.map → enclave-O4NOROCJ.js.map} +0 -0
- /package/dist/{executor-CYJQLKKI.js.map → executor-CZDXBXKE.js.map} +0 -0
- /package/dist/{executor-FHHLXO4Y.js.map → executor-DVJAWN3B.js.map} +0 -0
- /package/dist/{executor-S4QIKEHW.js.map → executor-VRLG4TQJ.js.map} +0 -0
- /package/dist/{export-accessible-PE5AYKZR.js.map → export-accessible-UXSMG7AB.js.map} +0 -0
- /package/dist/{extract-partition-X2L26O6A.js.map → extract-partition-TVY7AMKH.js.map} +0 -0
- /package/dist/{find-RBBYKXI7.js.map → find-WSARMJBW.js.map} +0 -0
- /package/dist/{issue-RNU55YOF.js.map → issue-3JNJNPT3.js.map} +0 -0
- /package/dist/{ledger-4WJL66BW.js.map → ledger-MVPNG5OS.js.map} +0 -0
- /package/dist/{liberate-PX4CAT5V.js.map → liberate-4LOJOLDT.js.map} +0 -0
- /package/dist/{link-set-N4ZPV6E6.js.map → link-set-3POMQR3I.js.map} +0 -0
- /package/dist/{managed-secret-OTHS64IF.js.map → managed-secret-IARIJJ7Q.js.map} +0 -0
- /package/dist/{noydb-YPSEGK32.js.map → noydb-IRILUDED.js.map} +0 -0
- /package/dist/{post-register-ZAG2CAIY.js.map → post-register-Z5T7FY45.js.map} +0 -0
- /package/dist/{register-TMD6YRCX.js.map → register-UZ37QPDB.js.map} +0 -0
- /package/dist/{registry-CZVINVQH.js.map → registry-ETJNA7PO.js.map} +0 -0
- /package/dist/{registry-FVLDWQKT.js.map → registry-F7U6KSLX.js.map} +0 -0
- /package/dist/{registry-IKUR2MXD.js.map → registry-L4ELKX6C.js.map} +0 -0
- /package/dist/{registry-JET4HBLY.js.map → registry-SDT5LIGO.js.map} +0 -0
- /package/dist/{request-withdrawal-RH55LNZL.js.map → request-withdrawal-PVFEYX57.js.map} +0 -0
- /package/dist/{reveal-CMOJY4VO.js.map → reveal-DQ2E3QAZ.js.map} +0 -0
- /package/dist/{revoke-R5TBYVA6.js.map → revoke-YXY6SHUH.js.map} +0 -0
- /package/dist/{signer-AGALOXOO.js.map → signer-Q7XQBMPA.js.map} +0 -0
- /package/dist/{stale-ZWXSOWJI.js.map → stale-NI7SAXAZ.js.map} +0 -0
- /package/dist/{storage-N7LB275C.js.map → storage-3VNGSHXS.js.map} +0 -0
- /package/dist/{ulid-PTAIMDG4.js.map → ulid-VQW6ERQN.js.map} +0 -0
- /package/dist/{verify-RO5RTZOJ.js.map → verify-6J2YLKSO.js.map} +0 -0
- /package/dist/{walk-OCUHCYJ6.js.map → walk-WRA7RAPY.js.map} +0 -0
- /package/dist/{withdraw-accessible-QQNWM5BO.js.map → withdraw-accessible-4JTDFRFZ.js.map} +0 -0
|
@@ -5,7 +5,14 @@ import {
|
|
|
5
5
|
readDirectoryConfig,
|
|
6
6
|
readUserVisibility,
|
|
7
7
|
saveUserEnvelope
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LX46B56Q.js";
|
|
9
|
+
import {
|
|
10
|
+
rekeyBlobSet,
|
|
11
|
+
rekeyEnvelopeIfNeeded
|
|
12
|
+
} from "./chunk-OHOKBMTM.js";
|
|
13
|
+
import {
|
|
14
|
+
buildRecordEnvelope
|
|
15
|
+
} from "./chunk-PENPHA45.js";
|
|
9
16
|
import {
|
|
10
17
|
base64ToBuffer,
|
|
11
18
|
bufferToBase64,
|
|
@@ -20,25 +27,73 @@ import {
|
|
|
20
27
|
generateSalt,
|
|
21
28
|
unwrapKey,
|
|
22
29
|
wrapKey
|
|
23
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-AU3AUJIZ.js";
|
|
24
31
|
import {
|
|
25
|
-
NOYDB_FORMAT_VERSION,
|
|
26
32
|
NOYDB_KEYRING_VERSION
|
|
27
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-IGNJOJEP.js";
|
|
28
34
|
import {
|
|
29
35
|
DirectoryDisabledError,
|
|
30
36
|
EchoCeremonyRequiredError,
|
|
31
37
|
InvalidKeyError,
|
|
32
38
|
KeyringCorruptError,
|
|
33
39
|
KeyringExpiredError,
|
|
40
|
+
KeyringTamperedError,
|
|
34
41
|
NoAccessError,
|
|
35
42
|
NoydbError,
|
|
36
43
|
PermissionDeniedError,
|
|
37
44
|
PrivilegeEscalationError,
|
|
45
|
+
ROSTER_KEY_ID,
|
|
38
46
|
USER_ENVELOPE_COLLECTION,
|
|
39
47
|
ValidationError,
|
|
40
48
|
WrongPromptError
|
|
41
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-CWM4GQD2.js";
|
|
50
|
+
|
|
51
|
+
// src/with-party/team/roster-tag.ts
|
|
52
|
+
function stable(value) {
|
|
53
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(",")}]`;
|
|
54
|
+
if (value !== null && typeof value === "object") {
|
|
55
|
+
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)}`);
|
|
56
|
+
return `{${entries.join(",")}}`;
|
|
57
|
+
}
|
|
58
|
+
return JSON.stringify(value) ?? "null";
|
|
59
|
+
}
|
|
60
|
+
function rosterCanonical(file) {
|
|
61
|
+
return stable({
|
|
62
|
+
user_id: file.user_id,
|
|
63
|
+
role: file.role,
|
|
64
|
+
permissions: file.permissions,
|
|
65
|
+
granted_by: file.granted_by,
|
|
66
|
+
expires_at: file.expires_at ?? null,
|
|
67
|
+
export_capability: file.export_capability ?? null,
|
|
68
|
+
import_capability: file.import_capability ?? null
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async function mintRosterTag(file, rosterKey) {
|
|
72
|
+
return encrypt(rosterCanonical(file), rosterKey);
|
|
73
|
+
}
|
|
74
|
+
async function verifyRosterTag(file, tag, rosterKey) {
|
|
75
|
+
if (tag == null) return false;
|
|
76
|
+
try {
|
|
77
|
+
return await decrypt(tag.iv, tag.data, rosterKey) === rosterCanonical(file);
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function assertRosterAuthenticated(file, deks, userId) {
|
|
83
|
+
const rosterKey = deks.get(ROSTER_KEY_ID);
|
|
84
|
+
if (rosterKey === void 0) {
|
|
85
|
+
throw new KeyringTamperedError({ userId, reason: "roster-key-missing" });
|
|
86
|
+
}
|
|
87
|
+
await assertRosterTagValid(file, rosterKey, userId);
|
|
88
|
+
}
|
|
89
|
+
async function assertRosterTagValid(file, rosterKey, userId) {
|
|
90
|
+
if (!await verifyRosterTag(file, file.roster_tag, rosterKey)) {
|
|
91
|
+
throw new KeyringTamperedError({
|
|
92
|
+
userId,
|
|
93
|
+
reason: file.roster_tag == null ? "roster-tag-missing" : "roster-tag-mismatch"
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
42
97
|
|
|
43
98
|
// src/kernel/validation.ts
|
|
44
99
|
var WeakSecretError = class extends NoydbError {
|
|
@@ -294,6 +349,18 @@ async function verifyKeyringCanary(wrappedCanary, kek) {
|
|
|
294
349
|
return false;
|
|
295
350
|
}
|
|
296
351
|
}
|
|
352
|
+
function rosterKeyOf(keyring) {
|
|
353
|
+
return keyring.deks.get(ROSTER_KEY_ID) ?? null;
|
|
354
|
+
}
|
|
355
|
+
function requireRosterKey(keyring, fn) {
|
|
356
|
+
const rosterKey = rosterKeyOf(keyring);
|
|
357
|
+
if (!rosterKey) {
|
|
358
|
+
throw new ValidationError(
|
|
359
|
+
`${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.`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
return rosterKey;
|
|
363
|
+
}
|
|
297
364
|
async function deriveKekForKeyring(file, secret, salt) {
|
|
298
365
|
if (file.echo !== void 0) {
|
|
299
366
|
if (typeof secret === "string") throw new EchoCeremonyRequiredError();
|
|
@@ -327,10 +394,12 @@ async function loadKeyring(store, vault, opts) {
|
|
|
327
394
|
throw new NoAccessError(`No keyring found for user "${userId}" in vault "${vault}"`);
|
|
328
395
|
}
|
|
329
396
|
const { file: keyringFile } = found;
|
|
330
|
-
assertKeyringNotExpired(keyringFile);
|
|
331
397
|
const salt = base64ToBuffer(keyringFile.salt);
|
|
332
398
|
const kek = await deriveKekForKeyring(keyringFile, secret, salt);
|
|
333
|
-
|
|
399
|
+
if (keyringFile.canary === void 0) {
|
|
400
|
+
throw new KeyringTamperedError({ userId, reason: "canary-missing" });
|
|
401
|
+
}
|
|
402
|
+
const canaryOk = await verifyKeyringCanary(keyringFile.canary, kek);
|
|
334
403
|
const deks = /* @__PURE__ */ new Map();
|
|
335
404
|
const failedCollections = [];
|
|
336
405
|
let firstUnwrapError = null;
|
|
@@ -343,11 +412,18 @@ async function loadKeyring(store, vault, opts) {
|
|
|
343
412
|
if (firstUnwrapError === null) firstUnwrapError = err;
|
|
344
413
|
}
|
|
345
414
|
}
|
|
346
|
-
|
|
415
|
+
const pendingDeks = /* @__PURE__ */ new Map();
|
|
416
|
+
for (const [collName, wrapped] of Object.entries(keyringFile.pending_deks ?? {})) {
|
|
417
|
+
try {
|
|
418
|
+
pendingDeks.set(collName, await unwrapKey(wrapped, kek));
|
|
419
|
+
} catch {
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (canaryOk) {
|
|
347
423
|
if (failedCollections.length > 0) {
|
|
348
424
|
throw new KeyringCorruptError({ failedCollections, intactCount: deks.size });
|
|
349
425
|
}
|
|
350
|
-
} else
|
|
426
|
+
} else {
|
|
351
427
|
if (deks.size > 0) {
|
|
352
428
|
throw new KeyringCorruptError({
|
|
353
429
|
failedCollections: [...failedCollections, "_canary"],
|
|
@@ -355,20 +431,16 @@ async function loadKeyring(store, vault, opts) {
|
|
|
355
431
|
});
|
|
356
432
|
}
|
|
357
433
|
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
434
|
}
|
|
435
|
+
await assertRosterAuthenticated(keyringFile, deks, userId);
|
|
436
|
+
assertKeyringNotExpired(keyringFile);
|
|
366
437
|
return {
|
|
367
438
|
userId: keyringFile.user_id,
|
|
368
439
|
displayName: keyringFile.display_name,
|
|
369
440
|
role: keyringFile.role,
|
|
370
441
|
permissions: keyringFile.permissions,
|
|
371
442
|
deks,
|
|
443
|
+
pendingDeks,
|
|
372
444
|
kek,
|
|
373
445
|
salt,
|
|
374
446
|
authenticators: keyringFile.authenticators ?? [],
|
|
@@ -404,18 +476,27 @@ async function createOwnerKeyring(store, vault, opts) {
|
|
|
404
476
|
const kek = typeof secret === "string" ? await deriveKey(secret, salt) : await deriveEchoKey(secret, salt);
|
|
405
477
|
const userEnvelopeDek = await generateDEK();
|
|
406
478
|
const wrappedUserEnvelopeDek = await wrapKey(userEnvelopeDek, kek);
|
|
479
|
+
const rosterKey = await generateDEK();
|
|
480
|
+
const wrappedRosterKey = await wrapKey(rosterKey, kek);
|
|
407
481
|
const canary = await mintKeyringCanary(kek);
|
|
408
|
-
const
|
|
409
|
-
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
482
|
+
const authority = {
|
|
410
483
|
user_id: userId,
|
|
411
484
|
display_name: userId,
|
|
412
485
|
role: "owner",
|
|
413
486
|
permissions: {},
|
|
414
|
-
|
|
487
|
+
granted_by: userId
|
|
488
|
+
};
|
|
489
|
+
const keyringFile = {
|
|
490
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
491
|
+
...authority,
|
|
492
|
+
deks: {
|
|
493
|
+
[USER_ENVELOPE_COLLECTION]: wrappedUserEnvelopeDek,
|
|
494
|
+
[ROSTER_KEY_ID]: wrappedRosterKey
|
|
495
|
+
},
|
|
415
496
|
salt: bufferToBase64(salt),
|
|
416
497
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
417
|
-
granted_by: userId,
|
|
418
498
|
canary,
|
|
499
|
+
roster_tag: await mintRosterTag(authority, rosterKey),
|
|
419
500
|
// The presence of this block is what makes the keyring an echo keyring —
|
|
420
501
|
// `deriveKekForKeyring` reads it to refuse a single-string unlock (AG-1).
|
|
421
502
|
...typeof secret !== "string" ? {
|
|
@@ -432,7 +513,10 @@ async function createOwnerKeyring(store, vault, opts) {
|
|
|
432
513
|
displayName: userId,
|
|
433
514
|
role: "owner",
|
|
434
515
|
permissions: {},
|
|
435
|
-
deks: /* @__PURE__ */ new Map([
|
|
516
|
+
deks: /* @__PURE__ */ new Map([
|
|
517
|
+
[USER_ENVELOPE_COLLECTION, userEnvelopeDek],
|
|
518
|
+
[ROSTER_KEY_ID, rosterKey]
|
|
519
|
+
]),
|
|
436
520
|
kek,
|
|
437
521
|
salt,
|
|
438
522
|
authenticators: []
|
|
@@ -444,11 +528,18 @@ async function grant(store, vault, callerKeyring, options) {
|
|
|
444
528
|
"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
529
|
);
|
|
446
530
|
}
|
|
531
|
+
const callerRosterKey = requireRosterKey(callerKeyring, "grant");
|
|
447
532
|
if (!canGrant(callerKeyring.role, options.role)) {
|
|
448
533
|
throw new PermissionDeniedError(
|
|
449
534
|
`Role "${callerKeyring.role}" cannot grant role "${options.role}"`
|
|
450
535
|
);
|
|
451
536
|
}
|
|
537
|
+
const secret = options.secret;
|
|
538
|
+
if (typeof secret !== "string" || secret.trim() === "") {
|
|
539
|
+
throw new ValidationError(
|
|
540
|
+
`grant: \`secret\` is required and must be a non-empty string (got ${secret === void 0 ? "undefined" : typeof secret === "string" ? "an empty string" : typeof secret}). The grantee's key is derived from it, so a missing secret produces a keyring slot nobody can unlock. Note the 0.4.0-pre rename: the option is \`secret\`, not \`passphrase\`.`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
452
543
|
if (options.validateSecret && !options.allowWeakSecret) {
|
|
453
544
|
assertStrongSecret(options.secret);
|
|
454
545
|
}
|
|
@@ -456,15 +547,26 @@ async function grant(store, vault, callerKeyring, options) {
|
|
|
456
547
|
const newSalt = generateSalt();
|
|
457
548
|
const newKek = await deriveKey(options.secret, newSalt);
|
|
458
549
|
const granteeMayHoldSecrets = options.role === "owner" || options.role === "admin";
|
|
550
|
+
let mintedForGrant = false;
|
|
551
|
+
for (const collName of Object.keys(permissions)) {
|
|
552
|
+
if (collName === PERMISSION_WILDCARD) continue;
|
|
553
|
+
if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
|
|
554
|
+
if (callerKeyring.deks.has(collName)) continue;
|
|
555
|
+
if (await collectionHasRecords(store, vault, collName)) continue;
|
|
556
|
+
callerKeyring.deks.set(collName, await generateDEK());
|
|
557
|
+
mintedForGrant = true;
|
|
558
|
+
}
|
|
559
|
+
if (mintedForGrant) await persistKeyring(store, vault, callerKeyring);
|
|
459
560
|
const wrappedDeks = {};
|
|
460
561
|
for (const collName of Object.keys(permissions)) {
|
|
562
|
+
if (collName === PERMISSION_WILDCARD) continue;
|
|
461
563
|
if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
|
|
462
564
|
const dek = callerKeyring.deks.get(collName);
|
|
463
565
|
if (dek) {
|
|
464
566
|
wrappedDeks[collName] = await wrapKey(dek, newKek);
|
|
465
567
|
}
|
|
466
568
|
}
|
|
467
|
-
if (options.role === "owner" || options.role === "admin" || options.role === "custodian" || options.role === "viewer") {
|
|
569
|
+
if (options.role === "owner" || options.role === "admin" || options.role === "custodian" || options.role === "viewer" || permissionsAreWildcard(options.permissions)) {
|
|
468
570
|
for (const [collName, dek] of callerKeyring.deks) {
|
|
469
571
|
if (collName in wrappedDeks) continue;
|
|
470
572
|
if (isSecretBearingReservedCollection(collName) && !granteeMayHoldSecrets) continue;
|
|
@@ -482,19 +584,25 @@ async function grant(store, vault, callerKeyring, options) {
|
|
|
482
584
|
}
|
|
483
585
|
}
|
|
484
586
|
const canary = await mintKeyringCanary(newKek);
|
|
485
|
-
const
|
|
486
|
-
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
587
|
+
const authority = {
|
|
487
588
|
user_id: options.userId,
|
|
488
589
|
display_name: options.displayName,
|
|
489
590
|
role: options.role,
|
|
490
591
|
permissions,
|
|
592
|
+
granted_by: callerKeyring.userId,
|
|
593
|
+
...options.exportCapability !== void 0 && { export_capability: options.exportCapability },
|
|
594
|
+
...options.importCapability !== void 0 && { import_capability: options.importCapability }
|
|
595
|
+
};
|
|
596
|
+
const keyringFile = {
|
|
597
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
598
|
+
...authority,
|
|
491
599
|
deks: wrappedDeks,
|
|
492
600
|
salt: bufferToBase64(newSalt),
|
|
493
601
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
494
|
-
granted_by: callerKeyring.userId,
|
|
495
602
|
canary,
|
|
496
|
-
|
|
497
|
-
|
|
603
|
+
// The grantee's own copy of the roster key rode in via the `_`-prefix
|
|
604
|
+
// propagation loop above, so they can verify this tag on first unlock.
|
|
605
|
+
roster_tag: await mintRosterTag(authority, callerRosterKey)
|
|
498
606
|
};
|
|
499
607
|
await writeKeyringFile(store, vault, options.userId, keyringFile);
|
|
500
608
|
const userEnvelopeDek = callerKeyring.deks.get(USER_ENVELOPE_COLLECTION);
|
|
@@ -509,13 +617,14 @@ async function grant(store, vault, callerKeyring, options) {
|
|
|
509
617
|
);
|
|
510
618
|
}
|
|
511
619
|
}
|
|
512
|
-
async function findAdminDescendants(store, vault, rootUserId) {
|
|
620
|
+
async function findAdminDescendants(store, vault, rootUserId, rosterKey) {
|
|
513
621
|
const allUserIds = await store.list(vault, "_keyring");
|
|
514
622
|
const childrenByParent = /* @__PURE__ */ new Map();
|
|
515
623
|
for (const userId of allUserIds) {
|
|
516
624
|
const found = await readKeyringFile(store, vault, userId);
|
|
517
625
|
if (!found) continue;
|
|
518
626
|
const kf = found.file;
|
|
627
|
+
await assertRosterTagValid(kf, rosterKey, userId);
|
|
519
628
|
if (kf.role !== "admin") continue;
|
|
520
629
|
if (kf.user_id === rootUserId) continue;
|
|
521
630
|
const list = childrenByParent.get(kf.granted_by) ?? [];
|
|
@@ -539,9 +648,16 @@ async function findAdminDescendants(store, vault, rootUserId) {
|
|
|
539
648
|
async function revoke(store, vault, callerKeyring, options) {
|
|
540
649
|
const targetFound = await readKeyringFile(store, vault, options.userId);
|
|
541
650
|
if (!targetFound) {
|
|
651
|
+
const pending = [...callerKeyring.pendingDeks?.keys() ?? []];
|
|
652
|
+
if (pending.length > 0) {
|
|
653
|
+
await rotateKeys(store, vault, callerKeyring, { collections: pending });
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
542
656
|
throw new NoAccessError(`User "${options.userId}" has no keyring in vault "${vault}"`);
|
|
543
657
|
}
|
|
544
658
|
const targetKeyring = targetFound.file;
|
|
659
|
+
const callerRosterKey = requireRosterKey(callerKeyring, "revoke");
|
|
660
|
+
await assertRosterTagValid(targetKeyring, callerRosterKey, options.userId);
|
|
545
661
|
if (!canRevoke(callerKeyring.role, targetKeyring.role)) {
|
|
546
662
|
throw new PermissionDeniedError(
|
|
547
663
|
`Role "${callerKeyring.role}" cannot revoke role "${targetKeyring.role}"`
|
|
@@ -551,7 +667,7 @@ async function revoke(store, vault, callerKeyring, options) {
|
|
|
551
667
|
const usersToRevoke = [options.userId];
|
|
552
668
|
const affectedCollections = new Set(Object.keys(targetKeyring.deks));
|
|
553
669
|
if (targetKeyring.role === "admin") {
|
|
554
|
-
const descendants = await findAdminDescendants(store, vault, options.userId);
|
|
670
|
+
const descendants = await findAdminDescendants(store, vault, options.userId, callerRosterKey);
|
|
555
671
|
if (descendants.length > 0) {
|
|
556
672
|
if (cascadeMode === "warn") {
|
|
557
673
|
console.warn(
|
|
@@ -561,6 +677,7 @@ async function revoke(store, vault, callerKeyring, options) {
|
|
|
561
677
|
for (const userId of descendants) {
|
|
562
678
|
const descFound = await readKeyringFile(store, vault, userId);
|
|
563
679
|
if (!descFound) continue;
|
|
680
|
+
await assertRosterTagValid(descFound.file, callerRosterKey, userId);
|
|
564
681
|
usersToRevoke.push(userId);
|
|
565
682
|
for (const c of Object.keys(descFound.file.deks)) affectedCollections.add(c);
|
|
566
683
|
}
|
|
@@ -572,9 +689,103 @@ async function revoke(store, vault, callerKeyring, options) {
|
|
|
572
689
|
await deleteUserEnvelope(store, vault, userId);
|
|
573
690
|
await deleteUserVisibility(store, vault, userId);
|
|
574
691
|
}
|
|
575
|
-
|
|
576
|
-
|
|
692
|
+
affectedCollections.delete(ROSTER_KEY_ID);
|
|
693
|
+
if (affectedCollections.size > 0) {
|
|
694
|
+
const { unverified } = await rotateKeys(store, vault, callerKeyring, {
|
|
695
|
+
collections: [...affectedCollections]
|
|
696
|
+
});
|
|
697
|
+
if (unverified.length > 0) {
|
|
698
|
+
console.warn(
|
|
699
|
+
`[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).`
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
async function readKeyringForAudit(store, vault, userId) {
|
|
705
|
+
const envelope = await store.get(vault, "_keyring", userId);
|
|
706
|
+
if (!envelope) return void 0;
|
|
707
|
+
try {
|
|
708
|
+
return { file: parseKeyringEnvelope(envelope) };
|
|
709
|
+
} catch {
|
|
710
|
+
return { unparseable: true };
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
async function verifyRoster(store, vault, callerKeyring) {
|
|
714
|
+
const rosterKey = requireRosterKey(callerKeyring, "verifyRoster");
|
|
715
|
+
const unverified = [];
|
|
716
|
+
let checked = 0;
|
|
717
|
+
for (const userId of await store.list(vault, "_keyring")) {
|
|
718
|
+
const found = await readKeyringForAudit(store, vault, userId);
|
|
719
|
+
if (!found) continue;
|
|
720
|
+
checked += 1;
|
|
721
|
+
if ("unparseable" in found) {
|
|
722
|
+
unverified.push({ userId, reason: "unparseable" });
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
try {
|
|
726
|
+
await assertRosterTagValid(found.file, rosterKey, userId);
|
|
727
|
+
} catch (err) {
|
|
728
|
+
if (!(err instanceof KeyringTamperedError)) throw err;
|
|
729
|
+
unverified.push({ userId, reason: err.details.reason });
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return { checked, unverified };
|
|
733
|
+
}
|
|
734
|
+
async function quarantineKeyring(store, vault, callerKeyring, userId) {
|
|
735
|
+
if (callerKeyring.role !== "owner") {
|
|
736
|
+
throw new PermissionDeniedError(
|
|
737
|
+
`Role "${callerKeyring.role}" cannot quarantine a keyring \u2014 quarantine is owner-only, because it removes a file whose own authority claims cannot be trusted.`
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
if (userId === callerKeyring.userId) {
|
|
741
|
+
throw new ValidationError(
|
|
742
|
+
"quarantineKeyring: refusing to quarantine the calling keyring. Removing your own keyring and rotating behind it would leave the vault with no reachable owner."
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
const rosterKey = requireRosterKey(callerKeyring, "quarantineKeyring");
|
|
746
|
+
const found = await readKeyringForAudit(store, vault, userId);
|
|
747
|
+
if (!found) {
|
|
748
|
+
const pending = [...callerKeyring.pendingDeks?.keys() ?? []];
|
|
749
|
+
if (pending.length > 0) {
|
|
750
|
+
const resumed = await rotateKeys(store, vault, callerKeyring, { collections: pending });
|
|
751
|
+
return {
|
|
752
|
+
userId,
|
|
753
|
+
reason: "roster-tag-mismatch",
|
|
754
|
+
rotated: pending,
|
|
755
|
+
needsRegrant: resumed.needsRegrant,
|
|
756
|
+
alsoUnverified: resumed.unverified
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
throw new NoAccessError(`No keyring found for user "${userId}" in vault "${vault}"`);
|
|
577
760
|
}
|
|
761
|
+
let reason = null;
|
|
762
|
+
if ("unparseable" in found) {
|
|
763
|
+
reason = "unparseable";
|
|
764
|
+
} else {
|
|
765
|
+
try {
|
|
766
|
+
await assertRosterTagValid(found.file, rosterKey, userId);
|
|
767
|
+
} catch (err) {
|
|
768
|
+
if (!(err instanceof KeyringTamperedError)) throw err;
|
|
769
|
+
reason = err.details.reason;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (reason === null) {
|
|
773
|
+
throw new ValidationError(
|
|
774
|
+
`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.`
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
await store.delete(vault, "_keyring", userId);
|
|
778
|
+
await deleteUserEnvelope(store, vault, userId);
|
|
779
|
+
await deleteUserVisibility(store, vault, userId);
|
|
780
|
+
const rotated = [...callerKeyring.deks.keys()].filter((c) => c !== ROSTER_KEY_ID);
|
|
781
|
+
const result = rotated.length > 0 ? await rotateKeys(store, vault, callerKeyring, { collections: rotated }) : { needsRegrant: [], unverified: [] };
|
|
782
|
+
return {
|
|
783
|
+
userId,
|
|
784
|
+
reason,
|
|
785
|
+
rotated,
|
|
786
|
+
needsRegrant: result.needsRegrant,
|
|
787
|
+
alsoUnverified: result.unverified
|
|
788
|
+
};
|
|
578
789
|
}
|
|
579
790
|
async function updateKeyringIdentity(store, vault, callerKeyring, options) {
|
|
580
791
|
if (options.role === void 0 && options.displayName === void 0 && options.permissions === void 0) {
|
|
@@ -589,6 +800,8 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
|
|
|
589
800
|
);
|
|
590
801
|
}
|
|
591
802
|
const target = found.file;
|
|
803
|
+
const rosterKey = requireRosterKey(callerKeyring, "updateKeyringIdentity");
|
|
804
|
+
await assertRosterTagValid(target, rosterKey, options.userId);
|
|
592
805
|
if (!canUpdateRole(callerKeyring.role, target.role)) {
|
|
593
806
|
throw new PermissionDeniedError(
|
|
594
807
|
`Role "${callerKeyring.role}" cannot update a keyring with role "${target.role}"`
|
|
@@ -599,7 +812,7 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
|
|
|
599
812
|
`Role "${callerKeyring.role}" cannot promote target to role "${options.role}"`
|
|
600
813
|
);
|
|
601
814
|
}
|
|
602
|
-
const
|
|
815
|
+
const edited = {
|
|
603
816
|
...target,
|
|
604
817
|
...options.role !== void 0 && { role: options.role },
|
|
605
818
|
...options.displayName !== void 0 && {
|
|
@@ -608,50 +821,113 @@ async function updateKeyringIdentity(store, vault, callerKeyring, options) {
|
|
|
608
821
|
},
|
|
609
822
|
...options.permissions !== void 0 && { permissions: options.permissions }
|
|
610
823
|
};
|
|
824
|
+
const next = { ...edited, roster_tag: await mintRosterTag(edited, rosterKey) };
|
|
611
825
|
await writeKeyringFile(store, vault, options.userId, next);
|
|
612
826
|
}
|
|
827
|
+
var HISTORY_COLLECTION = "_history";
|
|
828
|
+
var LEDGER_COLLECTION = "_ledger";
|
|
829
|
+
var LEDGER_DELTAS_COLLECTION = "_ledger_deltas";
|
|
830
|
+
var BLOB_COLLECTION = "_blob";
|
|
831
|
+
var BLOB_SLOTS_PREFIX = "_blob_slots_";
|
|
832
|
+
var BLOB_VERSIONS_PREFIX = "_blob_versions_";
|
|
833
|
+
var BLOB_INTENT_COLLECTION = "_blob_intent";
|
|
834
|
+
async function listOrEmpty(store, vault, collection) {
|
|
835
|
+
try {
|
|
836
|
+
return await store.list(vault, collection);
|
|
837
|
+
} catch {
|
|
838
|
+
return [];
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
async function derivedRefsFor(store, vault, rotated) {
|
|
842
|
+
const out = [];
|
|
843
|
+
if (!rotated.startsWith("_")) {
|
|
844
|
+
for (const id of await listOrEmpty(store, vault, HISTORY_COLLECTION)) {
|
|
845
|
+
if (id.startsWith(`${rotated}:`)) out.push({ collection: HISTORY_COLLECTION, id });
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (rotated === LEDGER_COLLECTION) {
|
|
849
|
+
for (const id of await listOrEmpty(store, vault, LEDGER_DELTAS_COLLECTION)) {
|
|
850
|
+
out.push({ collection: LEDGER_DELTAS_COLLECTION, id });
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
if (!rotated.startsWith("_")) {
|
|
854
|
+
for (const id of await listOrEmpty(store, vault, `${BLOB_SLOTS_PREFIX}${rotated}`)) {
|
|
855
|
+
out.push({ collection: `${BLOB_SLOTS_PREFIX}${rotated}`, id });
|
|
856
|
+
}
|
|
857
|
+
for (const id of await listOrEmpty(store, vault, `${BLOB_VERSIONS_PREFIX}${rotated}`)) {
|
|
858
|
+
out.push({ collection: `${BLOB_VERSIONS_PREFIX}${rotated}`, id });
|
|
859
|
+
}
|
|
860
|
+
for (const id of await listOrEmpty(store, vault, BLOB_INTENT_COLLECTION)) {
|
|
861
|
+
if (id.startsWith(`${rotated}::`)) out.push({ collection: BLOB_INTENT_COLLECTION, id });
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return out;
|
|
865
|
+
}
|
|
613
866
|
async function rotateKeys(store, vault, callerKeyring, opts) {
|
|
614
867
|
const { collections } = opts;
|
|
868
|
+
if (collections.includes(ROSTER_KEY_ID)) {
|
|
869
|
+
throw new ValidationError(
|
|
870
|
+
`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\`.`
|
|
871
|
+
);
|
|
872
|
+
}
|
|
615
873
|
if (callerKeyring.role === "custodian") {
|
|
616
874
|
throw new PermissionDeniedError(
|
|
617
875
|
"custodian cannot rotate keys (FR-6: re-key is an owner-only meta-capability; use the Deed owner)"
|
|
618
876
|
);
|
|
619
877
|
}
|
|
878
|
+
const callerRosterKey = requireRosterKey(callerKeyring, "rotateKeys");
|
|
620
879
|
const newDeks = /* @__PURE__ */ new Map();
|
|
621
880
|
for (const collName of collections) {
|
|
622
|
-
newDeks.set(collName, await generateDEK());
|
|
881
|
+
newDeks.set(collName, callerKeyring.pendingDeks?.get(collName) ?? await generateDEK());
|
|
623
882
|
}
|
|
883
|
+
const pending = callerKeyring.pendingDeks ?? /* @__PURE__ */ new Map();
|
|
884
|
+
callerKeyring.pendingDeks = pending;
|
|
885
|
+
for (const [collName, newDek] of newDeks) {
|
|
886
|
+
pending.set(collName, newDek);
|
|
887
|
+
}
|
|
888
|
+
await persistKeyring(store, vault, callerKeyring);
|
|
624
889
|
for (const collName of collections) {
|
|
625
890
|
const oldDek = callerKeyring.deks.get(collName);
|
|
626
891
|
const newDek = newDeks.get(collName);
|
|
627
892
|
if (!oldDek) continue;
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
const
|
|
893
|
+
if (collName === BLOB_COLLECTION || collName.startsWith(`${BLOB_COLLECTION}#`)) {
|
|
894
|
+
const others = [];
|
|
895
|
+
for (const [name, dek] of callerKeyring.deks) if (name !== collName) others.push(dek);
|
|
896
|
+
for (const [name, dek] of newDeks) if (name !== collName) others.push(dek);
|
|
897
|
+
await rekeyBlobSet(store, vault, oldDek, newDek, others);
|
|
898
|
+
}
|
|
899
|
+
const refs = [
|
|
900
|
+
...(await store.list(vault, collName)).map((id) => ({ collection: collName, id })),
|
|
901
|
+
...await derivedRefsFor(store, vault, collName)
|
|
902
|
+
];
|
|
903
|
+
for (const ref of refs) {
|
|
904
|
+
const { collection: refColl, id } = ref;
|
|
905
|
+
const envelope = await store.get(vault, refColl, id);
|
|
631
906
|
if (!envelope || !envelope._iv) continue;
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
const newEnvelope = {
|
|
635
|
-
_noydb: NOYDB_FORMAT_VERSION,
|
|
636
|
-
_v: envelope._v,
|
|
637
|
-
_ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
638
|
-
_iv: iv,
|
|
639
|
-
_data: data
|
|
640
|
-
};
|
|
641
|
-
await store.put(vault, collName, id, newEnvelope);
|
|
907
|
+
const newEnvelope = await rekeyEnvelopeIfNeeded({ collection: refColl, id }, envelope, oldDek, newDek);
|
|
908
|
+
if (newEnvelope !== null) await store.put(vault, refColl, id, newEnvelope);
|
|
642
909
|
}
|
|
643
910
|
}
|
|
644
911
|
for (const [collName, newDek] of newDeks) {
|
|
645
912
|
callerKeyring.deks.set(collName, newDek);
|
|
913
|
+
callerKeyring.pendingDeks?.delete(collName);
|
|
646
914
|
}
|
|
647
915
|
await persistKeyring(store, vault, callerKeyring);
|
|
648
916
|
const needsRegrant = [];
|
|
917
|
+
const unverified = [];
|
|
649
918
|
const userIds = await store.list(vault, "_keyring");
|
|
650
919
|
for (const userId of userIds) {
|
|
651
920
|
if (userId === callerKeyring.userId) continue;
|
|
652
921
|
const userFound = await readKeyringFile(store, vault, userId);
|
|
653
922
|
if (!userFound) continue;
|
|
654
923
|
const userKeyringFile = userFound.file;
|
|
924
|
+
try {
|
|
925
|
+
await assertRosterTagValid(userKeyringFile, callerRosterKey, userId);
|
|
926
|
+
} catch (err) {
|
|
927
|
+
if (!(err instanceof KeyringTamperedError)) throw err;
|
|
928
|
+
unverified.push({ userId, reason: err.details.reason });
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
655
931
|
const updatedDeks = { ...userKeyringFile.deks };
|
|
656
932
|
const updatedPermissions = { ...userKeyringFile.permissions };
|
|
657
933
|
for (const collName of collections) {
|
|
@@ -661,17 +937,24 @@ async function rotateKeys(store, vault, callerKeyring, opts) {
|
|
|
661
937
|
delete updatedDeks[collName];
|
|
662
938
|
delete updatedPermissions[collName];
|
|
663
939
|
}
|
|
664
|
-
const
|
|
940
|
+
const edited = {
|
|
665
941
|
...userKeyringFile,
|
|
666
942
|
deks: updatedDeks,
|
|
667
943
|
permissions: updatedPermissions
|
|
668
944
|
};
|
|
945
|
+
const updatedKeyring = {
|
|
946
|
+
...edited,
|
|
947
|
+
roster_tag: await mintRosterTag(edited, callerRosterKey)
|
|
948
|
+
};
|
|
669
949
|
await writeKeyringFile(store, vault, userId, updatedKeyring);
|
|
670
950
|
}
|
|
671
|
-
return { needsRegrant };
|
|
951
|
+
return { needsRegrant, unverified };
|
|
672
952
|
}
|
|
673
953
|
async function changeSecret(store, vault, keyring, opts) {
|
|
674
954
|
const existingFound = await readKeyringFile(store, vault, keyring.userId);
|
|
955
|
+
if (existingFound) {
|
|
956
|
+
await assertRosterTagValid(existingFound.file, requireRosterKey(keyring, "changeSecret"), keyring.userId);
|
|
957
|
+
}
|
|
675
958
|
if (existingFound && existingFound.file.echo !== void 0) throw new EchoCeremonyRequiredError();
|
|
676
959
|
const { newSecret } = opts;
|
|
677
960
|
if (!opts.allowWeakSecret) {
|
|
@@ -683,18 +966,28 @@ async function changeSecret(store, vault, keyring, opts) {
|
|
|
683
966
|
for (const [collName, dek] of keyring.deks) {
|
|
684
967
|
wrappedDeks[collName] = await wrapKey(dek, newKek);
|
|
685
968
|
}
|
|
969
|
+
const rosterKey = requireRosterKey(keyring, "changeSecret");
|
|
686
970
|
const canary = await mintKeyringCanary(newKek);
|
|
687
|
-
const
|
|
688
|
-
|
|
971
|
+
const existingAuthority = existingFound?.file;
|
|
972
|
+
const keptGrantedBy = existingAuthority?.granted_by ?? keyring.userId;
|
|
973
|
+
const authority = {
|
|
689
974
|
user_id: keyring.userId,
|
|
690
975
|
display_name: keyring.displayName,
|
|
691
976
|
role: keyring.role,
|
|
692
977
|
permissions: keyring.permissions,
|
|
978
|
+
granted_by: keptGrantedBy,
|
|
979
|
+
...existingAuthority?.expires_at !== void 0 && { expires_at: existingAuthority.expires_at },
|
|
980
|
+
...existingAuthority?.export_capability !== void 0 && { export_capability: existingAuthority.export_capability },
|
|
981
|
+
...existingAuthority?.import_capability !== void 0 && { import_capability: existingAuthority.import_capability }
|
|
982
|
+
};
|
|
983
|
+
const keyringFile = {
|
|
984
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
985
|
+
...authority,
|
|
693
986
|
deks: wrappedDeks,
|
|
694
987
|
salt: bufferToBase64(newSalt),
|
|
695
988
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
696
|
-
|
|
697
|
-
|
|
989
|
+
canary,
|
|
990
|
+
roster_tag: await mintRosterTag(authority, rosterKey)
|
|
698
991
|
};
|
|
699
992
|
await writeKeyringFile(store, vault, keyring.userId, keyringFile);
|
|
700
993
|
return {
|
|
@@ -721,6 +1014,7 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
|
|
|
721
1014
|
"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."
|
|
722
1015
|
);
|
|
723
1016
|
}
|
|
1017
|
+
const rosterKey = requireRosterKey(callerKeyring, "buildRecipientKeyringFile");
|
|
724
1018
|
const role = recipient.role ?? "viewer";
|
|
725
1019
|
const permissions = resolvePermissions(role, recipient.permissions);
|
|
726
1020
|
const newSalt = generateSalt();
|
|
@@ -750,20 +1044,26 @@ async function buildRecipientKeyringFile(callerKeyring, recipient) {
|
|
|
750
1044
|
}
|
|
751
1045
|
}
|
|
752
1046
|
const canary = await mintKeyringCanary(newKek);
|
|
753
|
-
|
|
754
|
-
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1047
|
+
const authority = {
|
|
755
1048
|
user_id: recipient.id,
|
|
756
1049
|
display_name: recipient.displayName ?? recipient.id,
|
|
757
1050
|
role,
|
|
758
1051
|
permissions,
|
|
1052
|
+
granted_by: callerKeyring.userId,
|
|
1053
|
+
...recipient.exportCapability !== void 0 ? { export_capability: recipient.exportCapability } : {},
|
|
1054
|
+
...recipient.importCapability !== void 0 ? { import_capability: recipient.importCapability } : {},
|
|
1055
|
+
...recipient.expiresAt !== void 0 ? { expires_at: recipient.expiresAt } : {}
|
|
1056
|
+
};
|
|
1057
|
+
return {
|
|
1058
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1059
|
+
...authority,
|
|
759
1060
|
deks: wrappedDeks,
|
|
760
1061
|
salt: bufferToBase64(newSalt),
|
|
761
1062
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
762
|
-
granted_by: callerKeyring.userId,
|
|
763
1063
|
canary,
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1064
|
+
// The recipient's copy of the roster key rode in via the `_`-prefix
|
|
1065
|
+
// propagation loop above, so the slot verifies on first unlock.
|
|
1066
|
+
roster_tag: await mintRosterTag(authority, rosterKey),
|
|
767
1067
|
// The presence of this block is what makes the recipient's slot an
|
|
768
1068
|
// echo keyring — mirrors `createOwnerKeyring`'s same dance so the
|
|
769
1069
|
// ceremony (prompt → echo → key) travels with the pod, not just a
|
|
@@ -817,6 +1117,23 @@ async function listUsersWithEnvelopes(store, vault, userEnvelopeDek, callerRole,
|
|
|
817
1117
|
}
|
|
818
1118
|
return out;
|
|
819
1119
|
}
|
|
1120
|
+
async function collectionHasRecords(store, vault, collectionName) {
|
|
1121
|
+
const ids = await store.list(vault, collectionName);
|
|
1122
|
+
return ids.length > 0;
|
|
1123
|
+
}
|
|
1124
|
+
async function reloadPersistedDEK(store, vault, keyring, collectionName) {
|
|
1125
|
+
if (!keyring.kek) return null;
|
|
1126
|
+
const found = await readKeyringFile(store, vault, keyring.userId);
|
|
1127
|
+
const wrapped = found?.file.deks[collectionName];
|
|
1128
|
+
if (wrapped === void 0) return null;
|
|
1129
|
+
try {
|
|
1130
|
+
const dek = await unwrapKey(wrapped, keyring.kek);
|
|
1131
|
+
keyring.deks.set(collectionName, dek);
|
|
1132
|
+
return dek;
|
|
1133
|
+
} catch {
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
820
1137
|
async function ensureCollectionDEK(store, vault, keyring) {
|
|
821
1138
|
const inFlight = /* @__PURE__ */ new Map();
|
|
822
1139
|
return async (collectionName) => {
|
|
@@ -825,6 +1142,20 @@ async function ensureCollectionDEK(store, vault, keyring) {
|
|
|
825
1142
|
const pending = inFlight.get(collectionName);
|
|
826
1143
|
if (pending) return pending;
|
|
827
1144
|
const promise = (async () => {
|
|
1145
|
+
if (!collectionName.startsWith("_")) {
|
|
1146
|
+
if (!hasAccess(keyring, collectionName)) {
|
|
1147
|
+
throw new NoAccessError(
|
|
1148
|
+
`No access \u2014 user does not have a key for collection "${collectionName}". Grant them this collection in \`permissions\` to give them one.`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
if (await collectionHasRecords(store, vault, collectionName)) {
|
|
1152
|
+
const refreshed = await reloadPersistedDEK(store, vault, keyring, collectionName);
|
|
1153
|
+
if (refreshed) return refreshed;
|
|
1154
|
+
throw new NoAccessError(
|
|
1155
|
+
`No access \u2014 user "${keyring.userId}" is entitled to collection "${collectionName}" but holds no key for it, because the collection was created AFTER their grant. A collection DEK can only be wrapped at grant time (wrapping needs the grantee's secret, which the vault never stores), so this cannot be back-filled \u2014 re-grant the user to give them the key.`
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
828
1159
|
const dek = await generateDEK();
|
|
829
1160
|
keyring.deks.set(collectionName, dek);
|
|
830
1161
|
await persistKeyring(store, vault, keyring);
|
|
@@ -838,14 +1169,20 @@ async function ensureCollectionDEK(store, vault, keyring) {
|
|
|
838
1169
|
}
|
|
839
1170
|
};
|
|
840
1171
|
}
|
|
1172
|
+
var PERMISSION_WILDCARD = "*";
|
|
1173
|
+
function permissionsAreWildcard(permissions) {
|
|
1174
|
+
return permissions !== void 0 && PERMISSION_WILDCARD in permissions;
|
|
1175
|
+
}
|
|
841
1176
|
function hasWritePermission(keyring, collectionName) {
|
|
842
1177
|
if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian") return true;
|
|
843
1178
|
if (keyring.role === "viewer" || keyring.role === "client") return false;
|
|
1179
|
+
if (keyring.permissions[PERMISSION_WILDCARD] === "rw") return true;
|
|
844
1180
|
return keyring.permissions[collectionName] === "rw";
|
|
845
1181
|
}
|
|
846
1182
|
function hasAccess(keyring, collectionName) {
|
|
847
1183
|
if (keyring.role === "owner" || keyring.role === "admin" || keyring.role === "custodian" || keyring.role === "viewer")
|
|
848
1184
|
return true;
|
|
1185
|
+
if (PERMISSION_WILDCARD in keyring.permissions) return true;
|
|
849
1186
|
return collectionName in keyring.permissions;
|
|
850
1187
|
}
|
|
851
1188
|
async function persistKeyring(store, vault, keyring) {
|
|
@@ -854,26 +1191,41 @@ async function persistKeyring(store, vault, keyring) {
|
|
|
854
1191
|
"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."
|
|
855
1192
|
);
|
|
856
1193
|
}
|
|
1194
|
+
const rosterKey = requireRosterKey(keyring, "persistKeyring");
|
|
857
1195
|
const existingFound = await readKeyringFile(store, vault, keyring.userId);
|
|
1196
|
+
if (existingFound) await assertRosterTagValid(existingFound.file, rosterKey, keyring.userId);
|
|
858
1197
|
const existingEcho = existingFound?.file.echo;
|
|
1198
|
+
const existingGrantedBy = existingFound?.file.granted_by;
|
|
1199
|
+
const existingCreatedAt = existingFound?.file.created_at;
|
|
1200
|
+
const existingExpiresAt = existingFound?.file.expires_at;
|
|
859
1201
|
const wrappedDeks = {};
|
|
860
1202
|
for (const [collName, dek] of keyring.deks) {
|
|
861
1203
|
wrappedDeks[collName] = await wrapKey(dek, keyring.kek);
|
|
862
1204
|
}
|
|
1205
|
+
const wrappedPending = {};
|
|
1206
|
+
for (const [collName, dek] of keyring.pendingDeks ?? []) {
|
|
1207
|
+
wrappedPending[collName] = await wrapKey(dek, keyring.kek);
|
|
1208
|
+
}
|
|
863
1209
|
const canary = await mintKeyringCanary(keyring.kek);
|
|
864
|
-
const
|
|
865
|
-
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1210
|
+
const authority = {
|
|
866
1211
|
user_id: keyring.userId,
|
|
867
1212
|
display_name: keyring.displayName,
|
|
868
1213
|
role: keyring.role,
|
|
869
1214
|
permissions: keyring.permissions,
|
|
1215
|
+
granted_by: existingGrantedBy ?? keyring.userId,
|
|
1216
|
+
...existingExpiresAt !== void 0 && { expires_at: existingExpiresAt },
|
|
1217
|
+
...keyring.exportCapability !== void 0 && { export_capability: keyring.exportCapability },
|
|
1218
|
+
...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability }
|
|
1219
|
+
};
|
|
1220
|
+
const keyringFile = {
|
|
1221
|
+
_noydb_keyring: NOYDB_KEYRING_VERSION,
|
|
1222
|
+
...authority,
|
|
870
1223
|
deks: wrappedDeks,
|
|
1224
|
+
...Object.keys(wrappedPending).length > 0 ? { pending_deks: wrappedPending } : {},
|
|
871
1225
|
salt: bufferToBase64(keyring.salt),
|
|
872
|
-
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
873
|
-
granted_by: keyring.userId,
|
|
1226
|
+
created_at: existingCreatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
874
1227
|
canary,
|
|
875
|
-
|
|
876
|
-
...keyring.importCapability !== void 0 && { import_capability: keyring.importCapability },
|
|
1228
|
+
roster_tag: await mintRosterTag(authority, rosterKey),
|
|
877
1229
|
...keyring.authenticators.length > 0 && { authenticators: keyring.authenticators },
|
|
878
1230
|
...keyring.policy !== void 0 && { policy: keyring.policy },
|
|
879
1231
|
...existingEcho !== void 0 && { echo: existingEcho }
|
|
@@ -918,17 +1270,17 @@ function resolvePermissions(role, explicit) {
|
|
|
918
1270
|
return explicit ?? {};
|
|
919
1271
|
}
|
|
920
1272
|
async function writeKeyringFile(store, vault, userId, keyringFile) {
|
|
921
|
-
const envelope =
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
_iv: "",
|
|
926
|
-
_data: JSON.stringify(keyringFile)
|
|
927
|
-
};
|
|
1273
|
+
const envelope = buildRecordEnvelope(
|
|
1274
|
+
{ collection: "_keyring", id: userId, version: 1 },
|
|
1275
|
+
{ iv: "", data: JSON.stringify(keyringFile) }
|
|
1276
|
+
);
|
|
928
1277
|
await store.put(vault, "_keyring", userId, envelope);
|
|
929
1278
|
}
|
|
930
1279
|
|
|
931
1280
|
export {
|
|
1281
|
+
mintRosterTag,
|
|
1282
|
+
assertRosterAuthenticated,
|
|
1283
|
+
assertRosterTagValid,
|
|
932
1284
|
WeakSecretError,
|
|
933
1285
|
validateSecret,
|
|
934
1286
|
assertStrongSecret,
|
|
@@ -945,6 +1297,7 @@ export {
|
|
|
945
1297
|
BROKER_COLLECTION,
|
|
946
1298
|
isSecretBearingReservedCollection,
|
|
947
1299
|
mintKeyringCanary,
|
|
1300
|
+
requireRosterKey,
|
|
948
1301
|
deriveKekForKeyring,
|
|
949
1302
|
readKeyringFile,
|
|
950
1303
|
assertKeyringNotExpired,
|
|
@@ -953,6 +1306,8 @@ export {
|
|
|
953
1306
|
createOwnerKeyring,
|
|
954
1307
|
grant,
|
|
955
1308
|
revoke,
|
|
1309
|
+
verifyRoster,
|
|
1310
|
+
quarantineKeyring,
|
|
956
1311
|
updateKeyringIdentity,
|
|
957
1312
|
rotateKeys,
|
|
958
1313
|
changeSecret,
|
|
@@ -968,4 +1323,4 @@ export {
|
|
|
968
1323
|
hasImportCapability,
|
|
969
1324
|
evaluateImportCapability
|
|
970
1325
|
};
|
|
971
|
-
//# sourceMappingURL=chunk-
|
|
1326
|
+
//# sourceMappingURL=chunk-CAGI4LLJ.js.map
|