termdock 1.4.173 → 1.4.175
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/.desktop-dist/main.js +204 -7
- package/dist/client/assets/App-BXYZyI_k.js +150 -0
- package/dist/client/assets/App-BXYZyI_k.js.br +0 -0
- package/dist/client/assets/App-BXYZyI_k.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js +2 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DeVtCf73.js.gz +0 -0
- package/dist/client/assets/DagPlayground-ZIqLZoMo.js +1 -0
- package/dist/client/assets/DagPlayground-ZIqLZoMo.js.br +0 -0
- package/dist/client/assets/DagPlayground-ZIqLZoMo.js.gz +0 -0
- package/dist/client/assets/{DiffLab-xuS6rtpv.js → DiffLab-CSMf8So2.js} +2 -2
- package/dist/client/assets/DiffLab-CSMf8So2.js.br +0 -0
- package/dist/client/assets/DiffLab-CSMf8So2.js.gz +0 -0
- package/dist/client/assets/DiffReview-CE5lW1zQ.js +1 -0
- package/dist/client/assets/DiffReview-CE5lW1zQ.js.br +0 -0
- package/dist/client/assets/DiffReview-CE5lW1zQ.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-CpUW75fZ.js +2 -0
- package/dist/client/assets/DiffReviewLab-CpUW75fZ.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-CpUW75fZ.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-BQGXUr8t.js → DiffViewer-BomvZ4kq.js} +5 -5
- package/dist/client/assets/DiffViewer-BomvZ4kq.js.br +0 -0
- package/dist/client/assets/DiffViewer-BomvZ4kq.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-BfZzS1_d.js → ModelPreview-BWdZFyhh.js} +3 -3
- package/dist/client/assets/ModelPreview-BWdZFyhh.js.br +0 -0
- package/dist/client/assets/ModelPreview-BWdZFyhh.js.gz +0 -0
- package/dist/client/assets/RightSidebar-CYVwFbOc.js +79 -0
- package/dist/client/assets/RightSidebar-CYVwFbOc.js.br +0 -0
- package/dist/client/assets/RightSidebar-CYVwFbOc.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-Dn_lmPcC.js → abnfDiagram-VCTEODGH-BIPsLStE.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH-BIPsLStE.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-BIPsLStE.js.gz +0 -0
- package/dist/client/assets/app-D6KzR8B3.js +34 -0
- package/dist/client/assets/app-D6KzR8B3.js.br +0 -0
- package/dist/client/assets/app-D6KzR8B3.js.gz +0 -0
- package/dist/client/assets/{arc-DUybtKGZ.js → arc-DzeT6hBj.js} +1 -1
- package/dist/client/assets/arc-DzeT6hBj.js.br +0 -0
- package/dist/client/assets/arc-DzeT6hBj.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-CLEmOHBp.js → architectureDiagram-5GKGNRK7-B1qsxd_5.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-B1qsxd_5.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-B1qsxd_5.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-O_XM5b9_.js → blockDiagram-I7D4REHJ-NLsa29Jp.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-NLsa29Jp.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-NLsa29Jp.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-DlUEslcV.js → c4Diagram-7LVT6UL2-DBAUvou3.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-DBAUvou3.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DBAUvou3.js.gz +0 -0
- package/dist/client/assets/channel-BhwBAWhq.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-DCrP6W79.js → chunk-2Q5K7J3B-BsVAXGLa.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-Zm6_pYQv.js → chunk-5VM5RSS4-BKtILsst.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-DkyD-X9z.js → chunk-F27PBJKO-Dn9zcQZG.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-Dn9zcQZG.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-Dn9zcQZG.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-UvWxXAkh.js → chunk-IMKFNOWR-MVtVQeTr.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-MVtVQeTr.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-MVtVQeTr.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-BjeBAFYM.js → chunk-JWPE2WC7-DliIIMe7.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-CnqrQdZC.js → chunk-POPQ4Y6H-XBSus3IC.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-C4a1s0v4.js → chunk-SVP7TREG-wleSQcJR.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-wleSQcJR.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-wleSQcJR.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-hnmjYAl_.js → chunk-TICWLB2K-Dq6LdswN.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-Dq6LdswN.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-Dq6LdswN.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-xR4LOz28.js → chunk-XXDRQBXY-xafyKlM-.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-slQB0ns2.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-slQB0ns2.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-CqY84fFp.js → cose-bilkent-JH36ORCC-BR7Ohnm_.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-BR7Ohnm_.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-BR7Ohnm_.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-Bd14zuaD.js → cynefin-OW5HDTMX-CmPjQgER.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-CmPjQgER.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CmPjQgER.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-DSHcNXDs.js → cynefinDiagram-5FMLGOSQ-CFyR9RvM.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CFyR9RvM.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CFyR9RvM.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-CIGWQYeP.js → dagre-GXQ25YYZ-D_RVMRO1.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-D_RVMRO1.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-D_RVMRO1.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-C9Fg7OLN.js → diagram-S7CK7UJ4-6fr-ZoPj.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-6fr-ZoPj.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-6fr-ZoPj.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-DoRhl7Fd.js → diagram-UQ7AKVKN-CgwiUtz0.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-CgwiUtz0.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-CgwiUtz0.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-CI83g3iM.js → diagram-VSXAHHWV-CLxzTB9k.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-CLxzTB9k.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CLxzTB9k.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-Dp2kP4PB.js → diagram-VX7I27RA-DjxDAvdZ.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-DjxDAvdZ.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-DjxDAvdZ.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-KtLMCM-V.js → diagram-Z3DM3KII-CDPOjiz4.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-CDPOjiz4.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-CDPOjiz4.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-D_zseu1m.js → ebnfDiagram-PWID7BFC-BLbwyF1O.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-BLbwyF1O.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-BLbwyF1O.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-DaWUl_Kz.js → erDiagram-RLTQ6QDP-DbjtZnJx.js} +1 -1
- package/dist/client/assets/{erDiagram-RLTQ6QDP-DaWUl_Kz.js.br → erDiagram-RLTQ6QDP-DbjtZnJx.js.br} +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DbjtZnJx.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-BMoJ46zJ.js → flowDiagram-HODETNUW-BirJInAN.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-BirJInAN.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BirJInAN.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-lV_iVmoW.js → ganttDiagram-EL5Y4UJY-d6Q9vSbv.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-d6Q9vSbv.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-d6Q9vSbv.js.gz +0 -0
- package/dist/client/assets/gestureArbiter-C2J0D0kV.css +1 -0
- package/dist/client/assets/gestureArbiter-C2J0D0kV.css.br +0 -0
- package/dist/client/assets/gestureArbiter-C2J0D0kV.css.gz +0 -0
- package/dist/client/assets/gestureArbiter-D1SNS_gK.js +1 -0
- package/dist/client/assets/gestureArbiter-D1SNS_gK.js.br +0 -0
- package/dist/client/assets/gestureArbiter-D1SNS_gK.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-BVCT9Btd.js → gitGraphDiagram-WWUBYQGX-BWBoIeS6.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BWBoIeS6.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BWBoIeS6.js.gz +0 -0
- package/dist/client/assets/{index-CnKSUg74.css → index-DUI8KRf6.css} +1 -1
- package/dist/client/assets/index-DUI8KRf6.css.br +0 -0
- package/dist/client/assets/index-DUI8KRf6.css.gz +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-BVhXOx1S.js → infoDiagram-27XIBGKW-BH4AynkM.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-Cyzfuoys.js → ishikawaDiagram-5VMMS53U-BQw90AKR.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BQw90AKR.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-BQw90AKR.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-CEIPDhQF.js → journeyDiagram-3NMN7TZE-DISkQocf.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DISkQocf.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-DISkQocf.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-C8OulEOF.js → kanban-definition-UXKFOSKX-Qg2LyEVy.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-Qg2LyEVy.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-Qg2LyEVy.js.gz +0 -0
- package/dist/client/assets/{layout-YN93qRzb.js → layout-Cd7-4_Vr.js} +1 -1
- package/dist/client/assets/layout-Cd7-4_Vr.js.br +0 -0
- package/dist/client/assets/layout-Cd7-4_Vr.js.gz +0 -0
- package/dist/client/assets/{linear-D9T4dYh7.js → linear-DFTi2fTn.js} +1 -1
- package/dist/client/assets/linear-DFTi2fTn.js.br +0 -0
- package/dist/client/assets/linear-DFTi2fTn.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-BdCvvCGV.js → mermaid.core-2v726lUU.js} +5 -5
- package/dist/client/assets/mermaid.core-2v726lUU.js.br +0 -0
- package/dist/client/assets/mermaid.core-2v726lUU.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-CSpES3Jh.js → mindmap-definition-YA3MSWOX-_FpwteCy.js} +4 -4
- package/dist/client/assets/mindmap-definition-YA3MSWOX-_FpwteCy.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-_FpwteCy.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js +2 -0
- package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-D9Ffc7NY.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-6f4vAA1Q.js → pegDiagram-XKGWAZYB-DLFf-HmZ.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB-DLFf-HmZ.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-DLFf-HmZ.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-BC4Jd-I8.js → pieDiagram-E7YTZNPT-DqTnfXHG.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-DqTnfXHG.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DqTnfXHG.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-B0pnPaEN.js → quadrantDiagram-AXDQQJYC-B_oBH52Q.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B_oBH52Q.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B_oBH52Q.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-B-oOamZT.js → railroadDiagram-O6MQD6OU-HxFQ7Pce.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-HxFQ7Pce.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-HxFQ7Pce.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-B3NGIjvv.js +1 -0
- package/dist/client/assets/referenceLongPress-B3NGIjvv.js.br +0 -0
- package/dist/client/assets/referenceLongPress-B3NGIjvv.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-DBSyxt0k.js → requirementDiagram-BXWQKSXE-NZdfaZFk.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-NZdfaZFk.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-NZdfaZFk.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-CcohzEZE.js → sankeyDiagram-P5KCCOFB-BgCctv6X.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BgCctv6X.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-BgCctv6X.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-D7KS6rq_.js → sequenceDiagram-WJ2MYXX4-Br42Za1R.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Br42Za1R.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Br42Za1R.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-CaRXtn4g.js → sizeCapture-INFHLROL-FT4z4S1b.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-FT4z4S1b.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-FT4z4S1b.js.gz +0 -0
- package/dist/client/assets/sparkles-BaGaevME.js +1 -0
- package/dist/client/assets/{stateDiagram-D77RDMKH-Dz836LqY.js → stateDiagram-D77RDMKH-DD0t9f84.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-DD0t9f84.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DD0t9f84.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BhvVVB56.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-BK4KAY00.js → swimlanes-42K2YHIH-DcC8pGLx.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-DcC8pGLx.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-DcC8pGLx.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DSC5lz0i.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-DWgxTDhO.js → timeline-definition-24CTP7MA-DnwO_Pxd.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-DnwO_Pxd.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-DnwO_Pxd.js.gz +0 -0
- package/dist/client/assets/triangle-alert-BCQO9Ap9.js +1 -0
- package/dist/client/assets/{vennDiagram-4TSXK5OY-BV1TIfOp.js → vennDiagram-4TSXK5OY-IVNSrZDM.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-IVNSrZDM.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-IVNSrZDM.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-GYClC5F0.js → wardleyDiagram-VM6X3IG4-BnGetaQL.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BnGetaQL.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-BnGetaQL.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-D1oKV5W0.js → xychartDiagram-S5SC5T6Z-C01eBN-W.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-C01eBN-W.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-C01eBN-W.js.gz +0 -0
- package/dist/client/index.html +2 -2
- package/dist/client/index.html.br +0 -0
- package/dist/client/index.html.gz +0 -0
- package/dist/client/preview-shell.html +1 -0
- package/dist/client/preview-shell.js +20 -0
- package/dist/client/preview-shell.js.br +0 -0
- package/dist/client/preview-shell.js.gz +0 -0
- package/dist/client/sw-secure-api.js +132 -0
- package/dist/client/sw-secure-api.js.br +0 -0
- package/dist/client/sw-secure-api.js.gz +0 -0
- package/dist/client/sw.js +1 -1
- package/dist/client/sw.js.br +0 -0
- package/dist/client/sw.js.gz +0 -0
- package/dist/server/agent/collaborationCli.js +298 -0
- package/dist/server/agent/collaborationPrompt.js +12 -3
- package/dist/server/agent/collaborationProtocol.js +50 -0
- package/dist/server/agent/collaborationRoutes.js +86 -0
- package/dist/server/agent/collaborationStore.js +311 -20
- package/dist/server/cli.js +28 -163
- package/dist/server/entry.js +114 -51
- package/dist/server/federation/accessPolicy.js +122 -0
- package/dist/server/federation/authorization.js +226 -0
- package/dist/server/federation/bootstrapCors.js +23 -0
- package/dist/server/federation/cli.js +185 -0
- package/dist/server/federation/deviceNames.js +29 -0
- package/dist/server/federation/directRoutes.js +72 -0
- package/dist/server/federation/invitations.js +123 -0
- package/dist/server/federation/openAccess.js +25 -0
- package/dist/server/federation/originPolicy.js +19 -0
- package/dist/server/federation/packets.js +116 -0
- package/dist/server/federation/passwordBootstrap.js +86 -0
- package/dist/server/federation/passwordLoginRoutes.js +98 -0
- package/dist/server/federation/relay.js +346 -0
- package/dist/server/federation/requestContext.js +4 -0
- package/dist/server/federation/routeAccess.js +124 -0
- package/dist/server/federation/routeInvitations.js +160 -0
- package/dist/server/federation/runtime.js +537 -0
- package/dist/server/federation/secureProtocol.js +223 -0
- package/dist/server/federation/socketDuplex.js +42 -0
- package/dist/server/federation/streamLimits.js +5 -0
- package/dist/server/routes/filesystem.js +2 -1
- package/dist/server/routes/terminal.js +89 -79
- package/dist/server/utils/authProtection.js +13 -0
- package/dist/server/utils/csrfProtection.js +3 -0
- package/dist/server/utils/publicSecurity.js +8 -3
- package/package.json +10 -3
- package/runtime-manifest.json +4 -4
- package/dist/client/assets/ChangeWalkthroughPanel-DKj7F0ex.js +0 -2
- package/dist/client/assets/ChangeWalkthroughPanel-DKj7F0ex.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-DKj7F0ex.js.gz +0 -0
- package/dist/client/assets/DagPlayground-D2Wui4jd.js +0 -1
- package/dist/client/assets/DagPlayground-D2Wui4jd.js.br +0 -0
- package/dist/client/assets/DagPlayground-D2Wui4jd.js.gz +0 -0
- package/dist/client/assets/DiffLab-xuS6rtpv.js.br +0 -0
- package/dist/client/assets/DiffLab-xuS6rtpv.js.gz +0 -0
- package/dist/client/assets/DiffReview-CAwpqM_w.js +0 -1
- package/dist/client/assets/DiffReview-CAwpqM_w.js.br +0 -0
- package/dist/client/assets/DiffReview-CAwpqM_w.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-BM_uGdyI.js +0 -2
- package/dist/client/assets/DiffReviewLab-BM_uGdyI.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-BM_uGdyI.js.gz +0 -0
- package/dist/client/assets/DiffViewer-BQGXUr8t.js.br +0 -0
- package/dist/client/assets/DiffViewer-BQGXUr8t.js.gz +0 -0
- package/dist/client/assets/ModelPreview-BfZzS1_d.js.br +0 -0
- package/dist/client/assets/ModelPreview-BfZzS1_d.js.gz +0 -0
- package/dist/client/assets/RightSidebar-Cdqt3C7V.js +0 -55
- package/dist/client/assets/RightSidebar-Cdqt3C7V.js.br +0 -0
- package/dist/client/assets/RightSidebar-Cdqt3C7V.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-Dn_lmPcC.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-Dn_lmPcC.js.gz +0 -0
- package/dist/client/assets/app-DDCkFsNe.js +0 -175
- package/dist/client/assets/app-DDCkFsNe.js.br +0 -0
- package/dist/client/assets/app-DDCkFsNe.js.gz +0 -0
- package/dist/client/assets/arc-DUybtKGZ.js.br +0 -0
- package/dist/client/assets/arc-DUybtKGZ.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CLEmOHBp.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-CLEmOHBp.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-O_XM5b9_.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-O_XM5b9_.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DlUEslcV.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-DlUEslcV.js.gz +0 -0
- package/dist/client/assets/channel-Bk1VkYvd.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-DkyD-X9z.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DkyD-X9z.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-UvWxXAkh.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-UvWxXAkh.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-C4a1s0v4.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-C4a1s0v4.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-hnmjYAl_.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-hnmjYAl_.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-_tUz3Cxt.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-_tUz3Cxt.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-CqY84fFp.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-CqY84fFp.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-Bd14zuaD.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-Bd14zuaD.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DSHcNXDs.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DSHcNXDs.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CIGWQYeP.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CIGWQYeP.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-C9Fg7OLN.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-C9Fg7OLN.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DoRhl7Fd.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-DoRhl7Fd.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CI83g3iM.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CI83g3iM.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-Dp2kP4PB.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-Dp2kP4PB.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-KtLMCM-V.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-KtLMCM-V.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-D_zseu1m.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-D_zseu1m.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-DaWUl_Kz.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BMoJ46zJ.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BMoJ46zJ.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-lV_iVmoW.js.br +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-lV_iVmoW.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BVCT9Btd.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BVCT9Btd.js.gz +0 -0
- package/dist/client/assets/index-CnKSUg74.css.br +0 -0
- package/dist/client/assets/index-CnKSUg74.css.gz +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-Cyzfuoys.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-Cyzfuoys.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-CEIPDhQF.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-CEIPDhQF.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-C8OulEOF.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-C8OulEOF.js.gz +0 -0
- package/dist/client/assets/layout-YN93qRzb.js.br +0 -0
- package/dist/client/assets/layout-YN93qRzb.js.gz +0 -0
- package/dist/client/assets/linear-D9T4dYh7.js.br +0 -0
- package/dist/client/assets/linear-D9T4dYh7.js.gz +0 -0
- package/dist/client/assets/mermaid.core-BdCvvCGV.js.br +0 -0
- package/dist/client/assets/mermaid.core-BdCvvCGV.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CSpES3Jh.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-CSpES3Jh.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-6f4vAA1Q.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-6f4vAA1Q.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-BC4Jd-I8.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-BC4Jd-I8.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B0pnPaEN.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-B0pnPaEN.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-B-oOamZT.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-B-oOamZT.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DBSyxt0k.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-DBSyxt0k.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CcohzEZE.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-CcohzEZE.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D7KS6rq_.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-D7KS6rq_.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CaRXtn4g.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-CaRXtn4g.js.gz +0 -0
- package/dist/client/assets/sparkles-CUdUt4qy.js +0 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-Dz836LqY.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-Dz836LqY.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-5V5Y3wwn.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-BK4KAY00.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-BK4KAY00.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-Sj-7YK2P.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-DWgxTDhO.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-DWgxTDhO.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-BV1TIfOp.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-BV1TIfOp.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-GYClC5F0.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-GYClC5F0.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-D1oKV5W0.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-D1oKV5W0.js.gz +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { isAuthEnabled } from '../utils/authProtection.js';
|
|
3
|
+
import { bootstrapCors } from './bootstrapCors.js';
|
|
4
|
+
/** Open mode is per connection and transient; it must never create persisted grants. */
|
|
5
|
+
export function openAccessAllowed(direct, authEnabled, action) {
|
|
6
|
+
return direct && !authEnabled && (!action || !action.startsWith('authorization.') && !action.startsWith('route.'));
|
|
7
|
+
}
|
|
8
|
+
export function createOpenAccessRouter(serviceIdentity) {
|
|
9
|
+
const router = express.Router();
|
|
10
|
+
router.use(bootstrapCors);
|
|
11
|
+
router.get('/parameters', (_req, res) => {
|
|
12
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
13
|
+
if (isAuthEnabled()) {
|
|
14
|
+
res.status(403).json({ error: 'Password authentication required' });
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const identity = serviceIdentity();
|
|
18
|
+
if (!identity) {
|
|
19
|
+
res.status(503).json({ error: 'Encrypted access is starting' });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
res.json({ serviceIdentity: identity });
|
|
23
|
+
});
|
|
24
|
+
return router;
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Noise endpoints never use cookies as authority. A browser may legitimately
|
|
2
|
+
* connect to a pinned target from another HTTPS service's PWA; possession of
|
|
3
|
+
* the paired device key is still required before any business operation.
|
|
4
|
+
* Legacy cookie-authenticated endpoints must not use this relaxed policy. */
|
|
5
|
+
export function secureChannelOriginAllowed(origin, sameOriginAllowed) {
|
|
6
|
+
if (origin === undefined)
|
|
7
|
+
return true; // Native CLI/Mac relay.
|
|
8
|
+
try {
|
|
9
|
+
const parsed = new URL(origin);
|
|
10
|
+
if (parsed.origin !== origin)
|
|
11
|
+
return false;
|
|
12
|
+
if (parsed.protocol === 'https:')
|
|
13
|
+
return true;
|
|
14
|
+
return parsed.protocol === 'http:' && sameOriginAllowed();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/** Bounded queues are also used at the untrusted, pre-handshake boundary. */
|
|
2
|
+
export class AsyncQueue {
|
|
3
|
+
limit;
|
|
4
|
+
items = [];
|
|
5
|
+
wake;
|
|
6
|
+
ended = false;
|
|
7
|
+
error;
|
|
8
|
+
constructor(limit = 64) {
|
|
9
|
+
this.limit = limit;
|
|
10
|
+
}
|
|
11
|
+
push(value) {
|
|
12
|
+
if (this.ended)
|
|
13
|
+
throw new Error('Channel closed');
|
|
14
|
+
if (this.items.length >= this.limit) {
|
|
15
|
+
this.end(new Error('Channel queue exceeded'));
|
|
16
|
+
throw this.error;
|
|
17
|
+
}
|
|
18
|
+
this.items.push(value);
|
|
19
|
+
this.wake?.();
|
|
20
|
+
}
|
|
21
|
+
end(error) { this.ended = true; this.error ??= error; this.wake?.(); }
|
|
22
|
+
async *[Symbol.asyncIterator]() {
|
|
23
|
+
while (true) {
|
|
24
|
+
if (this.error)
|
|
25
|
+
throw this.error;
|
|
26
|
+
const item = this.items.shift();
|
|
27
|
+
if (item !== undefined) {
|
|
28
|
+
yield item;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (this.ended)
|
|
32
|
+
return;
|
|
33
|
+
await new Promise(resolve => { this.wake = resolve; });
|
|
34
|
+
this.wake = undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const MAX_PACKET = 2 * 1024 * 1024;
|
|
39
|
+
const encoder = new TextEncoder();
|
|
40
|
+
const decoder = new TextDecoder('utf-8', { fatal: true });
|
|
41
|
+
/** Application records live entirely inside Noise; transport routing never sees them. */
|
|
42
|
+
export class PacketChannel {
|
|
43
|
+
duplex;
|
|
44
|
+
outgoing = new AsyncQueue(256);
|
|
45
|
+
outgoingBytes = 0;
|
|
46
|
+
done;
|
|
47
|
+
constructor(duplex) {
|
|
48
|
+
this.duplex = duplex;
|
|
49
|
+
const self = this;
|
|
50
|
+
this.done = duplex.sink((async function* () {
|
|
51
|
+
for await (const bytes of self.outgoing) {
|
|
52
|
+
try {
|
|
53
|
+
yield bytes;
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
self.outgoingBytes -= bytes.byteLength;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
})());
|
|
60
|
+
void this.done.catch(error => this.close(error instanceof Error ? error : new Error('Transport failed')));
|
|
61
|
+
}
|
|
62
|
+
send(packet) {
|
|
63
|
+
const body = encoder.encode(JSON.stringify(packet));
|
|
64
|
+
if (body.length > MAX_PACKET)
|
|
65
|
+
throw new Error('Record too large');
|
|
66
|
+
const bytes = new Uint8Array(body.length + 4);
|
|
67
|
+
new DataView(bytes.buffer).setUint32(0, body.length);
|
|
68
|
+
bytes.set(body, 4);
|
|
69
|
+
if (this.outgoingBytes + bytes.byteLength > 4 * 1024 * 1024)
|
|
70
|
+
throw new Error('Record queue byte budget exceeded');
|
|
71
|
+
this.outgoingBytes += bytes.byteLength;
|
|
72
|
+
try {
|
|
73
|
+
this.outgoing.push(bytes);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.outgoingBytes -= bytes.byteLength;
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
close(error) { this.outgoing.end(error); this.duplex.close?.(error); }
|
|
81
|
+
async *read() {
|
|
82
|
+
let pending = new Uint8Array(0);
|
|
83
|
+
for await (const chunk of this.duplex.source) {
|
|
84
|
+
if (pending.length + chunk.length > MAX_PACKET * 2 + 8)
|
|
85
|
+
throw new Error('Record buffer exceeded');
|
|
86
|
+
const next = new Uint8Array(pending.length + chunk.length);
|
|
87
|
+
next.set(pending);
|
|
88
|
+
next.set(chunk, pending.length);
|
|
89
|
+
pending = next;
|
|
90
|
+
while (pending.length >= 4) {
|
|
91
|
+
const size = new DataView(pending.buffer, pending.byteOffset, 4).getUint32(0);
|
|
92
|
+
if (size > MAX_PACKET || size === 0)
|
|
93
|
+
throw new Error('Invalid record size');
|
|
94
|
+
if (pending.length < size + 4)
|
|
95
|
+
break;
|
|
96
|
+
const packet = JSON.parse(decoder.decode(pending.subarray(4, size + 4)));
|
|
97
|
+
if (!packet || typeof packet !== 'object' || typeof packet.id !== 'string' || typeof packet.type !== 'string')
|
|
98
|
+
throw new Error('Invalid record');
|
|
99
|
+
yield packet;
|
|
100
|
+
pending = pending.slice(size + 4);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (pending.length)
|
|
104
|
+
throw new Error('Truncated record');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function toBase64(bytes) {
|
|
108
|
+
let value = '';
|
|
109
|
+
for (let offset = 0; offset < bytes.length; offset += 8192)
|
|
110
|
+
value += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
|
|
111
|
+
return btoa(value);
|
|
112
|
+
}
|
|
113
|
+
export function fromBase64(value) {
|
|
114
|
+
const raw = atob(value);
|
|
115
|
+
return Uint8Array.from(raw, c => c.charCodeAt(0));
|
|
116
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as opaque from '@serenity-kit/opaque';
|
|
2
|
+
import { scryptAsync } from '@noble/hashes/scrypt.js';
|
|
3
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
|
|
4
|
+
// Existing credentials are password-equivalent here. Never send the stored hash
|
|
5
|
+
// over the wire. OPAQUE authenticates both Noise identities before any grant.
|
|
6
|
+
const keyStretching = 'memory-constrained';
|
|
7
|
+
const userIdentifier = 'termdock-password-bootstrap-v1';
|
|
8
|
+
const TTL = 60_000;
|
|
9
|
+
function identifiers(clientIdentity, serverIdentity, origin) {
|
|
10
|
+
if (!clientIdentity || clientIdentity.length > 256 || !serverIdentity || serverIdentity.length > 256 ||
|
|
11
|
+
origin.length > 2048 || new URL(origin).origin !== origin)
|
|
12
|
+
throw new Error('Invalid bootstrap identity');
|
|
13
|
+
return { client: JSON.stringify([userIdentifier, origin, clientIdentity]), server: serverIdentity };
|
|
14
|
+
}
|
|
15
|
+
function bounded(value) {
|
|
16
|
+
if (typeof value !== 'string' || value.length < 1 || value.length > 8192)
|
|
17
|
+
throw new Error('Invalid OPAQUE message');
|
|
18
|
+
}
|
|
19
|
+
export async function startPasswordBootstrap(password, saltHex) {
|
|
20
|
+
if (!/^[0-9a-f]{32}$/.test(saltHex) || !password || password.length > 1024 || new TextEncoder().encode(password).length > 4096)
|
|
21
|
+
throw new Error('Invalid password parameters');
|
|
22
|
+
await opaque.ready;
|
|
23
|
+
const passwordKey = bytesToHex(await scryptAsync(password, hexToBytes(saltHex), { N: 32768, r: 8, p: 1, dkLen: 64, maxmem: 67108864 }));
|
|
24
|
+
return { ...opaque.client.startLogin({ password: passwordKey }), passwordKey };
|
|
25
|
+
}
|
|
26
|
+
export async function finishPasswordBootstrap(state, response, binding) {
|
|
27
|
+
await opaque.ready;
|
|
28
|
+
bounded(response.loginResponse);
|
|
29
|
+
const result = opaque.client.finishLogin({ clientLoginState: state.clientLoginState, password: state.passwordKey,
|
|
30
|
+
loginResponse: response.loginResponse, keyStretching,
|
|
31
|
+
identifiers: identifiers(binding.clientIdentity, response.serverIdentity, binding.origin) });
|
|
32
|
+
if (!result)
|
|
33
|
+
throw new Error('Password authentication failed');
|
|
34
|
+
return { attemptId: response.attemptId, finishLoginRequest: result.finishLoginRequest, serverIdentity: response.serverIdentity };
|
|
35
|
+
}
|
|
36
|
+
export class PasswordBootstrapServer {
|
|
37
|
+
options;
|
|
38
|
+
setup;
|
|
39
|
+
pending = new Map();
|
|
40
|
+
constructor(options) {
|
|
41
|
+
this.options = options;
|
|
42
|
+
}
|
|
43
|
+
now() { return this.options.now?.() ?? Date.now(); }
|
|
44
|
+
credential() {
|
|
45
|
+
const hash = this.options.getPasswordHash();
|
|
46
|
+
if (!hash || !/^scrypt\$[0-9a-f]{32}\$[0-9a-f]{128}$/.test(hash))
|
|
47
|
+
throw new Error('Password authentication unavailable');
|
|
48
|
+
return hash;
|
|
49
|
+
}
|
|
50
|
+
parameters() { return { saltHex: this.credential().split('$')[1] }; }
|
|
51
|
+
async start(input) {
|
|
52
|
+
bounded(input.startLoginRequest);
|
|
53
|
+
const ids = identifiers(input.clientIdentity, this.options.serverIdentity, input.origin);
|
|
54
|
+
for (const [id, entry] of this.pending)
|
|
55
|
+
if (entry.expiresAt <= this.now())
|
|
56
|
+
this.pending.delete(id);
|
|
57
|
+
if (this.pending.size >= 32)
|
|
58
|
+
throw new Error('Too many password login attempts');
|
|
59
|
+
const credential = this.credential();
|
|
60
|
+
await opaque.ready;
|
|
61
|
+
if (this.pending.size >= 32)
|
|
62
|
+
throw new Error('Too many password login attempts');
|
|
63
|
+
this.setup ??= opaque.server.createSetup();
|
|
64
|
+
const password = credential.split('$')[2];
|
|
65
|
+
// Registration is local migration from the existing scrypt credential; no
|
|
66
|
+
// unauthenticated network registration endpoint or password reset is needed.
|
|
67
|
+
const registration = opaque.client.startRegistration({ password });
|
|
68
|
+
const { registrationResponse } = opaque.server.createRegistrationResponse({ ...registration, serverSetup: this.setup, userIdentifier });
|
|
69
|
+
const { registrationRecord } = opaque.client.finishRegistration({ ...registration, registrationResponse, password, identifiers: ids, keyStretching });
|
|
70
|
+
const result = opaque.server.startLogin({ serverSetup: this.setup, registrationRecord, userIdentifier,
|
|
71
|
+
startLoginRequest: input.startLoginRequest, identifiers: ids });
|
|
72
|
+
const attemptId = globalThis.crypto.randomUUID();
|
|
73
|
+
this.pending.set(attemptId, { serverLoginState: result.serverLoginState, clientIdentity: input.clientIdentity, credential, expiresAt: this.now() + TTL });
|
|
74
|
+
return { attemptId, loginResponse: result.loginResponse, serverIdentity: this.options.serverIdentity };
|
|
75
|
+
}
|
|
76
|
+
async finish(input) {
|
|
77
|
+
bounded(input.finishLoginRequest);
|
|
78
|
+
const entry = this.pending.get(input.attemptId);
|
|
79
|
+
this.pending.delete(input.attemptId);
|
|
80
|
+
if (!entry || entry.expiresAt <= this.now() || entry.credential !== this.credential())
|
|
81
|
+
throw new Error('Password login expired');
|
|
82
|
+
await opaque.ready;
|
|
83
|
+
opaque.server.finishLogin({ serverLoginState: entry.serverLoginState, finishLoginRequest: input.finishLoginRequest });
|
|
84
|
+
return { clientIdentity: entry.clientIdentity, serverIdentity: this.options.serverIdentity };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { PasswordBootstrapServer } from './passwordBootstrap.js';
|
|
3
|
+
import { getLoginBlockMs, getPasswordVerifier, recordLoginFailure, recordLoginSuccess } from '../utils/authProtection.js';
|
|
4
|
+
import { getPublicOrigin } from '../utils/publicSecurity.js';
|
|
5
|
+
import { bootstrapCors } from './bootstrapCors.js';
|
|
6
|
+
export function createPasswordLoginRouter(getRuntime) {
|
|
7
|
+
const router = express.Router();
|
|
8
|
+
let bootstrap;
|
|
9
|
+
let runtime;
|
|
10
|
+
let computing = 0;
|
|
11
|
+
const starts = [];
|
|
12
|
+
const pending = new Map();
|
|
13
|
+
router.use(bootstrapCors);
|
|
14
|
+
router.use((_req, res, next) => {
|
|
15
|
+
const ready = getRuntime();
|
|
16
|
+
if (!ready) {
|
|
17
|
+
res.status(503).json({ error: 'Encrypted login is starting' });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (ready !== runtime) {
|
|
21
|
+
runtime = ready;
|
|
22
|
+
bootstrap = new PasswordBootstrapServer({ getPasswordHash: getPasswordVerifier, serverIdentity: ready.serviceId });
|
|
23
|
+
pending.clear();
|
|
24
|
+
}
|
|
25
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
26
|
+
next();
|
|
27
|
+
});
|
|
28
|
+
router.get('/parameters', (_req, res) => {
|
|
29
|
+
try {
|
|
30
|
+
res.json(bootstrap.parameters());
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
res.status(503).json({ error: 'Password login unavailable' });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
router.use(express.json({ limit: '8kb' }));
|
|
37
|
+
router.post('/start', async (req, res) => {
|
|
38
|
+
const ip = req.socket.remoteAddress ?? 'unknown';
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
while (starts.length && starts[0] <= now - 60_000)
|
|
41
|
+
starts.shift();
|
|
42
|
+
for (const [id, entry] of pending)
|
|
43
|
+
if (entry.expiresAt <= now)
|
|
44
|
+
pending.delete(id);
|
|
45
|
+
if (getLoginBlockMs(ip) > 0 || starts.length >= 30 || computing >= 2) {
|
|
46
|
+
res.status(429).json({ error: 'Too many login attempts' });
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (typeof req.body?.clientIdentity !== 'string' || typeof req.body?.startLoginRequest !== 'string' ||
|
|
50
|
+
Object.keys(req.body).some(key => !['clientIdentity', 'startLoginRequest'].includes(key))) {
|
|
51
|
+
res.status(400).json({ error: 'Invalid login request' });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const origin = req.headers.origin ?? getPublicOrigin() ?? `${req.protocol}://${req.headers.host}`;
|
|
55
|
+
starts.push(now);
|
|
56
|
+
computing++;
|
|
57
|
+
recordLoginFailure(ip);
|
|
58
|
+
try {
|
|
59
|
+
const result = await bootstrap.start({ ...req.body, origin });
|
|
60
|
+
pending.set(result.attemptId, { ip, origin, expiresAt: Date.now() + 60_000 });
|
|
61
|
+
res.json(result);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
res.status(401).json({ error: 'Password authentication failed' });
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
computing--;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
router.post('/finish', async (req, res) => {
|
|
71
|
+
const ip = req.socket.remoteAddress ?? 'unknown';
|
|
72
|
+
const origin = req.headers.origin ?? getPublicOrigin() ?? `${req.protocol}://${req.headers.host}`;
|
|
73
|
+
if (typeof req.body?.attemptId !== 'string' || typeof req.body?.finishLoginRequest !== 'string' ||
|
|
74
|
+
Object.keys(req.body).some(key => !['attemptId', 'finishLoginRequest'].includes(key))) {
|
|
75
|
+
res.status(400).json({ error: 'Invalid login request' });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const entry = pending.get(req.body.attemptId);
|
|
79
|
+
if (!entry || entry.ip !== ip || entry.origin !== origin || entry.expiresAt <= Date.now()) {
|
|
80
|
+
res.status(401).json({ error: 'Password authentication failed' });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
pending.delete(req.body.attemptId);
|
|
84
|
+
try {
|
|
85
|
+
const credential = getPasswordVerifier();
|
|
86
|
+
const result = await bootstrap.finish(req.body);
|
|
87
|
+
if (!credential || credential !== getPasswordVerifier())
|
|
88
|
+
throw new Error('Password changed');
|
|
89
|
+
runtime.store.grantPassword(result.clientIdentity);
|
|
90
|
+
recordLoginSuccess(ip);
|
|
91
|
+
res.json({ ok: true });
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
res.status(401).json({ error: 'Password authentication failed' });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return router;
|
|
98
|
+
}
|