@noy-db/hub 0.4.0-pre.3 → 0.4.0-pre.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/aggregate/index.js +4 -4
- package/dist/api-22ES33N3.js +20 -0
- package/dist/as/index.js +11 -11
- package/dist/attestation/index.js +20 -20
- package/dist/{backup-JL2GOTYQ.js → backup-YCHAC575.js} +11 -11
- package/dist/blobs/index.js +12 -12
- package/dist/broker/index.js +12 -12
- package/dist/by/index.js +2 -2
- package/dist/cargo/index.js +29 -29
- package/dist/{chunk-2RMDVWGB.js → chunk-2CRZOVDI.js} +2 -2
- package/dist/{chunk-T7S4V52L.js → chunk-2PDBYF2O.js} +4 -4
- package/dist/{chunk-WJDYZP3U.js → chunk-3LJYHE7S.js} +36 -36
- package/dist/chunk-3LJYHE7S.js.map +1 -0
- package/dist/{chunk-CCT4WZI6.js → chunk-3OWYOECI.js} +5 -5
- package/dist/chunk-3OWYOECI.js.map +1 -0
- package/dist/{chunk-L4OA7G5T.js → chunk-522MBPE5.js} +2 -2
- package/dist/{chunk-ZJGZDOQO.js → chunk-5ATANZSW.js} +6 -6
- package/dist/{chunk-M25ZIEB2.js → chunk-5HSSTOOG.js} +2 -2
- package/dist/{chunk-LLVW4V7R.js → chunk-5WLDZZ6S.js} +3 -3
- package/dist/{chunk-7B4GPCX7.js → chunk-6RPWILRH.js} +5 -5
- package/dist/{chunk-IGM563N5.js → chunk-6SYOHUHM.js} +6 -6
- package/dist/chunk-6SYOHUHM.js.map +1 -0
- package/dist/{chunk-HGVFJL63.js → chunk-7BIJS6II.js} +15 -5
- package/dist/{chunk-HGVFJL63.js.map → chunk-7BIJS6II.js.map} +1 -1
- package/dist/{chunk-KR5EGWI4.js → chunk-7ICV4ASA.js} +5 -5
- package/dist/{chunk-J3VW4J4E.js → chunk-7LP7FFJ2.js} +2 -2
- package/dist/chunk-7LP7FFJ2.js.map +1 -0
- package/dist/{chunk-5P5AAXYA.js → chunk-7U6AB7DZ.js} +7 -7
- package/dist/chunk-7U6AB7DZ.js.map +1 -0
- package/dist/{chunk-KMONEHDJ.js → chunk-AFAPBZAA.js} +2 -2
- package/dist/{chunk-GBEXTC6Z.js → chunk-BAAUFCJK.js} +3 -3
- package/dist/chunk-BAAUFCJK.js.map +1 -0
- package/dist/{chunk-CGPO3ZYB.js → chunk-BY5YBSXA.js} +2 -2
- package/dist/{chunk-3O62PVDW.js → chunk-CJVEAVYH.js} +5 -5
- package/dist/chunk-CJVEAVYH.js.map +1 -0
- package/dist/{chunk-3XVOLB75.js → chunk-CKUYQO2P.js} +3 -3
- package/dist/{chunk-2JZ763FV.js → chunk-D4LAJ737.js} +2 -2
- package/dist/{chunk-OOTTXUNV.js → chunk-DYZ5CCVM.js} +2 -2
- package/dist/{chunk-LUH2MPEF.js → chunk-EEZCB74X.js} +808 -1175
- package/dist/chunk-EEZCB74X.js.map +1 -0
- package/dist/{chunk-N4FZ6ITM.js → chunk-EGU4PWQW.js} +2 -2
- package/dist/{chunk-JKWNG3TM.js → chunk-FHHYCBBP.js} +2 -2
- package/dist/{chunk-UBF7HTOR.js → chunk-FIP7KI2P.js} +40 -40
- package/dist/chunk-FIP7KI2P.js.map +1 -0
- package/dist/{chunk-N4A3DCBT.js → chunk-FTCGEUOY.js} +8 -8
- package/dist/{chunk-5BB3N4KU.js → chunk-G4MFQXYX.js} +2 -2
- package/dist/{chunk-PEY6ZTCF.js → chunk-GEEJPID6.js} +9 -9
- package/dist/{chunk-PEY6ZTCF.js.map → chunk-GEEJPID6.js.map} +1 -1
- package/dist/{chunk-KVHWEPT2.js → chunk-GHEMAVTN.js} +2 -2
- package/dist/{chunk-OVFCAS7U.js → chunk-GP54LU5N.js} +9 -9
- package/dist/{chunk-XW7NWO4A.js → chunk-GTNRMJYH.js} +2 -2
- package/dist/{chunk-J6U4BOKP.js → chunk-HAPY7WFP.js} +12 -12
- package/dist/{chunk-GU5AYA2Q.js → chunk-HQKRZIX6.js} +5 -5
- package/dist/{chunk-OWE4BNTM.js → chunk-I2XEBEEF.js} +4 -4
- package/dist/{chunk-WEC3JGBA.js → chunk-IGLU4ME7.js} +3 -3
- package/dist/{chunk-BZK6JEX4.js → chunk-ILM6X6CP.js} +5 -5
- package/dist/{chunk-2KL6NDD4.js → chunk-J5TEU77Q.js} +4 -4
- package/dist/{chunk-UEO5CHOP.js → chunk-JAMDDLIM.js} +13 -13
- package/dist/chunk-JAMDDLIM.js.map +1 -0
- package/dist/{chunk-V7RGFJ3Z.js → chunk-JIHH26WQ.js} +6 -6
- package/dist/{chunk-XGVDLQJE.js → chunk-JN3SGXJE.js} +3 -3
- package/dist/{chunk-XULHFSHO.js → chunk-K4BWPNJO.js} +5 -5
- package/dist/chunk-K4BWPNJO.js.map +1 -0
- package/dist/{chunk-BZEPJJOK.js → chunk-K7BADAOS.js} +4 -4
- package/dist/{chunk-OWLPX2GI.js → chunk-KJTIAECI.js} +17 -17
- package/dist/chunk-KJTIAECI.js.map +1 -0
- package/dist/{chunk-SN75YVM5.js → chunk-L2CAWCQL.js} +18 -18
- package/dist/{chunk-SN75YVM5.js.map → chunk-L2CAWCQL.js.map} +1 -1
- package/dist/{chunk-MSO2C3KG.js → chunk-LC7DSMHR.js} +2 -2
- package/dist/{chunk-JA5B3SLH.js → chunk-M6QA2KK2.js} +2 -2
- package/dist/{chunk-IUIVDVXM.js → chunk-MUPZ6IZX.js} +2 -2
- package/dist/{chunk-QAFWRGVD.js → chunk-OGVE4IJW.js} +7 -7
- package/dist/{chunk-BCOKZC4B.js → chunk-OHV6T5JB.js} +19 -19
- package/dist/chunk-OHV6T5JB.js.map +1 -0
- package/dist/{chunk-25ZDB25Z.js → chunk-OMSKGLYA.js} +3 -3
- package/dist/chunk-OMSKGLYA.js.map +1 -0
- package/dist/{chunk-2U7Y7BNJ.js → chunk-ONB627HU.js} +2 -2
- package/dist/{chunk-GYB2QK5H.js → chunk-OWLZONGX.js} +2 -2
- package/dist/{chunk-YHPUDB7Q.js → chunk-OWOT7CFC.js} +2 -2
- package/dist/{chunk-VEX6G3YL.js → chunk-P4NUJVWL.js} +2 -2
- package/dist/{chunk-NM4LVUXH.js → chunk-PTT2NB46.js} +2 -2
- package/dist/{chunk-2ITLQZV5.js → chunk-PWWWMRT7.js} +2 -2
- package/dist/{chunk-GS3Q2KOZ.js → chunk-QBOQI6PG.js} +2 -2
- package/dist/{chunk-JIMEB4C6.js → chunk-QMMOMBH6.js} +4 -4
- package/dist/{chunk-QDNVHA2F.js → chunk-QMZ5A6FQ.js} +2 -2
- package/dist/{chunk-FLHOTGOO.js → chunk-QVGEVZ5F.js} +3 -3
- package/dist/{chunk-AFBJHD55.js → chunk-QZJEYZ4I.js} +5 -5
- package/dist/{chunk-V3YB7RGR.js → chunk-R2N37AA6.js} +3 -3
- package/dist/{chunk-JLHKBAFY.js → chunk-RWW3I3PO.js} +3 -3
- package/dist/{chunk-IGAIOGCN.js → chunk-S4YERMGJ.js} +4 -4
- package/dist/{chunk-OGFPMCSP.js → chunk-SBOHNJUD.js} +2 -2
- package/dist/{chunk-QXPOEAKR.js → chunk-SC4JLMZI.js} +2 -2
- package/dist/{chunk-EDC6MLXV.js → chunk-SEU6QHH4.js} +2 -2
- package/dist/{chunk-F4COUZLL.js → chunk-TMR64XPL.js} +2 -2
- package/dist/{chunk-DEQ2JTBR.js → chunk-TNATF3HB.js} +3 -3
- package/dist/{chunk-GZ2GKOF6.js → chunk-TNJVDG2B.js} +3 -3
- package/dist/{chunk-5OZCCLNC.js → chunk-TSNRZSRC.js} +2 -2
- package/dist/{chunk-QCUH2G77.js → chunk-UTEATWHO.js} +6 -6
- package/dist/{chunk-JA5O7UEZ.js → chunk-UVBHM7AK.js} +6 -6
- package/dist/chunk-UVBHM7AK.js.map +1 -0
- package/dist/{chunk-HKR25MGX.js → chunk-V5JUAW4M.js} +10 -10
- package/dist/chunk-V5JUAW4M.js.map +1 -0
- package/dist/{chunk-YGBIBOCR.js → chunk-VEGQYPCP.js} +7 -7
- package/dist/{chunk-25G7U6RD.js → chunk-WQN5SM7P.js} +2 -2
- package/dist/{chunk-NOT7XA7Q.js → chunk-X3MWEFPT.js} +5 -5
- package/dist/chunk-XUFAFHQM.js +78 -0
- package/dist/chunk-XUFAFHQM.js.map +1 -0
- package/dist/{chunk-MEKAMERX.js → chunk-YCAJDK4O.js} +29 -29
- package/dist/chunk-YCAJDK4O.js.map +1 -0
- package/dist/{chunk-BVZGNMVM.js → chunk-YFFEF6HR.js} +3 -3
- package/dist/{chunk-H4SMS4O2.js → chunk-YJ3TREAC.js} +2 -2
- package/dist/{chunk-5SZ24DL5.js → chunk-YQQPNXNO.js} +6 -6
- package/dist/{chunk-5BHNQNBI.js → chunk-YTW6ZIPD.js} +1 -1
- package/dist/chunk-YTW6ZIPD.js.map +1 -0
- package/dist/{chunk-EW4HFWGJ.js → chunk-YWZEDG7F.js} +6 -6
- package/dist/{chunk-2VUISNGP.js → chunk-YXIVUWDU.js} +5 -5
- package/dist/{chunk-YG2EG6CB.js → chunk-YXZNGHID.js} +4 -4
- package/dist/{chunk-QYJIOWKN.js → chunk-YZN6NRTR.js} +9 -9
- package/dist/{chunk-IZX3WZBF.js → chunk-YZNKFYDC.js} +4 -4
- package/dist/{chunk-E2R5NRZD.js → chunk-Z4D3YGAB.js} +2 -2
- package/dist/{chunk-STR7WBOB.js → chunk-ZUDAK436.js} +2 -2
- package/dist/{chunk-ITI3SJRB.js → chunk-ZX5FNWH7.js} +2 -2
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-7S5IEP6Q.js → classified-marker-52P3D72W.js} +3 -3
- package/dist/collection-facade-XKCFHSLR.js +44 -0
- package/dist/{computed-WZNKRLCY.js → computed-33DRJDOC.js} +3 -3
- package/dist/consent/index.js +9 -9
- package/dist/{cover-B5MY43CK.js → cover-5BSNJREH.js} +4 -4
- package/dist/{dead-filter-KRRD57MR.js → dead-filter-JXVCKS6W.js} +2 -2
- package/dist/delegation-WWZPEDK2.js +25 -0
- package/dist/derivations/index.js +12 -12
- package/dist/derive-CWWFEQ4N.js +21 -0
- package/dist/dispatch-PKQQFDBT.js +193 -0
- package/dist/dispatch-PKQQFDBT.js.map +1 -0
- package/dist/dispatch-SMXUO7W5.js +72 -0
- package/dist/dispatch-SMXUO7W5.js.map +1 -0
- package/dist/{enclave-YFKA3GJT.js → enclave-I5H3GINP.js} +11 -11
- package/dist/executor-FCJEH3FU.js +9 -0
- package/dist/executor-OHET7TF7.js +9 -0
- package/dist/executor-SHYLIBOM.js +26 -0
- package/dist/export-accessible-FBL6TDSK.js +23 -0
- package/dist/extract-partition-ZYIRYMTI.js +38 -0
- package/dist/{fanout-sidecar-HJV3A7PM.js → fanout-sidecar-WWXVHHUH.js} +9 -9
- package/dist/find-LEBUXVL7.js +11 -0
- package/dist/forget/index.js +9 -9
- package/dist/guards/index.js +3 -3
- package/dist/history/index.js +12 -12
- package/dist/i18n/index.js +14 -14
- package/dist/index.d.ts +13 -9
- package/dist/index.js +119 -118
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +2 -2
- package/dist/issue-F76PAGIR.js +19 -0
- package/dist/kernel/collection-config.d.ts +11 -69
- package/dist/kernel/collection.d.ts +10 -22
- package/dist/kernel/enclave/crypto.d.ts +8 -8
- package/dist/kernel/enclave/index.d.ts +2 -2
- package/dist/kernel/errors.d.ts +20 -20
- package/dist/kernel/noydb.d.ts +43 -103
- package/dist/kernel/types.d.ts +82 -41
- package/dist/kernel/validation.d.ts +33 -33
- package/dist/kernel/vault.d.ts +31 -215
- package/dist/kernel/via/dispatch.d.ts +7 -0
- package/dist/{ledger-SW6QQOAV.js → ledger-M2BKCNVS.js} +11 -11
- package/dist/liberate-ZP7UB57K.js +24 -0
- package/dist/{link-set-GNGGCFI4.js → link-set-ZU4E5J5S.js} +11 -11
- package/dist/materialized-views/index.js +19 -19
- package/dist/noydb-BNDGEK5X.js +71 -0
- package/dist/on/index.js +9 -9
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +13 -13
- package/dist/pod/index.js +11 -11
- package/dist/{policy-EAXSJNOT.js → policy-IY2YEA3A.js} +5 -5
- package/dist/port/at/index.d.ts +4 -4
- package/dist/port/with/archive-strategy.d.ts +31 -0
- package/dist/port/with/collection-options.d.ts +171 -0
- package/dist/port/with/strategies.d.ts +134 -0
- package/dist/port/with/team-strategy.d.ts +1 -1
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-A7HCFBP5.js → post-register-XIAHB6KF.js} +6 -6
- package/dist/query/index.js +6 -6
- package/dist/register-HB4TQZSV.js +23 -0
- package/dist/registry-6BMBFEXT.js +19 -0
- package/dist/registry-7CZWF3EQ.js +9 -0
- package/dist/registry-JOFO7RAH.js +18 -0
- package/dist/request-withdrawal-UN3TRHWI.js +31 -0
- package/dist/{reveal-PB3YG544.js → reveal-VWXRV2XU.js} +6 -6
- package/dist/revoke-OLO2OVR7.js +24 -0
- package/dist/satellites/index.js +1 -1
- package/dist/sealed-record/index.js +12 -12
- package/dist/{seed-EKF4DIBB.js → seed-CDBFOTIK.js} +12 -12
- package/dist/seed-CDBFOTIK.js.map +1 -0
- package/dist/session/index.js +9 -9
- package/dist/shadow/index.js +2 -2
- package/dist/signer-MGGKPYAN.js +25 -0
- package/dist/snapshots/index.js +10 -10
- package/dist/{stale-4UNPHWOU.js → stale-NBROTA66.js} +10 -10
- package/dist/storage-3R37QZLA.js +23 -0
- package/dist/{store-coordination-provider-3AELCZ4Y.js → store-coordination-provider-P3IU5JHD.js} +3 -3
- package/dist/sync/index.js +10 -10
- package/dist/team/index.js +20 -20
- package/dist/tiers/index.js +10 -10
- package/dist/to/index.js +1 -1
- package/dist/tx/index.js +3 -3
- package/dist/util/index.js +1 -1
- package/dist/{verify-MXN66IIH.js → verify-5X37OXRY.js} +5 -5
- package/dist/{walk-H4YE52S2.js → walk-E3OVLBFR.js} +11 -11
- package/dist/with/index.js +11 -11
- package/dist/with-audit/portability/export-accessible.d.ts +4 -4
- package/dist/with-audit/portability/request-withdrawal.d.ts +3 -3
- package/dist/with-audit/portability/withdraw-accessible.d.ts +2 -2
- package/dist/with-audit/sealed-record/strategy.d.ts +1 -1
- package/dist/with-cargo/adopt-partition.d.ts +7 -7
- package/dist/with-cargo/index.d.ts +1 -1
- package/dist/with-commit/history/time-machine.d.ts +2 -2
- package/dist/with-formula/derivations/dispatch.d.ts +98 -0
- package/dist/with-formula/materialized-views/dispatch.d.ts +75 -0
- package/dist/with-party/custody/liberate.d.ts +3 -3
- package/dist/with-party/session/dev-unlock.d.ts +2 -2
- package/dist/with-party/session/session.d.ts +2 -2
- package/dist/with-party/team/deed.d.ts +8 -8
- package/dist/with-party/team/index.d.ts +2 -2
- package/dist/with-party/team/keyring.d.ts +19 -19
- package/dist/with-party/team/{managed-passphrase.d.ts → managed-secret.d.ts} +27 -27
- package/dist/with-party/team/noydb-facade.d.ts +41 -41
- package/dist/with-party/team/peer-recover.d.ts +13 -13
- package/dist/with-party/team/recovery.d.ts +5 -5
- package/dist/with-party/team/rotate-recover.d.ts +30 -30
- package/dist/with-pod/bundle.d.ts +30 -30
- package/dist/with-pod/format.d.ts +2 -2
- package/dist/withdraw-accessible-Q2EMATCU.js +28 -0
- package/package.json +3 -3
- package/dist/api-UPAT2O3M.js +0 -20
- package/dist/chunk-25ZDB25Z.js.map +0 -1
- package/dist/chunk-3O62PVDW.js.map +0 -1
- package/dist/chunk-5BHNQNBI.js.map +0 -1
- package/dist/chunk-5P5AAXYA.js.map +0 -1
- package/dist/chunk-BCOKZC4B.js.map +0 -1
- package/dist/chunk-CCT4WZI6.js.map +0 -1
- package/dist/chunk-GBEXTC6Z.js.map +0 -1
- package/dist/chunk-HKR25MGX.js.map +0 -1
- package/dist/chunk-IGM563N5.js.map +0 -1
- package/dist/chunk-J3VW4J4E.js.map +0 -1
- package/dist/chunk-JA5O7UEZ.js.map +0 -1
- package/dist/chunk-LUH2MPEF.js.map +0 -1
- package/dist/chunk-MEKAMERX.js.map +0 -1
- package/dist/chunk-OWLPX2GI.js.map +0 -1
- package/dist/chunk-UBF7HTOR.js.map +0 -1
- package/dist/chunk-UEO5CHOP.js.map +0 -1
- package/dist/chunk-WJDYZP3U.js.map +0 -1
- package/dist/chunk-XULHFSHO.js.map +0 -1
- package/dist/collection-facade-ENLTJZBT.js +0 -44
- package/dist/delegation-UT76AQIG.js +0 -25
- package/dist/derive-SGRCCKVP.js +0 -21
- package/dist/executor-TEZYRDN3.js +0 -9
- package/dist/executor-WQQRU6DO.js +0 -9
- package/dist/executor-YORMIBWV.js +0 -26
- package/dist/export-accessible-HXMKW5LJ.js +0 -23
- package/dist/extract-partition-QGQ2R42J.js +0 -38
- package/dist/find-VV6PBIHK.js +0 -11
- package/dist/issue-7JJ4Z7E2.js +0 -19
- package/dist/liberate-FK7JI2QX.js +0 -24
- package/dist/noydb-KUX2TJMX.js +0 -70
- package/dist/register-YBMRLZLO.js +0 -23
- package/dist/registry-3LXKNXWI.js +0 -9
- package/dist/registry-4V4YQCXJ.js +0 -19
- package/dist/registry-ZULMLCJK.js +0 -18
- package/dist/request-withdrawal-SEVUEUDG.js +0 -31
- package/dist/revoke-5QULL5GT.js +0 -24
- package/dist/seed-EKF4DIBB.js.map +0 -1
- package/dist/signer-ZMH35FT4.js +0 -25
- package/dist/storage-QOZKQMID.js +0 -23
- package/dist/withdraw-accessible-HPOVNG2G.js +0 -28
- /package/dist/{api-UPAT2O3M.js.map → api-22ES33N3.js.map} +0 -0
- /package/dist/{backup-JL2GOTYQ.js.map → backup-YCHAC575.js.map} +0 -0
- /package/dist/{chunk-2RMDVWGB.js.map → chunk-2CRZOVDI.js.map} +0 -0
- /package/dist/{chunk-T7S4V52L.js.map → chunk-2PDBYF2O.js.map} +0 -0
- /package/dist/{chunk-L4OA7G5T.js.map → chunk-522MBPE5.js.map} +0 -0
- /package/dist/{chunk-ZJGZDOQO.js.map → chunk-5ATANZSW.js.map} +0 -0
- /package/dist/{chunk-M25ZIEB2.js.map → chunk-5HSSTOOG.js.map} +0 -0
- /package/dist/{chunk-LLVW4V7R.js.map → chunk-5WLDZZ6S.js.map} +0 -0
- /package/dist/{chunk-7B4GPCX7.js.map → chunk-6RPWILRH.js.map} +0 -0
- /package/dist/{chunk-KR5EGWI4.js.map → chunk-7ICV4ASA.js.map} +0 -0
- /package/dist/{chunk-KMONEHDJ.js.map → chunk-AFAPBZAA.js.map} +0 -0
- /package/dist/{chunk-CGPO3ZYB.js.map → chunk-BY5YBSXA.js.map} +0 -0
- /package/dist/{chunk-3XVOLB75.js.map → chunk-CKUYQO2P.js.map} +0 -0
- /package/dist/{chunk-2JZ763FV.js.map → chunk-D4LAJ737.js.map} +0 -0
- /package/dist/{chunk-OOTTXUNV.js.map → chunk-DYZ5CCVM.js.map} +0 -0
- /package/dist/{chunk-N4FZ6ITM.js.map → chunk-EGU4PWQW.js.map} +0 -0
- /package/dist/{chunk-JKWNG3TM.js.map → chunk-FHHYCBBP.js.map} +0 -0
- /package/dist/{chunk-N4A3DCBT.js.map → chunk-FTCGEUOY.js.map} +0 -0
- /package/dist/{chunk-5BB3N4KU.js.map → chunk-G4MFQXYX.js.map} +0 -0
- /package/dist/{chunk-KVHWEPT2.js.map → chunk-GHEMAVTN.js.map} +0 -0
- /package/dist/{chunk-OVFCAS7U.js.map → chunk-GP54LU5N.js.map} +0 -0
- /package/dist/{chunk-XW7NWO4A.js.map → chunk-GTNRMJYH.js.map} +0 -0
- /package/dist/{chunk-J6U4BOKP.js.map → chunk-HAPY7WFP.js.map} +0 -0
- /package/dist/{chunk-GU5AYA2Q.js.map → chunk-HQKRZIX6.js.map} +0 -0
- /package/dist/{chunk-OWE4BNTM.js.map → chunk-I2XEBEEF.js.map} +0 -0
- /package/dist/{chunk-WEC3JGBA.js.map → chunk-IGLU4ME7.js.map} +0 -0
- /package/dist/{chunk-BZK6JEX4.js.map → chunk-ILM6X6CP.js.map} +0 -0
- /package/dist/{chunk-2KL6NDD4.js.map → chunk-J5TEU77Q.js.map} +0 -0
- /package/dist/{chunk-V7RGFJ3Z.js.map → chunk-JIHH26WQ.js.map} +0 -0
- /package/dist/{chunk-XGVDLQJE.js.map → chunk-JN3SGXJE.js.map} +0 -0
- /package/dist/{chunk-BZEPJJOK.js.map → chunk-K7BADAOS.js.map} +0 -0
- /package/dist/{chunk-MSO2C3KG.js.map → chunk-LC7DSMHR.js.map} +0 -0
- /package/dist/{chunk-JA5B3SLH.js.map → chunk-M6QA2KK2.js.map} +0 -0
- /package/dist/{chunk-IUIVDVXM.js.map → chunk-MUPZ6IZX.js.map} +0 -0
- /package/dist/{chunk-QAFWRGVD.js.map → chunk-OGVE4IJW.js.map} +0 -0
- /package/dist/{chunk-2U7Y7BNJ.js.map → chunk-ONB627HU.js.map} +0 -0
- /package/dist/{chunk-GYB2QK5H.js.map → chunk-OWLZONGX.js.map} +0 -0
- /package/dist/{chunk-YHPUDB7Q.js.map → chunk-OWOT7CFC.js.map} +0 -0
- /package/dist/{chunk-VEX6G3YL.js.map → chunk-P4NUJVWL.js.map} +0 -0
- /package/dist/{chunk-NM4LVUXH.js.map → chunk-PTT2NB46.js.map} +0 -0
- /package/dist/{chunk-2ITLQZV5.js.map → chunk-PWWWMRT7.js.map} +0 -0
- /package/dist/{chunk-GS3Q2KOZ.js.map → chunk-QBOQI6PG.js.map} +0 -0
- /package/dist/{chunk-JIMEB4C6.js.map → chunk-QMMOMBH6.js.map} +0 -0
- /package/dist/{chunk-QDNVHA2F.js.map → chunk-QMZ5A6FQ.js.map} +0 -0
- /package/dist/{chunk-FLHOTGOO.js.map → chunk-QVGEVZ5F.js.map} +0 -0
- /package/dist/{chunk-AFBJHD55.js.map → chunk-QZJEYZ4I.js.map} +0 -0
- /package/dist/{chunk-V3YB7RGR.js.map → chunk-R2N37AA6.js.map} +0 -0
- /package/dist/{chunk-JLHKBAFY.js.map → chunk-RWW3I3PO.js.map} +0 -0
- /package/dist/{chunk-IGAIOGCN.js.map → chunk-S4YERMGJ.js.map} +0 -0
- /package/dist/{chunk-OGFPMCSP.js.map → chunk-SBOHNJUD.js.map} +0 -0
- /package/dist/{chunk-QXPOEAKR.js.map → chunk-SC4JLMZI.js.map} +0 -0
- /package/dist/{chunk-EDC6MLXV.js.map → chunk-SEU6QHH4.js.map} +0 -0
- /package/dist/{chunk-F4COUZLL.js.map → chunk-TMR64XPL.js.map} +0 -0
- /package/dist/{chunk-DEQ2JTBR.js.map → chunk-TNATF3HB.js.map} +0 -0
- /package/dist/{chunk-GZ2GKOF6.js.map → chunk-TNJVDG2B.js.map} +0 -0
- /package/dist/{chunk-5OZCCLNC.js.map → chunk-TSNRZSRC.js.map} +0 -0
- /package/dist/{chunk-QCUH2G77.js.map → chunk-UTEATWHO.js.map} +0 -0
- /package/dist/{chunk-YGBIBOCR.js.map → chunk-VEGQYPCP.js.map} +0 -0
- /package/dist/{chunk-25G7U6RD.js.map → chunk-WQN5SM7P.js.map} +0 -0
- /package/dist/{chunk-NOT7XA7Q.js.map → chunk-X3MWEFPT.js.map} +0 -0
- /package/dist/{chunk-BVZGNMVM.js.map → chunk-YFFEF6HR.js.map} +0 -0
- /package/dist/{chunk-H4SMS4O2.js.map → chunk-YJ3TREAC.js.map} +0 -0
- /package/dist/{chunk-5SZ24DL5.js.map → chunk-YQQPNXNO.js.map} +0 -0
- /package/dist/{chunk-EW4HFWGJ.js.map → chunk-YWZEDG7F.js.map} +0 -0
- /package/dist/{chunk-2VUISNGP.js.map → chunk-YXIVUWDU.js.map} +0 -0
- /package/dist/{chunk-YG2EG6CB.js.map → chunk-YXZNGHID.js.map} +0 -0
- /package/dist/{chunk-QYJIOWKN.js.map → chunk-YZN6NRTR.js.map} +0 -0
- /package/dist/{chunk-IZX3WZBF.js.map → chunk-YZNKFYDC.js.map} +0 -0
- /package/dist/{chunk-E2R5NRZD.js.map → chunk-Z4D3YGAB.js.map} +0 -0
- /package/dist/{chunk-STR7WBOB.js.map → chunk-ZUDAK436.js.map} +0 -0
- /package/dist/{chunk-ITI3SJRB.js.map → chunk-ZX5FNWH7.js.map} +0 -0
- /package/dist/{classified-marker-7S5IEP6Q.js.map → classified-marker-52P3D72W.js.map} +0 -0
- /package/dist/{collection-facade-ENLTJZBT.js.map → collection-facade-XKCFHSLR.js.map} +0 -0
- /package/dist/{computed-WZNKRLCY.js.map → computed-33DRJDOC.js.map} +0 -0
- /package/dist/{cover-B5MY43CK.js.map → cover-5BSNJREH.js.map} +0 -0
- /package/dist/{dead-filter-KRRD57MR.js.map → dead-filter-JXVCKS6W.js.map} +0 -0
- /package/dist/{delegation-UT76AQIG.js.map → delegation-WWZPEDK2.js.map} +0 -0
- /package/dist/{derive-SGRCCKVP.js.map → derive-CWWFEQ4N.js.map} +0 -0
- /package/dist/{enclave-YFKA3GJT.js.map → enclave-I5H3GINP.js.map} +0 -0
- /package/dist/{executor-TEZYRDN3.js.map → executor-FCJEH3FU.js.map} +0 -0
- /package/dist/{executor-WQQRU6DO.js.map → executor-OHET7TF7.js.map} +0 -0
- /package/dist/{executor-YORMIBWV.js.map → executor-SHYLIBOM.js.map} +0 -0
- /package/dist/{export-accessible-HXMKW5LJ.js.map → export-accessible-FBL6TDSK.js.map} +0 -0
- /package/dist/{extract-partition-QGQ2R42J.js.map → extract-partition-ZYIRYMTI.js.map} +0 -0
- /package/dist/{fanout-sidecar-HJV3A7PM.js.map → fanout-sidecar-WWXVHHUH.js.map} +0 -0
- /package/dist/{find-VV6PBIHK.js.map → find-LEBUXVL7.js.map} +0 -0
- /package/dist/{issue-7JJ4Z7E2.js.map → issue-F76PAGIR.js.map} +0 -0
- /package/dist/{ledger-SW6QQOAV.js.map → ledger-M2BKCNVS.js.map} +0 -0
- /package/dist/{liberate-FK7JI2QX.js.map → liberate-ZP7UB57K.js.map} +0 -0
- /package/dist/{link-set-GNGGCFI4.js.map → link-set-ZU4E5J5S.js.map} +0 -0
- /package/dist/{noydb-KUX2TJMX.js.map → noydb-BNDGEK5X.js.map} +0 -0
- /package/dist/{policy-EAXSJNOT.js.map → policy-IY2YEA3A.js.map} +0 -0
- /package/dist/{post-register-A7HCFBP5.js.map → post-register-XIAHB6KF.js.map} +0 -0
- /package/dist/{register-YBMRLZLO.js.map → register-HB4TQZSV.js.map} +0 -0
- /package/dist/{registry-3LXKNXWI.js.map → registry-6BMBFEXT.js.map} +0 -0
- /package/dist/{registry-4V4YQCXJ.js.map → registry-7CZWF3EQ.js.map} +0 -0
- /package/dist/{registry-ZULMLCJK.js.map → registry-JOFO7RAH.js.map} +0 -0
- /package/dist/{request-withdrawal-SEVUEUDG.js.map → request-withdrawal-UN3TRHWI.js.map} +0 -0
- /package/dist/{reveal-PB3YG544.js.map → reveal-VWXRV2XU.js.map} +0 -0
- /package/dist/{revoke-5QULL5GT.js.map → revoke-OLO2OVR7.js.map} +0 -0
- /package/dist/{signer-ZMH35FT4.js.map → signer-MGGKPYAN.js.map} +0 -0
- /package/dist/{stale-4UNPHWOU.js.map → stale-NBROTA66.js.map} +0 -0
- /package/dist/{storage-QOZKQMID.js.map → storage-3R37QZLA.js.map} +0 -0
- /package/dist/{store-coordination-provider-3AELCZ4Y.js.map → store-coordination-provider-P3IU5JHD.js.map} +0 -0
- /package/dist/{verify-MXN66IIH.js.map → verify-5X37OXRY.js.map} +0 -0
- /package/dist/{walk-H4YE52S2.js.map → walk-E3OVLBFR.js.map} +0 -0
- /package/dist/{withdraw-accessible-HPOVNG2G.js.map → withdraw-accessible-Q2EMATCU.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
QuiesceTimeoutError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6SYOHUHM.js";
|
|
4
4
|
|
|
5
5
|
// src/port/by/types.ts
|
|
6
6
|
function isQuorum(writers, generation, excludeWriterId) {
|
|
@@ -51,4 +51,4 @@ export {
|
|
|
51
51
|
isQuorum,
|
|
52
52
|
runDrainBarrier
|
|
53
53
|
};
|
|
54
|
-
//# sourceMappingURL=chunk-
|
|
54
|
+
//# sourceMappingURL=chunk-AFAPBZAA.js.map
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
base64ToBuffer,
|
|
6
6
|
bufferToBase64
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-7U6AB7DZ.js";
|
|
8
8
|
import {
|
|
9
9
|
SessionExpiredError,
|
|
10
10
|
SessionNotFoundError,
|
|
11
11
|
SessionPolicyError,
|
|
12
12
|
ValidationError
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-6SYOHUHM.js";
|
|
14
14
|
|
|
15
15
|
// src/with-party/session/session.ts
|
|
16
16
|
var subtle = globalThis.crypto.subtle;
|
|
@@ -364,4 +364,4 @@ export {
|
|
|
364
364
|
clearDevUnlock,
|
|
365
365
|
isDevUnlockActive
|
|
366
366
|
};
|
|
367
|
-
//# sourceMappingURL=chunk-
|
|
367
|
+
//# sourceMappingURL=chunk-BAAUFCJK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-party/session/session.ts","../src/with-party/session/session-policy.ts","../src/with-party/session/dev-unlock.ts"],"sourcesContent":["/**\n * Session tokens —\n *\n * After a vault is unlocked (via secret, WebAuthn, OIDC, or magic-\n * link), the caller can call `createSession()` to get a session token that\n * allows re-establishing the KEK for the session lifetime without re-running\n * PBKDF2 or any interactive auth challenge.\n *\n * Security model\n * ──────────────\n * A session consists of two pieces that must both be present to recover the\n * KEK:\n *\n * 1. The **session key** — a non-extractable AES-256-GCM CryptoKey that\n * exists only in memory. \"Non-extractable\" is enforced by the WebCrypto\n * API: the key object cannot be serialized, exported, or sent over\n * postMessage. When the JS context is GC'd (tab close, navigation away,\n * worker termination) the key becomes unrecoverable.\n *\n * 2. The **session token** — a JSON object that carries the KEK wrapped\n * with the session key (AES-256-GCM, fresh IV per session), plus\n * unencrypted session metadata (sessionId, userId, vault, role,\n * expiresAt). The token can be serialized to JSON and stored in\n * sessionStorage or passed across callsites within the same tab, but\n * it is useless without the session key.\n *\n * The session key is kept in a module-level Map indexed by sessionId. Callers\n * that need to re-use a session must hold on to the sessionId returned from\n * `createSession()`; the key is looked up automatically by `resolveSession()`.\n *\n * Revocation: `revokeSession()` removes the entry from the Map. Because the\n * key is non-extractable, removal is sufficient — no one holds a serializable\n * copy of the key.\n *\n * Tab-scoped lifetime: the module-level Map lives only as long as the JS\n * module. Tab close → module unloaded → Map GC'd → all session keys gone.\n * This is the zero-effort logout: closing the tab is always a secure logout.\n *\n * Expiry: `createSession()` accepts a `ttlMs` option. `resolveSession()`\n * checks `expiresAt` and throws `SessionExpiredError` if the token is stale,\n * even if the session key is still in the Map.\n */\n\nimport { bufferToBase64, base64ToBuffer, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { generateULID } from '../../with-pod/ulid.js'\nimport type { Role } from '../../kernel/types.js'\nimport type { UnlockedKeyring } from '../team/keyring.js'\nimport { SessionExpiredError, SessionNotFoundError } from '../../kernel/errors.js'\n\nconst subtle = globalThis.crypto.subtle\n\n// Default session TTL: 60 minutes\nconst DEFAULT_TTL_MS = 60 * 60 * 1000\n\n// Module-level session key store. Tab-scoped by construction.\nconst sessionKeyStore = new Map<string, CryptoKey>()\n\n// ─── Public types ──────────────────────────────────────────────────────\n\n/** The serializable part of a session token. Safe to store in sessionStorage. */\nexport interface SessionToken {\n readonly _noydb_session: 1\n /** Unique session identifier (ULID). Use this as the handle for resolve/revoke. */\n readonly sessionId: string\n readonly userId: string\n readonly vault: string\n readonly role: Role\n /** ISO timestamp — resolveSession() rejects this token after this time. */\n readonly expiresAt: string\n /** KEK wrapped with the session key (AES-256-GCM). Base64. */\n readonly wrappedKek: string\n /** IV used for the wrapping operation. Base64. */\n readonly kekIv: string\n}\n\n/** Result returned from `createSession()`. */\nexport interface CreateSessionResult {\n /** Serializable token — store in sessionStorage or pass to `resolveSession()`. */\n token: SessionToken\n /** The sessionId — use this handle for `resolveSession()` and `revokeSession()`. */\n sessionId: string\n}\n\n/** Options for `createSession()`. */\nexport interface CreateSessionOptions {\n /**\n * Session lifetime in milliseconds. Defaults to 60 minutes.\n * After this duration, `resolveSession()` throws `SessionExpiredError`.\n */\n ttlMs?: number\n}\n\n// ─── Core session operations ───────────────────────────────────────────\n\n/**\n * Create a session for an already-unlocked keyring.\n *\n * Call this after any successful unlock (secret, WebAuthn, OIDC,\n * magic-link). The returned `sessionId` is the handle for later\n * `resolveSession()` and `revokeSession()` calls.\n *\n * The session key is generated fresh (non-extractable) and stored in the\n * module-level Map. The KEK from `keyring.kek` is exported (it must be\n * extractable — it was derived by `deriveKey()` which sets extractable: false,\n * but it's unwrapped from the keyring which sets extractable: true) and then\n * re-wrapped with the session key.\n *\n * @param keyring - An already-unlocked keyring whose `kek` is available.\n * @param vault - The vault name this session is scoped to.\n * @param options - Optional session configuration.\n */\nexport async function createSession(\n keyring: UnlockedKeyring,\n vault: string,\n options: CreateSessionOptions = {},\n): Promise<CreateSessionResult> {\n const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS\n const sessionId = generateULID()\n const expiresAt = new Date(Date.now() + ttlMs).toISOString()\n\n // Generate a fresh non-extractable session key.\n // AES-256-GCM is used here (rather than AES-KW) because the session key\n // wraps raw key bytes (the exported KEK) rather than a CryptoKey object.\n const sessionKey = await subtle.generateKey(\n { name: 'AES-GCM', length: 256 },\n false, // non-extractable — this is the tab-scope security invariant\n ['encrypt', 'decrypt'],\n )\n\n // Export the KEK as raw bytes so we can wrap it.\n // The KEK is AES-256-KW, which must have been importable (extractable: true)\n // to allow wrapKey — it is, because unwrapKey sets extractable: true for\n // DEKs, but the KEK itself is derived with extractable: false (see\n // crypto.ts deriveKey). We use a separate raw export + encrypt path.\n //\n // Wait — the KEK is AES-KW with extractable:false. We cannot export it.\n // Instead, we wrap the DEKs (which ARE extractable) and the salt+role+userId\n // metadata together. This means resolveSession() reconstructs an\n // UnlockedKeyring by re-wrapping the DEKs list from the token.\n //\n // Simpler approach: export each DEK (they're extractable) and encrypt\n // the serialized DEK map with the session key. The keyring is reconstructed\n // from the session token without the original KEK — only DEKs matter for\n // record operations.\n //\n // This is the right design: sessions don't need the KEK (no re-grant,\n // no re-derive during session lifetime). They need the DEK set.\n\n const dekMap: Record<string, string> = {}\n for (const [collName, dek] of keyring.deks) {\n const raw = await subtle.exportKey('raw', dek)\n dekMap[collName] = bufferToBase64(raw)\n }\n\n const payload = JSON.stringify({\n userId: keyring.userId,\n displayName: keyring.displayName,\n role: keyring.role,\n permissions: keyring.permissions,\n deks: dekMap,\n salt: bufferToBase64(keyring.salt),\n })\n\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12))\n const encrypted = await subtle.encrypt(\n { name: 'AES-GCM', iv },\n sessionKey,\n new TextEncoder().encode(payload),\n )\n\n const token: SessionToken = {\n _noydb_session: 1,\n sessionId,\n userId: keyring.userId,\n vault,\n role: keyring.role,\n expiresAt,\n wrappedKek: bufferToBase64(encrypted),\n kekIv: bufferToBase64(iv),\n }\n\n sessionKeyStore.set(sessionId, sessionKey)\n return { token, sessionId }\n}\n\n/**\n * Resolve a session token back into an UnlockedKeyring.\n *\n * Looks up the session key by `sessionId`, checks the token is not expired,\n * then decrypts the payload to reconstruct the keyring's DEK set.\n *\n * Throws `SessionExpiredError` if the token's `expiresAt` is in the past.\n * Throws `SessionNotFoundError` if the session key is not in the store\n * (tab was reloaded, session was revoked, or the sessionId is wrong).\n *\n * @param token - The SessionToken from `createSession()`.\n */\nexport async function resolveSession(token: SessionToken): Promise<UnlockedKeyring> {\n // Expiry check first — fast path without touching crypto\n if (Date.now() > new Date(token.expiresAt).getTime()) {\n sessionKeyStore.delete(token.sessionId)\n throw new SessionExpiredError(token.sessionId)\n }\n\n const sessionKey = sessionKeyStore.get(token.sessionId)\n if (!sessionKey) {\n throw new SessionNotFoundError(token.sessionId)\n }\n\n const iv = base64ToBuffer(token.kekIv)\n const ciphertext = base64ToBuffer(token.wrappedKek)\n\n let plaintext: ArrayBuffer\n try {\n plaintext = await subtle.decrypt(\n { name: 'AES-GCM', iv },\n sessionKey,\n ciphertext,\n )\n } catch {\n throw new SessionNotFoundError(token.sessionId)\n }\n\n const payload = JSON.parse(new TextDecoder().decode(plaintext)) as {\n userId: string\n displayName: string\n role: Role\n permissions: Record<string, 'rw' | 'ro'>\n deks: Record<string, string>\n salt: string\n }\n\n const deks = new Map<string, EnclaveKey>()\n for (const [collName, rawBase64] of Object.entries(payload.deks)) {\n const dek = await subtle.importKey(\n 'raw',\n base64ToBuffer(rawBase64),\n { name: 'AES-GCM', length: 256 },\n true,\n ['encrypt', 'decrypt'],\n )\n deks.set(collName, dek)\n }\n\n return {\n userId: payload.userId,\n displayName: payload.displayName,\n role: payload.role,\n permissions: payload.permissions,\n deks,\n kek: null, // KEK not available in session context\n salt: base64ToBuffer(payload.salt),\n authenticators: [],\n }\n}\n\n/**\n * Revoke a session by removing its key from the store.\n *\n * After revocation, `resolveSession()` will throw `SessionNotFoundError`\n * for this sessionId. The session token (if held by the caller) becomes\n * permanently useless. This is the explicit logout path.\n *\n * No-op if the session was already expired or does not exist.\n */\nexport function revokeSession(sessionId: string): void {\n sessionKeyStore.delete(sessionId)\n}\n\n/**\n * Check if a session is still alive (key in store + not expired).\n * Does not decrypt anything — purely a metadata check.\n */\nexport function isSessionAlive(token: SessionToken): boolean {\n if (Date.now() > new Date(token.expiresAt).getTime()) return false\n return sessionKeyStore.has(token.sessionId)\n}\n\n/**\n * Revoke all active sessions. Used by `Noydb.close()` to ensure that\n * closing the instance destroys all session state, not just the keyring\n * cache.\n */\nexport function revokeAllSessions(): void {\n sessionKeyStore.clear()\n}\n\n/**\n * Return the number of active sessions currently in the store.\n * Useful for diagnostics and tests.\n */\nexport function activeSessionCount(): number {\n return sessionKeyStore.size\n}\n","/**\n * Session policies —\n *\n * A `SessionPolicy` is a small declarative object that controls how long a\n * session lives and which operations require re-authentication. It is\n * evaluated by the `PolicyEnforcer` class, which the Noydb instance\n * integrates to replace the bare `sessionTimeout` timer from.\n *\n * Design decisions\n * ────────────────\n * Policies are stateless value objects — no timers, no event listeners.\n * The Noydb instance is the stateful coordinator: it holds the enforcer,\n * calls `enforcer.touch()` on every operation, and calls\n * `enforcer.checkOperation()` before high-risk operations.\n *\n * This keeps the policy module easy to unit-test (no global timers to mock)\n * and avoids the \"who owns cleanup\" problem that comes with timer-based\n * callbacks embedded in a value object.\n *\n * `lockOnBackground` registers a `visibilitychange` listener on the document\n * at enforcer creation time and removes it on `destroy()`. It is a no-op in\n * non-browser environments (no `document`).\n */\n\nimport type { SessionPolicy, ReAuthOperation } from '../../kernel/types.js'\nimport { SessionExpiredError, SessionPolicyError } from '../../kernel/errors.js'\nimport { revokeSession } from './session.js'\n\n// ─── PolicyEnforcer ────────────────────────────────────────────────────\n\nexport interface PolicyEnforcerOptions {\n /** The policy to enforce. */\n policy: SessionPolicy\n /** The session ID to revoke when idle/absolute timeouts fire. */\n sessionId: string\n /**\n * Called when the policy decides the session should end (idle timeout,\n * absolute timeout, or lockOnBackground). Use this to trigger the\n * same cleanup that `Noydb.close()` would perform.\n */\n onRevoke: (reason: 'idle' | 'absolute' | 'background') => void\n}\n\n/**\n * Stateful enforcer for a single session policy.\n *\n * Create one per open session, call `touch()` on every operation,\n * call `checkOperation(op)` before export/grant/revoke/rotate/changeSecret,\n * and call `destroy()` when the session ends.\n */\nexport class PolicyEnforcer {\n private readonly policy: SessionPolicy\n private readonly sessionId: string\n private readonly onRevoke: PolicyEnforcerOptions['onRevoke']\n private readonly createdAt: number\n private lastActivityAt: number\n private idleTimer: ReturnType<typeof setTimeout> | null = null\n private absoluteTimer: ReturnType<typeof setTimeout> | null = null\n private visibilityHandler: (() => void) | null = null\n\n constructor(opts: PolicyEnforcerOptions) {\n this.policy = opts.policy\n this.sessionId = opts.sessionId\n this.onRevoke = opts.onRevoke\n this.createdAt = Date.now()\n this.lastActivityAt = Date.now()\n\n this.scheduleIdleTimer()\n this.scheduleAbsoluteTimer()\n this.registerBackgroundLock()\n }\n\n /**\n * Record an activity timestamp and reset the idle timer.\n * Call this at the top of every Noydb public method.\n */\n touch(): void {\n this.lastActivityAt = Date.now()\n this.scheduleIdleTimer()\n }\n\n /**\n * Check whether the given operation is allowed under the active policy.\n * Throws `SessionPolicyError` if the operation requires re-authentication.\n * Throws `SessionExpiredError` if the absolute timeout has been exceeded\n * (defensive check in case the timer fired before the call arrived).\n *\n * This is a synchronous check — callers don't await it.\n */\n checkOperation(op: ReAuthOperation): void {\n // Defensive absolute-timeout check (timer may have fired late)\n const { absoluteTimeoutMs } = this.policy\n if (absoluteTimeoutMs !== undefined && Date.now() - this.createdAt >= absoluteTimeoutMs) {\n this.expire('absolute')\n throw new SessionExpiredError(this.sessionId)\n }\n\n const required = this.policy.requireReAuthFor ?? []\n if (required.includes(op)) {\n throw new SessionPolicyError(op)\n }\n }\n\n /**\n * Tear down timers and background-lock listener. Call from `Noydb.close()`\n * and whenever the session is revoked externally.\n */\n destroy(): void {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = null\n }\n if (this.absoluteTimer) {\n clearTimeout(this.absoluteTimer)\n this.absoluteTimer = null\n }\n if (this.visibilityHandler && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', this.visibilityHandler)\n this.visibilityHandler = null\n }\n }\n\n /** How long since the last activity, in ms. */\n get idleMs(): number {\n return Date.now() - this.lastActivityAt\n }\n\n /** How long since session creation, in ms. */\n get ageMs(): number {\n return Date.now() - this.createdAt\n }\n\n // ── Private ──────────────────────────────────────────────────────────\n\n private scheduleIdleTimer(): void {\n const { idleTimeoutMs } = this.policy\n if (!idleTimeoutMs) return\n\n if (this.idleTimer) clearTimeout(this.idleTimer)\n this.idleTimer = setTimeout(() => {\n this.expire('idle')\n }, idleTimeoutMs)\n }\n\n private scheduleAbsoluteTimer(): void {\n const { absoluteTimeoutMs } = this.policy\n if (!absoluteTimeoutMs) return\n\n if (this.absoluteTimer) clearTimeout(this.absoluteTimer)\n this.absoluteTimer = setTimeout(() => {\n this.expire('absolute')\n }, absoluteTimeoutMs)\n }\n\n private registerBackgroundLock(): void {\n if (!this.policy.lockOnBackground) return\n if (typeof document === 'undefined') return\n\n this.visibilityHandler = () => {\n if (document.hidden) {\n this.expire('background')\n }\n }\n document.addEventListener('visibilitychange', this.visibilityHandler)\n }\n\n private expire(reason: 'idle' | 'absolute' | 'background'): void {\n this.destroy()\n revokeSession(this.sessionId)\n this.onRevoke(reason)\n }\n}\n\n// ─── Helpers ───────────────────────────────────────────────────────────\n\n/**\n * Build a `PolicyEnforcer` from a policy + session token, and return it\n * alongside a cleanup function. Convenience wrapper for Noydb.\n */\nexport function createEnforcer(opts: PolicyEnforcerOptions): PolicyEnforcer {\n return new PolicyEnforcer(opts)\n}\n\n/**\n * Validate that a `SessionPolicy` is well-formed.\n * Throws a plain `Error` (not `NoydbError`) because this is a developer\n * error — invalid policies passed at construction time, not at runtime.\n */\nexport function validateSessionPolicy(policy: SessionPolicy): void {\n const { idleTimeoutMs, absoluteTimeoutMs } = policy\n if (idleTimeoutMs !== undefined && (typeof idleTimeoutMs !== 'number' || idleTimeoutMs <= 0)) {\n throw new Error(`SessionPolicy.idleTimeoutMs must be a positive number, got ${idleTimeoutMs}`)\n }\n if (absoluteTimeoutMs !== undefined && (typeof absoluteTimeoutMs !== 'number' || absoluteTimeoutMs <= 0)) {\n throw new Error(`SessionPolicy.absoluteTimeoutMs must be a positive number, got ${absoluteTimeoutMs}`)\n }\n if (idleTimeoutMs !== undefined && absoluteTimeoutMs !== undefined && idleTimeoutMs >= absoluteTimeoutMs) {\n throw new Error(\n `SessionPolicy.idleTimeoutMs (${idleTimeoutMs}ms) must be less than absoluteTimeoutMs (${absoluteTimeoutMs}ms)`,\n )\n }\n}\n","/**\n * Dev-mode persistent unlock —\n *\n * Solves the developer inner-loop friction: hot-reload destroys the session\n * (page navigation semantics), forcing a secret re-entry every refresh.\n *\n * This module provides an opt-in, deliberately-named escape hatch that lets\n * developers store the keyring payload in sessionStorage or localStorage so\n * the vault auto-unlocks on every page load — without a secret,\n * without a biometric prompt, without any OIDC flow.\n *\n * ⚠️ WARNING — this is a loaded footgun ⚠️\n * ─────────────────────────────────────────\n * The keyring payload stored by this module contains the DEKs. Whoever has\n * access to sessionStorage/localStorage has access to the DEKs. On a shared\n * development machine, a compromised browser extension, or a mis-configured\n * origin, this is a complete key exposure.\n *\n * This module is ONLY safe for local development. It must NEVER be active\n * in production builds.\n *\n * Guardrails (all enforced by the module, not by the caller)\n * ──────────────────────────────────────────────────────────\n * 1. **Production guard:** `enableDevUnlock()` throws immediately if\n * `process.env.NODE_ENV === 'production'` or if `import.meta.env?.PROD === true`\n * (Vite convention). Also throws if the hostname is NOT localhost or 127.0.0.1.\n *\n * 2. **Explicit acknowledgement string:** the caller must pass\n * `acknowledge: 'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY'` or the call\n * throws. This string appears in every grep for `devUnlock` in the codebase,\n * making it impossible to enable this feature accidentally.\n *\n * 3. **Scope is vault + userId:** the storage key includes both the\n * vault name and the userId, so dev-unlock for vault-A does\n * NOT auto-unlock vault-B.\n *\n * 4. **Storage scope:** default is `sessionStorage` (cleared on tab close).\n * `localStorage` is opt-in and requires an additional\n * `persistAcrossTabs: true` flag in the options.\n *\n * 5. **Clear method:** `clearDevUnlock()` removes the stored payload. Wire\n * this to a dev toolbar button or `Ctrl+Shift+L` so clearing is one action.\n *\n * 6. **Console banner:** on first enable, a highly visible console warning\n * fires. Cannot be suppressed.\n *\n * Usage\n * ─────\n * ```ts\n * // In your dev entry point only (guarded by import.meta.env.DEV):\n * if (import.meta.env.DEV) {\n * const { enableDevUnlock, loadDevUnlock } = await import('@noy-db/hub')\n * enableDevUnlock('my-compartment', 'alice', keyring, {\n * acknowledge: 'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY',\n * })\n * }\n *\n * // On page load:\n * if (import.meta.env.DEV) {\n * const keyring = await loadDevUnlock('my-compartment', 'alice')\n * if (keyring) {\n * // Skip unlock prompt, use keyring directly\n * }\n * }\n * ```\n */\n\nimport { bufferToBase64, base64ToBuffer, type EnclaveKey } from '../../kernel/enclave/index.js'\nimport { ValidationError } from '../../kernel/errors.js'\nimport type { UnlockedKeyring } from '../team/keyring.js'\nimport type { Role } from '../../kernel/types.js'\n\n// The exact acknowledgement string callers must pass\nconst REQUIRED_ACKNOWLEDGE = 'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY'\n\nconst STORAGE_PREFIX = 'noydb:dev-unlock:'\n\n// ─── Options ──────────────────────────────────────────────────────────\n\nexport interface DevUnlockOptions {\n /**\n * Required: the exact string 'I-UNDERSTAND-THIS-DISABLES-UNLOCK-SECURITY'.\n * Any other value causes `enableDevUnlock()` to throw.\n */\n acknowledge: string\n /**\n * If `true`, stores in localStorage (persists across tabs and browser restarts).\n * If `false` (default), stores in sessionStorage (cleared on tab close).\n */\n persistAcrossTabs?: boolean\n}\n\n// ─── Production guard ─────────────────────────────────────────────────\n\nfunction assertDevEnvironment(): void {\n // Node.js: check NODE_ENV\n if (\n typeof process !== 'undefined' &&\n process.env.NODE_ENV === 'production'\n ) {\n throw new ValidationError(\n 'devUnlock is not available in production builds. ' +\n 'process.env.NODE_ENV is \"production\".',\n )\n }\n\n // Vite / build tool convention\n if (\n typeof globalThis !== 'undefined' &&\n (globalThis as Record<string, unknown>).__vite_is_production__ === true\n ) {\n throw new ValidationError('devUnlock is not available in production builds.')\n }\n\n // Browser: only allow on localhost\n if (\n typeof window !== 'undefined' &&\n typeof window.location !== 'undefined'\n ) {\n const host = window.location.hostname\n if (host !== 'localhost' && host !== '127.0.0.1' && host !== '::1' && !host.endsWith('.local')) {\n throw new ValidationError(\n `devUnlock is only available on localhost. Current hostname: \"${host}\". ` +\n 'Set NODE_ENV=development and run on localhost to use dev unlock.',\n )\n }\n }\n}\n\n// ─── Storage key ──────────────────────────────────────────────────────\n\nfunction storageKey(vault: string, userId: string): string {\n return `${STORAGE_PREFIX}${vault}:${userId}`\n}\n\nfunction resolveStorage(persistAcrossTabs?: boolean): Storage {\n if (typeof window === 'undefined') {\n throw new ValidationError('devUnlock requires a browser environment (window.sessionStorage / window.localStorage).')\n }\n return persistAcrossTabs ? window.localStorage : window.sessionStorage\n}\n\n// ─── Public API ────────────────────────────────────────────────────────\n\n/**\n * Serialize and store a keyring to browser storage for dev-mode auto-unlock.\n *\n * Throws immediately if:\n * - The acknowledge string is wrong.\n * - Running in a production environment (NODE_ENV=production).\n * - Running on a non-localhost hostname.\n *\n * Emits a highly visible console warning that cannot be suppressed.\n *\n * @param vault - The vault name.\n * @param userId - The user ID.\n * @param keyring - The unlocked keyring to persist.\n * @param options - Options including the required acknowledge string.\n */\nexport async function enableDevUnlock(\n vault: string,\n userId: string,\n keyring: UnlockedKeyring,\n options: DevUnlockOptions,\n): Promise<void> {\n if (options.acknowledge !== REQUIRED_ACKNOWLEDGE) {\n throw new ValidationError(\n `devUnlock requires acknowledge: '${REQUIRED_ACKNOWLEDGE}'. ` +\n `Got: '${options.acknowledge}'. This is intentional — the full string must appear in your source.`,\n )\n }\n\n assertDevEnvironment()\n\n const storage = resolveStorage(options.persistAcrossTabs)\n\n const dekMap: Record<string, string> = {}\n for (const [collName, dek] of keyring.deks) {\n const raw = await globalThis.crypto.subtle.exportKey('raw', dek)\n dekMap[collName] = bufferToBase64(raw)\n }\n\n const payload = JSON.stringify({\n _noydb_dev_unlock: 1,\n userId: keyring.userId,\n displayName: keyring.displayName,\n role: keyring.role,\n permissions: keyring.permissions,\n deks: dekMap,\n salt: bufferToBase64(keyring.salt),\n })\n\n storage.setItem(storageKey(vault, userId), payload)\n\n // Visible, unsuppressable warning\n console.warn(\n '%c⚠️ NOYDB DEV UNLOCK ACTIVE ⚠️',\n 'color: red; font-size: 16px; font-weight: bold',\n `\\n\\nVault \"${vault}\" user \"${userId}\" is stored in ` +\n `${options.persistAcrossTabs ? 'localStorage' : 'sessionStorage'} in PLAINTEXT DEKs.\\n` +\n 'This is ONLY safe for local development. Never use in production.\\n' +\n 'Call clearDevUnlock() to remove.',\n )\n}\n\n/**\n * Load a dev-mode keyring from browser storage.\n *\n * Returns `null` if no dev-unlock state is stored for this vault + user,\n * or if the stored payload is malformed.\n *\n * Does NOT perform the production environment check — it's safe to CALL\n * `loadDevUnlock` in production (it will simply return `null` because no\n * dev-unlock state was ever written). The guard only fires on `enableDevUnlock`.\n *\n * @param vault - The vault name.\n * @param userId - The user ID.\n * @param options - Optional storage override.\n */\nexport async function loadDevUnlock(\n vault: string,\n userId: string,\n options: { persistAcrossTabs?: boolean } = {},\n): Promise<UnlockedKeyring | null> {\n if (typeof window === 'undefined') return null\n\n const storage = resolveStorage(options.persistAcrossTabs)\n const raw = storage.getItem(storageKey(vault, userId))\n if (!raw) return null\n\n let parsed: {\n _noydb_dev_unlock?: number\n userId: string\n displayName: string\n role: Role\n permissions: Record<string, 'rw' | 'ro'>\n deks: Record<string, string>\n salt: string\n }\n try {\n parsed = JSON.parse(raw)\n } catch {\n return null\n }\n\n if (parsed._noydb_dev_unlock !== 1) return null\n\n const deks = new Map<string, EnclaveKey>()\n for (const [collName, rawBase64] of Object.entries(parsed.deks)) {\n const dek = await globalThis.crypto.subtle.importKey(\n 'raw',\n base64ToBuffer(rawBase64),\n { name: 'AES-GCM', length: 256 },\n true,\n ['encrypt', 'decrypt'],\n )\n deks.set(collName, dek)\n }\n\n return {\n userId: parsed.userId,\n displayName: parsed.displayName,\n role: parsed.role,\n permissions: parsed.permissions,\n deks,\n kek: null,\n salt: base64ToBuffer(parsed.salt),\n authenticators: [],\n }\n}\n\n/**\n * Remove dev-unlock state from browser storage.\n *\n * Safe to call in production (no-op if no dev state exists).\n */\nexport function clearDevUnlock(\n vault: string,\n userId: string,\n options: { persistAcrossTabs?: boolean } = {},\n): void {\n if (typeof window === 'undefined') return\n const storage = resolveStorage(options.persistAcrossTabs)\n storage.removeItem(storageKey(vault, userId))\n}\n\n/**\n * Check if dev-unlock state exists for this vault + user.\n *\n * Safe to call in production (returns false if nothing is stored).\n */\nexport function isDevUnlockActive(\n vault: string,\n userId: string,\n options: { persistAcrossTabs?: boolean } = {},\n): boolean {\n if (typeof window === 'undefined') return false\n const storage = resolveStorage(options.persistAcrossTabs)\n return storage.getItem(storageKey(vault, userId)) !== null\n}\n"],"mappings":"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","names":[]}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
FieldNotQueryableError,
|
|
7
7
|
IndexRequiredError
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-6SYOHUHM.js";
|
|
9
9
|
|
|
10
10
|
// src/with-lookup/indexing/persisted-indexes.ts
|
|
11
11
|
var IDX_PREFIX = "_idx/";
|
|
@@ -521,4 +521,4 @@ export {
|
|
|
521
521
|
PersistedCollectionIndex,
|
|
522
522
|
LazyQuery
|
|
523
523
|
};
|
|
524
|
-
//# sourceMappingURL=chunk-
|
|
524
|
+
//# sourceMappingURL=chunk-BY5YBSXA.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
deriveSecretKey
|
|
3
|
+
} from "./chunk-7U6AB7DZ.js";
|
|
4
4
|
import {
|
|
5
5
|
NOYDB_FORMAT_VERSION
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YTW6ZIPD.js";
|
|
7
7
|
|
|
8
8
|
// src/with-party/team/wrapped-deks.ts
|
|
9
9
|
var PBKDF2_ITERATIONS = 6e5;
|
|
@@ -54,7 +54,7 @@ async function unwrapDeksFromBlob(blob, credential) {
|
|
|
54
54
|
return deks;
|
|
55
55
|
}
|
|
56
56
|
async function deriveWrappingKey(credential, salt) {
|
|
57
|
-
return
|
|
57
|
+
return deriveSecretKey(credential, salt, { iterations: PBKDF2_ITERATIONS, keyUsage: "aes-gcm" });
|
|
58
58
|
}
|
|
59
59
|
function bytesToBase64(b) {
|
|
60
60
|
let s = "";
|
|
@@ -202,4 +202,4 @@ export {
|
|
|
202
202
|
mintPaperRecoveryEntry,
|
|
203
203
|
unwrapDeksFromPaperEntry
|
|
204
204
|
};
|
|
205
|
-
//# sourceMappingURL=chunk-
|
|
205
|
+
//# sourceMappingURL=chunk-CJVEAVYH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/with-party/team/wrapped-deks.ts","../src/with-party/team/recovery.ts"],"sourcesContent":["/**\n * **Wrap-DEKs primitive** — a single canonical shape for the\n * pattern of \"serialize a DEK set, encrypt it under a credential-derived\n * AES-GCM key.\" Used by:\n *\n * - **tier-0** — paper recovery entries (`_meta/recovery-paper`),\n * credential = the printed code.\n * - **tier-2** — password authenticator slots (`KeyringFile.authenticators`,\n * `wrapKind: 'deks'`), credential = the user's password.\n *\n * **Not** used by `@noy-db/on-pin` — tier-3 wraps the DEK set under\n * the same conceptual pattern but at **100,000 PBKDF2 iterations**\n * (vs the 600,000 here), because the protection window for a PIN\n * slot is short (idle-timeout-bounded, typically 15 min) and 600k\n * iterations would make every PIN-resume noticeably slow. The wire\n * formats are deliberately incompatible. See `@noy-db/on-pin`'s\n * `PIN_PBKDF2_ITERATIONS` and the threat-model rationale in its\n * module docstring.\n *\n * Previously, the same crypto lived in two places: `mintPaperRecoveryEntry`\n * (in `team/recovery.ts`) and `enrollPasswordAuthenticator` (in\n * `@noy-db/on-password`). Both functions did identical work — PBKDF2\n * the credential, AES-GCM-encrypt the JSON-serialized DEK set — but\n * their implementations had drifted apart enough that fixing a bug\n * in one wouldn't fix the other.\n *\n * This module owns the canonical implementation. Consumers compose:\n *\n * - `mintPaperRecoveryEntry` is now a thin wrapper that calls\n * `mintWrappedDeksBlob` and adds `{ codeId, enrolledAt }`.\n * - `enrollPasswordAuthenticator` calls `mintWrappedDeksBlob` and\n * wraps the result in the slot envelope.\n *\n * @module\n */\n\nimport { deriveSecretKey, type EnclaveKey } from '../../kernel/enclave/index.js'\n\nconst PBKDF2_ITERATIONS = 600_000\nconst SALT_BYTES = 32\nconst IV_BYTES = 12\n\nconst subtle = globalThis.crypto.subtle\n\n// ─── Type ──────────────────────────────────────────────────────────────\n\n/**\n * The wrap-DEKs primitive — a serialized + AES-GCM-encrypted DEK set\n * keyed under a credential-derived key.\n *\n * All three fields are base64-encoded so the blob is JSON-safe and\n * round-trips through `_meta/*` envelopes (which carry plaintext\n * JSON in `_data`).\n *\n * Composition: `PaperRecoveryEntry extends WrappedDeksBlob` plus\n * `{ codeId, enrolledAt }`. `KeyringAuthenticatorWrappingDEKs`\n * carries the same three fields with `salt` stored in `meta` for\n * slot-format back-compat (defers moving it to top-level).\n */\nexport interface WrappedDeksBlob {\n /** Base64 PBKDF2 salt for the credential-derived wrapping key. */\n readonly salt: string\n /** Base64 AES-GCM IV used for the `wrappedDeks` ciphertext. */\n readonly iv: string\n /** Base64 AES-GCM ciphertext of `{ deks: { collection: base64rawDek } }`. */\n readonly wrappedDeks: string\n}\n\n// ─── Mint ──────────────────────────────────────────────────────────────\n\n/**\n * Mint a fresh `WrappedDeksBlob` from a DEK set + a string credential.\n *\n * Generates a random salt + IV, derives a 256-bit AES-GCM key via\n * PBKDF2-SHA256(credential, salt, 600K), serializes the DEK set as\n * `{ deks: { coll: rawBase64 } }`, and AES-GCM-encrypts.\n *\n * The `credential` is the user-typed string (recovery code, password,\n * PIN). Caller normalization rules apply (e.g. paper\n * recovery uppercase-strips the code before reaching this function).\n *\n * @param deks - DEK set to wrap. Each DEK must be exportable via\n * `subtle.exportKey('raw', dek)` (the hub mints DEKs\n * this way; consumers feeding non-extractable keys\n * will get `InvalidAccessError` from WebCrypto).\n * @param credential - String input the consumer minted (paper code,\n * password, PIN). Treated as opaque bytes by PBKDF2.\n */\nexport async function mintWrappedDeksBlob(\n deks: Map<string, EnclaveKey>,\n credential: string,\n): Promise<WrappedDeksBlob> {\n const salt = crypto.getRandomValues(new Uint8Array(SALT_BYTES))\n const iv = crypto.getRandomValues(new Uint8Array(IV_BYTES))\n const wrappingKey = await deriveWrappingKey(credential, salt)\n\n // Serialize the DEK set as JSON `{ deks: { collection: base64 } }`.\n const exported: Record<string, string> = {}\n for (const [coll, dek] of deks) {\n const raw = await subtle.exportKey('raw', dek)\n exported[coll] = bytesToBase64(new Uint8Array(raw))\n }\n const plaintext = new TextEncoder().encode(JSON.stringify({ deks: exported }))\n const ciphertext = await subtle.encrypt(\n { name: 'AES-GCM', iv: iv as BufferSource },\n wrappingKey,\n plaintext as BufferSource,\n )\n\n return {\n salt: bytesToBase64(salt),\n iv: bytesToBase64(iv),\n wrappedDeks: bytesToBase64(new Uint8Array(ciphertext)),\n }\n}\n\n// ─── Unwrap ────────────────────────────────────────────────────────────\n\n/**\n * Reverse of {@link mintWrappedDeksBlob}. Re-derives the wrapping key\n * from the credential + stored salt, AES-GCM-decrypts the wrapped DEK\n * set, and re-imports each DEK as an extractable AES-GCM CryptoKey.\n *\n * Throws (AES-GCM auth tag failure) when the credential doesn't\n * match the blob. Callers iterating over multiple blobs (e.g. paper\n * recovery's \"try every entry until one matches\") should catch.\n */\nexport async function unwrapDeksFromBlob(\n blob: WrappedDeksBlob,\n credential: string,\n): Promise<Map<string, EnclaveKey>> {\n const wrappingKey = await deriveWrappingKey(credential, base64ToBytes(blob.salt))\n const plaintext = await subtle.decrypt(\n { name: 'AES-GCM', iv: base64ToBytes(blob.iv) as BufferSource },\n wrappingKey,\n base64ToBytes(blob.wrappedDeks) as BufferSource,\n )\n const parsed = JSON.parse(new TextDecoder().decode(plaintext)) as { deks: Record<string, string> }\n const deks = new Map<string, EnclaveKey>()\n for (const [coll, b64] of Object.entries(parsed.deks)) {\n const raw = base64ToBytes(b64)\n const key = await subtle.importKey(\n 'raw',\n raw as BufferSource,\n { name: 'AES-GCM', length: 256 },\n true,\n ['encrypt', 'decrypt'],\n )\n deks.set(coll, key)\n }\n return deks\n}\n\n// ─── Internals ─────────────────────────────────────────────────────────\n\nasync function deriveWrappingKey(credential: string, salt: Uint8Array): Promise<CryptoKey> {\n return deriveSecretKey(credential, salt, { iterations: PBKDF2_ITERATIONS, keyUsage: 'aes-gcm' })\n}\n\nfunction bytesToBase64(b: Uint8Array): string {\n let s = ''\n for (const x of b) s += String.fromCharCode(x)\n return btoa(s)\n}\n\nfunction base64ToBytes(b64: string): Uint8Array {\n const s = atob(b64)\n const out = new Uint8Array(s.length)\n for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i)\n return out\n}\n","/**\n * Recovery profile persistence + dispatch.\n *\n * Wires the **paper** profile end-to-end through\n * `@noy-db/on-recovery`. The other three profiles (Shamir,\n * multi-channel, admin-mediated) ship the API surface and throw\n * {@link RecoveryProfileNotImplementedError} during use; per-profile\n * dispatch lands in follow-up issues.\n *\n * Storage layout:\n *\n * ```\n * _meta/recovery-paper — JSON { entries: RecoveryCodeEntry[] } produced by `on-recovery`.\n * _meta/recovery-shamir — reserved\n * _meta/recovery-multi — reserved\n * _meta/recovery-admin — reserved\n * ```\n *\n * Like `_meta/policy` and `_meta/handle`, the documents are plain JSON\n * with empty `_iv` — the recovery-code wrapping is what protects the\n * KEK; the entries themselves are inert without the user's code.\n *\n * @module\n */\nimport type { NoydbStore, EncryptedEnvelope } from '../../kernel/types.js'\nimport { NOYDB_FORMAT_VERSION } from '../../kernel/types.js'\nimport type { EnclaveKey } from '../../kernel/enclave/index.js'\nimport {\n mintWrappedDeksBlob,\n unwrapDeksFromBlob,\n type WrappedDeksBlob,\n} from './wrapped-deks.js'\nimport type { ShamirRecoveryProvider } from './shamir-recovery-provider.js'\n\n/**\n * One paper recovery code as persisted in `_meta/recovery-paper`.\n *\n * The hub's KEK is intentionally non-extractable (see `crypto.ts`),\n * so the recovery entry can't AES-KW-wrap the KEK directly. Instead\n * we wrap a serialized DEK set: the entry holds the AES-GCM\n * ciphertext of `{ deks: { collection: rawDekBase64 } }`. Recovery\n * deserializes the DEK set, then mints a fresh KEK from the new\n * secret and rewraps the DEKs under it.\n *\n * This is the same pattern `@noy-db/on-pin` uses for tier-3 quick\n * resume — the cryptographic guarantee is identical (AES-GCM with a\n * PBKDF2-derived key), and it sidesteps the non-extractable-KEK\n * constraint cleanly.\n *\n * Type-level composition: `PaperRecoveryEntry extends\n * WrappedDeksBlob` — the three crypto fields (`salt`, `iv`,\n * `wrappedDeks`) come from the shared primitive; `codeId` and\n * `enrolledAt` are paper-recovery's own metadata. Wire format\n * unchanged.\n */\nexport interface PaperRecoveryEntry extends WrappedDeksBlob {\n readonly codeId: string\n readonly enrolledAt: string\n}\n\nexport interface PaperRecoveryDoc {\n readonly _noydb_recovery: 1\n readonly profile: 'paper'\n readonly entries: ReadonlyArray<PaperRecoveryEntry>\n}\n\nconst PAPER_DOC_ID = 'recovery-paper'\n\n/** Read the paper-recovery entries. Returns empty array when absent. */\nexport async function loadPaperRecoveryEntries(\n store: NoydbStore,\n vault: string,\n): Promise<ReadonlyArray<PaperRecoveryEntry>> {\n const env = await store.get(vault, '_meta', PAPER_DOC_ID)\n if (!env) return []\n try {\n const doc = JSON.parse(env._data) as PaperRecoveryDoc\n if (doc.profile !== 'paper' || !Array.isArray(doc.entries)) return []\n return doc.entries\n } catch {\n return []\n }\n}\n\n/** Replace the paper-recovery entries (used after burn-on-recovery). */\nexport async function savePaperRecoveryEntries(\n store: NoydbStore,\n vault: string,\n entries: ReadonlyArray<PaperRecoveryEntry>,\n): Promise<void> {\n const doc: PaperRecoveryDoc = {\n _noydb_recovery: 1,\n profile: 'paper',\n entries,\n }\n const envelope: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(doc),\n }\n await store.put(vault, '_meta', PAPER_DOC_ID, envelope)\n}\n\n/** Drop a single paper-recovery entry (burn-on-use). */\nexport async function burnPaperRecoveryEntry(\n store: NoydbStore,\n vault: string,\n codeId: string,\n): Promise<void> {\n const entries = await loadPaperRecoveryEntries(store, vault)\n const remaining = entries.filter((e) => e.codeId !== codeId)\n await savePaperRecoveryEntries(store, vault, remaining)\n}\n\n/** Whether at least one recovery profile has any enrolled entries. */\nexport async function hasRecoveryEnrolled(\n store: NoydbStore,\n vault: string,\n): Promise<boolean> {\n const paper = await loadPaperRecoveryEntries(store, vault)\n if (paper.length > 0) return true\n const shamir = await loadShamirRecoveryEntries(store, vault)\n return shamir.length > 0\n}\n\n/**\n * Whether at least one **strong** recovery profile is enrolled.\n *\n * \"Strong\" excludes paper-alone — under managed-secret mode the\n * user has no memorized secret, so a stolen/lost paper sheet\n * would be a single point of total loss. Strong profiles today:\n *\n * - `shamir` (k-of-n threshold; survives loss of up to n-k shares)\n * - `multi-channel` (when shipped — follow-up slice)\n * - `admin-mediated` (when shipped — follow-up slice)\n *\n * Managed mode requires this check to pass before `openVault` returns.\n */\nexport async function hasStrongRecoveryEnrolled(\n store: NoydbStore,\n vault: string,\n): Promise<boolean> {\n const shamir = await loadShamirRecoveryEntries(store, vault)\n return shamir.length > 0\n // When multi-channel / admin-mediated land, extend this check.\n}\n\n// ─── Shamir recovery ─────────────────────────────────────────────────────\n\n/**\n * One Shamir-recovery entry as persisted in `_meta/recovery-shamir`.\n *\n * Like {@link PaperRecoveryEntry}, the entry composes\n * {@link WrappedDeksBlob} (DEKs wrapped under a fresh ephemeral\n * recovery secret) with profile-specific metadata. Unlike paper, the\n * \"credential\" was never visible to the user — it was 32 random\n * bytes split into N Shamir shares at enrollment. The shares ARE\n * the credential; the user holds them, the hub never sees them\n * again after `enrollRecovery` returns.\n *\n * The recovery secret is base64-encoded and\n * passed as the `credential` arg to\n * {@link mintWrappedDeksBlob} / {@link unwrapDeksFromBlob}. The\n * PBKDF2 round over high-entropy input is harmless overhead — it\n * keeps the shared primitive unchanged while letting Shamir reuse\n * the same wrapping pipeline as paper.\n */\nexport interface ShamirRecoveryEntry extends WrappedDeksBlob {\n /** Stable id for this entry. Allows multiple Shamir splits to coexist. */\n readonly entryId: string\n /** Threshold — minimum shares to reconstruct. */\n readonly k: number\n /** Total shares minted at enrollment. */\n readonly n: number\n /** x-coordinates of the n minted shares. Informational. Omitted as of 0.2\n * (string-level provider doesn't expose share x-coords); kept optional so\n * pre-0.2 entries still read. */\n readonly xCoords?: ReadonlyArray<number>\n /** ISO timestamp. */\n readonly enrolledAt: string\n /** Optional caller-supplied label (e.g., \"2-of-3 board escrow\"). */\n readonly label?: string\n}\n\nexport interface ShamirRecoveryDoc {\n readonly _noydb_recovery: 1\n readonly profile: 'shamir'\n readonly entries: ReadonlyArray<ShamirRecoveryEntry>\n}\n\nconst SHAMIR_DOC_ID = 'recovery-shamir'\n\n/** Read the Shamir-recovery entries. Returns empty array when absent. */\nexport async function loadShamirRecoveryEntries(\n store: NoydbStore,\n vault: string,\n): Promise<ReadonlyArray<ShamirRecoveryEntry>> {\n const env = await store.get(vault, '_meta', SHAMIR_DOC_ID)\n if (!env) return []\n try {\n const doc = JSON.parse(env._data) as ShamirRecoveryDoc\n if (doc.profile !== 'shamir' || !Array.isArray(doc.entries)) return []\n return doc.entries\n } catch {\n return []\n }\n}\n\n/** Replace the Shamir-recovery entries (used by enrollment and rotation). */\nexport async function saveShamirRecoveryEntries(\n store: NoydbStore,\n vault: string,\n entries: ReadonlyArray<ShamirRecoveryEntry>,\n): Promise<void> {\n const doc: ShamirRecoveryDoc = {\n _noydb_recovery: 1,\n profile: 'shamir',\n entries,\n }\n const envelope: EncryptedEnvelope = {\n _noydb: NOYDB_FORMAT_VERSION,\n _v: 1,\n _ts: new Date().toISOString(),\n _iv: '',\n _data: JSON.stringify(doc),\n }\n await store.put(vault, '_meta', SHAMIR_DOC_ID, envelope)\n}\n\n/**\n * Mint a fresh Shamir recovery entry from a DEK set.\n *\n * 1. Generates a 32-byte recovery secret.\n * 2. Wraps the DEK set under that secret via\n * {@link mintWrappedDeksBlob} (the recovery secret is base64-\n * encoded as the credential string — PBKDF2 over high-entropy\n * input is harmless overhead).\n * 3. Splits the recovery secret via Shamir into `n` shares with\n * threshold `k`.\n * 4. Zeros the in-memory recovery secret after wrapping + splitting.\n *\n * Returns:\n * - `entry` — the {@link ShamirRecoveryEntry} to persist.\n * - `shareStrings` — the `n` Base32-encoded share strings to\n * return to the caller. The HUB MUST NOT PERSIST THESE; once\n * returned they are the user's responsibility.\n *\n * @param deks - DEK set to wrap.\n * @param entryId - Stable id for this entry (caller-supplied or\n * hub-generated).\n * @param k - Threshold (>= 2).\n * @param n - Total shares (k <= n <= 255).\n * @param label - Optional caller label.\n */\nexport async function mintShamirRecoveryEntry(\n provider: ShamirRecoveryProvider,\n deks: Map<string, EnclaveKey>,\n entryId: string,\n k: number,\n n: number,\n label?: string,\n): Promise<{ entry: ShamirRecoveryEntry; shareStrings: string[] }> {\n const recoverySecret = crypto.getRandomValues(new Uint8Array(32))\n try {\n const credential = bytesToBase64(recoverySecret)\n const blob = await mintWrappedDeksBlob(deks, credential)\n const shareStrings = provider.splitToShares(recoverySecret, k, n)\n const entry: ShamirRecoveryEntry = {\n ...blob, entryId, k, n,\n enrolledAt: new Date().toISOString(),\n ...(label !== undefined && { label }),\n }\n return { entry, shareStrings }\n } finally {\n recoverySecret.fill(0)\n }\n}\n\n/**\n * Decrypt a Shamir recovery entry to recover the raw DEK set.\n *\n * Combines K or more `shares`, reconstructs the recovery secret,\n * unwraps the DEKs via {@link unwrapDeksFromBlob}.\n *\n * Throws (AES-GCM auth-tag mismatch) when the shares don't combine\n * to the secret originally used to mint the entry — typically\n * because they came from a different enrollment or were tampered\n * with. Callers iterating multiple entries should catch.\n */\nexport async function unwrapDeksFromShamirEntry(\n provider: ShamirRecoveryProvider,\n entry: ShamirRecoveryEntry,\n shareStrings: readonly string[],\n): Promise<Map<string, EnclaveKey>> {\n if (shareStrings.length < entry.k) {\n throw new Error(\n `Insufficient shares: this Shamir entry needs ${entry.k} of ${entry.n}, `\n + `but ${shareStrings.length} were provided.`,\n )\n }\n const secret = provider.combineShares(shareStrings)\n try {\n return await unwrapDeksFromBlob(entry, bytesToBase64(secret))\n } finally {\n secret.fill(0)\n }\n}\n\nfunction bytesToBase64(b: Uint8Array): string {\n let s = ''\n for (const x of b) s += String.fromCharCode(x)\n return btoa(s)\n}\n\n/**\n * Generate one paper-recovery entry from an unlocked DEK set.\n *\n * Returns the serializable entry (persisted via\n * {@link savePaperRecoveryEntries}). The recovery flow unwraps the\n * DEK set, then mints a fresh KEK from the user's new secret.\n *\n * Thin wrapper over {@link mintWrappedDeksBlob} — the crypto\n * lives in the shared primitive; this function just adds paper-\n * recovery's own metadata (`codeId`, `enrolledAt`).\n *\n * @param deks Map of collection-name → DEK (extractable).\n * @param code The plaintext recovery code (caller-supplied;\n * pair this with `@noy-db/on-recovery`'s code\n * generator/parser if available).\n * @param codeId Stable id used by `burnPaperRecoveryEntry`.\n */\nexport async function mintPaperRecoveryEntry(\n deks: Map<string, EnclaveKey>,\n code: string,\n codeId: string,\n): Promise<PaperRecoveryEntry> {\n const blob = await mintWrappedDeksBlob(deks, code)\n return {\n ...blob,\n codeId,\n enrolledAt: new Date().toISOString(),\n }\n}\n\n/**\n * Decrypt a recovery entry to recover the raw DEK set. Used by the\n * `recoverSecret` flow after the user's code has been parsed.\n *\n * Thin wrapper over {@link unwrapDeksFromBlob}.\n *\n * @throws when the code does not match the entry (AES-GCM auth tag fail).\n */\nexport async function unwrapDeksFromPaperEntry(\n entry: PaperRecoveryEntry,\n code: string,\n): Promise<Map<string, EnclaveKey>> {\n return unwrapDeksFromBlob(entry, code)\n}\n\n// The crypto helpers (deriveRecoveryWrappingKey, bytesToBase64,\n// base64ToBytes) live in the canonical wrap-DEKs primitive at\n// `./wrapped-deks.ts` and are reached via\n// `mintWrappedDeksBlob` / `unwrapDeksFromBlob`.\n"],"mappings":"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","names":["bytesToBase64"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RecordCodec
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GEEJPID6.js";
|
|
4
4
|
|
|
5
5
|
// src/with-formula/materialized-views/stale.ts
|
|
6
6
|
var _staleByRegistry = /* @__PURE__ */ new WeakMap();
|
|
@@ -100,7 +100,7 @@ async function resolveStaleMVOnRead(accessor, outputCollection, dispatchCtx) {
|
|
|
100
100
|
continue;
|
|
101
101
|
}
|
|
102
102
|
if (executor === null) {
|
|
103
|
-
({ MaterializedViewExecutor: executor } = await import("./executor-
|
|
103
|
+
({ MaterializedViewExecutor: executor } = await import("./executor-SHYLIBOM.js"));
|
|
104
104
|
}
|
|
105
105
|
for (const dep of reg.dependencies) {
|
|
106
106
|
await accessor.getCollection(dep).ensureHydrated();
|
|
@@ -132,4 +132,4 @@ export {
|
|
|
132
132
|
clearMVStale,
|
|
133
133
|
clearMVStaleFully
|
|
134
134
|
};
|
|
135
|
-
//# sourceMappingURL=chunk-
|
|
135
|
+
//# sourceMappingURL=chunk-CKUYQO2P.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ValidationError
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6SYOHUHM.js";
|
|
4
4
|
|
|
5
5
|
// src/with-formula/overlay-views/with-overlayed-view.ts
|
|
6
6
|
function withOverlayedView(spec) {
|
|
@@ -33,4 +33,4 @@ function withOverlayedView(spec) {
|
|
|
33
33
|
export {
|
|
34
34
|
withOverlayedView
|
|
35
35
|
};
|
|
36
|
-
//# sourceMappingURL=chunk-
|
|
36
|
+
//# sourceMappingURL=chunk-D4LAJ737.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MaterializedViewConfigError,
|
|
3
3
|
ValidationError
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-6SYOHUHM.js";
|
|
5
5
|
|
|
6
6
|
// src/with-formula/materialized-views/with-materialized-view.ts
|
|
7
7
|
function withMaterializedView(spec) {
|
|
@@ -177,4 +177,4 @@ function withMaterializedView(spec) {
|
|
|
177
177
|
export {
|
|
178
178
|
withMaterializedView
|
|
179
179
|
};
|
|
180
|
-
//# sourceMappingURL=chunk-
|
|
180
|
+
//# sourceMappingURL=chunk-DYZ5CCVM.js.map
|