@noy-db/hub 0.10.0-pre.0 → 0.10.0-pre.2
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 +90 -0
- package/README.md +2 -2
- package/dist/api-QSEKT4KS.js +15 -0
- package/dist/as/index.js +19 -12
- package/dist/as/index.js.map +1 -1
- package/dist/at/index.js +6 -9
- package/dist/attestation/index.js +16 -19
- package/dist/attestation/index.js.map +1 -1
- package/dist/{backup-E3GBMNDA.js → backup-BHKVK5SD.js} +9 -12
- package/dist/{backup-E3GBMNDA.js.map → backup-BHKVK5SD.js.map} +1 -1
- package/dist/blobs/index.js +10 -13
- package/dist/blobs/index.js.map +1 -1
- package/dist/broker/index.js +11 -14
- package/dist/broker/index.js.map +1 -1
- package/dist/by/index.js +2 -2
- package/dist/capsule/contract.d.ts +176 -16
- package/dist/capsule/contract.js +35 -30
- package/dist/capsule/enclave-aes/bound.d.ts +11 -1
- package/dist/capsule/enclave-aes/classify/digest.d.ts +4 -2
- package/dist/capsule/enclave-aes/index.d.ts +9 -0
- package/dist/capsule/enclave-aes/index.js +643 -78
- package/dist/capsule/enclave-aes/index.js.map +1 -1
- package/dist/capsule/enclave-aes/primitives.d.ts +2 -2
- package/dist/capsule/index.d.ts +1 -1
- package/dist/capsule/plumbing/classify-reveal.d.ts +29 -0
- package/dist/capsule/plumbing/classify-verify.d.ts +53 -0
- package/dist/capsule/plumbing/index.d.ts +17 -0
- package/dist/{capsule-YQKQZPH7.js → capsule-YAITMH77.js} +16 -9
- package/dist/cargo/index.js +34 -36
- package/dist/{chunk-U43RSPPX.js → chunk-23A4SBRR.js} +3 -4
- package/dist/{chunk-U43RSPPX.js.map → chunk-23A4SBRR.js.map} +1 -1
- package/dist/{chunk-PREYKC76.js → chunk-24AGG4PP.js} +3 -3
- package/dist/{chunk-ZVJKHGE5.js → chunk-24IJZA4M.js} +14 -8
- package/dist/chunk-24IJZA4M.js.map +1 -0
- package/dist/{chunk-4N4OIVK3.js → chunk-2NVQ5SEA.js} +15 -5
- package/dist/chunk-2NVQ5SEA.js.map +1 -0
- package/dist/{chunk-ZNVBCKMV.js → chunk-2SIREKBR.js} +2 -2
- package/dist/{chunk-N6EWQ5VB.js → chunk-37FOG6AE.js} +14 -7
- package/dist/chunk-37FOG6AE.js.map +1 -0
- package/dist/{chunk-UQRKAM2R.js → chunk-3M2KK7O7.js} +3 -3
- package/dist/{chunk-P6FATPXQ.js → chunk-3ZCVY3T6.js} +2 -2
- package/dist/{chunk-PBEHTFUG.js → chunk-4CGWLHPQ.js} +6 -6
- package/dist/{chunk-PFHDQY5L.js → chunk-56Z4NOC6.js} +3 -3
- package/dist/{chunk-GLSCL74M.js → chunk-5AVIUJP4.js} +13 -9
- package/dist/chunk-5AVIUJP4.js.map +1 -0
- package/dist/{chunk-F7X7MPK4.js → chunk-5BGHAVBN.js} +2 -2
- package/dist/{chunk-QHG7GQKM.js → chunk-5JNEDW7I.js} +2 -2
- package/dist/{chunk-GHMINSER.js → chunk-5OWJSDTO.js} +4 -4
- package/dist/{chunk-GHMINSER.js.map → chunk-5OWJSDTO.js.map} +1 -1
- package/dist/{chunk-DYIR6RC3.js → chunk-5Y4MO7UQ.js} +4 -4
- package/dist/{chunk-TLIFNRU5.js → chunk-63LZLRWG.js} +2 -2
- package/dist/{chunk-GZISWBCJ.js → chunk-74W56QPY.js} +4 -4
- package/dist/{chunk-CLOSXHR4.js → chunk-7KNYLHVX.js} +3 -3
- package/dist/chunk-7KNYLHVX.js.map +1 -0
- package/dist/{chunk-LFUID7OW.js → chunk-7VJ2SIEI.js} +42 -22
- package/dist/chunk-7VJ2SIEI.js.map +1 -0
- package/dist/{chunk-WVQDY3UM.js → chunk-AHA7UALU.js} +4 -4
- package/dist/{chunk-U277K5GR.js → chunk-AIHBYQHF.js} +259 -123
- package/dist/chunk-AIHBYQHF.js.map +1 -0
- package/dist/{chunk-S2ORTQGS.js → chunk-ANYX2M6P.js} +3 -3
- package/dist/{chunk-6WEPFEMJ.js → chunk-ATKYMRBC.js} +4 -4
- package/dist/{chunk-7UHLO4ZN.js → chunk-B2HU55QD.js} +2 -2
- package/dist/{chunk-Y22JWHCP.js → chunk-BEKMLYQ4.js} +4 -4
- package/dist/{chunk-27NCEPKM.js → chunk-BPFXD4EX.js} +23 -3
- package/dist/chunk-BPFXD4EX.js.map +1 -0
- package/dist/{chunk-7C7OF26T.js → chunk-C2NXAR3D.js} +5 -5
- package/dist/{chunk-Z2KP6PNF.js → chunk-CJBWJO5P.js} +6 -6
- package/dist/{chunk-Z2KP6PNF.js.map → chunk-CJBWJO5P.js.map} +1 -1
- package/dist/{chunk-6WIFF46H.js → chunk-CQLMRCKX.js} +2 -2
- package/dist/{chunk-URGHKZPL.js → chunk-CR5UPYIJ.js} +1 -1
- package/dist/chunk-CR5UPYIJ.js.map +1 -0
- package/dist/{chunk-LMC5L3MM.js → chunk-DS2JXSUA.js} +6 -6
- package/dist/{chunk-X6Z2SIRF.js → chunk-E2JIE4S3.js} +2 -2
- package/dist/{chunk-EQ6C2ZPT.js → chunk-E7Y4J4GL.js} +2 -2
- package/dist/{chunk-LZN6DTFI.js → chunk-ECDM6YRX.js} +2 -2
- package/dist/{chunk-WRLWEYHY.js → chunk-EFINFZIF.js} +11 -1
- package/dist/chunk-EFINFZIF.js.map +1 -0
- package/dist/{chunk-2T2TGRWE.js → chunk-EM56MHN5.js} +6 -6
- package/dist/{chunk-NK4LGBGQ.js → chunk-EQXOJH6O.js} +4 -4
- package/dist/{chunk-OK44MD3C.js → chunk-ET3U2C3A.js} +2 -2
- package/dist/{chunk-RF5WDM25.js → chunk-EU4G7PQH.js} +2 -2
- package/dist/{chunk-K5JNK63R.js → chunk-EUCUYK5E.js} +10 -10
- package/dist/{chunk-2ODEFG6K.js → chunk-EWSIJDWO.js} +4 -4
- package/dist/{chunk-LJ744WXU.js → chunk-F2FD4LSB.js} +2 -2
- package/dist/{chunk-L4UL4IAR.js → chunk-F7ENMZYJ.js} +2 -2
- package/dist/{chunk-KBIS64X4.js → chunk-FIIVYQOW.js} +5 -5
- package/dist/{chunk-TBVDI3LY.js → chunk-FPK2LPLR.js} +8 -8
- package/dist/chunk-FPK2LPLR.js.map +1 -0
- package/dist/{chunk-BQJ5ORU5.js → chunk-FQEUJGBV.js} +2 -2
- package/dist/{chunk-UORWAG7K.js → chunk-FY4FFNKK.js} +2 -2
- package/dist/{chunk-MMWX5OT5.js → chunk-GDCYZGPB.js} +2 -2
- package/dist/{chunk-7ONMS7QI.js → chunk-H6Q7GKLI.js} +2 -2
- package/dist/{chunk-654RE5YC.js → chunk-HDAPQF6Q.js} +3 -3
- package/dist/{chunk-5SMCBPJ2.js → chunk-HRLMUZLI.js} +5 -5
- package/dist/{chunk-R4GLRJ55.js → chunk-HRNQYGLQ.js} +2 -2
- package/dist/{chunk-HTRLQ4ZZ.js → chunk-I4S7IZSS.js} +321 -235
- package/dist/chunk-I4S7IZSS.js.map +1 -0
- package/dist/{chunk-UNAMWK2Q.js → chunk-IN4XZJYD.js} +4 -3
- package/dist/{chunk-UNAMWK2Q.js.map → chunk-IN4XZJYD.js.map} +1 -1
- package/dist/{chunk-2RH5SEY7.js → chunk-ISIXU45J.js} +2 -2
- package/dist/chunk-ISIXU45J.js.map +1 -0
- package/dist/{chunk-NHEKQWFI.js → chunk-J4L4AS2W.js} +2 -2
- package/dist/{chunk-UMV5GBTJ.js → chunk-JPSOFWJR.js} +2 -2
- package/dist/{chunk-A52BK5AY.js → chunk-JRCO67NG.js} +2 -2
- package/dist/{chunk-FVKQ5QUC.js → chunk-JW3I3NNZ.js} +2 -2
- package/dist/{chunk-BNYDOF2Q.js → chunk-JZMPSQBH.js} +4 -4
- package/dist/{chunk-ZK2WT532.js → chunk-K5TVSOPQ.js} +5 -5
- package/dist/{chunk-2IASHJPS.js → chunk-K75SL74I.js} +2 -2
- package/dist/{chunk-O3NGOHXQ.js → chunk-KA4AAUST.js} +2 -2
- package/dist/chunk-KHSBMLQE.js +161 -0
- package/dist/chunk-KHSBMLQE.js.map +1 -0
- package/dist/{chunk-QQLXKDGC.js → chunk-KUGUML7H.js} +3 -3
- package/dist/{chunk-AMSSAOOB.js → chunk-L4J5NC2Q.js} +2 -2
- package/dist/{chunk-AYOQZJDB.js → chunk-LIX4QPMM.js} +2 -2
- package/dist/{chunk-WIUH6EJ7.js → chunk-LWQFGR34.js} +3 -3
- package/dist/{chunk-QNOWNCZS.js → chunk-MMSSHMHW.js} +2 -2
- package/dist/{chunk-T5EXREMW.js → chunk-N25Z6PR4.js} +6 -5
- package/dist/chunk-N25Z6PR4.js.map +1 -0
- package/dist/{chunk-ND6S2DKC.js → chunk-N5S2BCW3.js} +2 -2
- package/dist/{chunk-X5RGG3ZW.js → chunk-NIZH2TYZ.js} +2 -2
- package/dist/{chunk-SXPLIRQH.js → chunk-NLDBQHDV.js} +2 -2
- package/dist/{chunk-AI35LNWQ.js → chunk-NQQ4MOPC.js} +4 -4
- package/dist/{chunk-B6EWMGFX.js → chunk-NUBI6PYH.js} +2 -2
- package/dist/{chunk-ID4ZBC3X.js → chunk-NYEXUZDW.js} +4 -4
- package/dist/{chunk-GU6QXHPZ.js → chunk-OIBFIWVJ.js} +4 -4
- package/dist/chunk-OIBFIWVJ.js.map +1 -0
- package/dist/{chunk-PWAMX5KU.js → chunk-OOLF5FU2.js} +2 -2
- package/dist/{chunk-WQUIJPN4.js → chunk-OQA7UF2Q.js} +3 -3
- package/dist/{chunk-7JRLCP2J.js → chunk-ORJPICJI.js} +2 -2
- package/dist/{chunk-IF2M43G4.js → chunk-PBRD75JF.js} +2 -2
- package/dist/{chunk-RKBCUP5D.js → chunk-PDCBL23X.js} +39 -17
- package/dist/chunk-PDCBL23X.js.map +1 -0
- package/dist/{chunk-BSEPI3HD.js → chunk-PR36DD2X.js} +7 -7
- package/dist/{chunk-52ZFNK46.js → chunk-PV4HIC4E.js} +2 -2
- package/dist/{chunk-TU6ZJTYT.js → chunk-PZNK4HTV.js} +3 -3
- package/dist/{chunk-LUQBG637.js → chunk-QBLPLNPE.js} +3 -3
- package/dist/{chunk-UADW5NOU.js → chunk-QZXUISRF.js} +5 -5
- package/dist/{chunk-BMAXBTYH.js → chunk-RBXYO7MD.js} +5 -5
- package/dist/{chunk-PZN76MMV.js → chunk-RIACRFWA.js} +4 -4
- package/dist/{chunk-ICHHNI5E.js → chunk-RL6RNDJQ.js} +8 -8
- package/dist/{chunk-4WLI37WK.js → chunk-SJ2P7UXD.js} +7 -7
- package/dist/{chunk-2QH3I7QC.js → chunk-SWXS4LV3.js} +11 -11
- package/dist/{chunk-4KYKRYRT.js → chunk-TOTCZFRF.js} +2 -2
- package/dist/{chunk-CSAN22QG.js → chunk-TT4D52EK.js} +6 -6
- package/dist/{chunk-VLRY2UH4.js → chunk-U7CMJY5P.js} +10 -10
- package/dist/{chunk-NIUHKKMM.js → chunk-UNKTZDA2.js} +11 -11
- package/dist/{chunk-NIUHKKMM.js.map → chunk-UNKTZDA2.js.map} +1 -1
- package/dist/{chunk-QVUMKG3Z.js → chunk-UXH4AKH3.js} +95 -151
- package/dist/chunk-UXH4AKH3.js.map +1 -0
- package/dist/{chunk-PU5ZEV65.js → chunk-UZVHSYIF.js} +2 -2
- package/dist/{chunk-3EXQXOQ2.js → chunk-VHKAW5OI.js} +29 -5
- package/dist/chunk-VHKAW5OI.js.map +1 -0
- package/dist/{chunk-XQGKS4OW.js → chunk-VKZ3TDET.js} +3 -3
- package/dist/{chunk-ZOFNZLAA.js → chunk-VLZPUG5A.js} +4 -4
- package/dist/{chunk-OR3OHKU6.js → chunk-VSN5FLMI.js} +2 -2
- package/dist/{chunk-JQMEXCUZ.js → chunk-VYBBE7RN.js} +8 -8
- package/dist/chunk-VYBBE7RN.js.map +1 -0
- package/dist/{chunk-SVXFCCSX.js → chunk-WHK4JVKF.js} +8 -8
- package/dist/{chunk-YVA6UPRN.js → chunk-X4LDBALX.js} +2 -2
- package/dist/{chunk-35CNOVQE.js → chunk-X67E7NIJ.js} +3 -3
- package/dist/{chunk-IPF5ORDI.js → chunk-XMARKBUC.js} +3 -3
- package/dist/{chunk-B3B6HV5S.js → chunk-XMBBTETD.js} +2 -2
- package/dist/{chunk-GQ4MF3CM.js → chunk-XMBTAN5E.js} +28 -14
- package/dist/chunk-XMBTAN5E.js.map +1 -0
- package/dist/{chunk-FUHUGKDN.js → chunk-XROQP6IQ.js} +4 -4
- package/dist/chunk-XWWDZKLS.js +196 -0
- package/dist/chunk-XWWDZKLS.js.map +1 -0
- package/dist/{chunk-DL4GCSTS.js → chunk-Y2RYUWSQ.js} +10 -10
- package/dist/{chunk-7IV7GVOS.js → chunk-YCTSQV7K.js} +5 -5
- package/dist/{chunk-IFUYLIEY.js → chunk-YF7P2DG7.js} +2 -2
- package/dist/{chunk-PIO6NW7O.js → chunk-ZG7DBIVX.js} +2 -2
- package/dist/{chunk-NUZ7ROUZ.js → chunk-ZGJ775X7.js} +2 -2
- package/dist/{chunk-6BEBWHNL.js → chunk-ZQQLOEWN.js} +3 -3
- package/dist/classified/index.js +5 -5
- package/dist/{classified-marker-7HXDMYHI.js → classified-marker-HKMRC7SC.js} +3 -3
- package/dist/{reveal-UAPBG743.js → classify-reveal-YSJKJEIR.js} +7 -12
- package/dist/classify-reveal-YSJKJEIR.js.map +1 -0
- package/dist/{verify-QWO5UKD6.js → classify-verify-ZCTKYQ43.js} +31 -41
- package/dist/classify-verify-ZCTKYQ43.js.map +1 -0
- package/dist/collection-facade-PPKNUR2J.js +38 -0
- package/dist/{computed-LZLSYOPG.js → computed-UCYVPMZA.js} +3 -3
- package/dist/consent/index.js +6 -9
- package/dist/consent/index.js.map +1 -1
- package/dist/cover/index.js +6 -9
- package/dist/custody/index.js +11 -13
- package/dist/{dead-filter-PZIJ4F4G.js → dead-filter-YOBWRG4O.js} +8 -11
- package/dist/{dead-filter-PZIJ4F4G.js.map → dead-filter-YOBWRG4O.js.map} +1 -1
- package/dist/debug/index.js +5 -8
- package/dist/debug/index.js.map +1 -1
- package/dist/delegation-SNALWRMO.js +24 -0
- package/dist/derivations/index.js +20 -23
- package/dist/derive-4GNU4E4I.js +17 -0
- package/dist/directory/index.js +7 -10
- package/dist/{dispatch-4BUYPWMJ.js → dispatch-QW32QBLW.js} +17 -20
- package/dist/{dispatch-4BUYPWMJ.js.map → dispatch-QW32QBLW.js.map} +1 -1
- package/dist/{dispatch-KOPDDPEQ.js → dispatch-V5WLDTVX.js} +5 -5
- package/dist/executor-7QN22OKI.js +8 -0
- package/dist/executor-BJZJNVBB.js +8 -0
- package/dist/executor-JEAAEJUM.js +32 -0
- package/dist/export-accessible-TVO2KQPG.js +20 -0
- package/dist/extract-partition-43CLDRN4.js +33 -0
- package/dist/{fanout-sidecar-FYDRL7F3.js → fanout-sidecar-DDONAFJT.js} +6 -9
- package/dist/{fanout-sidecar-FYDRL7F3.js.map → fanout-sidecar-DDONAFJT.js.map} +1 -1
- package/dist/forget/index.js +6 -9
- package/dist/forget/index.js.map +1 -1
- package/dist/guards/index.js +4 -4
- package/dist/history/index.js +11 -14
- package/dist/history/index.js.map +1 -1
- package/dist/i18n/index.js +11 -14
- package/dist/i18n/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +122 -123
- package/dist/index.js.map +1 -1
- package/dist/indexing/index.js +5 -5
- package/dist/introspection/index.js +8 -11
- package/dist/issue-EIHEDAXM.js +16 -0
- package/dist/kernel/capsule-requirements.d.ts +84 -0
- package/dist/kernel/errors.d.ts +19 -3
- package/dist/kernel/export-scope.d.ts +28 -1
- package/dist/kernel/store-segment-guard.d.ts +29 -0
- package/dist/kernel/types.d.ts +169 -13
- package/dist/kernel/validation.d.ts +32 -0
- package/dist/kernel/vault.d.ts +5 -5
- package/dist/{ledger-UFJCB6T5.js → ledger-BMVUFQ3R.js} +8 -11
- package/dist/liberate-7KHXCLNJ.js +23 -0
- package/dist/link-set-PC2EJRKL.js +26 -0
- package/dist/{managed-secret-H5WYSJ46.js → managed-secret-RPRVEYZA.js} +7 -10
- package/dist/materialized-views/index.js +23 -26
- package/dist/{migrate-cek-SPESQAAO.js → migrate-cek-DTSCWGEF.js} +2 -2
- package/dist/money/index.js +5 -5
- package/dist/noydb-AYDNOXAX.js +98 -0
- package/dist/on/index.js +11 -13
- package/dist/overlay-views/index.js +4 -4
- package/dist/periods/index.js +9 -12
- package/dist/pod/index.js +65 -67
- package/dist/{pod-handle-GJLCOUGK.js → pod-handle-KBPQRN6Q.js} +6 -9
- package/dist/{pod-handle-GJLCOUGK.js.map → pod-handle-KBPQRN6Q.js.map} +1 -1
- package/dist/policy/index.js +7 -10
- package/dist/port/at/index.d.ts +1 -0
- package/dist/port/with/admission-authority.d.ts +2 -1
- package/dist/port/with/collection-registries.d.ts +37 -0
- package/dist/portability/index.js +8 -8
- package/dist/{post-register-2AC2STHC.js → post-register-RITSNASO.js} +6 -6
- package/dist/query/all/index.js +15 -15
- package/dist/query/index.js +7 -7
- package/dist/query/live/index.js +7 -7
- package/dist/query/reduce/index.js +10 -10
- package/dist/query/relate/index.js +9 -9
- package/dist/reduce/index.js +8 -8
- package/dist/register-BB34SK7E.js +27 -0
- package/dist/registry-65L6V5TO.js +20 -0
- package/dist/registry-HU62GPWA.js +32 -0
- package/dist/registry-SMNK6UM4.js +8 -0
- package/dist/registry-X5VPBKCO.js +8 -0
- package/dist/request-withdrawal-4SKAH46W.js +26 -0
- package/dist/{restore-to-OOPKLAPU.js → restore-to-VQHT67OH.js} +7 -10
- package/dist/{restore-to-OOPKLAPU.js.map → restore-to-VQHT67OH.js.map} +1 -1
- package/dist/revoke-IF5PX2JY.js +21 -0
- package/dist/satellites/index.js +1 -1
- package/dist/schema-update/index.js +2 -2
- package/dist/sealed-record/index.js +9 -12
- package/dist/sealed-record/index.js.map +1 -1
- package/dist/search/index.js +4 -4
- package/dist/{seed-BVYDPVIW.js → seed-F2OSDZJR.js} +11 -13
- package/dist/{seed-BVYDPVIW.js.map → seed-F2OSDZJR.js.map} +1 -1
- package/dist/sequence/index.js +6 -9
- package/dist/session/index.js +6 -9
- package/dist/session/index.js.map +1 -1
- package/dist/shadow/index.js +2 -2
- package/dist/signer-5P7IZC2K.js +20 -0
- package/dist/snapshots/index.js +8 -10
- package/dist/snapshots/index.js.map +1 -1
- package/dist/{stale-CROGJBIJ.js → stale-PUCZH5GJ.js} +7 -10
- package/dist/storage-5EGY5OYE.js +18 -0
- package/dist/store/index.js +1 -1
- package/dist/{store-coordination-provider-2YD2PTTP.js → store-coordination-provider-SEQM7SHJ.js} +7 -10
- package/dist/{store-coordination-provider-2YD2PTTP.js.map → store-coordination-provider-SEQM7SHJ.js.map} +1 -1
- package/dist/sync/index.js +13 -15
- package/dist/sync/index.js.map +1 -1
- package/dist/team/index.js +14 -16
- package/dist/tiers/index.js +8 -11
- package/dist/to/index.js +2 -2
- package/dist/transactions/index.js +3 -3
- package/dist/util/index.js +1 -1
- package/dist/vault-diff-H7LT5ZBJ.js +8 -0
- package/dist/vault-head/index.js +5 -8
- package/dist/vault-head/index.js.map +1 -1
- package/dist/walk-Z4N7PCZ2.js +17 -0
- package/dist/with-audit/portability/export-accessible.d.ts +3 -1
- package/dist/with-cargo/vault-diff.d.ts +28 -0
- package/dist/with-commit/sequence/index.d.ts +22 -0
- package/dist/with-party/team/delegation.d.ts +16 -12
- package/dist/with-party/team/keyring.d.ts +77 -0
- package/dist/with-party/team/managed-secret.d.ts +40 -3
- package/dist/with-party/team/noydb-facade.d.ts +2 -2
- package/dist/with-party/team/peer-recover.d.ts +2 -2
- package/dist/with-party/team/rotate-recover.d.ts +3 -3
- package/dist/with-shape/manifest/storage.d.ts +2 -2
- package/dist/with-shape/manifest/writer.d.ts +2 -2
- package/dist/with-store/store-middleware.d.ts +18 -5
- package/dist/with-sync/engine.d.ts +0 -2
- package/dist/withdraw-accessible-CRCIQSTL.js +23 -0
- package/package.json +3 -3
- package/dist/api-H4B7ZQXP.js +0 -18
- package/dist/capsule/enclave-aes/classify/reveal.d.ts +0 -10
- package/dist/capsule/enclave-aes/classify/verify.d.ts +0 -36
- package/dist/chunk-27NCEPKM.js.map +0 -1
- package/dist/chunk-2RH5SEY7.js.map +0 -1
- package/dist/chunk-3EXQXOQ2.js.map +0 -1
- package/dist/chunk-4N4OIVK3.js.map +0 -1
- package/dist/chunk-5A3HOTP5.js +0 -591
- package/dist/chunk-5A3HOTP5.js.map +0 -1
- package/dist/chunk-CLOSXHR4.js.map +0 -1
- package/dist/chunk-EPPAEGH5.js +0 -157
- package/dist/chunk-EPPAEGH5.js.map +0 -1
- package/dist/chunk-GLSCL74M.js.map +0 -1
- package/dist/chunk-GQ4MF3CM.js.map +0 -1
- package/dist/chunk-GU6QXHPZ.js.map +0 -1
- package/dist/chunk-HTRLQ4ZZ.js.map +0 -1
- package/dist/chunk-JQMEXCUZ.js.map +0 -1
- package/dist/chunk-LFUID7OW.js.map +0 -1
- package/dist/chunk-N6EWQ5VB.js.map +0 -1
- package/dist/chunk-NH2UOV3B.js +0 -18
- package/dist/chunk-NH2UOV3B.js.map +0 -1
- package/dist/chunk-O6VOR4NH.js +0 -27
- package/dist/chunk-O6VOR4NH.js.map +0 -1
- package/dist/chunk-ODBOHPZU.js +0 -22
- package/dist/chunk-ODBOHPZU.js.map +0 -1
- package/dist/chunk-Q4DBU5RM.js +0 -78
- package/dist/chunk-Q4DBU5RM.js.map +0 -1
- package/dist/chunk-QVUMKG3Z.js.map +0 -1
- package/dist/chunk-RKBCUP5D.js.map +0 -1
- package/dist/chunk-T5EXREMW.js.map +0 -1
- package/dist/chunk-TBVDI3LY.js.map +0 -1
- package/dist/chunk-U277K5GR.js.map +0 -1
- package/dist/chunk-URGHKZPL.js.map +0 -1
- package/dist/chunk-VZFTON37.js +0 -40
- package/dist/chunk-VZFTON37.js.map +0 -1
- package/dist/chunk-WRLWEYHY.js.map +0 -1
- package/dist/chunk-ZVJKHGE5.js.map +0 -1
- package/dist/collection-facade-V63BEBGU.js +0 -41
- package/dist/delegation-7SSGIETH.js +0 -26
- package/dist/derive-IMI3BMDS.js +0 -20
- package/dist/executor-7SY3M6TZ.js +0 -8
- package/dist/executor-SNHW4ELF.js +0 -35
- package/dist/executor-Z3IKK7G3.js +0 -8
- package/dist/export-accessible-O7DIVFUO.js +0 -22
- package/dist/extract-partition-ELZEJKXM.js +0 -35
- package/dist/find-KWB2RYWJ.js +0 -10
- package/dist/issue-5ZHXK4HJ.js +0 -19
- package/dist/liberate-OEOSNNFH.js +0 -25
- package/dist/link-set-KMUBFN2L.js +0 -29
- package/dist/noydb-QDZ4QIKO.js +0 -100
- package/dist/register-PIORIMMR.js +0 -30
- package/dist/registry-BIMICFUH.js +0 -23
- package/dist/registry-JGWLW53D.js +0 -35
- package/dist/registry-SELCBYEQ.js +0 -8
- package/dist/registry-W327UNYH.js +0 -8
- package/dist/request-withdrawal-K3SXXODI.js +0 -28
- package/dist/reveal-UAPBG743.js.map +0 -1
- package/dist/revoke-6AFVXTWF.js +0 -24
- package/dist/signer-TW5MAQJP.js +0 -23
- package/dist/storage-SO2L2Z5J.js +0 -21
- package/dist/vault-diff-C6WUJFNP.js +0 -8
- package/dist/verify-QWO5UKD6.js.map +0 -1
- package/dist/walk-3KEOL7BY.js +0 -20
- package/dist/withdraw-accessible-OZU525ND.js +0 -25
- package/dist/withdraw-accessible-OZU525ND.js.map +0 -1
- /package/dist/{api-H4B7ZQXP.js.map → api-QSEKT4KS.js.map} +0 -0
- /package/dist/{capsule-YQKQZPH7.js.map → capsule-YAITMH77.js.map} +0 -0
- /package/dist/{chunk-PREYKC76.js.map → chunk-24AGG4PP.js.map} +0 -0
- /package/dist/{chunk-ZNVBCKMV.js.map → chunk-2SIREKBR.js.map} +0 -0
- /package/dist/{chunk-UQRKAM2R.js.map → chunk-3M2KK7O7.js.map} +0 -0
- /package/dist/{chunk-P6FATPXQ.js.map → chunk-3ZCVY3T6.js.map} +0 -0
- /package/dist/{chunk-PBEHTFUG.js.map → chunk-4CGWLHPQ.js.map} +0 -0
- /package/dist/{chunk-PFHDQY5L.js.map → chunk-56Z4NOC6.js.map} +0 -0
- /package/dist/{chunk-F7X7MPK4.js.map → chunk-5BGHAVBN.js.map} +0 -0
- /package/dist/{chunk-QHG7GQKM.js.map → chunk-5JNEDW7I.js.map} +0 -0
- /package/dist/{chunk-DYIR6RC3.js.map → chunk-5Y4MO7UQ.js.map} +0 -0
- /package/dist/{chunk-TLIFNRU5.js.map → chunk-63LZLRWG.js.map} +0 -0
- /package/dist/{chunk-GZISWBCJ.js.map → chunk-74W56QPY.js.map} +0 -0
- /package/dist/{chunk-WVQDY3UM.js.map → chunk-AHA7UALU.js.map} +0 -0
- /package/dist/{chunk-S2ORTQGS.js.map → chunk-ANYX2M6P.js.map} +0 -0
- /package/dist/{chunk-6WEPFEMJ.js.map → chunk-ATKYMRBC.js.map} +0 -0
- /package/dist/{chunk-7UHLO4ZN.js.map → chunk-B2HU55QD.js.map} +0 -0
- /package/dist/{chunk-Y22JWHCP.js.map → chunk-BEKMLYQ4.js.map} +0 -0
- /package/dist/{chunk-7C7OF26T.js.map → chunk-C2NXAR3D.js.map} +0 -0
- /package/dist/{chunk-6WIFF46H.js.map → chunk-CQLMRCKX.js.map} +0 -0
- /package/dist/{chunk-LMC5L3MM.js.map → chunk-DS2JXSUA.js.map} +0 -0
- /package/dist/{chunk-X6Z2SIRF.js.map → chunk-E2JIE4S3.js.map} +0 -0
- /package/dist/{chunk-EQ6C2ZPT.js.map → chunk-E7Y4J4GL.js.map} +0 -0
- /package/dist/{chunk-LZN6DTFI.js.map → chunk-ECDM6YRX.js.map} +0 -0
- /package/dist/{chunk-2T2TGRWE.js.map → chunk-EM56MHN5.js.map} +0 -0
- /package/dist/{chunk-NK4LGBGQ.js.map → chunk-EQXOJH6O.js.map} +0 -0
- /package/dist/{chunk-OK44MD3C.js.map → chunk-ET3U2C3A.js.map} +0 -0
- /package/dist/{chunk-RF5WDM25.js.map → chunk-EU4G7PQH.js.map} +0 -0
- /package/dist/{chunk-K5JNK63R.js.map → chunk-EUCUYK5E.js.map} +0 -0
- /package/dist/{chunk-2ODEFG6K.js.map → chunk-EWSIJDWO.js.map} +0 -0
- /package/dist/{chunk-LJ744WXU.js.map → chunk-F2FD4LSB.js.map} +0 -0
- /package/dist/{chunk-L4UL4IAR.js.map → chunk-F7ENMZYJ.js.map} +0 -0
- /package/dist/{chunk-KBIS64X4.js.map → chunk-FIIVYQOW.js.map} +0 -0
- /package/dist/{chunk-BQJ5ORU5.js.map → chunk-FQEUJGBV.js.map} +0 -0
- /package/dist/{chunk-UORWAG7K.js.map → chunk-FY4FFNKK.js.map} +0 -0
- /package/dist/{chunk-MMWX5OT5.js.map → chunk-GDCYZGPB.js.map} +0 -0
- /package/dist/{chunk-7ONMS7QI.js.map → chunk-H6Q7GKLI.js.map} +0 -0
- /package/dist/{chunk-654RE5YC.js.map → chunk-HDAPQF6Q.js.map} +0 -0
- /package/dist/{chunk-5SMCBPJ2.js.map → chunk-HRLMUZLI.js.map} +0 -0
- /package/dist/{chunk-R4GLRJ55.js.map → chunk-HRNQYGLQ.js.map} +0 -0
- /package/dist/{chunk-NHEKQWFI.js.map → chunk-J4L4AS2W.js.map} +0 -0
- /package/dist/{chunk-UMV5GBTJ.js.map → chunk-JPSOFWJR.js.map} +0 -0
- /package/dist/{chunk-A52BK5AY.js.map → chunk-JRCO67NG.js.map} +0 -0
- /package/dist/{chunk-FVKQ5QUC.js.map → chunk-JW3I3NNZ.js.map} +0 -0
- /package/dist/{chunk-BNYDOF2Q.js.map → chunk-JZMPSQBH.js.map} +0 -0
- /package/dist/{chunk-ZK2WT532.js.map → chunk-K5TVSOPQ.js.map} +0 -0
- /package/dist/{chunk-2IASHJPS.js.map → chunk-K75SL74I.js.map} +0 -0
- /package/dist/{chunk-O3NGOHXQ.js.map → chunk-KA4AAUST.js.map} +0 -0
- /package/dist/{chunk-QQLXKDGC.js.map → chunk-KUGUML7H.js.map} +0 -0
- /package/dist/{chunk-AMSSAOOB.js.map → chunk-L4J5NC2Q.js.map} +0 -0
- /package/dist/{chunk-AYOQZJDB.js.map → chunk-LIX4QPMM.js.map} +0 -0
- /package/dist/{chunk-WIUH6EJ7.js.map → chunk-LWQFGR34.js.map} +0 -0
- /package/dist/{chunk-QNOWNCZS.js.map → chunk-MMSSHMHW.js.map} +0 -0
- /package/dist/{chunk-ND6S2DKC.js.map → chunk-N5S2BCW3.js.map} +0 -0
- /package/dist/{chunk-X5RGG3ZW.js.map → chunk-NIZH2TYZ.js.map} +0 -0
- /package/dist/{chunk-SXPLIRQH.js.map → chunk-NLDBQHDV.js.map} +0 -0
- /package/dist/{chunk-AI35LNWQ.js.map → chunk-NQQ4MOPC.js.map} +0 -0
- /package/dist/{chunk-B6EWMGFX.js.map → chunk-NUBI6PYH.js.map} +0 -0
- /package/dist/{chunk-ID4ZBC3X.js.map → chunk-NYEXUZDW.js.map} +0 -0
- /package/dist/{chunk-PWAMX5KU.js.map → chunk-OOLF5FU2.js.map} +0 -0
- /package/dist/{chunk-WQUIJPN4.js.map → chunk-OQA7UF2Q.js.map} +0 -0
- /package/dist/{chunk-7JRLCP2J.js.map → chunk-ORJPICJI.js.map} +0 -0
- /package/dist/{chunk-IF2M43G4.js.map → chunk-PBRD75JF.js.map} +0 -0
- /package/dist/{chunk-BSEPI3HD.js.map → chunk-PR36DD2X.js.map} +0 -0
- /package/dist/{chunk-52ZFNK46.js.map → chunk-PV4HIC4E.js.map} +0 -0
- /package/dist/{chunk-TU6ZJTYT.js.map → chunk-PZNK4HTV.js.map} +0 -0
- /package/dist/{chunk-LUQBG637.js.map → chunk-QBLPLNPE.js.map} +0 -0
- /package/dist/{chunk-UADW5NOU.js.map → chunk-QZXUISRF.js.map} +0 -0
- /package/dist/{chunk-BMAXBTYH.js.map → chunk-RBXYO7MD.js.map} +0 -0
- /package/dist/{chunk-PZN76MMV.js.map → chunk-RIACRFWA.js.map} +0 -0
- /package/dist/{chunk-ICHHNI5E.js.map → chunk-RL6RNDJQ.js.map} +0 -0
- /package/dist/{chunk-4WLI37WK.js.map → chunk-SJ2P7UXD.js.map} +0 -0
- /package/dist/{chunk-2QH3I7QC.js.map → chunk-SWXS4LV3.js.map} +0 -0
- /package/dist/{chunk-4KYKRYRT.js.map → chunk-TOTCZFRF.js.map} +0 -0
- /package/dist/{chunk-CSAN22QG.js.map → chunk-TT4D52EK.js.map} +0 -0
- /package/dist/{chunk-VLRY2UH4.js.map → chunk-U7CMJY5P.js.map} +0 -0
- /package/dist/{chunk-PU5ZEV65.js.map → chunk-UZVHSYIF.js.map} +0 -0
- /package/dist/{chunk-XQGKS4OW.js.map → chunk-VKZ3TDET.js.map} +0 -0
- /package/dist/{chunk-ZOFNZLAA.js.map → chunk-VLZPUG5A.js.map} +0 -0
- /package/dist/{chunk-OR3OHKU6.js.map → chunk-VSN5FLMI.js.map} +0 -0
- /package/dist/{chunk-SVXFCCSX.js.map → chunk-WHK4JVKF.js.map} +0 -0
- /package/dist/{chunk-YVA6UPRN.js.map → chunk-X4LDBALX.js.map} +0 -0
- /package/dist/{chunk-35CNOVQE.js.map → chunk-X67E7NIJ.js.map} +0 -0
- /package/dist/{chunk-IPF5ORDI.js.map → chunk-XMARKBUC.js.map} +0 -0
- /package/dist/{chunk-B3B6HV5S.js.map → chunk-XMBBTETD.js.map} +0 -0
- /package/dist/{chunk-FUHUGKDN.js.map → chunk-XROQP6IQ.js.map} +0 -0
- /package/dist/{chunk-DL4GCSTS.js.map → chunk-Y2RYUWSQ.js.map} +0 -0
- /package/dist/{chunk-7IV7GVOS.js.map → chunk-YCTSQV7K.js.map} +0 -0
- /package/dist/{chunk-IFUYLIEY.js.map → chunk-YF7P2DG7.js.map} +0 -0
- /package/dist/{chunk-PIO6NW7O.js.map → chunk-ZG7DBIVX.js.map} +0 -0
- /package/dist/{chunk-NUZ7ROUZ.js.map → chunk-ZGJ775X7.js.map} +0 -0
- /package/dist/{chunk-6BEBWHNL.js.map → chunk-ZQQLOEWN.js.map} +0 -0
- /package/dist/{classified-marker-7HXDMYHI.js.map → classified-marker-HKMRC7SC.js.map} +0 -0
- /package/dist/{collection-facade-V63BEBGU.js.map → collection-facade-PPKNUR2J.js.map} +0 -0
- /package/dist/{computed-LZLSYOPG.js.map → computed-UCYVPMZA.js.map} +0 -0
- /package/dist/{delegation-7SSGIETH.js.map → delegation-SNALWRMO.js.map} +0 -0
- /package/dist/{derive-IMI3BMDS.js.map → derive-4GNU4E4I.js.map} +0 -0
- /package/dist/{dispatch-KOPDDPEQ.js.map → dispatch-V5WLDTVX.js.map} +0 -0
- /package/dist/{executor-7SY3M6TZ.js.map → executor-7QN22OKI.js.map} +0 -0
- /package/dist/{executor-SNHW4ELF.js.map → executor-BJZJNVBB.js.map} +0 -0
- /package/dist/{executor-Z3IKK7G3.js.map → executor-JEAAEJUM.js.map} +0 -0
- /package/dist/{export-accessible-O7DIVFUO.js.map → export-accessible-TVO2KQPG.js.map} +0 -0
- /package/dist/{extract-partition-ELZEJKXM.js.map → extract-partition-43CLDRN4.js.map} +0 -0
- /package/dist/{find-KWB2RYWJ.js.map → issue-EIHEDAXM.js.map} +0 -0
- /package/dist/{issue-5ZHXK4HJ.js.map → ledger-BMVUFQ3R.js.map} +0 -0
- /package/dist/{ledger-UFJCB6T5.js.map → liberate-7KHXCLNJ.js.map} +0 -0
- /package/dist/{liberate-OEOSNNFH.js.map → link-set-PC2EJRKL.js.map} +0 -0
- /package/dist/{link-set-KMUBFN2L.js.map → managed-secret-RPRVEYZA.js.map} +0 -0
- /package/dist/{migrate-cek-SPESQAAO.js.map → migrate-cek-DTSCWGEF.js.map} +0 -0
- /package/dist/{managed-secret-H5WYSJ46.js.map → noydb-AYDNOXAX.js.map} +0 -0
- /package/dist/{post-register-2AC2STHC.js.map → post-register-RITSNASO.js.map} +0 -0
- /package/dist/{noydb-QDZ4QIKO.js.map → register-BB34SK7E.js.map} +0 -0
- /package/dist/{register-PIORIMMR.js.map → registry-65L6V5TO.js.map} +0 -0
- /package/dist/{registry-BIMICFUH.js.map → registry-HU62GPWA.js.map} +0 -0
- /package/dist/{registry-JGWLW53D.js.map → registry-SMNK6UM4.js.map} +0 -0
- /package/dist/{registry-SELCBYEQ.js.map → registry-X5VPBKCO.js.map} +0 -0
- /package/dist/{registry-W327UNYH.js.map → request-withdrawal-4SKAH46W.js.map} +0 -0
- /package/dist/{request-withdrawal-K3SXXODI.js.map → revoke-IF5PX2JY.js.map} +0 -0
- /package/dist/{revoke-6AFVXTWF.js.map → signer-5P7IZC2K.js.map} +0 -0
- /package/dist/{signer-TW5MAQJP.js.map → stale-PUCZH5GJ.js.map} +0 -0
- /package/dist/{stale-CROGJBIJ.js.map → storage-5EGY5OYE.js.map} +0 -0
- /package/dist/{storage-SO2L2Z5J.js.map → vault-diff-H7LT5ZBJ.js.map} +0 -0
- /package/dist/{vault-diff-C6WUJFNP.js.map → walk-Z4N7PCZ2.js.map} +0 -0
- /package/dist/{walk-3KEOL7BY.js.map → withdraw-accessible-CRCIQSTL.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/capsule/plumbing/sealed-slot.ts","../src/capsule/plumbing/sealed-slots.ts","../src/capsule/plumbing/normalize.ts","../src/kernel/schema.ts","../src/capsule/plumbing/record-codec.ts","../src/capsule/plumbing/lifecycle.ts","../src/capsule/plumbing/rekey.ts","../src/capsule/plumbing/rekey-blob.ts","../src/capsule/plumbing/sealing.ts","../src/capsule/plumbing/deterministic.ts","../src/capsule/plumbing/index.ts","../src/capsule/plumbing/digest.ts","../src/capsule/plumbing/sign.ts","../src/capsule/plumbing/ct-equal.ts","../src/capsule/plumbing/kofn.ts","../src/capsule/contract.ts"],"sourcesContent":["/**\n * Sealed-slot (`iv:data`) parsing + the CEK/DEK dual-read, factored out so the\n * three callers that decrypt a `_sealed[field]` slot share ONE implementation\n * with no coupling between them:\n *\n * - {@link RecordCodec.unsealField} / `classifySealedShred` (read + classify),\n * - `record-keys/sealing.ts:rotateRecordCek` (re-seal under a new CEK).\n *\n * These are pure helpers over an explicit `(cek, dek)` pair — NOT a context\n * object — so `sealing.ts` (which holds only the DEK) can import them without\n * pulling in the whole codec, avoiding a codec↔sealing dependency cycle.\n */\nimport type { CapsulePrimitives } from '../contract.js'\n\n/** Parse an `iv:data` sealed slot (split on the FIRST `:`). Cipher-free, so it\n * needs no primitives and stays a plain module-level export. */\nexport function parseSealedSlot(blob: string): { iv: string; data: string } {\n const sep = blob.indexOf(':')\n return { iv: blob.slice(0, sep), data: blob.slice(sep + 1) }\n}\n\nexport function makeSealedSlot(p: CapsulePrimitives) {\n const { decrypt, deriveSealedFieldKey, deriveSealedFieldKeyFromCek } = p\n\n/**\n * Dual-read: decrypt a sealed slot trying the CEK-derived field key first,\n * falling back to the collection-DEK field key. Legacy records\n * (even ones whose body is CEK-encrypted) are sealed under the collection-DEK\n * key; current writes seal under the per-record CEK. Try the CEK key first; on\n * its AES-GCM auth failure, fall back to the DEK key. Throws only if BOTH fail.\n * Returns the plaintext string (callers `JSON.parse` as needed).\n */\n async function dualReadSealedSlot(\n blob: string,\n field: string,\n collection: string,\n cek: CryptoKey | undefined,\n dek: CryptoKey,\n): Promise<string> {\n const { iv, data } = parseSealedSlot(blob)\n if (cek !== undefined) {\n try {\n const fieldKey = await deriveSealedFieldKeyFromCek(cek, collection, field)\n return await decrypt(iv, data, fieldKey)\n } catch {\n // fall through to the legacy collection-DEK derivation\n }\n }\n const fieldKey = await deriveSealedFieldKey(dek, collection, field)\n return decrypt(iv, data, fieldKey)\n}\n\n return { dualReadSealedSlot }\n}\n","/**\n * Sealed-slot sub-step — the \"seal these declared fields into their own\n * `iv:data` slots\" mechanism, extracted byte-parity from `RecordCodec`\n * (#629 Task 1; see `.superpowers/sdd/seam-map-classified-blobs.md` §2).\n *\n * This is the ONE genuinely separable sub-step `RecordCodec.encryptRecord`/\n * `decryptRecord` perform (the digest-only `_vdig`/`_bidx` mechanism is NOT\n * separable the same way — it needs write-history `prev` context, see the\n * seam map's separability verdict). `sealFields`/`unsealFields` are the\n * natural shape of a `via` `encodeAtRest`/`decodeAtRest` hook for a \"seal\n * this field\" via-feature, and `makeSealedSlotCapability`/\n * `makeReservedEnvelopes` (below) are the `ViaCryptoCtx` capability\n * factories built on top of them.\n *\n * The capability factories live here (rather than a sibling\n * `kernel/via-crypto.ts`) so they can call `deriveSealedFieldKey`/\n * `deriveSealedFieldKeyFromCek`/`encrypt`/`decrypt` directly — this file IS\n * kernel enclave code (C3), so it is exempt from `enclave-barrel-only` the\n * same way `record-codec.ts`/`sealing.ts` are. A file outside\n * `capsule/enclave-aes/**` would have had to go through the enclave barrel\n * (`capsule/enclave-aes/index.ts`), which does not (yet) export these symbols.\n */\nimport { buildRecordEnvelope } from './record-envelope.js'\nimport { buildRecordAad, recordAadFor } from './record-aad.js'\nimport { SealedHandle, type EncryptedEnvelope } from '../../kernel/types.js'\nimport { ValidationError } from '../../kernel/errors.js'\nimport type { SealedSlotRef, ViaCryptoCtx } from '../../kernel/via/index.js'\nimport { sealedBodyArgs } from './envelope-body.js'\n\n/**\n * Key material for one collection's sealed-field derivation. `cek`, when\n * supplied, is preferred (record-scoped); `getDEK` is the fallback (and,\n * for legacy/no-CEK collections, the only key) — mirrors the dual-read\n * `RecordCodec` has always done.\n */\nimport type { CapsuleKey, CapsulePrimitives } from '../contract.js'\n\nexport interface SealKeyMaterial {\n readonly collection: string\n readonly cek?: CapsuleKey\n getDEK(): Promise<CapsuleKey>\n}\n\nexport interface SealFieldsResult {\n readonly openRecord: Record<string, unknown>\n readonly sealed: Record<string, string> | undefined\n}\n\n/** What {@link makeSealedSlotCapability} needs — a subset of `RecordCodecContext`. */\nexport interface SealedSlotCapabilityCtx {\n readonly name: string\n getDEK(): Promise<CapsuleKey>\n}\n\n/** Resolve the DEK for an arbitrary (possibly synthetic, e.g. `_dict_en`) collection name. */\nexport type ReservedEnvelopeDekResolver = (collection: string) => Promise<CapsuleKey>\n\nexport function makeSealedSlots(p: CapsulePrimitives, sealedSlot: { dualReadSealedSlot: (blob: string, field: string, collection: string, cek: CapsuleKey | undefined, dek: CapsuleKey) => Promise<string> }) {\n const { decrypt, deriveSealedFieldKey, deriveSealedFieldKeyFromCek, encrypt } = p\n const { dualReadSealedSlot } = sealedSlot\n\n /** Seal one field's value to an `iv:data` slot under its derived per-field key. */\n async function sealOneFieldWithDek(\n field: string,\n value: unknown,\n dek: CapsuleKey,\n keyMaterial: SealKeyMaterial,\n ): Promise<SealedSlotRef> {\n const fieldKey = keyMaterial.cek !== undefined\n ? await deriveSealedFieldKeyFromCek(keyMaterial.cek, keyMaterial.collection, field)\n : await deriveSealedFieldKey(dek, keyMaterial.collection, field)\n return encrypt(JSON.stringify(value), fieldKey)\n }\n\n /**\n * Seal one field's value on demand — fetches the DEK itself. A singular\n * counterpart to {@link sealFields} for callers that seal one field at a\n * time rather than a whole record. Not exported — `makeSealedSlotCapability`\n * is the only caller (#629 Task 11: knip-flagged unused export, dropped;\n * intra-module use only).\n */\n async function sealOneField(\n field: string,\n value: unknown,\n keyMaterial: SealKeyMaterial,\n ): Promise<SealedSlotRef> {\n const dek = await keyMaterial.getDEK()\n return sealOneFieldWithDek(field, value, dek, keyMaterial)\n }\n\n /**\n * Peel declared `sensitiveFields` out of `record` BEFORE building `_data`,\n * sealing each into its own `iv:data` slot. Extracted byte-parity from\n * `RecordCodec.encryptRecord` step 2 (pre-#629 `record-codec.ts:261-288`).\n * Returns `record` untouched (and `sealed: undefined`) when no declared\n * field is present with a defined value — the envelope stays byte-identical\n * to legacy output, exactly as before.\n */\n async function sealFields(\n record: Record<string, unknown>,\n sensitiveFields: ReadonlySet<string>,\n keyMaterial: SealKeyMaterial,\n ): Promise<SealFieldsResult> {\n const dek = await keyMaterial.getDEK()\n const open: Record<string, unknown> = { ...record }\n const slots: Record<string, string> = {}\n for (const field of sensitiveFields) {\n if (!(field in record)) continue\n const value = record[field]\n if (value === undefined) continue\n const { iv, data } = await sealOneFieldWithDek(field, value, dek, keyMaterial)\n slots[field] = `${iv}:${data}`\n delete open[field]\n }\n if (Object.keys(slots).length === 0) return { openRecord: record, sealed: undefined }\n return { openRecord: open, sealed: slots }\n }\n\n /**\n * Unseal one `_sealed[field]` slot to its plaintext value: dual-read (try\n * the CEK-derived key, fall back to the DEK-derived key for legacy\n * records), AES-GCM-decrypt the `iv:data` blob, and JSON-parse the result.\n * Extracted byte-parity from `RecordCodec.unsealField`\n * (pre-#629 `record-codec.ts:485-493`).\n */\n async function unsealOneField(field: string, blob: string, keyMaterial: SealKeyMaterial): Promise<unknown> {\n const dek = await keyMaterial.getDEK()\n return JSON.parse(await dualReadSealedSlot(blob, field, keyMaterial.collection, keyMaterial.cek, dek))\n }\n\n /**\n * Build a non-leaking {@link SealedHandle} producer over sealed key\n * material: each call captures only the ciphertext `blob` and a closure to\n * {@link unsealOneField} — the plaintext is never stored on the handle, and\n * (crucially) the DEK is fetched lazily on `reveal()`, never at handle-\n * construction time. Extracted byte-parity from\n * `RecordCodec.makeSealedHandle` (pre-#629 `record-codec.ts:552-554`).\n */\n function makeHandleProducer(keyMaterial: SealKeyMaterial): (field: string, blob: string) => SealedHandle<unknown> {\n return (field, blob) => new SealedHandle(() => unsealOneField(field, blob, keyMaterial))\n }\n\n /**\n * Restore every `[field]: blob` entry in `sealed` onto `record` (mutated in\n * place, matching the original's mutate-then-return shape): eagerly unseal\n * to plaintext (`opts.asHandles` falsy) or wrap each in a\n * {@link SealedHandle} (`opts.asHandles: true`) so the plaintext is never\n * materialised. Extracted byte-parity from `RecordCodec.decryptRecord`'s\n * sealed-handle block (pre-#629 `record-codec.ts:615-623`).\n */\n async function unsealFields(\n record: Record<string, unknown>,\n sealed: Record<string, string>,\n keyMaterial: SealKeyMaterial,\n opts?: { asHandles?: boolean },\n ): Promise<Record<string, unknown>> {\n const makeHandle = opts?.asHandles === true ? makeHandleProducer(keyMaterial) : undefined\n for (const [field, blob] of Object.entries(sealed)) {\n record[field] = makeHandle !== undefined ? makeHandle(field, blob) : await unsealOneField(field, blob, keyMaterial)\n }\n return record\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // ViaCryptoCtx capability factories (#629 Task 1, second commit)\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Build a `ViaCryptoCtx.sealedSlots` capability pre-bound to one\n * `(collection, recordId)`. The key material (`cek`/DEK resolver) is closed\n * over and never placed on the returned object — `Object.keys`/\n * `Object.values` of the capability expose only the three methods, never a\n * key (zero-knowledge: capabilities never expose keys).\n *\n * `delete(field)` is **advisory and capability-instance-local, not real\n * erasure** — it has no store to reach and never will by itself: it just\n * adds `field` to a `Set` closed over by THIS ONE capability object, so a\n * later `unseal` call on that SAME object refuses. No I/O happens. A fresh\n * capability for the same record (e.g. `seal()`'d again, or a new\n * `makeSealedSlotCapability(...)` call) has an empty set and sees no\n * deletion at all — `seal()` on a given field also clears any prior mark\n * for it. Do not build production logic on `delete()` persisting anything.\n * The real erasure guarantee for a forgotten record is `vault.forget()`'s\n * unconditional `_writeTombstone` call, which replaces the whole live\n * envelope with a fresh, `_sealed`-free tombstone — `_sealed` disappears\n * from the store regardless of what any capability's `delete()` marked.\n * (#629 Task 10 traced this end-to-end; see task-10-report.md \"sealedSlots\n * .delete()\" section. Removing/changing this method's shape is a phase-E\n * decision, not implied by this comment.)\n */\n function makeSealedSlotCapability(\n ctx: SealedSlotCapabilityCtx,\n recordId: string,\n cek?: CapsuleKey,\n ): ViaCryptoCtx['sealedSlots'] {\n const keyMaterial: SealKeyMaterial = {\n collection: ctx.name,\n ...(cek !== undefined ? { cek } : {}),\n getDEK: () => ctx.getDEK(),\n }\n const deletedFields = new Set<string>()\n\n return {\n async seal(field, plaintext) {\n const ref = await sealOneField(field, plaintext, keyMaterial)\n deletedFields.delete(field)\n return ref\n },\n async unseal(field, ref) {\n if (deletedFields.has(field)) {\n throw new ValidationError(`sealedSlots.unseal: field \"${field}\" on record \"${recordId}\" was deleted from this capability`)\n }\n return unsealOneField(field, `${ref.iv}:${ref.data}`, keyMaterial)\n },\n async delete(field) {\n deletedFields.add(field)\n },\n }\n }\n\n /**\n * Build a `ViaCryptoCtx.reservedEnvelopes` capability: a whole-envelope\n * encrypt/decrypt door scoped to collection names under a declared prefix\n * (e.g. `_dict_`) — the shape `via/i18n/dictionary.ts`'s\n * `DictionaryHandle` needs (seam map Part 3): build JSON → encrypt under a\n * DEK → wrap in an `EncryptedEnvelope`; decrypt → JSON text. No per-record\n * CEK — reserved envelopes are whole-DEK-encrypted, same as the dictionary\n * grandfather.\n *\n * `reservedEnvelopes(prefix)` throws `ValidationError` immediately when\n * `prefix` is not in `declaredPrefixes`; each `encrypt`/`decrypt` call\n * throws `ValidationError` when its `collection` argument does not start\n * with `prefix`.\n */\n function makeReservedEnvelopes(\n dekResolver: ReservedEnvelopeDekResolver,\n declaredPrefixes: readonly string[],\n ): ViaCryptoCtx['reservedEnvelopes'] {\n const declared = new Set(declaredPrefixes)\n\n return (prefix: string) => {\n if (!declared.has(prefix)) {\n throw new ValidationError(`reservedEnvelopes: prefix \"${prefix}\" is not declared by this binding's reservedPrefixes`)\n }\n\n const assertPrefixed = (collection: string, method: 'encrypt' | 'decrypt'): void => {\n if (!collection.startsWith(prefix)) {\n throw new ValidationError(\n `reservedEnvelopes('${prefix}').${method}: collection \"${collection}\" does not start with \"${prefix}\"`,\n )\n }\n }\n\n const encryptForPrefix = async (\n ref: { readonly collection: string; readonly id: string; readonly by?: string },\n json: string,\n v: number,\n ): Promise<EncryptedEnvelope> => {\n assertPrefixed(ref.collection, 'encrypt')\n const dek = await dekResolver(ref.collection)\n // `v` folds INTO the identity — it is `_v`, which the AAD binds, so the\n // version sealed and the version stamped come from one object (#1093).\n const identity = { ...ref, version: v }\n // `by` is part of the identity, so it is SEALED here and stamped by\n // `buildRecordEnvelope` from the same source. A caller that added `_by`\n // to the returned envelope afterwards would produce a record whose AAD\n // no reader can reproduce — which is exactly what via/lookup did (#1041).\n const { iv, data } = await encrypt(json, dek, buildRecordAad(identity))\n return buildRecordEnvelope(identity, { iv, data })\n }\n\n const decryptForPrefix = async (collection: string, id: string, env: EncryptedEnvelope): Promise<string> => {\n assertPrefixed(collection, 'decrypt')\n const dek = await dekResolver(collection)\n return decrypt(...sealedBodyArgs(env, 'openSealedSlots'), dek, recordAadFor({ collection, id }, env))\n }\n\n return { encrypt: encryptForPrefix, decrypt: decryptForPrefix }\n }\n }\n\n return { sealFields, unsealOneField, makeHandleProducer, unsealFields, makeSealedSlotCapability, makeReservedEnvelopes }\n}\n","/**\n * Preset normalization for verify candidates AND stored comparands (§4).\n * Both sides of every comparison route through the SAME function so\n * write-time digests and verify-time candidates agree. @module\n */\nexport type VerifyNormalizeMode = 'password' | 'secret-answer'\n\nexport function normalizeForVerify(mode: VerifyNormalizeMode, value: string): string {\n const nfc = value.normalize('NFC')\n if (mode === 'password') return nfc\n // secret-answer: NFC -> casefold -> re-NFC -> trim -> collapse internal\n // whitespace runs. `.toLowerCase()` is the chosen casefold approximation\n // (JS has no full Unicode caseless-match in the stdlib, e.g. ß stays ß);\n // the re-NFC after lowercasing keeps the result deterministic.\n return nfc.toLowerCase().normalize('NFC').trim().replace(/\\s+/g, ' ')\n}\n","/**\n * Standard Schema v1 integration.\n *\n * This file is the entry point for **schema validation**. Any\n * validator that implements the [Standard Schema v1\n * protocol](https://standardschema.dev) — Zod, Valibot, ArkType, Effect\n * Schema, etc. — can be attached to a `Collection` or `defineNoydbStore`\n * and will:\n *\n * 1. Validate the record BEFORE encryption on `put()` — bad data is\n * rejected at the store boundary with a rich issue list.\n * 2. Validate the record AFTER decryption on `get()`/`list()`/`query()`\n * — stored data that has drifted from the current schema throws\n * loudly instead of silently propagating garbage to the UI.\n *\n * ## Why vendor the types?\n *\n * Standard Schema is a protocol, not a library. The spec is <200 lines of\n * TypeScript and has no runtime. There's an official `@standard-schema/spec`\n * types package on npm, but pulling it in would add a dependency edge\n * purely for type definitions. Vendoring the minimal surface keeps\n * `@noy-db/core` at **zero runtime dependencies** and gives us freedom to\n * evolve the helpers without a version-lock on the spec package.\n *\n * If the spec changes in a breaking way (unlikely — it's frozen at v1),\n * we update this file and bump our minor.\n *\n * ## Why not just run `schema.parse(value)` directly?\n *\n * Because then we'd be locked to whichever validator happens to have\n * `.parse`. Standard Schema's `'~standard'.validate` contract is the same\n * across every implementation and includes a structured issues list,\n * which is much more useful than a thrown error for programmatic error\n * handling (e.g., rendering field-level messages in a Vue component).\n */\n\nimport { SchemaValidationError } from './errors.js'\n\n/**\n * The Standard Schema v1 protocol. A schema is any object that exposes a\n * `'~standard'` property with `version: 1` and a `validate` function.\n *\n * The type parameters are:\n * - `Input` — the type accepted by `validate` (what the user passes in)\n * - `Output` — the type produced by `validate` (what we store/return,\n * may differ from Input if the schema transforms or coerces)\n *\n * In most cases `Input === Output`, but validators that transform\n * (Zod's `.transform`, Valibot's `transform`, etc.) can narrow or widen.\n *\n * We intentionally keep the `types` field `readonly` and optional — the\n * spec marks it as optional because it's only used for inference, and\n * not every implementation bothers populating it at runtime.\n */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': {\n readonly version: 1\n readonly vendor: string\n readonly validate: (\n value: unknown,\n ) =>\n | StandardSchemaV1SyncResult<Output>\n | Promise<StandardSchemaV1SyncResult<Output>>\n readonly types?:\n | {\n readonly input: Input\n readonly output: Output\n }\n | undefined\n }\n}\n\n/**\n * The result of a single call to `schema['~standard'].validate`. Either\n * `{ value }` on success or `{ issues }` on failure — never both.\n *\n * The spec allows `issues` to be undefined on success (and some\n * validators leave it that way), so consumers should discriminate on\n * `issues?.length` rather than on truthiness of `value`.\n */\nexport type StandardSchemaV1SyncResult<Output> =\n | { readonly value: Output; readonly issues?: undefined }\n | {\n readonly value?: undefined\n readonly issues: readonly StandardSchemaV1Issue[]\n }\n\n/**\n * A single validation issue. The `message` is always present; the `path`\n * is optional and points at the offending field when the schema tracks\n * it (virtually every validator does for object types).\n *\n * The path is deliberately permissive — both a plain `PropertyKey` and a\n * `{ key }` wrapper are allowed so validators that wrap path segments in\n * objects (Zod does this in some modes) don't need special handling.\n */\nexport interface StandardSchemaV1Issue {\n readonly message: string\n readonly path?:\n | ReadonlyArray<PropertyKey | { readonly key: PropertyKey }>\n | undefined\n}\n\n/**\n * Infer the output type of a Standard Schema. Consumers use this to\n * pull the type out of a schema instance when they want to declare a\n * Collection<T> or defineNoydbStore<T> with `T` derived from the schema.\n *\n * Example:\n * ```ts\n * const InvoiceSchema = z.object({ id: z.string(), amount: z.number() })\n * type Invoice = InferOutput<typeof InvoiceSchema>\n * ```\n */\nexport type InferOutput<T extends StandardSchemaV1> =\n T extends StandardSchemaV1<unknown, infer O> ? O : never\n\n/**\n * Validate an input value against a schema. Throws\n * `SchemaValidationError` if the schema rejects, with the rich issue\n * list attached. Otherwise returns the (possibly transformed) output\n * value.\n *\n * The `context` string is included in the thrown error's message so the\n * caller knows where the failure happened (e.g. `\"put(inv-001)\"`) without\n * every caller having to wrap the throw in a try/catch.\n *\n * This function is ALWAYS async because some validators (notably Effect\n * Schema and Zod's `.refine` with async predicates) can return a\n * Promise. We `await` the result unconditionally to normalize the\n * contract — the extra microtask is free compared to the cost of an\n * encrypt/decrypt round-trip.\n */\nexport async function validateSchemaInput<Output>(\n schema: StandardSchemaV1<unknown, Output>,\n value: unknown,\n context: string,\n): Promise<Output> {\n const result = await schema['~standard'].validate(value)\n if (result.issues !== undefined && result.issues.length > 0) {\n throw new SchemaValidationError(\n `Schema validation failed on ${context}: ${summarizeIssues(result.issues)}`,\n result.issues,\n 'input',\n )\n }\n // Safe: the spec guarantees `value` is present when `issues` is absent.\n return result.value as Output\n}\n\n/**\n * Validate an already-stored value coming OUT of the collection. This\n * is a distinct helper from `validateSchemaInput` because the error\n * semantics differ: an output-validation failure means the data in\n * storage has drifted from the current schema (an unexpected state),\n * whereas an input-validation failure means the user passed bad data\n * (an expected state for a UI that isn't guarding its inputs).\n *\n * We still throw — silently returning bad data would be worse — but\n * the error carries `direction: 'output'` so upstream code (and a\n * potential migrate hook) can distinguish the two cases.\n */\nexport async function validateSchemaOutput<Output>(\n schema: StandardSchemaV1<unknown, Output>,\n value: unknown,\n context: string,\n): Promise<Output> {\n const result = await schema['~standard'].validate(value)\n if (result.issues !== undefined && result.issues.length > 0) {\n throw new SchemaValidationError(\n `Stored data for ${context} does not match the current schema — ` +\n `schema drift? ${summarizeIssues(result.issues)}`,\n result.issues,\n 'output',\n )\n }\n return result.value as Output\n}\n\n/**\n * Produce a short human-readable summary of an issue list for the\n * thrown error's message. The full issue array is still attached to the\n * error as a property — this is only for the `.message` string that\n * shows up in console.error / stack traces.\n *\n * Format: `field: message; field2: message2` (up to 3 issues, then `…`).\n * Issues without a path are shown as `root: message`.\n */\nfunction summarizeIssues(\n issues: readonly StandardSchemaV1Issue[],\n): string {\n const shown = issues.slice(0, 3).map((issue) => {\n const pathStr = formatPath(issue.path)\n return `${pathStr}: ${issue.message}`\n })\n const suffix = issues.length > 3 ? ` (+${issues.length - 3} more)` : ''\n return shown.join('; ') + suffix\n}\n\nfunction formatPath(\n path: StandardSchemaV1Issue['path'],\n): string {\n if (!path || path.length === 0) return 'root'\n return path\n .map((segment) =>\n typeof segment === 'object' && segment !== null\n ? String(segment.key)\n : String(segment),\n )\n .join('.')\n}\n","/**\n * RecordCodec — the per-record envelope build + encrypt/decrypt + per-record-CEK\n * + sealed-field crypto.\n *\n * `Collection` holds one `RecordCodec` instance and delegates the crypto write\n * path (`encryptRecord` / `encryptJsonString` / `buildDebugEnvelope`), the read\n * path (`decryptRecord` / `decryptJsonString` / `resolveEnvelopeCek`), and the\n * sealed-field helpers (`unsealField` / `makeSealedHandle` / `toCacheRecord` /\n * `classifySealedShred`) to it. The codec receives every `this.*` dependency\n * it needs via {@link RecordCodecContext}.\n *\n * The crux is `cekCache`: it is the SAME `Lru` reference `Collection` owns (not\n * a copy). `resolveEnvelopeCek` reads/writes it, and tier methods +\n * `vault.invalidateRecordCaches` keep mutating that same object — a copy would\n * silently break the \"single CEK delete kills the version chain\" invariant.\n *\n * Internal service — not exported as a `@noy-db/hub/*` subpath.\n */\nimport { NOYDB_FORMAT_VERSION, type EncryptedEnvelope, type CrdtMode, type CrdtState, type CrdtStrategy, type VdigFieldPolicy, type SealedHandle } from '../../kernel/types.js'\nimport { isTombstone, isDeleteMarker } from './tombstone.js'\nimport { parseSealedSlot } from './sealed-slot.js'\nimport { buildRecordEnvelope } from './record-envelope.js'\nimport { buildRecordAad, recordAadFor, type RecordIdentity, type RecordRef } from './record-aad.js'\nimport type { SealKeyMaterial } from './sealed-slots.js'\nimport { DebugReservedFieldError, ClassifiedConfigError, ValidationError } from '../../kernel/errors.js'\nimport { normalizeForVerify } from './normalize.js'\nimport { validateSchemaOutput, type StandardSchemaV1 } from '../../kernel/schema.js'\nimport type { Lru } from '../../kernel/cache/index.js'\nimport type { ViaCryptoCtx, SealedSlotRef } from '../../kernel/via/index.js'\nimport type { ViaPipeline } from '../../kernel/via/pipeline.js'\nimport { sealedBodyArgs } from './envelope-body.js'\n\n/**\n * One classified per-slot verdict from {@link RecordCodecBase.classifySealedShred}.\n * `shreddable` — CEK-only, tombstone makes it undecryptable. `dekResidue` —\n * collection-DEK-keyed, survives in synced/backup copies. The third class is\n * BOTH: a `_bidx` blind-index tag is live-dropped by the tombstone yet retained\n * under the surviving DEK in any pre-forget backup (honest dual accounting).\n */\nimport type { CapsuleKey, CapsulePrimitives } from '../contract.js'\nimport type { makeSealedSlots } from './sealed-slots.js'\n\nexport type SealedShredSlot = {\n readonly field: string\n readonly class: 'shreddable' | 'dekResidue' | 'live-shreddable+dekResidue-in-backups'\n}\n\n/**\n * The record codec, bound to a capsule.\n *\n * ⚠️ `RecordCodec` is a CLASS declared inside this factory, so the NAME is no\n * longer a module-level type. Hub names it in ~40 places as a type, which is\n * why `plumbing/index.ts` re-exports an `InstanceType<…>` alias under the same\n * name — the type keeps working unchanged for every existing consumer.\n */\n/** Everything the moving crypto methods touched on `this.*`, as a flat context. */\nexport interface RecordCodecContext<T> {\n /** Collection name — the crypto AAD scope and schema-error context. */\n readonly name: string\n /** Actor id stamped on `_by` (the collection's keyring.userId). */\n readonly actor: string\n /** False on plaintext collections — selects the no-ciphertext envelope branch. */\n readonly storeCiphertext: boolean\n /** keyring.debugPlaintext — debug-inline envelope on user collections. */\n readonly debugPlaintext: boolean\n /** Emit `_source`/`_sourceTs` provenance fields when a source is supplied. */\n readonly provenance: boolean\n /** Declared `sensitive` fields → sealed into `_sealed[field]`. */\n readonly sensitiveFields: ReadonlySet<string>\n /** Declared deterministic-index fields, or null. */\n readonly deterministicFields: ReadonlySet<string> | null\n /** Digest-only classified fields → verify policy (stage 2). Null when none. */\n readonly vdigFields: ReadonlyMap<string, VdigFieldPolicy> | null\n /**\n * C-A / R10 config-drift signal: the persisted `x-classified` marker's\n * declared digest-only field set (empty when the collection carries no\n * marker). Lazy, memoized (O(1) per handle after the first call). The R10\n * superset guard refuses whenever a field in this set is absent from\n * `vdigFields`. Consulted once per handle on any encrypted write path — the\n * cross-session, prev-free signal a fully-naive create has no `prev` for.\n * Undefined on codecs with no store access (direct-construction tests).\n */\n classifiedMarkerDigestOnly?(): Promise<readonly string[]>\n /** CRDT mode (decrypt resolves CrdtState→snapshot when set). */\n readonly crdtMode: CrdtMode | undefined\n /** CRDT strategy seam (resolveCrdtSnapshot). */\n readonly crdtStrategy: CrdtStrategy\n /** Output-schema validator, or undefined. */\n readonly schema: StandardSchemaV1<unknown, T> | undefined\n /**\n * Resolve a collection's DEK. Called with no argument for `this.name`'s\n * own DEK (every ordinary codec use); `viaCryptoCtx`'s `reservedEnvelopes`\n * door passes an explicit OTHER collection name (e.g. a reserved\n * `_dict_status` collection) so a binding's at-rest hook can address a\n * different collection's DEK without ever holding the resolver itself.\n */\n getDEK(collection?: string): Promise<CapsuleKey>\n /**\n * The collection's per-record CEK cache (SHARED reference, not a copy).\n * Ownership/lifetime stays on Collection; codec reads+writes it in\n * resolveEnvelopeCek exactly as the inline code did. `null` → no caching.\n */\n readonly cekCache: Lru<string, CapsuleKey> | null\n /**\n * Compiled Via pipeline (money, i18n, …), or undefined for a collection\n * with none declared. `encryptRecord`/`decryptRecord` consult\n * `via?.hasAtRestHooks` at the sealed-slot sub-step to choose between a\n * binding's `encodeAtRest`/`decodeAtRest` hooks and today's inline path.\n * The zero-via fast path (`via` undefined, or no binding declares an\n * at-rest hook) stays byte/behavior-identical (#629 Task 3).\n *\n * Mutable (not `readonly`, #638 Task 3): `Collection.via` can be\n * reassigned post-construction (the taint overlay, `via/graph-wiring.ts#\n * applyTaintOverlay`) — `RecordCodecBase.setVia` below keeps this in sync so\n * at-rest hooks read the LIVE pipeline, not a stale construction-time\n * snapshot.\n */\n via: ViaPipeline | undefined\n /**\n * Read-coverage observer (#1363), resolved once per Collection from\n * `strategies.coverage.observer(...)`. `undefined` for every un-opted-in\n * deployment AND for every collection the sensor does not account for, so\n * the decrypt path costs one `!== undefined` test.\n *\n * ⛔⛔ TELEMETRY, NOT A CONTROL: an insider holding the device and local keys\n * holds the DEK, so this observes an extraction it cannot prevent. It exists\n * to make one visible early, attributable and loud. It may never grow a\n * return value — a decrypt is not gated on it, and nothing here may refuse.\n */\n readonly onDecrypt?: ((id: string) => void) | undefined\n}\n\n/**\n * The codec's capsule-provided surface: the 15 primitives + sealed-slot helpers\n * its methods call.\n */\nexport type CapsuleBinding = CapsulePrimitives & ReturnType<typeof makeSealedSlots>\n\n/**\n * ⚠️ MODULE-LEVEL AND GENERIC ON PURPOSE. The obvious shape — declaring the\n * class inside `makeRecordCodec` and exporting `InstanceType<…>` — compiles and\n * silently DROPS `T`: `RecordCodec<Invoice>` degrades to `RecordCodec<unknown>`\n * at the six places hub parameterises it, turning typed records into `unknown`\n * with no error anywhere.\n *\n * The other way to keep `T` would be a hand-written `interface RecordCodec<T>`\n * that the class implements — i.e. a second declaration of 15 signatures that\n * nothing checks against the first. That is exactly the drift #16 was, so it\n * was not done twice. A base class has one declaration and cannot drift.\n */\nexport class RecordCodecBase<T> {\n constructor(\n protected readonly caps: CapsuleBinding,\n private readonly ctx: RecordCodecContext<T>,\n ) {}\n\n /**\n * @internal Update the live Via pipeline this codec's at-rest hooks read\n * (#638 Task 3) — called by `via/graph-wiring.ts#applyTaintOverlay` right\n * after it reassigns `Collection.via`, so `hasAtRestHooks`/`encodeAtRest`/\n * `decodeAtRest` see the newly-added `taint` binding instead of the\n * pipeline snapshot captured when this codec was constructed.\n */\n setVia(via: ViaPipeline | undefined): void {\n this.ctx.via = via\n }\n\n /** Sealed-slot key material for this codec's collection, for a given per-record CEK (or none). */\n private sealKeyMaterial(cek: CapsuleKey | undefined): SealKeyMaterial {\n return {\n collection: this.ctx.name,\n ...(cek !== undefined ? { cek } : {}),\n getDEK: () => this.ctx.getDEK(),\n }\n }\n\n /**\n * Build the `ViaCryptoCtx` handed to a binding's `encodeAtRest`/\n * `decodeAtRest` hook: `sealedSlots` pre-bound to `(this.ctx.name,\n * recordId)` — always threading `cek` when the caller has one, so the\n * capability's record-binding stays cryptographic rather than degrading\n * to collection-scope. `reservedEnvelopes`'s DEK resolver forwards its\n * `collection` argument straight to `this.ctx.getDEK` (#629 Task 4) — it\n * resolves the RESERVED collection's own DEK (e.g. `_dict_status`), never\n * `this.ctx.name`'s, matching `Vault.getDEK`'s per-collection-name\n * resolution (the same resolver `DictionaryHandle` used pre-cutover).\n */\n private viaCryptoCtx(recordId: string, cek: CapsuleKey | undefined): ViaCryptoCtx {\n const declaredPrefixes = this.ctx.via?.bindings.flatMap((b) => b.reservedPrefixes ?? []) ?? []\n return {\n sealedSlots: this.caps.makeSealedSlotCapability(this.ctx, recordId, cek),\n reservedEnvelopes: this.caps.makeReservedEnvelopes((collection) => this.ctx.getDEK(collection), declaredPrefixes),\n }\n }\n\n // ──────────────────────────────────────────────────────────────────────\n // Low-level literal builders\n // ──────────────────────────────────────────────────────────────────────\n\n /**\n * Assemble a canonical ciphertext body envelope literal from already-computed\n * parts. Pure, no crypto. Each call stamps its own `_ts` — provenance carries\n * a SEPARATE timestamp (computed by the caller), so the two never share one.\n */\n static buildEnvelope(identity: RecordIdentity, p: {\n iv: string\n data: string\n // No `by` — `_by` is stamped from `identity`, the single source (#1041).\n // It was declared here and silently ignored, which is exactly how a\n // dropped field hides.\n cek?: string\n provenance?: { source: string; sourceTs: string } | undefined\n extra?: Partial<Pick<EncryptedEnvelope, '_tier' | '_det' | '_sealed'>>\n }): EncryptedEnvelope {\n return buildRecordEnvelope(identity, p)\n }\n\n /** Plaintext (`_iv:''`) body envelope — the `!storeCiphertext` shape. */\n static buildPlaintextEnvelope(identity: RecordIdentity, p: {\n data: string\n provenance?: { source: string; sourceTs: string } | undefined\n }): EncryptedEnvelope {\n return buildRecordEnvelope(identity, { ...p, iv: '' })\n }\n\n // ──────────────────────────────────────────────────────────────────────\n // Write path\n // ──────────────────────────────────────────────────────────────────────\n\n /**\n * Build a debug-plaintext envelope: the record's own fields inlined as\n * top-level keys beside the reserved `_`-metadata, with `_debug: 1` and an\n * empty `_data`. Lets native store tooling read the record without\n * unwrapping. Only reached for user collections under `debugPlaintext`\n * (see {@link encryptRecord}). Rejects `_`-prefixed record fields, which\n * would collide with the reserved metadata namespace.\n */\n buildDebugEnvelope(id: string, record: T, version: number, source?: string, sourceTs?: string): EncryptedEnvelope {\n const rec = record as unknown as Record<string, unknown>\n for (const key of Object.keys(rec)) {\n if (key.startsWith('_')) throw new DebugReservedFieldError(this.ctx.name, key)\n }\n const base = buildRecordEnvelope(\n { collection: this.ctx.name, id, by: this.ctx.actor, version },\n {\n iv: '',\n data: '',\n \n ...(this.ctx.provenance && source !== undefined\n ? { provenance: { source, sourceTs: sourceTs ?? new Date().toISOString() } }\n : {}),\n },\n )\n // `_debug` and the inlined record fields are this envelope's own shape —\n // they are not part of the canonical constructor's contract.\n return { ...base, _debug: NOYDB_FORMAT_VERSION, ...rec } as unknown as EncryptedEnvelope\n }\n\n /**\n * Encrypt a JSON body into an envelope.\n *\n * When `cek` is supplied (per-record CEK collections), the body is\n * encrypted under the CEK and the CEK is AES-KW-wrapped under the\n * collection DEK and stamped on `_cek`. When `cek` is omitted, the legacy\n * path encrypts the body directly under the collection DEK — byte-identical\n * to pre-CEK behaviour, so non-adopting collections pay nothing.\n */\n /**\n * `ref` is the identity the envelope is sealed against, and it is **the\n * location the bytes are stored at** — not necessarily this codec's own\n * collection (#1051).\n *\n * That distinction was discovered here rather than assumed. Several callers\n * put the result somewhere else entirely: search's vector sidecar lands in\n * `_vec` under `encodeVecId(name, id)`, the lexical index under `_ft/<name>`,\n * indexing's entries under a synthetic `idxId`. A codec that supplied\n * `this.ctx.name` for those would seal them against a collection they are not\n * in, and once AAD binds identity every one of them would fail to verify.\n *\n * ⚠️ **`_history` is the open case, and it is #1041's to settle.** Snapshots\n * are produced with the LIVE record's identity here and then re-homed by\n * `saveHistory` into `_history/<historyId(collection,id,_v)>`. Both readings\n * are defensible — bind where it lives, or bind what it is a copy of — and\n * the choice must be made before AAD is switched on, not inferred from this\n * migration.\n */\n async encryptJsonString(\n ref: RecordRef,\n json: string,\n version: number,\n cek?: CapsuleKey,\n source?: string,\n sourceTs?: string,\n ): Promise<EncryptedEnvelope> {\n // No envelope may be sealed over a non-string plaintext (#1220).\n // `JSON.stringify(undefined)` is `undefined`, so a caller that passes an\n // undefined record seals over NOTHING: `_data` is a bare GCM tag over zero\n // ciphertext. The envelope is well-formed and correctly sealed, which is\n // why nothing downstream catches it — it reads back as `undefined` on the\n // cached path (measured; note that is off `get()`'s declared `T | null`,\n // so `rec === null` misses it too) and as a `SyntaxError` out of\n // `decryptRecord` on the hydrate path. Two failures that both point away\n // from the write that caused them, and neither is `null`, which is what a\n // genuinely absent id returns.\n // Stated on the OUTPUT condition so it holds for every caller, not just\n // the `put(record)` arity slip that surfaced it.\n if (typeof json !== 'string') {\n throw new TypeError(\n `record seal: plaintext must be a string for \"${String(ref.collection)}/${String(ref.id)}\", ` +\n `received ${String(json)} (${typeof json}). Sealing over an empty plaintext produces ` +\n `an envelope that is valid and undecodable.`,\n )\n }\n // `version` folds into the identity HERE rather than travelling beside it,\n // so what is sealed and what is stamped come from one object (#1093).\n const identity: RecordIdentity = { ...ref, version, by: this.ctx.actor }\n const provenance = this.ctx.provenance && source !== undefined\n ? { source, sourceTs: sourceTs ?? new Date().toISOString() }\n : undefined\n\n if (!this.ctx.storeCiphertext) {\n return RecordCodecBase.buildPlaintextEnvelope(identity, { data: json, provenance })\n }\n\n const dek = await this.ctx.getDEK()\n\n const aad = buildRecordAad(identity)\n\n if (cek !== undefined) {\n const { iv, data } = await this.caps.encrypt(json, cek, aad)\n const wrapped = await this.caps.wrapCek(cek, dek)\n return RecordCodecBase.buildEnvelope(identity, { iv, data, cek: wrapped, provenance })\n }\n\n const { iv, data } = await this.caps.encrypt(json, dek, aad)\n return RecordCodecBase.buildEnvelope(identity, { iv, data, provenance })\n }\n\n /**\n * `ref` is FIRST and required (#1051). It was previously a trailing optional\n * `id?: string`, which TypeScript cannot promote to required because it sits\n * after other optionals — so the migration would have needed an `id ?? ''`\n * fallback. That is exactly the silent default this issue exists to remove:\n * once identity is bound into the AEAD, every record written with an empty id\n * binds to the SAME identity and becomes interchangeable with the others.\n * An object, not a bare string, so a swap with `version`/`source` cannot\n * compile on the hottest write path. Carries the collection too — see\n * {@link encryptJsonString} for why the codec must not assume its own.\n */\n async encryptRecord(\n ref: RecordRef,\n record: T,\n version: number,\n cek?: CapsuleKey,\n source?: string,\n sourceTs?: string,\n vdig?: { readonly id: string; readonly prev: EncryptedEnvelope | null },\n ): Promise<EncryptedEnvelope> {\n const id = ref.id\n // Debug-plaintext: write user-collection records with their fields inlined\n // beside the envelope metadata so native store tools read them directly.\n // Internal (`_`-prefixed) collections keep the classic shape — some store\n // `_`-prefixed fields that the inline layout would collide with.\n if (!this.ctx.storeCiphertext && this.ctx.debugPlaintext && !this.ctx.name.startsWith('_')) {\n return this.buildDebugEnvelope(id, record, version, source, sourceTs)\n }\n\n // ── C-A / R10 config-drift guard (superset) ────────────────────────\n // A handle may write a classified collection only if its DECLARED\n // digest-only set is a SUPERSET of the collection's stored/marked\n // classified set. Any classified field this handle does NOT declare is a\n // field it cannot maintain: the `_vdig` loop below iterates only declared\n // fields, so an undeclared classified field is neither stripped nor tagged —\n // its value would be serialized into `_data` as DEK-recoverable plaintext\n // (the C-A leak), or its stored tag silently dropped. Fail loud.\n //\n // This subsumes the old naive-only guard (a fully-naive handle has an empty\n // declared set, so ANY stored classified field violates the superset) AND\n // closes the partial-handle door (declares Y, not X, over a record carrying\n // `_vdig[X]`/`_bidx[X]`): the narrow `vdigFields === null || size === 0`\n // gate skipped that entire case. The stored/marked set is the union of:\n // 1. the persisted `x-classified` marker's declared digest-only set\n // (cross-session, prev-free — the only signal a fully-naive create has);\n // 2. the keys the target `prev` already carries in `_vdig`/`_bidx` (the\n // overwrite arm; a classified-declaring handle DOES pass `prev`).\n // Only encrypted collections can be classified, so plaintext collections skip.\n if (this.ctx.storeCiphertext) {\n const declared = this.ctx.vdigFields\n const storedClassified = new Set<string>()\n if (vdig?.prev?._vdig) for (const k of Object.keys(vdig.prev._vdig)) storedClassified.add(k)\n if (vdig?.prev?._bidx) for (const k of Object.keys(vdig.prev._bidx)) storedClassified.add(k)\n if (this.ctx.classifiedMarkerDigestOnly !== undefined) {\n for (const k of await this.ctx.classifiedMarkerDigestOnly()) storedClassified.add(k)\n }\n let undeclared: string | undefined\n for (const field of storedClassified) {\n if (declared === null || !declared.has(field)) { undeclared = field; break }\n }\n if (undeclared !== undefined) {\n throw new ClassifiedConfigError(\n this.ctx.name,\n `this collection has classified digest-only field \"${undeclared}\" but this handle does not declare it — ` +\n 'refusing to write (would drop its tag or serialize the secret as plaintext)',\n )\n }\n }\n\n // Structural group-encryption: peel declared sensitive fields out\n // of the record BEFORE building `_data`, sealing each into its own\n // `_sealed[field]` slot under a per-field key. Default-off — with no\n // sensitive fields the open record is unchanged and no `_sealed` is\n // emitted, so the envelope stays byte-identical to legacy output.\n // A collection whose via pipeline declares at-rest hooks (#629 Task 3)\n // seals through THOSE instead — the inline `sensitiveFields` path and\n // the hook path are mutually exclusive per collection, gated on\n // `hasAtRestHooks` (the inline path now serves only the plain\n // `sensitive: [...]` collection option; since #629, `classifiedFields`-\n // declared fields seal through their own via-binding hook, and since\n // #638 so does any field the taint graph seals — `via/classified/\n // binding.ts` and `kernel/via/taint-binding.ts`, respectively). The\n // zero-via fast path (no `via`, or a pipeline with no at-rest hooks,\n // e.g. money-only) always takes the `else if` branch, unchanged.\n let openRecord = record\n let sealed: Record<string, string> | undefined\n if (this.ctx.storeCiphertext && this.ctx.via?.hasAtRestHooks) {\n // Was `id === undefined`, which #1051 made UNREACHABLE — `ref.id` is a\n // required `string` now, so the compiler asks the question instead. The\n // backstop is retargeted rather than deleted: an EMPTY id still gets\n // through, and it is the realistic hazard, not a hypothetical one. The\n // first draft of this very migration wrote `id ?? ''` to satisfy a\n // trailing-optional signature. Once identity is bound into the AEAD every\n // record written with `''` binds to the SAME identity and they become\n // mutually interchangeable — the failure would be silent at write time\n // and unrecoverable later.\n if (id === '') {\n throw new Error(\n `RecordCodecBase.encryptRecord: collection \"${this.ctx.name}\" has via at-rest hooks but this write ` +\n 'path supplied an empty record id (needed to scope the sealed-slot capability) — caller bug.',\n )\n }\n const src = record as unknown as Record<string, unknown>\n const result = await this.ctx.via.encodeAtRest(src, this.viaCryptoCtx(id, cek))\n openRecord = result.record as unknown as T\n if (result.sealed !== undefined) {\n sealed = {}\n for (const [field, ref] of Object.entries(result.sealed)) sealed[field] = `${ref.iv}:${ref.data}`\n }\n } else if (this.ctx.storeCiphertext && this.ctx.sensitiveFields.size > 0) {\n const src = record as unknown as Record<string, unknown>\n const result = await this.caps.sealFields(src, this.ctx.sensitiveFields, this.sealKeyMaterial(cek))\n if (result.sealed !== undefined) {\n sealed = result.sealed\n openRecord = result.openRecord as unknown as T\n }\n }\n\n // ── Digest-only classified fields (stage 2, C6) ────────────────────\n // Per field, exactly one of: carry-forward (absent) / rotate (string) /\n // clear (null) / loud error (anything else). Runs on a CLONE so the\n // caller's record object is never mutated.\n let vdigOut: Record<string, string> | undefined\n let bidxOutMap: Record<string, string> | undefined\n if (this.ctx.vdigFields !== null && this.ctx.vdigFields.size > 0 && this.ctx.storeCiphertext) {\n if (vdig === undefined) {\n throw new Error(\n `RecordCodecBase.encryptRecord: collection \"${this.ctx.name}\" declares digest-only classified ` +\n `fields but this write path supplied no { id, prev } context — it would silently destroy _vdig (C6). Caller bug.`,\n )\n }\n if (cek === undefined) {\n throw new Error(\n `RecordCodecBase.encryptRecord: digest-only fields require a per-record CEK (R1 invariant) — ` +\n `collection \"${this.ctx.name}\" wrote without one. Caller bug.`,\n )\n }\n const open: Record<string, unknown> = { ...(openRecord as unknown as Record<string, unknown>) }\n const out: Record<string, string> = {}\n // _bidx (blind-index equality tags) rides the SAME iteration as _vdig,\n // so the structural invariant _bidx[field] ⇒ _vdig[field] holds by\n // construction: a tag is only ever written where the _vdig slot is\n // written/carried. DEK is fetched lazily — only an equatable rotate needs it.\n const bidxOut: Record<string, string> = {}\n let bidxDek: CapsuleKey | undefined\n for (const [field, policy] of this.ctx.vdigFields) {\n const value = open[field]\n const prevBlob = vdig.prev?._vdig?.[field]\n if (vdig.prev?._sealed?.[field] !== undefined) {\n // R6 transition evidence: never silently delete recoverable\n // plaintext. Checked BEFORE the value-type dispatch so the\n // carry-forward (absent) and clear (null) branches refuse too —\n // both would otherwise drop the `_sealed` slot from the new\n // envelope, silently destroying the recoverable ciphertext.\n throw new ClassifiedConfigError(\n this.ctx.name,\n `field \"${field}\" carries a recoverable _sealed slot from a previous storage form — ` +\n `recoverable ↔ digest-only transitions are refused (R6); migrate explicitly`,\n )\n }\n if (value === undefined) {\n // 1. carry-forward: verbatim bytes (CEK version-stable, AAD _v-free;\n // byte-identity keeps the ledger payload hash deterministic).\n // I-3 monotonic carry: whenever the _vdig slot is carried, ALSO copy\n // prev._bidx[field] verbatim — regardless of THIS handle's equatable\n // knob. An unrelated put by a handle with equatable removed must not\n // drop another handle's tag (anti-flip-flop). If prev has no tag,\n // there is nothing to mint from (no plaintext) → stays uncovered.\n if (prevBlob !== undefined) {\n out[field] = prevBlob\n const prevTag = vdig.prev?._bidx?.[field]\n if (prevTag !== undefined) bidxOut[field] = prevTag\n }\n continue\n }\n if (value === null) {\n // 3. clear: the defined deletion short of forget().\n delete open[field]\n continue\n }\n if (typeof value !== 'string') {\n // 4. caller bug, fail-loud.\n throw new ValidationError(\n `digest-only classified field \"${field}\" in \"${this.ctx.name}\" must be a string (rotate) or null (clear), got ${typeof value}`,\n )\n }\n // 2. rotate: validate ran in the stage-1 write seam; digest + ring here.\n out[field] = await this.caps.mintVdigSlot(value, policy, prevBlob, cek, this.ctx.name, vdig.id, field)\n // _bidx branch 2: if this handle is equatable, mint a fresh tag through\n // the SAME normalization pipeline mintVdigSlot uses (normalizeForVerify)\n // — the tag provably equals computeBidxTarget for this value. If the\n // knob is OFF, emit nothing: the stale prev tag points at the now-\n // superseded value (carrying it would be wrong) and minting is disallowed\n // (knob off) → the tag is dropped. Confirm-by-verify keeps this sound.\n if (policy.equatable === true) {\n if (bidxDek === undefined) bidxDek = await this.ctx.getDEK()\n const normalized = normalizeForVerify(policy.normalize, value)\n bidxOut[field] = await this.caps.mintBidxTag(normalized, bidxDek, this.ctx.name, field)\n }\n delete open[field] // strip from _data — digest-only never persists plaintext\n }\n openRecord = open as unknown as T\n if (Object.keys(out).length > 0) vdigOut = out\n if (Object.keys(bidxOut).length > 0) bidxOutMap = bidxOut\n }\n\n const base = await this.encryptJsonString(ref, JSON.stringify(openRecord), version, cek, source, sourceTs)\n const withSealed = sealed ? { ...base, _sealed: sealed } : base\n const withVdig = vdigOut ? { ...withSealed, _vdig: vdigOut } : withSealed\n const withBidx = bidxOutMap ? { ...withVdig, _bidx: bidxOutMap } : withVdig\n if (!this.ctx.deterministicFields || !this.ctx.storeCiphertext) return withBidx\n\n // compute deterministic-ciphertext slots for every\n // declared field. Non-primitive values are JSON-stringified so\n // objects/arrays still dedupe on structural equality. Sealed fields are\n // excluded — they live only in `_sealed`, never the `_det` index.\n // L-1: `_det` encrypts under a dedicated HKDF-derived key (salt\n // `noydb-det`), never the raw DEK — the DEK's randomized-IV `_data`\n // regime and `_det`'s deterministic-IV regime must not share a key.\n const dek = await this.ctx.getDEK()\n const detKey = await this.caps.deriveDeterministicKey(dek)\n const rec = record as unknown as Record<string, unknown>\n const det: Record<string, string> = {}\n for (const field of this.ctx.deterministicFields) {\n if (this.ctx.sensitiveFields.has(field)) continue\n if (this.ctx.vdigFields?.has(field)) continue // I5: digest-only never equality-correlatable\n const value = rec[field]\n if (value === undefined || value === null) continue\n const plaintext = typeof value === 'string' ? value : JSON.stringify(value)\n const { iv, data } = await this.caps.encryptDeterministic(plaintext, detKey, `${this.ctx.name}/${field}`)\n det[field] = `${iv}:${data}`\n }\n if (Object.keys(det).length === 0) return withBidx\n return { ...withBidx, _det: det }\n }\n\n // ──────────────────────────────────────────────────────────────────────\n // Read path\n // ──────────────────────────────────────────────────────────────────────\n\n /**\n * Resolve the per-record CEK for a stored envelope, or `undefined` for a\n * legacy (`_cek`-absent) envelope. Unwraps `_cek` under the collection DEK and\n * memoises it in the CEK cache under `id` (when supplied) so repeated reads of\n * the same record skip the unwrap. Shared by the body-decrypt path\n * ({@link decryptJsonString}) and the sealed-field path ({@link decryptRecord}\n * / {@link toCacheRecord}) so both agree on the record's key.\n */\n async resolveEnvelopeCek(envelope: EncryptedEnvelope, id?: string): Promise<CapsuleKey | undefined> {\n if (envelope._cek === undefined) return undefined\n const cached = id !== undefined ? this.ctx.cekCache?.get(id) : undefined\n if (cached !== undefined) return cached\n const dek = await this.ctx.getDEK()\n const cek = await this.caps.unwrapCek(envelope._cek, dek)\n if (id !== undefined) this.ctx.cekCache?.set(id, cek, 1)\n return cek\n }\n\n /**\n * Low-level: decrypt an envelope and return the raw JSON string.\n *\n * `_cek` presence is the format discriminant (NOT `this.perRecordCek`),\n * so a mixed vault — and a recipient that never opted into\n * `perRecordKeys` — decrypts both legacy and CEK records:\n * - `_cek` present → unwrap the CEK under the collection DEK, decrypt the\n * body under the CEK (cache the unwrapped CEK so repeated reads skip it).\n * - `_cek` absent → legacy path, body decrypts directly under the\n * collection DEK.\n *\n * The optional `id` lets reads populate the CEK cache; it is omitted by\n * callers (history, conflict merge) that have only the envelope.\n */\n /**\n * `ref` must be the SAME identity the writer sealed against — which is the\n * storage address, not necessarily this codec's own collection. Search's\n * vector sidecar lives in `_vec`, its lexical index in `_ft`; passing\n * `this.ctx.name` for those would recompute the wrong AAD and fail to open a\n * perfectly good record (#1041).\n */\n async decryptJsonString(ref: RecordRef, envelope: EncryptedEnvelope): Promise<string | null> {\n const id = ref.id\n // RISK #1 (forget cascade): a shred tombstone carries `_data: ''` and no\n // `_cek`. Decrypting it would call `this.caps.decrypt('', '', dek)` → AES-GCM\n // OperationError → TamperedError. Return null so every read callsite\n // treats it as \"absent / skip\", matching how get()/list already drop\n // tombstones. Legacy plaintext collections (`!this.storeCiphertext`) legitimately\n // have empty `_iv`/`_data`, so `isTombstone` is false for them — preserved.\n if (isTombstone(envelope, this.ctx.storeCiphertext) || isDeleteMarker(envelope)) return null\n if (!this.ctx.storeCiphertext) {\n // Debug-plaintext layout: record fields were inlined as top-level keys\n // (see buildDebugEnvelope). Reconstruct the record from the non-`_`\n // keys. Self-describing via `_debug`, so a classic plaintext reader\n // handles debug-written envelopes too.\n if (envelope._debug !== undefined) {\n const rec: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(envelope)) {\n if (!key.startsWith('_')) rec[key] = value\n }\n return JSON.stringify(rec)\n }\n // #598: a real record never serializes to '' (JSON.stringify's floor is\n // '{}'), so an empty `_data` here is the sync engine's shape-only\n // tombstone (`isTombstoneShape` in team/sync.ts) landing on an\n // unencrypted collection, where the encryption-gated `isTombstone()`\n // check above doesn't fire. Treat it the same as an encrypted\n // tombstone: null, not a `JSON.parse('')` crash.\n // #15: an ABSENT `_data` lands in the same branch as an empty one —\n // both say \"no body\", and the tombstone reading above is what that\n // means on an unencrypted collection.\n const plain = envelope._data ?? ''\n return plain === '' ? null : plain\n }\n const aad = recordAadFor(ref, envelope)\n const cek = await this.resolveEnvelopeCek(envelope, id)\n const json = cek !== undefined\n ? await this.caps.decrypt(...sealedBodyArgs(envelope, 'RecordCodec'), cek, aad)\n : await this.caps.decrypt(...sealedBodyArgs(envelope, 'RecordCodec'), await this.ctx.getDEK(), aad)\n this.observeDecrypt(ref)\n return json\n }\n\n /**\n * Fire the read-coverage observer for a successful record decrypt (#1363).\n *\n * ⚠️ ONLY for this codec's OWN collection. `ref.collection` can be a storage\n * identity that is not the live collection — a history/version address, the\n * `_vec` vector sidecar, the `_ft` lexical index (#1041) — and their ids are\n * not this corpus's ids, so counting them would corrupt the very estimate\n * the sensor exists to produce. The cost is a deliberate UNDER-count: a\n * principal reading the corpus through its history sidecar is not accounted.\n * A sensor that under-counts is honest; one that inflates cries wolf and\n * gets turned off. Recorded rather than papered over.\n */\n private observeDecrypt(ref: RecordRef): void {\n const observe = this.ctx.onDecrypt\n if (observe === undefined || ref.collection !== this.ctx.name) return\n observe(ref.id)\n }\n\n /**\n * Unseal a single `_sealed[field]` slot to its plaintext value: derive the\n * per-field key off the collection DEK, AES-GCM-decrypt the `iv:data` blob,\n * and JSON-parse the result. Shared by both the inline-decrypt path and a\n * {@link Sealed} handle's `reveal()` — so the on-demand reveal and the eager\n * materialisation always agree byte-for-byte.\n */\n async unsealField(field: string, blob: string, cek?: CapsuleKey): Promise<unknown> {\n // Dual-read. Current writes seal under a key derived from the record's\n // per-record CEK; legacy records (even ones whose body is\n // CEK-encrypted) are sealed under the collection-DEK key. Try the CEK key\n // first; on its AES-GCM auth failure, fall back to the DEK key. Without this\n // fallback every legacy `_sealed` record would throw TamperedError (data loss).\n return this.caps.unsealOneField(field, blob, this.sealKeyMaterial(cek))\n }\n\n /**\n * Build the `ViaCryptoCtx` for forget()'s per-ref via-erase fold (#629\n * Task 10) — resolves the live envelope's CEK exactly like\n * `decryptRecord` does, then delegates to the same capability factory\n * `encodeAtRest`/`decodeAtRest` use.\n */\n async eraseCryptoCtx(id: string, live: EncryptedEnvelope): Promise<ViaCryptoCtx> {\n return this.viaCryptoCtx(id, await this.resolveEnvelopeCek(live, id))\n }\n\n /**\n * Classify a live envelope's `_sealed` slots for crypto-shred completeness.\n * `forget()` drops `_cek`/`_sealed` but\n * RETAINS the collection DEK, so only a slot keyed off the per-record CEK is\n * genuinely shredded by the tombstone; a legacy slot keyed off the\n * collection DEK survives in any synced/backup copy.\n *\n * Mirrors {@link unsealField}'s dual-read split: try the CEK-derived key per\n * slot — success → `shreddable`, AES-GCM auth failure → `dekResidue`. With no\n * `_cek` (pure legacy collection-DEK sealing) ALL slots are residue.\n */\n async classifySealedShred(\n live: EncryptedEnvelope,\n ): Promise<{ readonly slots: readonly SealedShredSlot[] }> {\n const slots: SealedShredSlot[] = []\n // Verify-digest slots are CEK-only by construction (I3): dropping `_cek`\n // makes every `_vdig[field]` permanently undecryptable — shreddable\n // unconditionally, no vdig-dekResidue class (spec §2 forget()). Same\n // honesty caveats as #306 D5 for synced/backup copies of the ciphertext.\n // A `_vdig` field ALSO carrying a `_bidx` tag is the third category: the\n // tombstone drops it from the live store, but a pre-forget backup retains\n // the blind-index tag under the surviving collection DEK — live-shreddable\n // yet dekResidue-in-backups. `_bidx[field] ⇒ _vdig[field]` (I4), so we\n // decide per vdig field and emit exactly one slot each (count-preserving).\n if (live._vdig !== undefined && live._cek !== undefined) {\n for (const field of Object.keys(live._vdig)) {\n slots.push({\n field,\n class: live._bidx?.[field] !== undefined\n ? 'live-shreddable+dekResidue-in-backups'\n : 'shreddable',\n })\n }\n }\n const sealed = live._sealed\n if (sealed === undefined) return { slots }\n const cek = await this.resolveEnvelopeCek(live)\n for (const [field, blob] of Object.entries(sealed)) {\n if (cek === undefined) { slots.push({ field, class: 'dekResidue' }); continue }\n const { iv, data } = parseSealedSlot(blob)\n try {\n await this.caps.decrypt(iv, data, await this.caps.deriveSealedFieldKeyFromCek(cek, this.ctx.name, field))\n slots.push({ field, class: 'shreddable' })\n } catch {\n slots.push({ field, class: 'dekResidue' })\n }\n }\n return { slots }\n }\n\n /**\n * Build a non-leaking {@link Sealed} handle over a sealed field's ciphertext.\n * The handle captures only the ciphertext `blob` and a closure to\n * {@link unsealField}; the plaintext is never stored on it — so the handle\n * may sit in the working-set cache (or be logged/serialised) without\n * exposing the value, which decrypts only on `reveal()`.\n */\n makeSealedHandle(field: string, blob: string, cek?: CapsuleKey): SealedHandle<unknown> {\n return this.caps.makeHandleProducer(this.sealKeyMaterial(cek))(field, blob)\n }\n\n /**\n * Replace every field the just-written envelope actually sealed with a\n * {@link Sealed} handle, leaving every other field as its plaintext value.\n * Used to populate the cache on the write path without ever materialising\n * sealed plaintext into it. Returns `record` untouched when nothing was\n * sealed. Keys off `envelope._sealed` itself (#642 fix) — NOT\n * `this.ctx.sensitiveFields`, which only names the inline `sensitive:[...]`/\n * classified-recoverable field set: a collection whose sealing comes\n * entirely from the `taint` via-binding (a derivation/MV output or rollup\n * target folded sealed from a classified SOURCE collection, #642) declares\n * no local `sensitiveFields` at all, so that stale gate skipped this\n * conversion and left a just-written taint-sealed field as cached\n * plaintext — read back correctly ONLY after a cold cache miss (`get()`'s\n * own `decodeAtRest` call, unaffected by this gate) forced a fresh decrypt.\n */\n async toCacheRecord(record: T, envelope: EncryptedEnvelope, id?: string): Promise<T> {\n const sealed = envelope._sealed\n if (sealed === undefined || !this.ctx.storeCiphertext) {\n return record\n }\n const cek = await this.resolveEnvelopeCek(envelope, id)\n const clone = { ...(record as unknown as Record<string, unknown>) }\n for (const [field, blob] of Object.entries(sealed)) {\n clone[field] = this.makeSealedHandle(field, blob, cek)\n }\n return clone as unknown as T\n }\n\n /**\n * Apply `_sealed`-slot post-processing to an ALREADY-DECRYPTED `record`:\n * restore each `_sealed[field]` blob inline (`sealedAsHandles` falsy) or\n * as an opaque {@link Sealed} handle (`sealedAsHandles: true`), routing\n * through a via pipeline's `decodeAtRest` hook when one is declared.\n * Extracted byte-parity from `decryptRecord`'s inline block (#635) so a\n * caller that resolves its OWN per-record CEK under a DIFFERENT DEK than\n * this codec's `this.ctx.getDEK()` — namely the tier>0 `getAtTier` leg in\n * `with-audit/tiers/index.ts`, whose `_cek` is wrapped under the TIER DEK,\n * not the collection DEK `decryptRecord`/`resolveEnvelopeCek` always\n * unwrap under — can still get correct sealed-slot handling without\n * routing its whole read through `decryptRecord` (which would resolve the\n * wrong DEK for it).\n *\n * Zero-knowledge: takes only the already-resolved per-record `cek` (or\n * `undefined` for a legacy/no-CEK record) — the same shape `makeSealedHandle`\n * already takes above. Never the keyring, never a raw DEK: the legacy\n * fallback key stays fully internal to `sealKeyMaterial`/`viaCryptoCtx`,\n * which close over THIS codec's own `getDEK` (permanently bound to\n * `this.ctx.name`'s tier-0 DEK at construction) — so even when called from\n * a tier>0 context, the legacy-fallback key resolution is correct: sealed\n * fields are always sealed under the CEK or the collection's tier-0 DEK,\n * never a tier DEK (tier writes don't seal fields; `_sealed` reaches a\n * tiered envelope only via a tier-0 write later elevated).\n */\n async applySealedSlots(\n record: T,\n sealed: Record<string, string>,\n cek: CapsuleKey | undefined,\n opts: { id?: string; sealedAsHandles?: boolean } = {},\n ): Promise<T> {\n if (this.ctx.via?.hasAtRestHooks) {\n // Retargeted from `=== undefined`, which #1041 made unreachable on the\n // read path too: `decryptRecord` now takes a required identity, so the\n // compiler asks that question. An EMPTY id can still get through, and it\n // would scope every record's sealed slots to the same capability.\n if (opts.id === undefined || opts.id === '') {\n throw new Error(\n `RecordCodecBase.decryptRecord: collection \"${this.ctx.name}\" has via at-rest hooks but this read ` +\n 'path supplied no record id (needed to scope the sealed-slot capability) — caller bug.',\n )\n }\n const sealedRefs: Record<string, SealedSlotRef> = {}\n for (const [field, blob] of Object.entries(sealed)) {\n const { iv, data } = parseSealedSlot(blob)\n sealedRefs[field] = { iv, data }\n }\n return await this.ctx.via.decodeAtRest(\n record as unknown as Record<string, unknown>,\n sealedRefs,\n this.viaCryptoCtx(opts.id, cek),\n { asHandles: opts.sealedAsHandles === true },\n ) as unknown as T\n }\n return await this.caps.unsealFields(\n record as unknown as Record<string, unknown>,\n sealed,\n this.sealKeyMaterial(cek),\n { asHandles: opts.sealedAsHandles === true },\n ) as unknown as T\n }\n\n /**\n * Decrypt an envelope under an EXPLICIT `dek` instead of this codec's own\n * `ctx.getDEK()` / cekCache resolution — for a from-tier>0 pre-move decode\n * (`with-audit/tiers/index.ts`'s `putAtTier`/`elevate`/`demote` sites,\n * #722 whole-branch review). Mirrors the manual unwrap-then-decrypt\n * `getAtTier`'s own tier>0 leg performs there (same primitives, same\n * `applySealedSlots` extraction point #635 built for exactly this caller).\n *\n * `decryptRecord` is tier-UNAWARE: `resolveEnvelopeCek` reads the cekCache\n * or falls back to `ctx.getDEK()` — this codec's DEFAULT (tier-0/\n * collection) DEK — never the envelope's OWN tier. That only happens to\n * decrypt a from-tier>0 envelope correctly when some other call in the\n * SAME op already primed the cekCache with this record's CEK (true for an\n * ordinary put()-then-elevate(0→1); NOT true for a `putAtTier`-origin\n * envelope, whose body is encrypted directly under its tier DEK with no\n * `_cek` ever minted, or a non-`perRecordKeys` collection, which has no\n * `_cek` at all) — those throw `TamperedError`. Decrypting under the\n * caller-resolved `dek` is correct regardless of cache state or\n * `perRecordKeys`.\n *\n * No CRDT resolution, no schema validation — same scope as `getAtTier`'s\n * tier>0 branch (the established precedent for a tier-aware read), which\n * skips both for the same reason: a tier>0 read is an internal/derived-\n * output-recompute path, not the public per-record read surface schema\n * validation guards.\n */\n async decryptRecordAtDek(ref: RecordRef, envelope: EncryptedEnvelope, dek: CapsuleKey): Promise<T | null> {\n const id = ref.id\n if (isTombstone(envelope, this.ctx.storeCiphertext) || isDeleteMarker(envelope)) return null\n let plaintext: string\n let cek: CapsuleKey | undefined\n const aad = recordAadFor(ref, envelope)\n if (envelope._cek !== undefined) {\n cek = await this.caps.unwrapCek(envelope._cek, dek)\n plaintext = await this.caps.decrypt(...sealedBodyArgs(envelope, 'RecordCodec'), cek, aad)\n } else {\n plaintext = await this.caps.decrypt(...sealedBodyArgs(envelope, 'RecordCodec'), dek, aad)\n }\n let record = JSON.parse(plaintext) as T\n if (envelope._sealed !== undefined && this.ctx.storeCiphertext) {\n record = await this.applySealedSlots(record, envelope._sealed, cek, { ...(id !== undefined ? { id } : {}), sealedAsHandles: true })\n }\n // The tier>0 side door bypasses decryptJsonString, so it must account for\n // itself (#1363). A read a sensor cannot see is a hole in the estimate.\n this.observeDecrypt(ref)\n return record\n }\n\n /**\n * Decrypt an envelope into a record of type `T`.\n *\n * When a schema is attached, the decrypted value is validated before\n * being returned. A divergence between the stored bytes and the\n * current schema throws `SchemaValidationError` with\n * `direction: 'output'` — silently returning drifted data would\n * propagate garbage into the UI and break the whole point of having\n * a schema.\n *\n * `skipValidation` exists for history reads: when calling\n * `getVersion()` the caller is explicitly asking for an old snapshot\n * that may predate a schema change, so validating it would be a\n * false positive. Every non-history read leaves this flag `false`.\n */\n async decryptRecord(\n ref: RecordRef,\n envelope: EncryptedEnvelope,\n opts: { skipValidation?: boolean; sealedAsHandles?: boolean } = {},\n ): Promise<T | null> {\n const json = await this.decryptJsonString(ref, envelope)\n // Tombstone (shredded record) → null, propagated from decryptJsonString.\n // Callers skip null exactly as they already skip a tombstone envelope.\n if (json === null) return null\n let parsed: unknown = JSON.parse(json)\n\n // CRDT resolution: if this collection is in CRDT mode, the\n // stored JSON is a CrdtState, not T directly. Resolve to the snapshot.\n if (this.ctx.crdtMode && parsed !== null && typeof parsed === 'object' && '_crdt' in parsed) {\n parsed = this.ctx.crdtStrategy.resolveCrdtSnapshot(parsed as CrdtState)\n }\n\n let record = parsed as T\n\n // Structural group-encryption + sealed access gate.\n // Each `_sealed[field]` slot is restored under its own per-field key.\n // `sealedAsHandles: false` (default — internal callers that compute on\n // real values) inline-decrypts to the plaintext value; `true` (the\n // public / cache path) yields an opaque {@link Sealed} handle so the\n // plaintext is never materialised into the working-set cache. A\n // collection whose via pipeline declares at-rest hooks (#629 Task 3)\n // decodes through THOSE instead of the inline dual-read below — the\n // zero-via fast path (no `via`, or a pipeline with no at-rest hooks)\n // always takes the `else` branch, unchanged.\n if (envelope._sealed !== undefined && this.ctx.storeCiphertext) {\n const sealedCek = await this.resolveEnvelopeCek(envelope, ref.id)\n record = await this.applySealedSlots(record, envelope._sealed, sealedCek, {\n id: ref.id,\n ...(opts.sealedAsHandles !== undefined ? { sealedAsHandles: opts.sealedAsHandles } : {}),\n })\n }\n\n // Skip output validation when sealed fields are returned as handles:\n // the record carries opaque `Sealed` handles in place of the declared\n // values, so a whole-record schema check would false-positive on them.\n // (The values were validated on input/write; the open fields are\n // unchanged from the validated body.) The inline-value path below still\n // validates fully.\n const sealedAsHandles = opts.sealedAsHandles === true && envelope._sealed !== undefined\n if (this.ctx.schema !== undefined && !opts.skipValidation && !sealedAsHandles) {\n // Context string deliberately avoids leaking the record id — the\n // envelope only carries the version, not the id (the id lives in\n // the adapter-side key). `<collection>@v<n>` is enough for the\n // developer to find the offending record.\n record = await validateSchemaOutput(\n this.ctx.schema,\n record,\n `${this.ctx.name}@v${envelope._v}`,\n )\n }\n\n return record\n }\n}\n\n\n/**\n * The capsule-bound codec constructor: same shape callers have always used\n * (`new RecordCodec(ctx)`), with the capsule already closed over.\n */\n/**\n * `BoundRecordCodec` — PUBLISHED API, reachable from @noy-db/hub/capsule. Flagged as unused because\n * nothing inside hub imports it from this module; a consumer does. Do not narrow\n * or delete without a seam change (#55).\n *\n * @public\n */\nexport type BoundRecordCodec =\n (new <T>(ctx: RecordCodecContext<T>) => RecordCodecBase<T>) &\n // The STATICS come along too. `buildPlaintextEnvelope` is called as\n // `RecordCodec.buildPlaintextEnvelope(...)` from outside the capsule, so a\n // bare construct signature silently drops it — `Omit<…, never>` keeps the\n // static side while discarding the base's own construct signature.\n Omit<typeof RecordCodecBase, never>\n\nexport function makeRecordCodec(p: CapsulePrimitives, slots: ReturnType<typeof makeSealedSlots>) {\n // One object, reached as `this.caps.*` from the base class. Destructuring the\n // names here would shadow nothing and be dead — the class closes over `caps`,\n // not over individual primitives.\n const caps: CapsuleBinding = { ...p, ...slots }\n\n\n\n // ⚠️ ANNOTATED, not inferred. Returning the class expression directly makes\n // it an ANONYMOUS class type in the emitted `.d.ts`, and TypeScript refuses\n // that for a class with protected members (TS4094). Naming the constructor\n // type keeps the declaration emittable and keeps `T` flowing to callers.\n const RecordCodec: BoundRecordCodec = class<T> extends RecordCodecBase<T> {\n constructor(ctx: RecordCodecContext<T>) {\n super(caps, ctx)\n }\n }\n\n return { RecordCodec }\n}\n","/**\n * Per-record CEK lifecycle helpers for the WRITE path.\n *\n * These are the collection-coupled CEK operations, lifted off `Collection`\n * behind narrow deps so the kernel file delegates instead of carrying the\n * crypto inline:\n *\n * - {@link resolveStableCek} — the stable-CEK rule: an update or history\n * snapshot reuses the record's existing CEK so a single CEK delete kills\n * the whole version chain; a genuine insert (or legacy record being\n * migrated) mints a fresh one.\n * - {@link rewrapBodyToDek} — move a record body's wrapping from one DEK to\n * another (tier elevate/demote, bundle re-key) WITHOUT changing the body\n * key: unwrap the CEK under the source DEK, re-encrypt the body under the\n * same CEK, re-wrap the CEK under the destination DEK. History-chain\n * identity is preserved because the body key never changes.\n *\n * Both are pure functions over their deps — the `cekCache` ownership stays on\n * the collection (its lifetime is tied to `load()`), so callers cache the\n * returned key themselves.\n */\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\nimport { buildRecordAad, type RecordIdentity } from './record-aad.js'\nimport type { Lru } from '../../kernel/cache/index.js'\nimport { sealedBodyArgs } from './envelope-body.js'\n\n/** Dependencies {@link resolveStableCek} needs from its collection. */\nimport type { CapsuleKey, CapsulePrimitives } from '../contract.js'\n\nexport interface StableCekDeps {\n /** The collection's per-record CEK cache (`null` → no caching). */\n readonly cache: Lru<string, CapsuleKey> | null\n /** Read the record's live envelope (to recover an existing `_cek`). */\n getLive(id: string): Promise<EncryptedEnvelope | null>\n /** The DEK the CEK is wrapped under (the collection DEK on the normal path). */\n getDEK(): Promise<CapsuleKey>\n}\n\n/** Re-wrapped body bytes + the (optional) CEK to cache. */\nexport interface RewrappedBody {\n readonly _iv: string\n readonly _data: string\n /** Present iff the source envelope carried a CEK (per-record-key record). */\n readonly _cek?: string\n /** The body key when one exists, so the caller can cache it; `null` for a legacy record. */\n readonly cek: CapsuleKey | null\n}\n\nexport function makeLifecycle(p: CapsulePrimitives) {\n const { decrypt, encrypt, generateDEK, unwrapCek, wrapCek } = p\n\n /**\n * Resolve the stable CEK for a record on the write path. Caches the resolved\n * key under `id` so an update + its history snapshot share one CEK.\n */\n async function resolveStableCek(deps: StableCekDeps, id: string): Promise<CapsuleKey> {\n const cached = deps.cache?.get(id)\n if (cached) return cached\n\n const live = await deps.getLive(id)\n if (live?._cek !== undefined) {\n const cek = await unwrapCek(live._cek, await deps.getDEK())\n deps.cache?.set(id, cek, 1)\n return cek\n }\n\n const fresh = await generateDEK()\n deps.cache?.set(id, fresh, 1)\n return fresh\n }\n\n /**\n * Move a record body from `fromDek`/`from` to `toDek`/`to`.\n *\n * - Per-record-key record (`_cek` present): unwrap the CEK under `fromDek`,\n * re-encrypt the body under the SAME CEK, re-wrap the CEK under `toDek`. The\n * body key is unchanged → history-chain identity preserved.\n * - Legacy record (no `_cek`): decrypt under `fromDek`, re-encrypt under `toDek`\n * directly.\n *\n * ## Why it takes TWO identities (#1041)\n *\n * A rewrap is the one operation where the identity genuinely **changes**. A\n * tier move rewrites `_tier`; a history snapshot lands at a different\n * `(collection, id)` entirely. Since AAD binds both, the body must be opened\n * under the identity it currently has and re-sealed under the one it is moving\n * to. A single identity would be wrong for one end or the other.\n *\n * ⚠️ **This retires an optimisation, deliberately.** Callers used to compute\n * ONE rewrap and reuse the bytes for both a tier move's live write and its\n * `_history` snapshot (#728). Those now have different destination identities,\n * so they need different AAD and therefore different ciphertext — the same\n * bytes cannot satisfy both. Reusing one rewrap for two destinations is now a\n * silent corruption, not a saving: it produces a record that opens at one\n * address and not the other.\n */\n async function rewrapBodyToDek(\n from: RecordIdentity,\n to: RecordIdentity,\n envelope: Pick<EncryptedEnvelope, '_iv' | '_data' | '_cek' | '_tier' | '_by'>,\n fromDek: CapsuleKey,\n toDek: CapsuleKey,\n ): Promise<RewrappedBody> {\n const openAad = buildRecordAad(from)\n const sealAad = buildRecordAad(to)\n if (envelope._cek !== undefined) {\n const cek = await unwrapCek(envelope._cek, fromDek)\n const plaintext = await decrypt(...sealedBodyArgs(envelope, 'rewrapBodyToDek'), cek, openAad)\n const { iv, data } = await encrypt(plaintext, cek, sealAad)\n return { _iv: iv, _data: data, _cek: await wrapCek(cek, toDek), cek }\n }\n const plaintext = await decrypt(...sealedBodyArgs(envelope, 'rewrapBodyToDek'), fromDek, openAad)\n const { iv, data } = await encrypt(plaintext, toDek, sealAad)\n return { _iv: iv, _data: data, cek: null }\n }\n\n /**\n * Merge an ALREADY-rewrapped {@link RewrappedBody} into `envelope`, keeping\n * every field but the re-keyed body (`_iv`/`_data`/`_cek`) unchanged.\n *\n * Body-field access (`_iv`/`_data`/`_cek`) is sanctioned inside the enclave;\n * this lets callers outside it get back a ready-to-store envelope without\n * reading those fields themselves. Takes a body the caller has ALREADY\n * computed (vs. {@link rewrapEnvelope}, which also runs `rewrapBodyToDek`)\n * so a caller building a SECOND envelope from the same rewrap — a tier\n * move's live write AND its pre-move `_history` snapshot, #728 — pays the\n * unwrap/decrypt/encrypt/wrap crypto once, not twice.\n */\n function applyRewrappedBody(envelope: EncryptedEnvelope, body: RewrappedBody): EncryptedEnvelope {\n const next: EncryptedEnvelope = {\n ...envelope,\n _iv: body._iv,\n _data: body._data,\n ...(body._cek !== undefined ? { _cek: body._cek } : {}),\n }\n if (body._cek === undefined && envelope._cek !== undefined) {\n delete (next as { _cek?: string })._cek\n }\n return next\n }\n\n /**\n * Move a FULL stored envelope from `fromDek` to `toDek`, preserving every\n * field other than the re-keyed body (`_iv`/`_data`/`_cek`) unchanged.\n * Mirrors the spread-merge idiom `with-audit/tiers/index.ts`'s\n * `elevate()`/`demote()` use to apply `rewrapBodyToDek`'s result to the live\n * envelope, generalized to any envelope shape (history callers don't bump\n * `_v`/`_ts`/`_tier` the way a tier move does).\n */\n async function rewrapEnvelope(\n identity: RecordIdentity,\n envelope: EncryptedEnvelope,\n fromDek: CapsuleKey,\n toDek: CapsuleKey,\n ): Promise<EncryptedEnvelope> {\n // Identity is UNCHANGED here — this rewrap only moves the wrapping key\n // (history re-key after a rotation), never the record's address or tags. So\n // the same identity opens and re-seals (#1041).\n return applyRewrappedBody(envelope, await rewrapBodyToDek(identity, identity, envelope, fromDek, toDek))\n }\n\n /**\n * Is `envelope` ALREADY wrapped under `dek` (a per-record CEK wrapped under\n * `dek`, or — legacy, no `_cek` — the body itself decryptable under `dek`)?\n *\n * A toDek-first idempotency check for retry-safe rewrap loops (#712 crash\n * atomicity, `with-commit/history/history.ts`'s `rewrapHistory`): a crash\n * mid-rewrap can strand some entries already moved to `toDek` while others\n * remain under `fromDek`. Calling this with `toDek` before attempting the\n * `fromDek` rewrap lets the caller skip an already-migrated entry instead of\n * re-wrapping it (a re-wrap would be a correctness no-op but doubles as an\n * unnecessary write) — and, more importantly, tells a retry loop that this\n * entry needs no `fromDek` unwrap attempt at all, so a crash that landed some\n * entries under `toDek` doesn't wedge on those already-moved entries.\n *\n * AES-KW (CEK unwrap) and AES-GCM (direct decrypt) are both authenticated —\n * unwrap/decrypt under the wrong key throws, never silently returns garbage\n * — so a caught error here reliably means \"not wrapped under `dek`\", not a\n * corrupt envelope (a genuinely corrupt envelope also throws under the\n * correct key, but callers that reach this helper only do so as a\n * best-effort probe before their own decisive fromDek attempt, which still\n * surfaces real corruption).\n *\n * Body-field access (`_iv`/`_data`/`_cek`) is sanctioned inside the enclave —\n * this is the sanctioned door so callers outside it (history.ts) never read\n * those fields themselves, preserving the check-architecture body-access\n * ratchet.\n */\n async function isRewrappedUnder(\n identity: RecordIdentity,\n envelope: Pick<EncryptedEnvelope, '_iv' | '_data' | '_cek'>,\n dek: CapsuleKey,\n ): Promise<boolean> {\n try {\n if (envelope._cek !== undefined) {\n await unwrapCek(envelope._cek, dek)\n } else {\n // MUST pass AAD: without it this trial decrypt fails for every bound\n // record, the answer is always `false`, and the crash-idempotency skip\n // it exists to provide silently stops working (#1041).\n await decrypt(...sealedBodyArgs(envelope, 'rewrapBodyToDek'), dek, buildRecordAad(identity))\n }\n return true\n } catch {\n return false\n }\n }\n\n return { resolveStableCek, rewrapBodyToDek, applyRewrappedBody, rewrapEnvelope, isRewrappedUnder }\n}\n","/**\n * Re-key one envelope from an old collection DEK to a new one (#1074).\n *\n * Lives in the enclave because it is envelope surgery: it reads and writes the\n * protected body slots (`_iv`/`_data`/`_cek`), which `enclave-body-only`\n * reserves to `capsule/enclave-aes/**`. `rotateKeys` previously did this inline in\n * `with-party/team/keyring.ts` and got it wrong in two ways that the guard\n * would have caught had the code lived here.\n *\n * @packageDocumentation\n */\nimport { recordAadFor } from './record-aad.js'\nimport type { EncryptedEnvelope } from '../../kernel/types.js'\nimport { sealedBodyArgs } from './envelope-body.js'\n\n/**\n * Produce the envelope `envelope` becomes once its collection's DEK rotates\n * from `oldDek` to `newDek`.\n *\n * **Every slot is carried forward except `_bidx`.** The previous inline version\n * built a fresh literal holding only `_noydb/_v/_ts/_iv/_data`, silently\n * discarding `_by`, `_tier`, `_cek`, `_sealed`, `_vdig` and `_source`. Losing\n * `_tier` was the worst of those: tier-0 reads treat elevated as missing, so an\n * elevated record did not error after a rotation — it simply disappeared.\n *\n * `_bidx` is dropped deliberately. It is a DEK-rooted equality tag, so a tag\n * carried across a DEK rotation can never be re-derived to match a query again\n * while still leaking the old equality partition. Index coverage regrows\n * per-record on the next `put()` under the new DEK.\n *\n * Two shapes, and picking the wrong one is why this needed a helper:\n *\n * - **per-record CEK** (`_cek` present) — the body is sealed under the CEK and\n * the CEK is wrapped under the collection DEK, so rotation re-wraps the CEK\n * and leaves the body **untouched**. The old inline code instead ran\n * `decrypt(body, oldDek)` on these, which throws: rotation could not complete\n * on a collection containing any CEK record.\n * - **bare** — body sealed directly under the DEK, so decrypt and re-encrypt.\n *\n * Idempotence is the caller's problem, not this function's: it assumes\n * `envelope` is still under `oldDek`. A resumable rotation must know which side\n * of the migration each record is on before calling.\n */\nimport type { CapsuleKey, CapsulePrimitives } from '../contract.js'\n\nexport function makeRekey(p: CapsulePrimitives) {\n const { decrypt, encrypt, unwrapCek, wrapCek } = p\n\n async function rekeyEnvelopeToDek(\n ref: { readonly collection: string; readonly id: string },\n envelope: EncryptedEnvelope,\n oldDek: CapsuleKey,\n newDek: CapsuleKey,\n ): Promise<EncryptedEnvelope> {\n // A DEK rotation moves the WRAPPING key only — the record keeps its address\n // and its tags — so the same AAD opens and re-seals it (#1041).\n const aad = recordAadFor(ref, envelope)\n const { _bidx, ...carried } = envelope\n void _bidx // dropped deliberately — see above\n\n if (envelope._cek !== undefined) {\n const cek = await unwrapCek(envelope._cek, oldDek)\n return { ...carried, _cek: await wrapCek(cek, newDek) }\n }\n\n const plaintext = await decrypt(...sealedBodyArgs(envelope, 'rekeyRecord'), oldDek, aad)\n const { iv, data } = await encrypt(plaintext, newDek, aad)\n return { ...carried, _ts: new Date().toISOString(), _iv: iv, _data: data }\n }\n\n /**\n * Resumable variant: returns the re-keyed envelope, or `null` when `envelope`\n * is **already** under `newDek` and needs no work (#1074 part 2).\n *\n * A rotation interrupted mid-collection leaves records on both sides of the\n * migration. Resuming means re-running the rotation with the *same* new DEK,\n * which requires distinguishing \"not yet moved\" from \"already moved\" without\n * the caller touching protected slots.\n *\n * Detection is by trial decryption under `oldDek`. There is no metadata that\n * says which DEK sealed a record — deliberately, since such a marker would be\n * store-writable and therefore a downgrade lever (the same reasoning that\n * keeps the reader from branching on `_noydb`).\n *\n * A record that opens under *neither* key is genuinely damaged and rethrows,\n * rather than being silently skipped: a rotation that quietly walked past\n * unreadable records would convert a loud failure into permanent silent loss,\n * which is the defect this whole issue is about.\n */\n async function rekeyEnvelopeIfNeeded(\n ref: { readonly collection: string; readonly id: string },\n envelope: EncryptedEnvelope,\n oldDek: CapsuleKey,\n newDek: CapsuleKey,\n ): Promise<EncryptedEnvelope | null> {\n try {\n return await rekeyEnvelopeToDek(ref, envelope, oldDek, newDek)\n } catch (errUnderOld) {\n // Already migrated? Then it opens under the new DEK and there is nothing\n // to do. Verified rather than assumed.\n try {\n if (envelope._cek !== undefined) await unwrapCek(envelope._cek, newDek)\n else await decrypt(...sealedBodyArgs(envelope, 'rekeyRecord'), newDek, recordAadFor(ref, envelope))\n return null\n } catch {\n throw errUnderOld\n }\n }\n }\n\n /**\n * Does this envelope open under ANY of `keys`? A read-only probe — decrypts\n * nothing into a caller-visible value and writes nothing.\n *\n * Exists so a rotation can tell *\"this envelope belongs to a DIFFERENT DEK slot\n * the caller also holds\"* from *\"this envelope is damaged\"* (#1125). Those two\n * look identical from a failed `rekeyEnvelopeIfNeeded`, and collapsing them\n * either aborts a legitimate rotation or walks past unreadable data.\n *\n * **The question is deliberately asked of the KEY, not of the envelope's claimed\n * `_tier`.** `_tier` is unencrypted — the store writes it — so routing a\n * rotation on it would let a store mark a tier-0 record `_tier: 5` and have the\n * rotation skip it, leaving a revoked member's DEK live on real data. That is\n * #1115's defect wearing a different hat. A key either opens the body or it does\n * not, and a store cannot forge that.\n *\n * Mirrors `rekeyBlobSet`'s `otherDeks` treatment, which asks the same question\n * about the same kind of ambiguity.\n */\n async function envelopeOpensUnderAny(\n ref: { readonly collection: string; readonly id: string },\n envelope: EncryptedEnvelope,\n keys: readonly CapsuleKey[],\n ): Promise<boolean> {\n for (const key of keys) {\n try {\n if (envelope._cek !== undefined) await unwrapCek(envelope._cek, key)\n else await decrypt(...sealedBodyArgs(envelope, 'rekeyRecord'), key, recordAadFor(ref, envelope))\n return true\n } catch { /* not this key */ }\n }\n return false\n }\n\n return { rekeyEnvelopeToDek, rekeyEnvelopeIfNeeded, envelopeOpensUnderAny }\n}\n","/**\n * Re-key a vault's blob set from an old `_blob` DEK to a new one (#1122).\n *\n * ## The defect this closes\n *\n * `rotateKeys` re-keys `store.list(vault, <slot>)` plus the derived refs\n * `derivedRefsFor` declares. That assumes a DEK slot named `X` protects data\n * filed under collection `X`. For `_blob` it is false: the slot is `_blob`, but\n * every byte it protects lives in `_blob_index` and `_blob_chunks`. So rotating\n * `_blob` minted a fresh DEK, re-encrypted **nothing**, and made every blob in\n * the vault permanently unreadable — reported to the user as `TamperedError`,\n * i.e. as an attack, when it was self-inflicted. An ordinary `revoke` of a\n * whole-vault grantee reaches it, because such a grantee's DEK map contains\n * `_blob`.\n *\n * ## Why this cannot be done by `rekeyEnvelopeIfNeeded` alone\n *\n * Two of the three surfaces do not have the shape that helper assumes:\n *\n * - a **chunk** envelope is sealed over raw bytes under a bespoke AAD\n * (`{eTag}:{index}:{count}`), not under the record AAD, so the generic\n * helper cannot open it;\n * - an **index** envelope's body is JSON that may itself carry a wrapped\n * per-blob content CEK (`_cek` / `_cekPending`). Re-encrypting the body under\n * the new DEK without re-wrapping that CEK leaves a blob whose metadata opens\n * and whose bytes never will.\n *\n * ## Why it lives in the enclave\n *\n * It reads and writes protected body slots and it unwraps/re-wraps keys — the\n * two things `enclave-body-only` reserves to `capsule/enclave-aes/**`, and the same\n * reason `rekeyEnvelopeToDek` moved here in #1074. `with-party` orchestrates\n * the rotation and must not touch a body to do it.\n *\n * The blob layout below is duplicated from its owner rather than imported:\n * `with-shape/blobs/blob-set.ts` already imports `with-party/team/tiers.js`, so\n * a `with-party → with-shape` import would close a cycle, and the kernel may\n * not depend on a service. `BlobObject` itself is a kernel type, so only the\n * three collection/id string layouts are copied, with their owner named.\n *\n * @packageDocumentation\n */\nimport { recordAadFor } from './record-aad.js'\nimport type { BlobObject, EncryptedEnvelope, NoydbStore } from '../../kernel/types.js'\nimport { sealedBodyArgs } from './envelope-body.js'\n\n/** Blob metadata envelopes, keyed by eTag. owner: with-shape/blobs/blob-set.ts */\nimport type { CapsuleKey, CapsulePrimitives } from '../contract.js'\n\n/**\n * What a rotation did to the blob set.\n *\n * Deliberately NOT on the enclave barrel: no caller outside this module binds\n * it, and that surface is the fork-swap trust boundary, so exporting a type\n * nobody consumes is pure widening.\n *\n * It is a return value rather than a log line because `foreign` and `blobs`\n * are the only way to tell \"left alone, correctly\" from \"left alone, because\n * the caller passed a wrong `otherDeks` set\". Both re-key nothing and both\n * return successfully. `rotate-preserves-blobs.test.ts` asserts these counts\n * against a direct call for exactly that reason.\n */\nexport interface BlobRekeyReport {\n /** Index envelopes moved onto the new DEK. */\n readonly blobs: number\n /** Chunk envelopes re-encrypted under the new DEK. */\n readonly chunks: number\n /**\n * Index envelopes left alone because they open under a DEK the caller holds\n * for a DIFFERENT slot — an elevated blob, sealed under `_blob#<tier>`. Those\n * are that slot's rotation to do, not this one's.\n */\n readonly foreign: number\n /** Index envelopes already under the new DEK — a resumed rotation. */\n readonly alreadyMoved: number\n}\n\nexport function makeRekeyBlob(p: CapsulePrimitives) {\n const { decrypt, decryptBytesWithAAD, encrypt, encryptBytesWithAAD, unwrapCek, wrapCek } = p\n\n const BLOB_INDEX_COLLECTION = '_blob_index'\n /** Chunk envelopes, keyed by `{eTag}_{chunkIndex}`. owner: with-shape/blobs/blob-set.ts */\n const BLOB_CHUNKS_COLLECTION = '_blob_chunks'\n\n /**\n * Chunk integrity binding: `{eTag}:{chunkIndex}:{chunkCount}`.\n * Mirrors `chunkAAD` in `with-shape/blobs/blob-set.ts` — the same duplication\n * `with-cargo/extract-partition.ts` already carries, for the same reason.\n */\n function chunkAad(eTag: string, index: number, chunkCount: number): Uint8Array {\n return new TextEncoder().encode(`${eTag}:${index}:${chunkCount}`)\n }\n\n /** Did `key` open this envelope's JSON body? */\n async function opensJson(\n ref: { collection: string; id: string },\n env: EncryptedEnvelope,\n key: CapsuleKey,\n ): Promise<string | null> {\n try {\n return await decrypt(...sealedBodyArgs(env, 'rekeyBlob'), key, recordAadFor(ref, env))\n } catch {\n return null\n }\n }\n\n /**\n * Move every blob sealed under `oldDek` onto `newDek`.\n *\n * Chunks are re-encrypted BEFORE their index entry is re-sealed, and that order\n * is the resume property: an index entry still readable under `oldDek` means\n * its chunks may be half-moved (each one is probed individually), and an index\n * entry readable under `newDek` means its chunks are already done. An\n * interrupted rotation is finished by re-running it with the same `newDek`.\n *\n * `otherDeks` is what keeps a tier-elevated blob from being mistaken for a\n * damaged one. Membership in `_blob_index` is by DEK, not by collection name:\n * a record elevated to tier N has its blob metadata sealed under `_blob#N`. An\n * envelope that opens under NONE of the keys the caller holds is genuinely\n * unreadable and throws, exactly as `rekeyEnvelopeIfNeeded` does — silently\n * walking past it would turn a loud failure into permanent quiet loss.\n */\n async function rekeyBlobSet(\n adapter: NoydbStore,\n vault: string,\n oldDek: CapsuleKey,\n newDek: CapsuleKey,\n otherDeks: readonly CapsuleKey[] = [],\n ): Promise<BlobRekeyReport> {\n let blobs = 0\n let chunks = 0\n let foreign = 0\n let alreadyMoved = 0\n\n let eTags: string[]\n try {\n eTags = await adapter.list(vault, BLOB_INDEX_COLLECTION)\n } catch {\n return { blobs, chunks, foreign, alreadyMoved }\n }\n\n for (const eTag of eTags) {\n const idxEnv = await adapter.get(vault, BLOB_INDEX_COLLECTION, eTag)\n if (!idxEnv || !idxEnv._iv) continue // no sealed body — nothing to move\n\n const ref = { collection: BLOB_INDEX_COLLECTION, id: eTag }\n const json = await opensJson(ref, idxEnv, oldDek)\n if (json === null) {\n if ((await opensJson(ref, idxEnv, newDek)) !== null) { alreadyMoved++; continue }\n let claimed = false\n for (const key of otherDeks) {\n if ((await opensJson(ref, idxEnv, key)) !== null) { claimed = true; break }\n }\n if (claimed) { foreign++; continue }\n throw new Error(\n `[noy-db] rotateKeys: blob index entry \"${eTag}\" opens under neither the retiring `\n + 'nor the new `_blob` DEK, nor any other key this keyring holds. Rotation stopped '\n + 'rather than leaving it unreadable and unreported.',\n )\n }\n\n const blob = JSON.parse(json) as BlobObject\n\n // Chunks first. A legacy blob's bytes sit under the `_blob` DEK itself; an\n // erasable blob's sit under its per-blob content CEK and must NOT be\n // touched — re-wrapping that CEK below is the whole of its rotation. A\n // blob interrupted mid-`migrate()` is mixed, so every chunk is probed.\n for (let i = 0; i < blob.chunkCount; i++) {\n const id = `${eTag}_${i}`\n const chunkEnv = await adapter.get(vault, BLOB_CHUNKS_COLLECTION, id)\n if (!chunkEnv || !chunkEnv._iv) continue\n const aad = chunkAad(eTag, i, blob.chunkCount)\n let plain: Uint8Array\n try {\n plain = await decryptBytesWithAAD(...sealedBodyArgs(chunkEnv, 'rekeyBlob'), oldDek, aad)\n } catch {\n // A blob that holds a content CEK (settled or pending) keeps its bytes\n // under that CEK, not under the `_blob` DEK — re-wrapping the CEK below\n // is the whole of its rotation, and a chunk that refuses `oldDek` is\n // simply one of those. Nothing to do, and nothing suspicious.\n if (blob._cek !== undefined || blob._cekPending !== undefined) continue\n // Otherwise the blob is legacy: every one of its chunks MUST be under a\n // `_blob` DEK. `newDek` means a previous run already moved this one —\n // verified, not assumed. Neither key means the chunk is damaged, and it\n // throws for the same reason `rekeyEnvelopeIfNeeded` does: the index\n // entry opened fine under `oldDek` and says nothing about its chunks, so\n // continuing here would walk past unreadable data and report success.\n try {\n await decryptBytesWithAAD(...sealedBodyArgs(chunkEnv, 'rekeyBlob'), newDek, aad)\n continue\n } catch {\n throw new Error(\n `[noy-db] rotateKeys: blob chunk \"${id}\" (${i + 1}/${blob.chunkCount} of eTag `\n + `\"${eTag}\") opens under neither the retiring nor the new \\`_blob\\` DEK, and its `\n + 'blob holds no content CEK that could explain it. Rotation stopped rather than '\n + 'leaving it unreadable and unreported.',\n )\n }\n }\n const { iv, data } = await encryptBytesWithAAD(plain, newDek, aad)\n await adapter.put(vault, BLOB_CHUNKS_COLLECTION, id, { ...chunkEnv, _iv: iv, _data: data })\n chunks++\n }\n\n // The wrapped content CEKs travel with the metadata: they are wrapped under\n // the `_blob` DEK, so a rotation that re-encrypted the body and left them\n // alone would produce metadata that opens and bytes that never do.\n const rewrapped: BlobObject = {\n ...blob,\n ...(blob._cek !== undefined\n ? { _cek: await wrapCek(await unwrapCek(blob._cek, oldDek), newDek) }\n : {}),\n ...(blob._cekPending !== undefined\n ? { _cekPending: await wrapCek(await unwrapCek(blob._cekPending, oldDek), newDek) }\n : {}),\n }\n\n const { iv, data } = await encrypt(JSON.stringify(rewrapped), newDek, recordAadFor(ref, idxEnv))\n await adapter.put(vault, BLOB_INDEX_COLLECTION, eTag, { ...idxEnv, _iv: iv, _data: data })\n blobs++\n }\n\n return { blobs, chunks, foreign, alreadyMoved }\n }\n\n return { rekeyBlobSet }\n}\n","/**\n * Record-scoped CEK sealing — grantor side.\n *\n * The **grantor** holds the collection DEK and seals ONE record's CEK to an\n * `at-*` host so that host — and only that host — can decrypt exactly that\n * record, with no access to the vault DEK and no ability to read any other\n * record. This is the counterpart to the host-side `openSealedRecord`\n * (`@noy-db/hub/sealed-record`), which holds no DEK.\n *\n * These functions are the orchestration behind `vault.sealRecordToHost` /\n * `vault.revokeSealedRecord` / `vault.rotateRecordCek`, lifted off `Vault`\n * behind a narrow {@link SealingContext} so the kernel file delegates. They\n * live in `record-keys/` (the vault-side CEK policy layer), NOT in\n * `sealed-record/`, so the host-side subpath stays DEK-free — the wire\n * *types* they share are spine-owned (`kernel/types.ts`), not imported from\n * `sealed-record/` directly.\n */\nimport { buildRecordAad, recordAadFor, type RecordIdentity } from './record-aad.js'\nimport {\n type EncryptedEnvelope,\n type NoydbStore,\n type RecipientSealer,\n type SealedCekBinding,\n type SealedCekDeliveryEnvelope,\n} from '../../kernel/types.js'\nimport { RecordCekNotFoundError, ValidationError } from '../../kernel/errors.js'\nimport { buildRecordEnvelope } from './record-envelope.js'\nimport { sealedBodyArgs } from './envelope-body.js'\n\nimport type { CapsuleKey, CapsulePrimitives } from '../contract.js'\n\n/** What the grantor functions need from their `Vault`. */\nexport interface SealingContext {\n readonly adapter: NoydbStore\n /** Vault id (the adapter's first coordinate). */\n readonly vault: string\n /** Resolve the DEK a record's CEK is wrapped under. */\n getDEK(collection: string): Promise<CapsuleKey>\n /** Actor id stamped on `_by` (empty string → omit). */\n readonly actor: string\n /** Evict the per-record CEK cache + decrypted-record cache after a rotation. */\n invalidateRecordCaches(collection: string, id: string): Promise<void>\n}\n\nexport function makeSealing(p: CapsulePrimitives, sealedSlot: { dualReadSealedSlot: (blob: string, field: string, collection: string, cek: CapsuleKey | undefined, dek: CapsuleKey) => Promise<string> }) {\n const { openVdigPayload, sealVdigPayload, bufferToBase64, decrypt, deriveSealedFieldKeyFromCek, encrypt, generateDEK, unwrapCek, wrapCek } = p\n const { dualReadSealedSlot } = sealedSlot\n\n const subtle = globalThis.crypto.subtle\n\n /** The `_sealed_cek` delivery namespace (one envelope per `collection/id/pid`). */\n const SEALED_CEK_NS = '_sealed_cek'\n\n /**\n * Seal a record's CEK to a host. See `vault.sealRecordToHost` for the contract.\n * Returns `{ pid, envelopeKey }`.\n */\n async function sealRecordToHost(\n ctx: SealingContext,\n collection: string,\n id: string,\n hostSealer: RecipientSealer,\n opts: { expiresAt: string },\n ): Promise<{ pid: string; envelopeKey: string }> {\n // Guard separators: the `_sealed_cek` id is `collection/id/pid`, so a `/` in\n // any component would make the prefix-delete in rotateRecordCek() (which\n // matches `${collection}/${id}/`) ambiguous and could over- or under-match.\n if (collection.includes('/')) throw new ValidationError(`sealRecordToHost: collection \"${collection}\" must not contain \"/\"`)\n if (id.includes('/')) throw new ValidationError(`sealRecordToHost: id \"${id}\" must not contain \"/\"`)\n // M-4: reject a malformed/empty expiry at seal time. `Date.parse('')` (and any\n // non-ISO string) is NaN, and the open-time check `NaN <= now` is `false` — so\n // a malformed expiry would silently mint an ETERNAL grant. A *past* (but valid)\n // timestamp is still allowed: it seals, then fails closed at open time.\n if (Number.isNaN(Date.parse(opts.expiresAt))) {\n throw new ValidationError(`sealRecordToHost: expiresAt \"${opts.expiresAt}\" is not a valid ISO 8601 timestamp`)\n }\n\n const live = await ctx.adapter.get(ctx.vault, collection, id)\n if (!live || live._cek === undefined) {\n throw new RecordCekNotFoundError(collection, id)\n }\n\n const dek = await ctx.getDEK(collection)\n const cek = await unwrapCek(live._cek, dek)\n const rawCek = await subtle.exportKey('raw', cek)\n const cekB64 = bufferToBase64(rawCek)\n\n const hint = await hostSealer.publishRecipientHint()\n if (hint.pid.includes('/')) throw new ValidationError(`sealRecordToHost: recipient pid \"${hint.pid}\" must not contain \"/\"`)\n\n const binding: SealedCekBinding = {\n collection,\n id,\n cek: cekB64,\n expiresAt: opts.expiresAt,\n }\n const sealed = await hostSealer.sealForRecipient(\n new TextEncoder().encode(JSON.stringify(binding)),\n hint,\n )\n\n const delivery: SealedCekDeliveryEnvelope = {\n v: 1,\n _noydb_sealed_cek: 1,\n pid: hint.pid,\n payload: bufferToBase64(sealed),\n expiresAt: opts.expiresAt,\n }\n\n const envelopeKey = `${collection}/${id}/${hint.pid}`\n const prior = await ctx.adapter.get(ctx.vault, SEALED_CEK_NS, envelopeKey)\n // Identity is the STORAGE address — `(SEALED_CEK_NS, envelopeKey)` — not the\n // record this delivery describes. Binding the subject record's collection/id\n // here would produce an envelope that cannot be read back from where it lives.\n const env: EncryptedEnvelope = buildRecordEnvelope(\n { collection: SEALED_CEK_NS, id: envelopeKey, version: (prior?._v ?? 0) + 1 },\n {\n // AES-GCM bypassed — the sealing layer is the security boundary, exactly\n // like the managed-secret `_meta/sealed-secret` envelope.\n iv: '',\n data: JSON.stringify(delivery),\n ...(ctx.actor ? { by: ctx.actor } : {}),\n },\n )\n await ctx.adapter.put(ctx.vault, SEALED_CEK_NS, envelopeKey, env)\n\n return { pid: hint.pid, envelopeKey }\n }\n\n /**\n * Revoke one sealed-CEK grant.\n *\n * **Default (`{ hard: false }`) is SOFT** — it only deletes the delivery\n * envelope from the store. A host that already fetched (or cached the unsealed\n * CEK from) that envelope KEEPS decrypt capability for the record; soft revoke\n * is a \"stop new fetches\" control, not a cryptographic cutoff.\n *\n * **`{ hard: true }`** additionally rotates the record's CEK (via\n * {@link rotateRecordCek}): the live body is re-encrypted under a fresh CEK and\n * EVERY sealed-CEK delivery envelope for the record (all pids) is deleted, so\n * any previously-sealed CEK fails the AES-GCM auth tag on the rotated body —\n * future opens are cryptographically denied. (PRE-rotation history versions,\n * which keep their old `_cek`, remain openable — same semantics as\n * `rotateRecordCek`.)\n */\n async function revokeSealedRecord(\n ctx: SealingContext,\n collection: string,\n id: string,\n pid: string,\n opts?: { hard?: boolean },\n ): Promise<void> {\n if (opts?.hard === true) {\n // Hard revocation supersedes the single-pid soft delete: rotateRecordCek\n // re-keys the record and drops every delivery envelope for it.\n await rotateRecordCek(ctx, collection, id)\n return\n }\n await ctx.adapter.delete(ctx.vault, SEALED_CEK_NS, `${collection}/${id}/${pid}`)\n }\n\n /**\n * HARD-rotate a record's CEK: re-encrypt the live body under a fresh CEK, evict\n * caches, and delete EVERY sealed-CEK delivery envelope for the record. See\n * `vault.rotateRecordCek` for why this bypasses `Collection.put` (no guards, no\n * history bump — a key rotation must not re-encrypt prior history under the new\n * CEK).\n */\n async function rotateRecordCek(\n ctx: SealingContext,\n collection: string,\n id: string,\n ): Promise<void> {\n const live = await ctx.adapter.get(ctx.vault, collection, id)\n if (!live || live._cek === undefined) {\n throw new RecordCekNotFoundError(collection, id)\n }\n\n const dek = await ctx.getDEK(collection)\n const oldCek = await unwrapCek(live._cek, dek)\n // A CEK rotation moves the body key, not the record's address — but it MAY\n // restamp `_by` (the rotating actor). So the identity is not symmetric: open\n // under what the live record carries, re-seal under what the new envelope\n // will carry (#1041).\n const openAad = recordAadFor({ collection, id }, live)\n const json = await decrypt(...sealedBodyArgs(live, 'reseal'), oldCek, openAad)\n\n const rotatedIdentity: RecordIdentity = {\n // A CEK rotation DOES bump `_v` (asserted in `record-scoped-cek-sealing`:\n // \"the live envelope was re-keyed — new `_cek`, bumped `_v`\"), so it seals\n // at the new version, not the one it just opened.\n //\n // ⚠️ Do not confuse this with `rekeyEnvelopeToDek`, the DEK-rotation helper,\n // which PRESERVES `_v` (#1074) because it re-keys a record without writing\n // one. Two rotations, opposite answers — and with `_v` bound (#1093) getting\n // it backwards no longer produces a wrong number, it produces an\n // unreadable record.\n collection, id, version: live._v + 1,\n ...(live._tier !== undefined ? { tier: live._tier } : {}),\n ...(ctx.actor ? { by: ctx.actor } : live._by !== undefined ? { by: live._by } : {}),\n }\n const newCek = await generateDEK()\n const { iv, data } = await encrypt(json, newCek, buildRecordAad(rotatedIdentity))\n\n // Sealed fields are keyed off the per-record CEK, so a rotation\n // must RE-ENCRYPT each `_sealed[field]` under the new CEK (carrying the old\n // slots forward would orphan them — the old CEK is gone after this rotate).\n // Dual-read the old slot: try the old-CEK-derived key, fall\n // back to the collection-DEK key (legacy), then re-seal under newCek.\n let sealedOut: Record<string, string> | undefined\n if (live._sealed !== undefined) {\n const out: Record<string, string> = {}\n for (const [field, slot] of Object.entries(live._sealed)) {\n const plaintext = await dualReadSealedSlot(slot, field, collection, oldCek, dek)\n const resealed = await encrypt(plaintext, await deriveSealedFieldKeyFromCek(newCek, collection, field))\n out[field] = `${resealed.iv}:${resealed.data}`\n }\n sealedOut = out\n }\n\n // Verify-digest slots are keyed off the per-record CEK too (I3: CEK-only,\n // no DEK fallback → single read, not a dual-read). Rotation must re-seal\n // each `_vdig[field]` under the new CEK with the SAME reconstructed AAD, or\n // the correct password would false-reject forever (C3).\n let vdigOut: Record<string, string> | undefined\n if (live._vdig !== undefined) {\n const out: Record<string, string> = {}\n for (const [field, blob] of Object.entries(live._vdig)) {\n const payload = await openVdigPayload(blob, oldCek, collection, id, field)\n out[field] = await sealVdigPayload(payload, newCek, collection, id, field)\n }\n vdigOut = out\n }\n\n // `_by`/`_tier` ride on `rotatedIdentity` — the same value the AAD above was\n // built from. They used to be passed in the body via a conditional SPREAD,\n // which TypeScript does not excess-property-check, so they flowed silently\n // into a shape that no longer reads them: stamped nowhere, sealed as if\n // present. Exactly the mismatch class this issue exists to close.\n const env: EncryptedEnvelope = buildRecordEnvelope(rotatedIdentity, {\n iv,\n data,\n cek: await wrapCek(newCek, dek),\n extra: {\n ...(live._det !== undefined ? { _det: live._det } : {}),\n // `_bidx` (equality-index tag) is DEK-rooted and CEK-independent, so it is\n // carried forward VERBATIM — not recomputed — unlike `_vdig`/`_sealed`\n // below, which are CEK-bound and must be re-sealed under the new CEK.\n // Dropping this line would silently orphan the equality index on rotation\n // (the #306 Slice-A data-loss bug, replayed on the index).\n ...(live._bidx !== undefined ? { _bidx: live._bidx } : {}),\n // Re-encrypt sealed (`sensitive`) fields under the new CEK. Sealed\n // keys derive off the per-record CEK, so the rotated record's old CEK is\n // destroyed here — carrying the old `_sealed` slots forward verbatim would\n // orphan them (unreadable under the new CEK). `sealedOut` holds the slots\n // dual-read from the old CEK (or the legacy DEK) and re-sealed under newCek.\n ...(sealedOut !== undefined ? { _sealed: sealedOut } : {}),\n ...(vdigOut !== undefined ? { _vdig: vdigOut } : {}),\n },\n })\n await ctx.adapter.put(ctx.vault, collection, id, env)\n\n // Evict BOTH caches synchronously so no read returns the stale old-CEK record.\n await ctx.invalidateRecordCaches(collection, id)\n\n // Delete every sealed-CEK delivery envelope for this record — all pids.\n const prefix = `${collection}/${id}/`\n const keys = await ctx.adapter.list(ctx.vault, SEALED_CEK_NS)\n for (const key of keys) {\n if (key.startsWith(prefix)) {\n await ctx.adapter.delete(ctx.vault, SEALED_CEK_NS, key)\n }\n }\n }\n\n return { SEALED_CEK_NS, sealRecordToHost, revokeSealedRecord, rotateRecordCek }\n}\n","/**\n * Deterministic-index lookups (`findByDet` / `queryByDet`).\n *\n * A collection that declares `deterministicFields` stamps a deterministic\n * AES-GCM ciphertext for each such field on the envelope's `_det` slot at write\n * time (the encrypt side lives in {@link RecordCodec.encryptRecord}). These\n * helpers are the READ side: recompute the deterministic ciphertext for a query\n * value and scan the adapter for envelopes whose `_det[field]` matches — no\n * record bodies are decrypted during the scan, which is the whole point of a\n * deterministic index.\n *\n * Both functions take a small {@link DeterministicContext} (the exact `this.*`\n * the moving methods touched) instead of `this`, mirroring the `record-keys/`\n * siblings. Behaviour is byte-identical to the inline code they replaced.\n * Elevated records (_tier > 0) are skipped: invisible to tier-0 scans (#691).\n *\n * Internal service — not exported as a `@noy-db/hub/*` subpath.\n */\nimport type { NoydbStore } from '../../kernel/types.js'\nimport type { RecordCodec } from './index.js'\n\n/** Everything the moving deterministic-index methods touched on `this.*`. */\nimport type { CapsuleKey, CapsulePrimitives } from '../contract.js'\n\nexport interface DeterministicContext<T> {\n /** Collection name — the deterministic-encryption AAD scope. */\n readonly name: string\n /** Vault namespace the records live under. */\n readonly vault: string\n /** The ciphertext store (scanned envelope-by-envelope). */\n readonly adapter: NoydbStore\n /** Declared deterministic-index fields, or null when the feature is off. */\n readonly deterministicFields: ReadonlySet<string> | null\n /** False on plaintext collections — det lookups are encrypted-only. */\n readonly storeCiphertext: boolean\n /** The collection DEK resolver. */\n getDEK(): Promise<CapsuleKey>\n /** The record codec — decrypts a matched envelope to T. */\n readonly codec: RecordCodec<T>\n}\n\nexport function makeDeterministic(p: CapsulePrimitives) {\n const { deriveDeterministicKey, encryptDeterministic } = p\n\n /**\n * Recompute the deterministic ciphertext targets for a query value.\n *\n * L-1: _det migrated to a dedicated HKDF key (salt noydb-det); dual-query\n * keeps pre-migration envelopes findable — they self-heal to the new key on\n * their next write. Index 0 is the current (derived-key) target, index 1 the\n * legacy raw-DEK target. Both are derived once per query call, not per\n * envelope, so the extra derivation is a fixed cost.\n */\n async function detTargets<T>(ctx: DeterministicContext<T>, field: string, value: unknown): Promise<[string, string]> {\n const dek = await ctx.getDEK()\n const detKey = await deriveDeterministicKey(dek)\n const plaintext = typeof value === 'string' ? value : JSON.stringify(value)\n const context = `${ctx.name}/${field}`\n const [current, legacy] = await Promise.all([\n encryptDeterministic(plaintext, detKey, context),\n encryptDeterministic(plaintext, dek, context),\n ])\n return [`${current.iv}:${current.data}`, `${legacy.iv}:${legacy.data}`]\n }\n\n function assertDetField<T>(ctx: DeterministicContext<T>, field: string, method: string): void {\n if (!ctx.deterministicFields || !ctx.deterministicFields.has(field)) {\n throw new Error(\n `Collection \"${ctx.name}\": field \"${field}\" is not declared in deterministicFields`,\n )\n }\n if (!ctx.storeCiphertext) {\n throw new Error(\n `Collection \"${ctx.name}\": ${method} is only meaningful on encrypted collections`,\n )\n }\n }\n\n /**\n * Find the first record whose deterministic field matches the given plaintext.\n * Returns `null` when no match exists.\n *\n * Reads every envelope via the adapter and compares the stored `_det[field]` to\n * a freshly computed deterministic ciphertext — no record bodies are decrypted\n * during the search.\n *\n * Throws when the field is not declared in `deterministicFields`, so a typo\n * fails loudly at the call site rather than silently returning null forever.\n */\n async function findByDet<T>(ctx: DeterministicContext<T>, field: string, value: unknown): Promise<T | null> {\n assertDetField(ctx, field, 'findByDet')\n const [target, legacyTarget] = await detTargets(ctx, field, value)\n\n const ids = await ctx.adapter.list(ctx.vault, ctx.name)\n for (const id of ids) {\n const env = await ctx.adapter.get(ctx.vault, ctx.name, id)\n // #691: an elevated record's _det slot is tier-independent (#662 carries\n // it), so it MATCHES — but its key material is tier-wrapped. Skip it\n // explicitly: invisible to tier-0 det scans regardless of cekCache state\n // (a catch-based fix would still leak plaintext, audit-free, in the\n // session that performed the elevate).\n if (!env || !env._det || (env._tier ?? 0) > 0) continue\n if (env._det[field] === target || env._det[field] === legacyTarget) {\n return ctx.codec.decryptRecord({ collection: ctx.name, id }, env)\n }\n }\n return null\n }\n\n /**\n * Return every record whose deterministic field matches. Same semantics as\n * {@link findByDet} but without the short-circuit.\n */\n async function queryByDet<T>(ctx: DeterministicContext<T>, field: string, value: unknown): Promise<T[]> {\n assertDetField(ctx, field, 'queryByDet')\n const [target, legacyTarget] = await detTargets(ctx, field, value)\n\n const ids = await ctx.adapter.list(ctx.vault, ctx.name)\n const matches: T[] = []\n for (const id of ids) {\n const env = await ctx.adapter.get(ctx.vault, ctx.name, id)\n // #691: skip elevated — see findByDet above\n if (!env || !env._det || (env._tier ?? 0) > 0) continue\n if (env._det[field] === target || env._det[field] === legacyTarget) {\n const rec = await ctx.codec.decryptRecord({ collection: ctx.name, id }, env)\n if (rec !== null) matches.push(rec) // skip tombstone (defensive)\n }\n }\n return matches\n }\n\n return { findByDet, queryByDet }\n}\n","/**\n * `makeCapsule()` — hub's envelope plumbing, bound to one capsule's cipher.\n *\n * ⭐ WHY THIS EXISTS. `enclave-aes` is ~4,200 lines, but only ~1,200 touch\n * `crypto.subtle`. The other ~3,000 — envelope assembly, record-identity AAD,\n * tombstones, the record codec, sealed slots, rekeying — encode HUB'S ENVELOPE\n * FORMAT, not anyone's cipher, so they are identical for every capsule. This\n * module is the single copy; a capsule supplies {@link CapsulePrimitives} and\n * gets the whole surface back.\n *\n * The alternative was copying the plumbing into each capsule. Rejected because\n * nothing would fail when the copies drifted: a change to the tombstone shape\n * or the AAD would have to be made twice, and the miss surfaces as an envelope\n * one capsule writes and the other cannot read — at runtime, in a consumer.\n *\n * ⚠️ BINDING IS PER-CALL, NOT PER-PROCESS, and that is load-bearing. An\n * earlier sketch bound primitives once into module state (`setPrimitives(p)`),\n * which is simpler and wrong: the conformance kit runs two capsules in the\n * same process, so a single global would make the second one silently inherit\n * the first one's cipher — and both suites would still pass.\n *\n * Cipher-FREE helpers are NOT in here. They are plain exports of their own\n * modules (`buildRecordAad`, `isTombstone`, `hasSealedBody`, …) because they\n * inspect envelope fields and need nothing bound. Re-exported below so the\n * capsule surface stays one import for consumers.\n */\nimport type { CapsulePrimitives } from '../contract.js'\nimport type { ClassifyVerifyDeps, VerifyEngineCtx } from './classify-verify.js'\nimport type { RevealEngineCtx } from './classify-reveal.js'\nimport type { VerifyNormalizeMode } from './normalize.js'\nimport type { VdigFieldPolicy, ClassifiedVerdict } from '../../kernel/types.js'\nimport { makeEnvelopeBody } from './envelope-body.js'\nimport { makeSealedSlot } from './sealed-slot.js'\nimport { makeSealedSlots } from './sealed-slots.js'\nimport { makeRecordCodec } from './record-codec.js'\nimport { makeLifecycle } from './lifecycle.js'\nimport { makeRekey } from './rekey.js'\nimport { makeRekeyBlob } from './rekey-blob.js'\nimport { makeSealing } from './sealing.js'\nimport { makeDeterministic } from './deterministic.js'\n\n// ─── cipher-free plumbing, shared verbatim ────────────────────────────\nexport {\n buildRecordAad,\n recordAadFor,\n type RecordIdentity,\n type RecordRef,\n} from './record-aad.js'\nexport { buildRecordEnvelope } from './record-envelope.js'\nexport {\n isTombstone,\n isTombstoneShape,\n buildTombstone,\n isDeleteMarker,\n buildDeleteMarker,\n} from './tombstone.js'\nexport { normalizeForVerify } from './normalize.js'\n// ⚠️ The ENGINE CTX types only. `ClassifyVerifyDeps`/`ClassifyRevealDeps` stay\n// unexported from here on purpose: they are what `makeCapsule()` assembles out\n// of a capsule's primitives, so a capsule author never names them, and knip is\n// right to call a re-export of them dead.\nexport type { VerifyEngineCtx } from './classify-verify.js'\nexport type { RevealEngineCtx } from './classify-reveal.js'\nexport { parseSealedSlot } from './sealed-slot.js'\nexport {\n requireSealedBody,\n sealedBodyArgs,\n envelopeBodySize,\n hasPerRecordKey,\n envelopeBodyForHash,\n hasSealedBody,\n} from './envelope-body.js'\n\n/**\n * Bind the plumbing to a capsule's primitives.\n *\n * Composition order is a real dependency order, not a style choice:\n * `sealedSlot` feeds both `sealedSlots` and `sealing`; `sealedSlots` feeds the\n * codec. Everything else depends only on `p`.\n */\nexport function makeCapsule(p: CapsulePrimitives) {\n const sealedSlot = makeSealedSlot(p)\n const envelopeBody = makeEnvelopeBody(p)\n const sealedSlots = makeSealedSlots(p, sealedSlot)\n const codec = makeRecordCodec(p, sealedSlots)\n\n // ─── the classify engines, bound LAZILY (family#168) ────────────────\n //\n // ⛔ THE `import()` BELOW IS THE WHOLE DESIGN, not a style choice. The four\n // orchestrators are hub's verify PROTOCOL — the C4 timing pad, the R6\n // refusal, the k-of-n denominator — so they belong in the shared plumbing,\n // and family#168 moved them here. But they are also a PBKDF2 oracle that a\n // consumer who never calls `reveal`/`verify` must not pay for, and\n // `makeCapsule()` is called at module scope by every capsule: spreading the\n // engines in eagerly would put them in the floor bundle of every build.\n //\n // So the capsule's surface gains four thunks and the engine modules stay\n // separate chunks, loaded on the first call. The laziness that used to live\n // at `via/classified/active.ts`'s dynamic import now lives HERE — which is\n // why the call sites can finally go through the door.\n //\n // ⚠️ Binding is per-call, like the rest of this function: the deps object\n // closes over THIS capsule's primitives, so two capsules in one process get\n // two engines over two ciphers. A module-level `let` here would give the\n // second capsule the first one's oracle and both suites would still pass.\n const classify: ClassifyVerifyDeps = {\n pbkdf2VerifyDigest: p.pbkdf2VerifyDigest,\n blindedEqual: p.blindedEqual,\n openVdigPayload: p.openVdigPayload,\n dualReadSealedSlot: sealedSlot.dualReadSealedSlot,\n }\n\n return {\n revealSealedField: (ctx: RevealEngineCtx, id: string, field: string): Promise<unknown> =>\n import('./classify-reveal.js').then((m) => m.revealSealedField(classify, ctx, id, field)),\n verifyDigestField: (\n ctx: VerifyEngineCtx, id: string, field: string, candidate: string, policy: VdigFieldPolicy,\n ): Promise<ClassifiedVerdict> =>\n import('./classify-verify.js').then((m) => m.verifyDigestField(classify, ctx, id, field, candidate, policy)),\n verifyTextField: (\n ctx: VerifyEngineCtx, id: string, field: string, candidate: string, normalize: VerifyNormalizeMode,\n ): Promise<ClassifiedVerdict> =>\n import('./classify-verify.js').then((m) => m.verifyTextField(classify, ctx, id, field, candidate, normalize)),\n matchGroupFields: (\n ctx: VerifyEngineCtx,\n id: string,\n answers: Record<string, string>,\n members: ReadonlyArray<{ readonly field: string; readonly policy: VdigFieldPolicy }>,\n opts: { readonly min: number },\n ): Promise<{ readonly passed: boolean }> =>\n import('./classify-verify.js').then((m) => m.matchGroupFields(classify, ctx, id, answers, members, opts)),\n\n ...envelopeBody,\n ...sealedSlot,\n ...sealedSlots,\n ...codec,\n ...makeLifecycle(p),\n ...makeRekey(p),\n ...makeRekeyBlob(p),\n ...makeSealing(p, sealedSlot),\n ...makeDeterministic(p),\n }\n}\n\n/**\n * `RecordCodec` as a TYPE is the module-level generic base class; as a VALUE\n * it is the capsule-bound subclass `makeCapsule()` returns. Hub parameterises\n * the type in six places, and an `InstanceType<…>` alias would have dropped\n * `T` at every one of them without an error — see the note in\n * `record-codec.ts`.\n */\nexport type { RecordCodecBase as RecordCodec } from './record-codec.js'\n\nexport type Capsule = ReturnType<typeof makeCapsule>\n","/**\n * The digest group — hashing, HKDF over public inputs, randomness, base64.\n *\n * Shared by every capsule, and that is the design's own answer rather than a\n * convenience: the capsule-seam spec's group table marks `digest` \"as today\"\n * for `enclave-aes`, `exclave-plain` AND `enclave-pqc` alike. Hashes are not\n * secrets, so there is nothing here to swap — a plaintext capsule computes the\n * same SHA-256 as an encrypting one, and a blob address derived differently\n * per capsule would simply be a bug.\n *\n * ⚠️ This module calls `crypto.subtle`, inside `capsule/**`, which is where the\n * `subtle-outside-capsule` ban permits it. The ban's TARGET is code outside the\n * capsule reaching for WebCrypto on its own; a shared capsule-layer primitive\n * is the thing it is pushing callers toward.\n */\nimport { ValidationError } from '../../kernel/errors.js'\n\nconst subtle = globalThis.crypto.subtle\n\n/** AES key size in bits. Shared: every capsule that derives an AES-GCM subkey\n * from the digest group produces a 256-bit key, or the derivations diverge. */\nexport const KEY_BITS = 256\n\nconst SALT_BYTES = 32\nexport const IV_BYTES = 12\nconst RECOVERY_SECRET_BYTES = 32\nconst PRESENCE_TAG_KEY_DOMAIN = 'noydb.presence.tag.v1'\n\n/** The echo-secret typed parts. Encoding lives here with `encodeEchoParts`; the\n * KEK derivation that consumes it stays in the capsule's `authenticate` group. */\nexport interface EchoSecretParts {\n readonly prompt: string\n readonly echo: string\n readonly key: string\n}\n\nconst ECHO_KDF_CONTEXT = new Uint8Array([0xff, ...new TextEncoder().encode('noydb-echo-secret-v1')])\n\n/**\n * AG-1 encoding: domain context + 4-byte big-endian length prefix per\n * part. Two independent guarantees make a single typed string unable to\n * derive an echo vault's KEK (spec AG-1):\n *\n * 1. The `0xFF` context prefix — UTF-8 never produces that byte, so the\n * encoding is provably not the encoding of ANY string (not merely of\n * no separator-joined form).\n * 2. Structural length prefixes — part boundaries are key material, so\n * re-splitting the same characters changes the derived KEK.\n *\n * @throws ValidationError when any part is not a string — the single\n * chokepoint every echo KEK derivation and block mint passes through, so a\n * malformed parts object cannot be `TextEncoder`-coerced into key material.\n */\nexport function encodeEchoParts(parts: EchoSecretParts): Uint8Array {\n for (const name of ['prompt', 'echo', 'key'] as const) {\n if (typeof parts?.[name] !== 'string') {\n throw new ValidationError('echo secret parts must be three strings: prompt, echo, key')\n }\n }\n const enc = new TextEncoder()\n const segments = [parts.prompt, parts.echo, parts.key].map((p) => enc.encode(p))\n const total = ECHO_KDF_CONTEXT.length + segments.reduce((n, s) => n + 4 + s.length, 0)\n const out = new Uint8Array(total)\n out.set(ECHO_KDF_CONTEXT, 0)\n let offset = ECHO_KDF_CONTEXT.length\n for (const s of segments) {\n new DataView(out.buffer).setUint32(offset, s.length, false)\n out.set(s, offset + 4)\n offset += 4 + s.length\n }\n return out\n}\n\n/**\n * SHA-256 hex digest of raw bytes. Used to derive content-addressed\n * eTags for blob deduplication. Computed on plaintext bytes\n * before compression and encryption so the eTag identifies content, not\n * ciphertext, and survives re-encryption (key rotation, re-upload).\n */\nexport async function sha256Hex(data: Uint8Array): Promise<string> {\n const hash = await subtle.digest('SHA-256', data as unknown as BufferSource)\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/** SHA-256 of raw bytes, as bytes. */\nexport async function sha256Bytes(data: Uint8Array): Promise<Uint8Array> {\n return new Uint8Array(await subtle.digest('SHA-256', data as unknown as BufferSource))\n}\n\n// ─── HMAC-SHA-256 ─────────────────────────────\n\n/**\n * Derive the blob CONTENT-ADDRESSING key for one tier (#1126).\n *\n * The blob eTag is an HMAC over the plaintext. It used to be keyed by the\n * `_blob` DEK — the very key `rotateKeys` replaces — so after any rotation\n * `HMAC(live DEK, plaintext) !== storedETag` for every blob written before it,\n * permanently. `decryptResponse()` checks that unconditionally, so the\n * presigned-URL read path raised `TamperedError` on legitimate data forever;\n * dedup also split, minting a second address for identical bytes.\n *\n * The fix is a `root` that rotation never touches (`_blob_addr`, refused by\n * `rotateKeys` exactly as `_roster` is). Rotation then re-keys chunk BODIES\n * while every address stays stable, which is what makes the AAD\n * `{eTag}:{i}:{count}` and every `_blob_index` / `_blob_slots_*` /\n * `_blob_versions_*` row survive it untouched.\n *\n * **Derived PER TIER, deliberately.** A single vault-wide address would make an\n * elevated blob and a tier-0 blob with identical content share an eTag,\n * leaking equality across a boundary the tier system exists to enforce — and\n * `rehomeForTier` re-addresses on a tier move precisely because the address is\n * tier-scoped today. Domain separation keeps that property while removing the\n * rotation coupling: the two are independent axes, and only one of them was\n * ever meant to change the address.\n *\n * Same HKDF idiom as `asKwKey`, with its own salt so the addressing key can\n * never coincide with a wrapping or encryption key.\n */\nexport async function deriveBlobAddressKey(root: CryptoKey, tier: number): Promise<CryptoKey> {\n const rawRoot = await subtle.exportKey('raw', root)\n const hkdfKey = await subtle.importKey('raw', rawRoot, 'HKDF', false, ['deriveBits'])\n const salt = new TextEncoder().encode('noydb-blob-address')\n const info = new TextEncoder().encode(`tier:${tier}`)\n const bits = await subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt, info }, hkdfKey, KEY_BITS)\n // Imported as AES-GCM only because `hmacSha256Hex` re-exports raw and\n // re-imports as HMAC; the algorithm tag here is a carrier, never used to\n // encrypt anything.\n return subtle.importKey('raw', bits, { name: 'AES-GCM' }, true, ['encrypt', 'decrypt'])\n}\n\n/**\n * HMAC-SHA-256(key, data) as hex.\n *\n * Keyed, unlike {@link sha256Hex}: blob eTags derived with this are opaque to\n * the store, so an attacker with store access cannot pre-compute the address of\n * a file they guess at. Dedup still works inside a vault, where the key is shared.\n */\nexport async function hmacSha256Hex(key: CryptoKey, data: Uint8Array): Promise<string> {\n // Export AES-GCM DEK raw bytes → import as HMAC key\n const rawKey = await subtle.exportKey('raw', key)\n const hmacKey = await subtle.importKey(\n 'raw',\n rawKey,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign'],\n )\n const sig = await subtle.sign('HMAC', hmacKey, data as unknown as BufferSource)\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n// ─── AAD-aware Binary Encrypt / Decrypt ──\n\n/**\n * Derive an AES-256-GCM presence key from a collection DEK using HKDF-SHA256.\n *\n * The presence key is domain-separated from the data DEK by the fixed salt\n * `'noydb-presence'` and the `info` = collection name. This means:\n * - The adapter never sees the presence key.\n * - Presence payloads rotate automatically when the collection DEK is rotated.\n * - Revoked users cannot derive the new presence key after a DEK rotation.\n *\n * @param dek The collection's AES-256-GCM DEK (extractable).\n * @param collectionName Used as the HKDF `info` parameter for domain separation.\n * @returns A non-extractable AES-256-GCM key suitable for presence payload encryption.\n */\nexport async function derivePresenceKey(dek: CryptoKey, collectionName: string): Promise<CryptoKey> {\n // Step 1: export DEK raw bytes\n const rawDek = await subtle.exportKey('raw', dek)\n\n // Step 2: import as HKDF key material\n const hkdfKey = await subtle.importKey(\n 'raw',\n rawDek,\n 'HKDF',\n false,\n ['deriveBits'],\n )\n\n // Step 3: derive 256 bits with salt='noydb-presence' and info=collectionName\n const salt = new TextEncoder().encode('noydb-presence')\n const info = new TextEncoder().encode(collectionName)\n const bits = await subtle.deriveBits(\n { name: 'HKDF', hash: 'SHA-256', salt, info },\n hkdfKey,\n KEY_BITS,\n )\n\n // Step 4: import derived bits as AES-GCM key\n return subtle.importKey(\n 'raw',\n bits,\n { name: 'AES-GCM', length: KEY_BITS },\n false,\n ['encrypt', 'decrypt'],\n )\n}\n\n/**\n * Derive the presence-TAG key from a collection DEK: a non-extractable,\n * sign-only HMAC-SHA256 key, HKDF-separated from the presence PAYLOAD key\n * ({@link derivePresenceKey}) by its own salt. The tag is the adapter-opaque\n * record id for storage-polled presence; the adapter never sees the userId.\n */\nexport async function derivePresenceTagKey(dek: CryptoKey, collectionName: string): Promise<CryptoKey> {\n const rawDek = await subtle.exportKey('raw', dek)\n const hkdfKey = await subtle.importKey('raw', rawDek, 'HKDF', false, ['deriveBits'])\n const salt = new TextEncoder().encode(PRESENCE_TAG_KEY_DOMAIN)\n const info = new TextEncoder().encode(collectionName)\n const bits = await subtle.deriveBits({ name: 'HKDF', hash: 'SHA-256', salt, info }, hkdfKey, KEY_BITS)\n return subtle.importKey('raw', bits, { name: 'HMAC', hash: 'SHA-256' }, false, ['sign'])\n}\n\n/**\n * HMAC-SHA256 hex with a key that IS an HMAC key (e.g. from\n * {@link derivePresenceTagKey}). {@link hmacSha256Hex} takes an AES-GCM\n * carrier and re-imports its raw bytes, which needs an extractable key;\n * this one signs directly and works with non-extractable keys.\n */\nexport async function hmacSignHex(key: CryptoKey, data: Uint8Array): Promise<string> {\n const sig = await subtle.sign('HMAC', key, data as unknown as BufferSource)\n return Array.from(new Uint8Array(sig))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('')\n}\n\n/**\n * HKDF-SHA256 → non-extractable AES-256-GCM key from caller-supplied input\n * key material. The magic-link content key derives here from\n * `(serverSecret, SHA-256(token), info)`; `salt`/`info` are the caller's\n * domain separation, this function adds none.\n */\nexport async function hkdfAesGcmKey(ikm: Uint8Array, salt: Uint8Array, info: Uint8Array): Promise<CryptoKey> {\n const key = await subtle.importKey('raw', ikm as BufferSource, 'HKDF', false, ['deriveKey'])\n return subtle.deriveKey(\n { name: 'HKDF', hash: 'SHA-256', salt: salt as BufferSource, info: info as BufferSource },\n key,\n { name: 'AES-GCM', length: KEY_BITS },\n false,\n ['encrypt', 'decrypt'],\n )\n}\n\n// ─── Sealed Field Key Derivation ──────────────────────────\n\n/** Generate a random 12-byte IV for AES-GCM. */\nexport function generateIV(): Uint8Array {\n return globalThis.crypto.getRandomValues(new Uint8Array(IV_BYTES))\n}\n\n/** Generate a random 32-byte salt for PBKDF2. */\nexport function generateSalt(): Uint8Array {\n return globalThis.crypto.getRandomValues(new Uint8Array(SALT_BYTES))\n}\n\n/**\n * Mint a fresh recovery secret — the high-entropy value a recovery profile\n * wraps the DEK set under and then hands out (Shamir splits it into shares;\n * paper prints it). Not a salt: a salt is public, this is not, and a fork\n * may size them differently. The reference enclave returns 32 bytes.\n *\n * Callers must zero the buffer once it has been wrapped and split.\n */\nexport function generateRecoverySecret(): Uint8Array {\n return globalThis.crypto.getRandomValues(new Uint8Array(RECOVERY_SECRET_BYTES))\n}\n\n// ─── Recipient sealing (RSA-OAEP-SHA256) ───────────────────────────────\n//\n// The managed-secret TLV wraps a per-blob CEK for a recipient who holds an\n// RSA private key — locally (`MemoryRecipientSealer`) or in a KMS\n// (`@noy-db/at-aws-kms`, wire-compatible with RSAES_OAEP_SHA_256). Only the\n// asymmetric steps live here; the TLV layout stays in `managed-secret.ts`.\nexport function bufferToBase64(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]!)\n }\n return btoa(binary)\n}\nexport function base64ToBuffer(base64: string): Uint8Array<ArrayBuffer> {\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n}\n","/**\n * The enclave's `sign` group — Ed25519 over WebCrypto, base64url encoded.\n *\n * Moved in from `@noy-db/attestation` (capsule seam spec, D5): signing is a\n * crypto primitive, and the one a post-quantum enclave swaps (Ed25519 →\n * ML-DSA). Attestation issue/revoke and the pod signature convention call\n * these; `@noy-db/attestation` keeps its own copy for hub-less verifiers,\n * and `__tests__/enclave-sign-group.test.ts` pins the two interoperable.\n *\n * Encoding is base64url without padding — the same bytes-to-string mapping\n * attestation uses, so a key or signature is one string in both places.\n *\n * ⚠️ SHARED, with a known limit. Ed25519 is the `sign` group for BOTH\n * `enclave-aes` and `exclave-plain` — the seam design's group table says so —\n * which is why this lives in the plumbing rather than in a capsule. It is NOT\n * shared with a future `enclave-pqc`, whose whole point is swapping Ed25519\n * for ML-DSA. When that capsule arrives, `sign` becomes a parameterised group\n * like `cipher`; it is not one today because there is no second implementation\n * to parameterise against, and a seam with one implementation is a guess.\n */\nimport { bufferToBase64, base64ToBuffer } from './digest.js'\n\nconst ALG = 'Ed25519'\nconst subtle = globalThis.crypto.subtle\n\nfunction toB64url(bytes: Uint8Array): string {\n return bufferToBase64(bytes).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction fromB64url(s: string): Uint8Array {\n const pad = s.length % 4 === 0 ? '' : '='.repeat(4 - (s.length % 4))\n return base64ToBuffer(s.replace(/-/g, '+').replace(/_/g, '/') + pad)\n}\n\nexport async function generateSigningKeyPair(): Promise<{\n /** base64url raw (32 bytes) — non-secret, publishable */\n publicKeyB64: string\n /** base64url pkcs8 — secret, wrap before persisting */\n privateKeyPkcs8B64: string\n}> {\n const kp = await subtle.generateKey(ALG, true, ['sign', 'verify'])\n const rawPub = new Uint8Array(await subtle.exportKey('raw', kp.publicKey))\n const pkcs8 = new Uint8Array(await subtle.exportKey('pkcs8', kp.privateKey))\n return { publicKeyB64: toB64url(rawPub), privateKeyPkcs8B64: toB64url(pkcs8) }\n}\n\nexport async function signBytes(privateKeyPkcs8B64: string, message: Uint8Array): Promise<string> {\n const key = await subtle.importKey('pkcs8', fromB64url(privateKeyPkcs8B64) as BufferSource, ALG, false, ['sign'])\n return toB64url(new Uint8Array(await subtle.sign(ALG, key, message as BufferSource)))\n}\n\n/** Fails closed: any malformed input is `false`, never a throw. */\nexport async function verifyBytes(publicKeyB64: string, sigB64url: string, message: Uint8Array): Promise<boolean> {\n try {\n const key = await subtle.importKey('raw', fromB64url(publicKeyB64) as BufferSource, ALG, false, ['verify'])\n return await subtle.verify(ALG, key, fromB64url(sigB64url) as BufferSource, message as BufferSource)\n } catch {\n return false\n }\n}\n","/**\n * Constant-time 32-byte tag comparison.\n *\n * Pure — no WebCrypto, no key material — so it is plumbing rather than a\n * capsule primitive. It lived in `enclave-aes/classify/compare.ts` only\n * because that is where its first caller was; leaving it there would have made\n * `@noy-db/hub/capsule` import the AES capsule to reach it, which is precisely\n * the coupling the seam exists to remove.\n */\n/** Compare exactly-32-byte tags. XOR-accumulate over all 32 bytes, no early exit. */\nexport function ctEqualTags(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== 32 || b.length !== 32) {\n throw new Error(\n `ctEqualTags: tags must be exactly 32 bytes (got ${a.length}/${b.length}) — caller bug; ` +\n `reduce comparands with blindedEqual first`,\n )\n }\n let diff = 0\n for (let i = 0; i < 32; i++) diff |= (a[i]! ^ b[i]!)\n return diff === 0\n}\n","/**\n * k-of-n aggregate for matchGroup (§3). Deliberately a full scan that\n * never breaks: per-member results must never influence control flow in\n * a way an observer could time or decompose. Returns ONLY the aggregate.\n * @module\n */\nexport function evaluateKofN(results: readonly boolean[], min: number): boolean {\n if (!Number.isInteger(min) || min < 1 || min > results.length) {\n throw new Error(\n `evaluateKofN: min ${min} out of range 1..${results.length} — caller bug ` +\n `(matchGroup validates bounds up front, before any PBKDF2)`,\n )\n }\n let count = 0\n for (const r of results) {\n if (r) count += 1 // no short-circuit — collect, never break\n }\n return count >= min\n}\n","/**\n * The capsule seam (capsule seam spec, Section 2).\n *\n * A capsule is hub's crypto interior behind one contract. `enclave-aes` is the\n * default and ships INSIDE hub, so `npm install @noy-db/hub` alone is a working\n * noy-db. An alternative binds at BUILD TIME through hub's `imports` map —\n * deliberately not at runtime: a `createNoydb({ capsule })` option would be a\n * hot-swap surface, and a build condition lives in build config where the\n * shipped bundle stays auditable (`npm ls` tells an auditor whether a build is\n * plaintext-at-rest).\n *\n * ⛔ `CapsuleKey` is OPAQUE outside `capsule/`. Never construct, inspect or\n * serialise one; only pass it between contract functions. A fork may define it\n * as something that is not a `CryptoKey` at all — hardware-backed,\n * post-quantum, or null for a capsule that does no encryption.\n */\nimport { NoydbError } from '../kernel/errors.js'\nimport type { VdigFieldPolicy } from '../kernel/types.js'\n\n/** Opaque key at the capsule seam. `CryptoKey` in the reference capsule. */\nexport type CapsuleKey = CryptoKey\n\n/** Opaque asymmetric pair at the capsule seam. */\nexport type CapsuleKeyPair = CryptoKeyPair\n\n/**\n * The groups a capsule may support. A capsule declares its set via\n * `capabilities()`; a service needing a group asserts at `createNoydb()`.\n *\n * ⭐ `authenticate` AND `seal` ARE POSTURE GROUPS (family#156/#168). They say\n * whether a capsule holds secrets at all — `authenticate` is `deriveKey`\n * (secret → KEK) and `seal` is `wrapKey`/`unwrapKey` (KEK wraps DEK), the\n * keyring's unlock chain. {@link PRIMITIVE_GROUPS} tags no primitive with\n * either, so no field form and no service implies them: what implies them is\n * opening a vault with a secret. An `enclave-*` MUST support both and an\n * `exclave-*` MUST refuse both, and that rule is asserted by the conformance\n * suite FROM THE PACKAGE NAME — which is what makes the prefix a trust posture\n * rather than a naming convention, and why a primitive-completeness check names\n * these two as exempt instead of reporting a green it cannot earn.\n *\n * ⛔ THE SHIPPED EXCLAVE DECLARES FOUR AND REFUSES SIX, and the old wording\n * here named two. `exclave-plain`'s `capabilities()` is exactly `cipher`,\n * `digest`, `sign`, `codec` — so beyond the two posture groups it also refuses\n * `per-record-keys`, `deterministic`, `sealing` and `classify`. Reading \"an\n * exclave refuses authenticate and seal\" as the whole story is how a consumer\n * concludes a plaintext capsule can still seal a field.\n */\nexport type CapsuleGroup =\n | 'authenticate'\n | 'seal'\n | 'cipher'\n | 'digest'\n | 'sign'\n | 'codec'\n | 'sealing'\n | 'deterministic'\n | 'classify'\n // ⭐ ADDED 2026-09-16 (core#42). `wrapCek` / `unwrapCek` are real primitives\n // a capsule may refuse independently, and the conformance kit has carried a\n // live refusal test for them under this name since it shipped — but no\n // member of this union covered them, so the refusal could not be NAMED.\n // Found when `EnclaveNotSupportedError` turned out to accept it while this\n // union did not: three vocabularies disagreed, and this one was the one\n // missing a group rather than the error inventing one.\n | 'per-record-keys'\n\n/**\n * Which group each primitive belongs to — the tag table family#168 asked for,\n * so `capabilities()` cannot drift from the implementation.\n *\n * ⭐ `Record<keyof CapsulePrimitives, …>` IS THE MECHANISM, and the reason this\n * is a table rather than a comment. A new primitive added without a tag is a\n * COMPILE error here, and a tag for a primitive that no longer exists is a\n * compile error too. A hand-kept list of group members is the second copy that\n * drifts; three vocabularies disagreeing about `per-record-keys` (core#42) is\n * what that drift looks like when nothing checks it.\n *\n * ⚠️ FOUR GROUPS HAVE NO PRIMITIVES, and that is correct, not a gap:\n *\n * - `authenticate` and `seal` are POSTURE groups. They say whether a capsule\n * holds secrets at all, which is a property of the implementation's\n * intent — enforced by the `enclave-`/`exclave-` package-name rule the\n * conformance kit asserts, never by calling a function. A completeness\n * check over primitives is VACUOUSLY true for them, so it must name them as\n * exempt rather than report a green it cannot earn.\n * - `digest` and `sign` are PLUMBING. `sha256Hex`, `generateSigningKeyPair`,\n * `signBytes`, `verifyBytes` are shared verbatim by every capsule\n * (`plumbing/digest.ts`, `plumbing/sign.ts`) and are deliberately not\n * primitives — hashes are not secrets, and a capsule choosing its own hash\n * buys nothing but an envelope another capsule cannot read.\n *\n * ⛔ `wrapCek`/`unwrapCek` are `per-record-keys`, NOT `seal`. Ruled 2026-10-12\n * after the two shipped artefacts were found disagreeing: `exclave-plain`\n * refused them as `'seal'` while this union's own `per-record-keys` member was\n * added by core#42 expressly for them, and the conformance kit has tested their\n * refusal under that name since it shipped. The exclave is being corrected.\n */\nexport const PRIMITIVE_GROUPS: Readonly<Record<keyof CapsulePrimitives, CapsuleGroup>> = {\n // ─── cipher: the capsule's actual encryption ────────────────────────\n encrypt: 'cipher',\n decrypt: 'cipher',\n encryptBytesWithAAD: 'cipher',\n decryptBytesWithAAD: 'cipher',\n generateDEK: 'cipher',\n // ─── codec: encoding, identical for every capsule ───────────────────\n bufferToBase64: 'codec',\n // ─── sealing: per-field key derivation ──────────────────────────────\n deriveSealedFieldKey: 'sealing',\n deriveSealedFieldKeyFromCek: 'sealing',\n // ─── deterministic: equality search needs a key ─────────────────────\n encryptDeterministic: 'deterministic',\n deriveDeterministicKey: 'deterministic',\n // ─── per-record keys: the CEK lifecycle ─────────────────────────────\n wrapCek: 'per-record-keys',\n unwrapCek: 'per-record-keys',\n // ─── classify: the verify/blind-index group ─────────────────────────\n mintVdigSlot: 'classify',\n mintBidxTag: 'classify',\n openVdigPayload: 'classify',\n sealVdigPayload: 'classify',\n pbkdf2VerifyDigest: 'classify',\n blindedEqual: 'classify',\n}\n\n/**\n * The groups `PRIMITIVE_GROUPS` cannot speak for, with the reason.\n *\n * Published because a completeness check MUST distinguish \"this group has no\n * unimplemented primitive\" from \"this group has no primitive to check\" — those\n * are the same green and different facts, and conflating them is how a capsule\n * declares `seal` while implementing nothing a check can name.\n */\nexport const GROUPS_WITHOUT_PRIMITIVES: Readonly<Partial<Record<CapsuleGroup, string>>> = {\n authenticate: 'posture group — asserted from the enclave-/exclave- package name, not from a primitive',\n seal: 'posture group — asserted from the enclave-/exclave- package name, not from a primitive',\n digest: 'plumbing — plumbing/digest.ts is shared verbatim; hashes are not secrets',\n sign: 'plumbing — plumbing/sign.ts is shared verbatim',\n}\n\n/**\n * The three primitives a capsule MODULE does not expose, so a check driven off\n * the module object reaches 15 of 18.\n *\n * ⚠️ Not a gap in the contract — these are codec-internal by design: the record\n * codec calls them, nothing outside `capsule/` does, and putting them on the\n * door would publish the `_vdig` payload format as a consumer-facing API. A\n * check that silently iterated 15 and reported \"every primitive\" would be\n * stating a conjunction it never evaluated, which is this repo's own\n * false-green shape.\n */\nexport const PRIMITIVES_NOT_ON_THE_DOOR = ['mintVdigSlot', 'openVdigPayload', 'sealVdigPayload'] as const\n\n/**\n * Thrown at `createNoydb()` when a service needs a group the bound capsule\n * does not support.\n *\n * ⚠️ Never thrown on first write, and never discovered by try/catch probing: a\n * consumer learns at startup or not at all. A capability discovered mid-write\n * is a half-built vault.\n */\n/**\n * Refuse at CONSTRUCTION when a service needs a capsule group the bound\n * capsule lacks.\n *\n * ⛔ At startup or not at all, and \"startup\" is TWO POINTS since family#156:\n * `createNoydb()` for an enabled SERVICE, and `vault.collection()` for a\n * declared FIELD. Both are needed — a `classifiedFields` declaration writes\n * `_vdig` with no `classifiedStrategy` at all, so a single check at\n * `createNoydb()` would leave every write-side requirement unenforced while\n * looking complete. See `kernel/capsule-requirements.ts`.\n *\n * A capability discovered on first write is a half-built vault: some records\n * sealed, a service silently inert, and a consumer who cannot tell a missing\n * capability from a bug. `capabilities()` is a set, so this is a membership\n * test — never a try/catch probe against the primitive itself, which would make\n * \"unsupported\" and \"broken\" the same observation.\n *\n * Lives on the CONTRACT, not in the kernel: it depends on nothing but the\n * declared set, and every capsule's consumers need the same assertion.\n *\n * ⚠️ WIRED SINCE family#156, and the reason it waited still shapes how it is\n * tested: `enclave-aes` supports every group, so NO integration path in this\n * repo can reach a refusal, and a test that opens a real vault proves nothing\n * about whether the assertion exists. So the requirement tables are pure\n * functions tested against stub sets\n * (`__tests__/capsule-requirements.test.ts`) and a separate file mocks\n * `capabilities()` down to the exclave's four groups to prove the call sites\n * actually call them (`__tests__/capsule-requirements-wiring.test.ts`). A\n * predicate with no wiring test is the family#172 defect by construction.\n */\nexport function assertCapsuleSupports(\n caps: ReadonlySet<CapsuleGroup>,\n group: CapsuleGroup,\n requirer: string,\n): void {\n if (!caps.has(group)) throw new CapsuleNotSupportedError(group, requirer)\n}\n\n/**\n * ⭐ THE CANONICAL not-supported refusal, ruled 2026-10-12 (family#168). Hub\n * throws it, and every capsule throws it for a group it refuses.\n *\n * ⛔ `EnclaveNotSupportedError` (`kernel/errors.ts`, code `ENCLAVE_NOT_SUPPORTED`)\n * is DEPRECATED and removed at a later breaking cut. Two classes with two codes\n * shipped side by side: hub never threw the enclave one, `exclave-plain` threw\n * this one, and the conformance kit matched only the enclave code — so a capsule\n * refusing a group CORRECTLY was reported as not refusing at all. The kit now\n * accepts both codes so existing forks keep passing; new code throws this.\n */\nexport class CapsuleNotSupportedError extends NoydbError {\n constructor(readonly group: CapsuleGroup, readonly requirer: string) {\n super(\n 'CAPSULE_NOT_SUPPORTED',\n `Capsule does not support the \"${group}\" group, which \"${requirer}\" requires. ` +\n `A capsule declares its groups at build time: \"authenticate\" and \"seal\" are the ` +\n `POSTURE groups an exclave refuses by design, and the shipped exclave also refuses ` +\n `\"per-record-keys\", \"deterministic\", \"sealing\" and \"classify\" — six in all, declaring ` +\n `only cipher/digest/sign/codec. Check capabilities(), and either drop what needs this ` +\n `group or bind a capsule that has it.`,\n )\n this.name = 'CapsuleNotSupportedError'\n }\n}\n\n/**\n * Re-exported at the capsule seam (family#156), because a capsule author binding\n * `@noy-db/hub/capsule` could not previously catch — or construct — the error\n * their own capsule is expected to THROW.\n *\n * ⛔ The asymmetry was the defect: `classify/vdig.ts` raises `TamperedError` on\n * any AAD or auth-tag mismatch, and `record-codec.ts` converts a subtle\n * `OperationError` into one, so it is part of the contract a capsule implements.\n * It shipped only on hub's ROOT barrel, so a capsule package — which must not\n * import hub's root, that being the whole point of a narrow seam — had to\n * either re-declare it (two classes, `instanceof` false across the door) or\n * throw something hub does not recognise.\n *\n * ⚠️ Only the base error belongs here. `KeyringTamperedError` is raised by the\n * keyring, which lives in the kernel on hub's side of the seam — a capsule never\n * throws it and does not need to name it.\n */\nexport { TamperedError } from '../kernel/errors.js'\n\n/**\n * The cipher primitives the shared plumbing needs — the ENTIRE surface a\n * capsule must actually implement.\n *\n * ⛔ NO COUNT IS STATED HERE, deliberately. This docstring said \"the 13 cipher\n * primitives\" while the interface had 16 members, and family#168 added two\n * more; nothing fails when a number in prose goes stale. Count the members.\n *\n * ⭐ This is the measurement that shaped Stage C. `enclave-aes` is ~4,200\n * lines, but only ~1,200 of them touch `crypto.subtle`; the other ~3,000 are\n * envelope assembly, record-identity AAD, tombstones and the record codec —\n * plumbing that is IDENTICAL for every capsule because it encodes hub's\n * envelope format, not anyone's cipher. `makeCapsule()` binds that plumbing to\n * these primitives, so a second capsule writes ~400 lines instead of ~3,400\n * and the format lives in exactly one place.\n *\n * The alternative — copying the plumbing into each capsule — was rejected for\n * a specific reason: nothing would fail when the two copies drifted. A change\n * to the tombstone shape or the AAD would have to be made twice, and the\n * second miss surfaces as an envelope that one capsule writes and the other\n * cannot read.\n *\n * ⚠️ Declared with PROPERTY syntax (`encrypt: (…) => …`), never method\n * shorthand. Two reasons, both measured. TypeScript checks method shorthand\n * BIVARIANTLY, so a capsule whose `encrypt` took the wrong number of arguments\n * would satisfy a shorthand declaration and fail only at a call site — the\n * exact hole #16 fell through. And `@typescript-eslint/unbound-method` refuses\n * to let a shorthand method be destructured off the object, which is precisely\n * how the plumbing consumes these.\n *\n * ⚠️ Every primitive here is `async` except `bufferToBase64`, including ones a\n * plaintext capsule answers instantly. That is deliberate: a capsule that\n * needs real asynchrony (hardware-backed keys, a remote KMS) must be\n * expressible without changing this type, and a synchronous implementation\n * loses nothing by returning a resolved promise.\n */\nexport interface CapsulePrimitives<K = CapsuleKey> {\n encrypt: (plaintext: string, key: K, aad?: Uint8Array) => Promise<{ iv: string; data: string }>\n decrypt: (iv: string, data: string, key: K, aad?: Uint8Array) => Promise<string>\n encryptBytesWithAAD: (bytes: Uint8Array, key: K, aad: Uint8Array) => Promise<{ iv: string; data: string }>\n decryptBytesWithAAD: (iv: string, data: string, key: K, aad: Uint8Array) => Promise<Uint8Array>\n /** Same plaintext + key + context ⇒ same ciphertext. Refused by a capsule without the group. */\n encryptDeterministic: (plaintext: string, key: K, context: string) => Promise<{ iv: string; data: string }>\n /** ⚠️ ONE argument. The AES capsule derives the CONTEXT into the IV, not the key\n * (`encryptDeterministic` calls `deriveDeterministicIV(dek, context, plaintext)`),\n * so a `(dek, context)` signature here would have been a plausible-looking lie\n * that only the call sites caught. */\n deriveDeterministicKey: (dek: K) => Promise<K>\n deriveSealedFieldKey: (dek: K, collectionName: string, field: string) => Promise<K>\n deriveSealedFieldKeyFromCek: (cek: K, collectionName: string, field: string) => Promise<K>\n generateDEK: () => Promise<K>\n wrapCek: (cek: K, dek: K) => Promise<string>\n unwrapCek: (wrapped: string, dek: K) => Promise<K>\n /** Accepts `ArrayBuffer` too — the AES capsule's callers pass raw `subtle` output. */\n bufferToBase64: (buffer: ArrayBuffer | Uint8Array) => string\n\n // ─── optional group: classify ──────────────────────────────────────\n //\n // The record codec reaches into exactly two cipher-bound classify functions\n // (plus `normalizeForVerify`, which is pure and lives in the plumbing). They\n // sit here rather than in a separate object because the codec calls them the\n // same way it calls `encrypt` — and a capsule that refuses the `classify`\n // group implements them as a throw, which is the refusal the group means.\n mintVdigSlot: (\n value: string,\n policy: VdigFieldPolicy,\n prevBlob: string | undefined,\n cek: K,\n collection: string,\n id: string,\n field: string,\n ) => Promise<string>\n mintBidxTag: (normalized: string, dek: K, collection: string, field: string) => Promise<string>\n /**\n * PBKDF2-SHA256 → a fixed 32-byte verify digest over a PRE-NORMALIZED\n * candidate. Promoted to a primitive by family#168.\n *\n * ⭐ It was already a door export and already implemented by both shipped\n * capsules (`enclave-aes` for real, `exclave-plain` as a `refuse('classify')`\n * throw) — but it was not a primitive, so the plumbing could not call it, so\n * the verify oracle that needs it had to live INSIDE the reference capsule\n * and be reached by a dynamic import past the barrel. The missing declaration\n * was the whole reason for `DYNAMIC_ENCLAVE_BYPASS`.\n *\n * ⚠️ `iterations` is a PARAMETER, not a constant read from the capsule: a\n * stored `_vdig` payload carries its own authenticated `iter`, and a verify\n * must digest at the value the payload declares or it cannot compare against\n * a record written before an iteration bump.\n */\n pbkdf2VerifyDigest: (value: string, salt: Uint8Array, iterations: number) => Promise<Uint8Array>\n /**\n * Blinded equality of two arbitrary-length byte strings — the ONE comparison\n * construction the classify engines use (capsule seam spec §3 rule 2).\n * Promoted by family#168 alongside `pbkdf2VerifyDigest`.\n *\n * ⛔ It is a CAPSULE primitive, not plumbing, although the comparison itself\n * is cipher-free: the reference construction reduces both comparands to\n * 32-byte tags under a fresh ephemeral HMAC key, which is WebCrypto, and\n * `capsule/plumbing/**` may not call `crypto.subtle` (Check 10b). A capsule\n * with no MAC of its own answers the whole `classify` group with a throw.\n *\n * ⚠️ The constant-time tag compare it ends in (`ctEqualTags`) IS plumbing and\n * stays shared — a capsule choosing the blinding must not also get to choose\n * whether the final compare is constant-time.\n */\n blindedEqual: (a: Uint8Array, b: Uint8Array) => Promise<boolean>\n /** The vdig payload is sealed under a capsule-derived slot key, so open/seal\n * are the capsule's, not the plumbing's. `rotateRecordCek` needs both to\n * carry a classified slot across a key rotation. */\n openVdigPayload: (blob: string, cek: K, collection: string, id: string, field: string) => Promise<VdigPayload>\n sealVdigPayload: (payload: VdigPayload, cek: K, collection: string, id: string, field: string) => Promise<string>\n}\n\n/** The classified verify-digest payload, at the seam because both the capsule\n * and the plumbing name it. Structural, not the capsule's own type. */\nexport interface VdigDigestEntry {\n readonly salt: string\n readonly hash: string\n}\n\n/**\n * The family's verify-digest iteration count (600K) — the value a NEW `_vdig`\n * slot is written at, and the default cost of the verify engines' C4 timing pad.\n *\n * ⚠️ A STORED payload's `iter` wins over this constant everywhere. This is the\n * write-side default only; a record written before an iteration bump must still\n * verify, which is why {@link CapsulePrimitives.pbkdf2VerifyDigest} takes\n * `iterations` as an argument.\n *\n * ⭐ It lives on the SEAM, next to the payload type that declares `iter`, for a\n * mechanical reason found in family#168: the verify engines moved into\n * `capsule/plumbing/` and must stay behind a lazy `import()` (they carry a\n * PBKDF2 oracle no consumer should pay for), while the enclave's WRITE side\n * needs the same constant eagerly. Importing it from the engine module would\n * have made the engine eager and silently undone the laziness — and the only\n * trace would have been a bundle-baseline number.\n */\nexport const VDIG_ITERATIONS = 600_000\n\nexport interface VdigPayload {\n readonly v: 1\n readonly alg: 'PBKDF2-SHA256'\n readonly iter: number\n readonly cur: VdigDigestEntry & { readonly at: string }\n readonly ring?: readonly VdigDigestEntry[]\n}\n\n\n// ─── what an alternative capsule builds on ─────────────────────────\n//\n// `@noy-db/hub/capsule` is the subpath an out-of-tree capsule installs, so the\n// shared halves have to be reachable from it: `makeCapsule()` binds hub's\n// envelope plumbing to a cipher, and the `digest` group is identical for every\n// capsule by design. Without these, an alternative capsule would have to\n// re-implement ~3,400 lines it does not own — and drift from them silently.\nexport { makeCapsule } from './plumbing/index.js'\nexport type { Capsule, RecordCodec } from './plumbing/index.js'\nexport {\n sha256Hex,\n sha256Bytes,\n hmacSha256Hex,\n hmacSignHex,\n hkdfAesGcmKey,\n deriveBlobAddressKey,\n derivePresenceKey,\n derivePresenceTagKey,\n generateIV,\n generateSalt,\n generateRecoverySecret,\n bufferToBase64,\n base64ToBuffer,\n encodeEchoParts,\n KEY_BITS,\n} from './plumbing/digest.js'\nexport type { EchoSecretParts } from './plumbing/digest.js'\nexport {\n buildRecordAad,\n recordAadFor,\n buildRecordEnvelope,\n buildTombstone,\n isTombstone,\n isTombstoneShape,\n buildDeleteMarker,\n isDeleteMarker,\n requireSealedBody,\n sealedBodyArgs,\n envelopeBodySize,\n hasPerRecordKey,\n envelopeBodyForHash,\n hasSealedBody,\n normalizeForVerify,\n parseSealedSlot,\n} from './plumbing/index.js'\nexport type { RecordIdentity, RecordRef } from './plumbing/index.js'\nexport { buildSealedRecordEnvelope } from './plumbing/record-envelope.js'\nexport { generateSigningKeyPair, signBytes, verifyBytes } from './plumbing/sign.js'\nexport { ctEqualTags } from './plumbing/ct-equal.js'\nexport { evaluateKofN } from './plumbing/kofn.js'\n"],"mappings":"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","names":["fieldKey","iv","data","ref","plaintext","iv","data","iv","data","bufferToBase64","subtle","subtle"]}
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
IllegalTransitionError,
|
|
3
3
|
RecordLockedError,
|
|
4
4
|
ValidationError
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ISIXU45J.js";
|
|
6
6
|
|
|
7
7
|
// src/with-audit/guards/with-guard.ts
|
|
8
8
|
function withGuard(strategy) {
|
|
@@ -90,7 +90,8 @@ function transitionGuard(config) {
|
|
|
90
90
|
const rec = incoming;
|
|
91
91
|
const to = stateOf(rec, field);
|
|
92
92
|
if (ctx.existing === null) {
|
|
93
|
-
|
|
93
|
+
const localCreate = ctx.origin === void 0 || ctx.origin === "local-write";
|
|
94
|
+
if (localCreate && initial !== void 0 && !initial.includes(to)) {
|
|
94
95
|
throw new IllegalTransitionError(collection, recordId2(rec), "(none)", to);
|
|
95
96
|
}
|
|
96
97
|
return;
|
|
@@ -124,4 +125,4 @@ export {
|
|
|
124
125
|
immutableGuard,
|
|
125
126
|
transitionGuard
|
|
126
127
|
};
|
|
127
|
-
//# sourceMappingURL=chunk-
|
|
128
|
+
//# sourceMappingURL=chunk-IN4XZJYD.js.map
|