@astralyn/sash 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +184 -0
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/THIRD_PARTY_NOTICES.md +54 -0
- package/dist/api.js +103 -0
- package/dist/browser.js +25 -0
- package/dist/cli-errors.js +15 -0
- package/dist/cli.js +122 -0
- package/dist/commands/lifecycle.js +50 -0
- package/dist/commands/logs.js +54 -0
- package/dist/commands/shared.js +8 -0
- package/dist/commands/status.js +39 -0
- package/dist/commands/update.js +5 -0
- package/dist/commands/upgrade.js +43 -0
- package/dist/commands/web.js +33 -0
- package/dist/contracts.js +412 -0
- package/dist/core-config-validation.js +72 -0
- package/dist/core-install-record.js +55 -0
- package/dist/core-install-transaction.js +114 -0
- package/dist/core-update-coordination.js +105 -0
- package/dist/core-update-service.js +245 -0
- package/dist/core-update.js +804 -0
- package/dist/core-version.js +18 -0
- package/dist/core.js +206 -0
- package/dist/daemon/app.js +157 -0
- package/dist/daemon/context.js +40 -0
- package/dist/daemon/entry.js +67 -0
- package/dist/daemon/errors.js +47 -0
- package/dist/daemon/handlers/core.js +17 -0
- package/dist/daemon/handlers/daemon.js +66 -0
- package/dist/daemon/handlers/profiles.js +57 -0
- package/dist/daemon/handlers/proxy.js +12 -0
- package/dist/daemon/handlers/settings.js +48 -0
- package/dist/daemon/router.js +312 -0
- package/dist/daemon/scheduler.js +33 -0
- package/dist/daemon/server.js +121 -0
- package/dist/daemon-auth.js +71 -0
- package/dist/daemon-client.js +55 -0
- package/dist/daemon-entry.js +13 -0
- package/dist/daemon-http.js +93 -0
- package/dist/daemon-lifecycle.js +378 -0
- package/dist/daemon-proxy.js +176 -0
- package/dist/daemon-static.js +106 -0
- package/dist/daemon.js +3 -0
- package/dist/error-utils.js +14 -0
- package/dist/fs-atomic.js +123 -0
- package/dist/github.js +187 -0
- package/dist/http.js +282 -0
- package/dist/json-shape.js +17 -0
- package/dist/log-follow.js +280 -0
- package/dist/log.js +17 -0
- package/dist/managed-state-transaction.js +377 -0
- package/dist/mihomo-config.js +334 -0
- package/dist/node-version-guard.js +8 -0
- package/dist/offline-mutation.js +58 -0
- package/dist/paths.js +57 -0
- package/dist/process.js +572 -0
- package/dist/profile-migration.js +101 -0
- package/dist/profile-service.js +667 -0
- package/dist/profiles.js +182 -0
- package/dist/runtime-lifecycle.js +205 -0
- package/dist/runtime-owner.js +87 -0
- package/dist/runtime-recovery.js +31 -0
- package/dist/sash-client.js +163 -0
- package/dist/settings-service.js +242 -0
- package/dist/settings.js +314 -0
- package/dist/state-lock.js +331 -0
- package/dist/status.js +279 -0
- package/dist/supervisor.js +301 -0
- package/dist/sysproxy/common.js +99 -0
- package/dist/sysproxy/darwin.js +287 -0
- package/dist/sysproxy/factory.js +71 -0
- package/dist/sysproxy/gnome.js +181 -0
- package/dist/sysproxy/legacy.js +58 -0
- package/dist/sysproxy/snapshot.js +284 -0
- package/dist/sysproxy/types.js +24 -0
- package/dist/sysproxy/windows.js +216 -0
- package/dist/sysproxy.js +8 -0
- package/dist/system-proxy-manager.js +460 -0
- package/dist/tun-guidance.js +11 -0
- package/dist/ui/assets/05129d5ad91a6181d272cc3d031363d6-DJkDXMrK.woff2 +0 -0
- package/dist/ui/assets/0526ebf94418638dfd5b6cd9b06c07d4-BzWe4Lji.woff2 +0 -0
- package/dist/ui/assets/053f2647a78f659f6f8a3ae8436bd265-BocXYDl5.woff2 +0 -0
- package/dist/ui/assets/078f666863424d3fd2d98e3ea528704c-DQ_QCvOt.woff2 +0 -0
- package/dist/ui/assets/07f3ff379663ffa0222c12f241421dd9-Cma7oDlr.woff2 +0 -0
- package/dist/ui/assets/08427b8d865853bbd7c439321b26495f-CJmC8fWx.woff2 +0 -0
- package/dist/ui/assets/095224e4db29d40c54098da02b266b0a-DjsP0nQn.woff2 +0 -0
- package/dist/ui/assets/0ada18ca45940b08accde5c61ee871e3-E1BZD3RX.woff2 +0 -0
- package/dist/ui/assets/0be242294f7d791af850c6df38ac78a0-2cL6Ntwf.woff2 +0 -0
- package/dist/ui/assets/0bf18c938d0c27cce06c39f855405fdb-CruLZZZJ.woff2 +0 -0
- package/dist/ui/assets/0cfb3e9f4edf60839c37dc83e6f75ea3-DzAtRHvi.woff2 +0 -0
- package/dist/ui/assets/0fbbd340d0467dfe3101d58846f30cb0-gtFcO250.woff2 +0 -0
- package/dist/ui/assets/1063fa2dc7334d1f383e30824e016eae-B7U-kD-0.woff2 +0 -0
- package/dist/ui/assets/109e26e62ddc85d946e699464afbece7-BLnUjAEV.woff2 +0 -0
- package/dist/ui/assets/12a443b0ae9748a993b5395f8fad412b-Btjff2Lz.woff2 +0 -0
- package/dist/ui/assets/135c4621de7a4d85d852e03d416f13ee-DdSuW36e.woff2 +0 -0
- package/dist/ui/assets/13aa415469ee761c968749b1c0c56ec6-Dwe-Wa_w.woff2 +0 -0
- package/dist/ui/assets/1435b1ad447bd9c0e8bc52035c9dcfa8-C05ldXtw.woff2 +0 -0
- package/dist/ui/assets/175e0915aa874e7446b79b6bc9b00721-Dp0-l77L.woff2 +0 -0
- package/dist/ui/assets/1876e7d02187f006e2b7f769391c3089-CNjpRdap.woff2 +0 -0
- package/dist/ui/assets/1886dc284b3b845e3b5fe04aa2107276-Bh_wHwL1.woff2 +0 -0
- package/dist/ui/assets/19a3dda6611aaf5d787403bd1159fe15-zoEBvdpA.woff2 +0 -0
- package/dist/ui/assets/1afa74e14da193125e45d4783d358a4f-BgjSTENX.woff2 +0 -0
- package/dist/ui/assets/1cd26a6a6d784720b4a43053e9058bcf-TAP3dvNi.woff2 +0 -0
- package/dist/ui/assets/1ce19bddc3beab385a863b1e05685678-BCdXitZw.woff2 +0 -0
- package/dist/ui/assets/1d3459ac6578c80502bdb876a4824a93-CSwdnEGP.woff2 +0 -0
- package/dist/ui/assets/1d479e364dab4af01c539f0a6a7ac036-ZFI-djGt.woff2 +0 -0
- package/dist/ui/assets/1dcea2408d17fcec4197adce50f8b32d-CnK2xXR-.woff2 +0 -0
- package/dist/ui/assets/1e855385699999e24a33f7df8aed0489-DGFHkDVQ.woff2 +0 -0
- package/dist/ui/assets/1f55944b8f7972580e3029694462a6b0-CB7ogVJ9.woff2 +0 -0
- package/dist/ui/assets/1f96eda70929089762dc3c4feb33f9c8-DtSQ6YOf.woff2 +0 -0
- package/dist/ui/assets/1fb275532caadc746455881b31da27c4-BHFTsg74.woff2 +0 -0
- package/dist/ui/assets/20e679bdc00d06f802ea71fd9154b284-D9gRjq_v.woff2 +0 -0
- package/dist/ui/assets/25aff66c4a9f8cb801cbd525f4a923e4-Dgi87hP7.woff2 +0 -0
- package/dist/ui/assets/28e6700380b023b23e77f2d75aee624f-b19D855Q.woff2 +0 -0
- package/dist/ui/assets/2a1c50aa9cd99ce3a0197f5d49948e63-DrztdXCB.woff2 +0 -0
- package/dist/ui/assets/2b4e0ae0bf3af610b97717dfd66a012d-CBoapvha.woff2 +0 -0
- package/dist/ui/assets/2be2b7a672b616bc2f07059a67bfaad8-BUR0aNML.woff2 +0 -0
- package/dist/ui/assets/2e0a358ada78ea98ec8ba86002a27eac-DvgbSr9v.woff2 +0 -0
- package/dist/ui/assets/2e1cf63a2a22a50516c49b1dc521ea8a-CmltIxig.woff2 +0 -0
- package/dist/ui/assets/2e4481801925fcb6573c4337c73f15d7-DYOoCk-m.woff2 +0 -0
- package/dist/ui/assets/2fe5e634c568cd874f94d24b8f02bfd1-DcEHY35G.woff2 +0 -0
- package/dist/ui/assets/309c33f63f29887fb557115aa24d4bb5-CUgqrXIj.woff2 +0 -0
- package/dist/ui/assets/3158f45c959e1b346b0bbd6f3193dbf0-kimcClqp.woff2 +0 -0
- package/dist/ui/assets/316adbc8037a36f8d804637b053bbe5e-CjbZmJf9.woff2 +0 -0
- package/dist/ui/assets/319e9631bc29fc7235b70fc9aac43cd9-IV6yZuHZ.woff2 +0 -0
- package/dist/ui/assets/32054f9bb88d9431b1cdf023f770e614-CYrpCgqK.woff2 +0 -0
- package/dist/ui/assets/3245fa1aac6338c1d9d04befb7c346f8-CzvPhVPA.woff2 +0 -0
- package/dist/ui/assets/326c730f98afa476057957f2743c81e2-GPHJ3Fpu.woff2 +0 -0
- package/dist/ui/assets/33885558dc783bf9bae6c5d4668fef07-D33us0yR.woff2 +0 -0
- package/dist/ui/assets/33cd11f5e61556e4618801c8231f2744-CoR_Tv3X.woff2 +0 -0
- package/dist/ui/assets/34a170749cb7e6c5851acc510882af6b-BCZwsihk.woff2 +0 -0
- package/dist/ui/assets/3538de2d387591258d7e9908aad803b9-CewOkERZ.woff2 +0 -0
- package/dist/ui/assets/38d7b029c036080067a2e94211098171-CettR5Qv.woff2 +0 -0
- package/dist/ui/assets/393f47efeefc9f9e9cca83eb10a82c69-Brhz4z1y.woff2 +0 -0
- package/dist/ui/assets/396a51f2b274d01f6ba606a94f3e5fe2-DgRk-jtI.woff2 +0 -0
- package/dist/ui/assets/3991bf955d4c925e84b4a6542760d4d5-II4HtRkQ.woff2 +0 -0
- package/dist/ui/assets/39b6cb6ae8d1ea0e9832b88d2c21ef99-CbJm-q6b.woff2 +0 -0
- package/dist/ui/assets/3a1133157fbd259f1a51aaa9f8418a26-3FmUjjvi.woff2 +0 -0
- package/dist/ui/assets/3a34cff07dca7a65ed2486be19229290-B7ml2yBN.woff2 +0 -0
- package/dist/ui/assets/3c339dd7c27e6b8a06eea431733e076f-dGQqXaEj.woff2 +0 -0
- package/dist/ui/assets/3d074ea67f60953b816ce7cf5d87a980-B5VkPOmg.woff2 +0 -0
- package/dist/ui/assets/3d38f54dcf4a9f4a50165d5849becf71-CVDtFSZn.woff2 +0 -0
- package/dist/ui/assets/3e3fc9a19a27f94e4ff1b815b55b2671-Ci736GLi.woff2 +0 -0
- package/dist/ui/assets/3e8abb1f606f29e2f0a8a8a6b702d263-C5sOSf5L.woff2 +0 -0
- package/dist/ui/assets/3f0ab4e1cd99bdbfdf9acbc8f4bcb9ce-0DcdTLxV.woff2 +0 -0
- package/dist/ui/assets/3f30c0482650345c981d66920f56ab61-CTD2CsZI.woff2 +0 -0
- package/dist/ui/assets/41a9aa12ad9a60758b304c1a998fb781-BTQUDLf1.woff2 +0 -0
- package/dist/ui/assets/424010b2bc5cee540b92b28da49b61bf-DT8upW2M.woff2 +0 -0
- package/dist/ui/assets/42fbdafff2d44b76c6665d51bb2cee9d-_UrojhXD.woff2 +0 -0
- package/dist/ui/assets/4341db13c311b3cb0eaaf6b4c71b89e5-CzjA7MzG.woff2 +0 -0
- package/dist/ui/assets/434db5a796128131fde78d4389d14b71-AeNdWKua.woff2 +0 -0
- package/dist/ui/assets/43cb51fc70a15274d49e2d956c5ec3b4-BQbufd96.woff2 +0 -0
- package/dist/ui/assets/43e79de1c7815f21efbc966ff96fcbd6-DlCQvOsQ.woff2 +0 -0
- package/dist/ui/assets/43faf6a8a3cde1088bd5f7c830890a89-SvDzF3nA.woff2 +0 -0
- package/dist/ui/assets/44731ae21d7690ce538d0f8242755833-CEmu9Z88.woff2 +0 -0
- package/dist/ui/assets/45324b2a64cc0c0db797d5c09e239d34-BuFudtvM.woff2 +0 -0
- package/dist/ui/assets/4584df05d00aa75ae6e1f850fdc9df1d-GNHt6xwM.woff2 +0 -0
- package/dist/ui/assets/48d539539e98481fb511a761031c59fd-Cd9gdi8B.woff2 +0 -0
- package/dist/ui/assets/49a1fb9fc6a35796b2e0d3539088ede8-CD1cRDOy.woff2 +0 -0
- package/dist/ui/assets/4bec02eb0c2371e59bfb7d41a141ea1e-CmBnNPo0.woff2 +0 -0
- package/dist/ui/assets/4c5a81ef5683f6fbf9935e640de804e7-B4wQxzVs.woff2 +0 -0
- package/dist/ui/assets/4f11c9c75e86214b7f69ddee3a9f8076-DHsQ2xpE.woff2 +0 -0
- package/dist/ui/assets/50434b8ff88e27101d1c67ba953ee873-PH_hIqxu.woff2 +0 -0
- package/dist/ui/assets/5107fd5153d52c6a3f8d43ff214dd6c2-ls3eo5Qd.woff2 +0 -0
- package/dist/ui/assets/520cb7d3683dee99e767650efc0aa5b1-Lkd4z-9S.woff2 +0 -0
- package/dist/ui/assets/5455087df2f091b7bf3d38c5a102a2e7-QNkiOxwe.woff2 +0 -0
- package/dist/ui/assets/556450fdf0978d329cf8277e3b71dcdb-CtypGAPw.woff2 +0 -0
- package/dist/ui/assets/5a1b5c1b71cad50c51a79a7f4825e6e6-DQA407Ec.woff2 +0 -0
- package/dist/ui/assets/5ba02e809d2ef4f7639178e7a7d654c0-Dty0omtl.woff2 +0 -0
- package/dist/ui/assets/5bd654afe48e094f95e5142e4eaadc75-D9pUq71R.woff2 +0 -0
- package/dist/ui/assets/5c4c3ca0a3f4115879a61a96d02e3efc-RcVLBj31.woff2 +0 -0
- package/dist/ui/assets/5c737d740638e8d764f12c1e5c1cd144-D2Npjq-N.woff2 +0 -0
- package/dist/ui/assets/5e78019102102aa5741aa591313fd5b5-V7Azrw-2.woff2 +0 -0
- package/dist/ui/assets/6287cacac64e1849941f7b7d7e6316c5-DzkbPOID.woff2 +0 -0
- package/dist/ui/assets/643df7194160d54edc48d16d76e5ba74-BKmmIRjw.woff2 +0 -0
- package/dist/ui/assets/65d3b214c69ff6786fd1cab06f8394b7-B0cRUJAr.woff2 +0 -0
- package/dist/ui/assets/6698103c11bfe260d44fcf00c6d8b623-DVWYOeyb.woff2 +0 -0
- package/dist/ui/assets/692e07b3a609a8f1a21c15c45631f348-DJW5LcZH.woff2 +0 -0
- package/dist/ui/assets/694e87456af181279c2bfd551aa3c6e3-Cu7kRuJB.woff2 +0 -0
- package/dist/ui/assets/6b88066bdc7a6e8bc823128dbd0f6f3b-BG38UZSq.woff2 +0 -0
- package/dist/ui/assets/6d6f36f15381826d113832bfdd41b021-Bgh7YPiq.woff2 +0 -0
- package/dist/ui/assets/6e22361162bd0a5cd189ca3c98d23d52-DqnptHqF.woff2 +0 -0
- package/dist/ui/assets/6e884dd501b8da24ecd37425a9b3d51d-BVNqra8s.woff2 +0 -0
- package/dist/ui/assets/71200dbc7497fbafa28db5cfeae74acb-B6xMqoC6.woff2 +0 -0
- package/dist/ui/assets/72a85441a9757146440e3985f3b02075-CzTSlCPZ.woff2 +0 -0
- package/dist/ui/assets/7472d7e02045dea3ff288eac850e38eb-CboJEy4X.woff2 +0 -0
- package/dist/ui/assets/795ffa953bea666763ae79ac88aa7e5a-C6UsoDmi.woff2 +0 -0
- package/dist/ui/assets/7aa8647d6c52d249169b73417aa8e444-kJejhQ0-.woff2 +0 -0
- package/dist/ui/assets/7be6b794fc233ccd8b5769496301313a-Ba2iZC0a.woff2 +0 -0
- package/dist/ui/assets/7c0748f04d52086809c9f551c5aaded7-DSzO_ybF.woff2 +0 -0
- package/dist/ui/assets/7c858bb53a7652227fab8cf9753d3e1f-Bi9fbvIF.woff2 +0 -0
- package/dist/ui/assets/7e7026091b4e6061b05963bdb3a7f944-CrmRDJpc.woff2 +0 -0
- package/dist/ui/assets/7f80369a7479d568ab8bf492000282bd-CqcI5ZOp.woff2 +0 -0
- package/dist/ui/assets/834ce2f2571aaa26e47a618c4ad55e58-DZfWJrxo.woff2 +0 -0
- package/dist/ui/assets/834faab602d9c795341bc7dfd6578fe5-B1VeBVWh.woff2 +0 -0
- package/dist/ui/assets/8513a8eea35b460a047a16a87c36f8bb-H259w_6b.woff2 +0 -0
- package/dist/ui/assets/8699e8a36465bf46671dc28ce24e188c-YlkPpQ6h.woff2 +0 -0
- package/dist/ui/assets/86ae8881eae9eac7e9c0182c8ce6c627-Ch2r3LNs.woff2 +0 -0
- package/dist/ui/assets/86b3ae1be58d76f7147793f09264451b-CpgTfcn-.woff2 +0 -0
- package/dist/ui/assets/8769f8d7d45adcfcbd3f80460d22720d-DV_yqcTo.woff2 +0 -0
- package/dist/ui/assets/87bb0ceea9f5515490c832bdf6ebf641-CJpxd8e6.woff2 +0 -0
- package/dist/ui/assets/8928b9a50b5efbaaafa039674a3c7301-C-42WRD6.woff2 +0 -0
- package/dist/ui/assets/899f2c2a36cbeac473a7caeeaabc46b1-xrIcXJXU.woff2 +0 -0
- package/dist/ui/assets/8a075d4043e6bf66740808b492186438-Dq0346Qm.woff2 +0 -0
- package/dist/ui/assets/8b86676936792154c53205ab5d90b797-BGH_CGsg.woff2 +0 -0
- package/dist/ui/assets/8c63ca479653200a0e12f428242e1171-CsBTcPwY.woff2 +0 -0
- package/dist/ui/assets/8c9c7ff8342f5fed60e73c4a7ad67325-CGSYpt52.woff2 +0 -0
- package/dist/ui/assets/8ca655fcc5d9cf33d49815e231f3f356-b8PyEeZg.woff2 +0 -0
- package/dist/ui/assets/8e96f0f9385ef826f900587d0fc18b3d-D7GIG6Uq.woff2 +0 -0
- package/dist/ui/assets/8ea917252cf2c1e664f06ee5badffeea-CDZ1AFr0.woff2 +0 -0
- package/dist/ui/assets/8f42f890fc37e292017446af85af2745-Qx6oGI0z.woff2 +0 -0
- package/dist/ui/assets/8f9589e71c53bb753749a9c412f15c01-BVR5mPJl.woff2 +0 -0
- package/dist/ui/assets/90e70b52a3cc2491bbb2b4c0caf0ae7e-BdwPu67j.woff2 +0 -0
- package/dist/ui/assets/92ac4ca42fe050c1e680c585551abd9c-CXLyuj7l.woff2 +0 -0
- package/dist/ui/assets/92c12bb794c491930fd06f3f97299807-u_0ognJ0.woff2 +0 -0
- package/dist/ui/assets/92f0b4d2bb274a801aac10fc34c1e0cc-DFHB6dyu.woff2 +0 -0
- package/dist/ui/assets/93247aebd628367f5518eafdb2770ce9-o1N4zvTO.woff2 +0 -0
- package/dist/ui/assets/940446ea160995af66a9ee223cc3d4d1-C-_7aPYO.woff2 +0 -0
- package/dist/ui/assets/94be430de618453b6ae27bd366827ba2-1YYf1n5N.woff2 +0 -0
- package/dist/ui/assets/98cd7d62095e466e55885b34f63c86f1-z33YCk0X.woff2 +0 -0
- package/dist/ui/assets/99049fd5cec5894cafe2a1e5e13d1e03-DNrg8nIk.woff2 +0 -0
- package/dist/ui/assets/99e2752b7351aaa7681b4d91db80e89a-b3hi7miY.woff2 +0 -0
- package/dist/ui/assets/9d1b670cbfd64716ac685dea26237d95-DAZ9U6es.woff2 +0 -0
- package/dist/ui/assets/CodeEditorModal-6m0TJWyF.css +1 -0
- package/dist/ui/assets/CodeEditorModal-CFEnWsyh.js +14 -0
- package/dist/ui/assets/ConnectionsView-DHo6yvU6.css +1 -0
- package/dist/ui/assets/ConnectionsView-DNGmZBSU.js +1 -0
- package/dist/ui/assets/LogsView-BoeE92VF.css +1 -0
- package/dist/ui/assets/LogsView-fSiaxQ13.js +1 -0
- package/dist/ui/assets/PaginationFooter-B3kHzRfB.js +1 -0
- package/dist/ui/assets/PaginationFooter-BNkpcNoB.css +1 -0
- package/dist/ui/assets/ProfileEditorDialog-BydoZthX.js +1 -0
- package/dist/ui/assets/ProfilesView-Btc1DOxE.js +2 -0
- package/dist/ui/assets/ProfilesView-ByqdFNHN.css +1 -0
- package/dist/ui/assets/RulesView-CHyynVqA.css +1 -0
- package/dist/ui/assets/RulesView-D9vZBiJ1.js +1 -0
- package/dist/ui/assets/SettingsFileDialog-CNFEAVs4.js +1 -0
- package/dist/ui/assets/SettingsView-BlDhZkXQ.js +2 -0
- package/dist/ui/assets/SettingsView-CngS3vBM.css +1 -0
- package/dist/ui/assets/a0f2d41645a113d821e7ff0891daad1e-XK0rDAci.woff2 +0 -0
- package/dist/ui/assets/a1d1345d8407dd6169f39c28ff6b1708-Bn7hMmv8.woff2 +0 -0
- package/dist/ui/assets/a29f51b4ebb6776e43e7492a036be28e-CrAKKz8u.woff2 +0 -0
- package/dist/ui/assets/a3252ac86b2c4e88bfb9baecabcad817-qnA_3LNE.woff2 +0 -0
- package/dist/ui/assets/a3d63a61e7eb2bb497dbc2da65bd0873-BRMJAm0m.woff2 +0 -0
- package/dist/ui/assets/a45eea933252dbe3529f30a0323c2661-D4kBOrXR.woff2 +0 -0
- package/dist/ui/assets/a4886c4ebed8c8b1f8a87b5b491a39aa-CBQdRqpF.woff2 +0 -0
- package/dist/ui/assets/a50b4ab745c7676d9d3ceb3c0a023a3d-CW990pgI.woff2 +0 -0
- package/dist/ui/assets/a5851ae4d7e58996d8bd681f80d4727c-BBytdb0t.woff2 +0 -0
- package/dist/ui/assets/a5cdaa96bf7d1db6c671432f9ab7bccb-slKJjgfX.woff2 +0 -0
- package/dist/ui/assets/a640dd2e962cf72c7fcf76234c973303-CUCWUDBY.woff2 +0 -0
- package/dist/ui/assets/a67e457bbd366aef5104ce743a2cf6d4-DkE8fLlE.woff2 +0 -0
- package/dist/ui/assets/a6c3f2257315d45aa212bd2e86a02852-CxDbXnZV.woff2 +0 -0
- package/dist/ui/assets/a73399a40a5f81fb7314b8b9a10cd221-DRAvJPKI.woff2 +0 -0
- package/dist/ui/assets/a73a64b0941803d9d807b9ec082df641-BbPpPyy5.woff2 +0 -0
- package/dist/ui/assets/a73d1a0a1dfd4f4234f7a0649ece4da4-BHeqktY9.woff2 +0 -0
- package/dist/ui/assets/a740044e77a400a450e71fcdea55523f-Co5hpBNK.woff2 +0 -0
- package/dist/ui/assets/a7669b29f404662c179c71b87e67c8d2-CVnuBgHy.woff2 +0 -0
- package/dist/ui/assets/a964a4def15811e91b05a91740a1cf37-CzcDfCRn.woff2 +0 -0
- package/dist/ui/assets/aac3f69132da164921fda75d2d90bfb7-B6rUZQ4_.woff2 +0 -0
- package/dist/ui/assets/aacd263c3bb4ba163a4d5079c159680a-DtrDLFlk.woff2 +0 -0
- package/dist/ui/assets/ac62ec881ded2cdb450e542b396e24d0-CRlVJvPa.woff2 +0 -0
- package/dist/ui/assets/aebcb9b7d4b174ab5a4b911206f24036-BuleSpgP.woff2 +0 -0
- package/dist/ui/assets/af70712dadc05c7b18d248996a5ab0a9-gCZSZGzb.woff2 +0 -0
- package/dist/ui/assets/afbd3a8a4cfaa4561b7848c28bc6c4f8-DgnqceEw.woff2 +0 -0
- package/dist/ui/assets/b02d3867e6e2ea921d2845b44c4a5a31-C9v4FrqD.woff2 +0 -0
- package/dist/ui/assets/b1b22825b930688403337d4fd7bbba62-vmMs2pJf.woff2 +0 -0
- package/dist/ui/assets/b3206c979bfb28d12d7ad6df58e74b9f-X1PqLaCR.woff2 +0 -0
- package/dist/ui/assets/b6280e42d4f11dcab70594f9d6774936-CtezImLG.woff2 +0 -0
- package/dist/ui/assets/b71b8cd1a70405c3dbc0e7a44055ca9a-Cd6cIOEr.woff2 +0 -0
- package/dist/ui/assets/b9198c4f220a4ca075f8daa4db529e50-Bgc7zlEt.woff2 +0 -0
- package/dist/ui/assets/baad304a58d7d42d0af29235c5e9490b-BQZYwARB.woff2 +0 -0
- package/dist/ui/assets/bb99e34f8784cc44f0eeadbe735dd238-UHFYI827.woff2 +0 -0
- package/dist/ui/assets/bc1cb0f523c8a2ff566a047aeddf90fd-BSlXQn97.woff2 +0 -0
- package/dist/ui/assets/bd3171e975d62612e50148bf38b2158f-CtZKzCzQ.woff2 +0 -0
- package/dist/ui/assets/be10640c403263dd5917ad65c4d453a6-B-rbuuja.woff2 +0 -0
- package/dist/ui/assets/be764e101e78b50b749aa1414d4810a7-yIPaQQih.woff2 +0 -0
- package/dist/ui/assets/bfa682a7f5f12945b41230c35eb859f6-CzagcI5f.woff2 +0 -0
- package/dist/ui/assets/bfa9753ed26f2e1f81ab7020167bd1a0-C6ZNQQ2p.woff2 +0 -0
- package/dist/ui/assets/branding/sash-cat.png +0 -0
- package/dist/ui/assets/c0e78c6ad709b19ce437677c91033489-BtwtlApg.woff2 +0 -0
- package/dist/ui/assets/c5a59409c610cc8c265d1691eb3f71eb-DmI_ky2X.woff2 +0 -0
- package/dist/ui/assets/c5ff66a1a57f2a29424e8de5d4ef3756-C9pOsaV1.woff2 +0 -0
- package/dist/ui/assets/c607bdee84a0e5b69721367ccc0c9c31-eoHbdXeg.woff2 +0 -0
- package/dist/ui/assets/c8095033941d18db730496eb4c863757-DoexTYks.woff2 +0 -0
- package/dist/ui/assets/c8295519e16f4cf3a350e9729f07653a-C8bAtIO_.woff2 +0 -0
- package/dist/ui/assets/c91f9c9f4db81afeba37c8e73afb0bfd-BGIl2iKW.woff2 +0 -0
- package/dist/ui/assets/c9ad9caa4afdd40766add91d321b0350-HfuiRq1k.woff2 +0 -0
- package/dist/ui/assets/cc4f271c90f9ea5157db9c0a4edc976b-MAjuDvHE.woff2 +0 -0
- package/dist/ui/assets/cdf7c3d96ba172214d7e1e9fe5e6ba8b-CUx4fWQE.woff2 +0 -0
- package/dist/ui/assets/cf70d5821ea7c3ca34653169ac763606-CE0T0Svc.woff2 +0 -0
- package/dist/ui/assets/d0d5efe92496b1ceea38e180b0ac97d3-7G71Z6v3.woff2 +0 -0
- package/dist/ui/assets/d1977142eceb878b06440375e64c4372-mr-7LWm-.woff2 +0 -0
- package/dist/ui/assets/d42feb13392b4dd9214c61b61a6e5ae8-DjnKADf_.woff2 +0 -0
- package/dist/ui/assets/d46a696ec6a077c6873fc39043c3c082-C62E-13k.woff2 +0 -0
- package/dist/ui/assets/d4c0bbf1605a8a9c36fff5a2ead81bb5-C2F6tzOj.woff2 +0 -0
- package/dist/ui/assets/d918d4e47697b7c752ea223e0d0d4c81-twovU2ty.woff2 +0 -0
- package/dist/ui/assets/d988c05da0b5624267dd825fedd133d1-Bgbc9SVH.woff2 +0 -0
- package/dist/ui/assets/da3a071df79531f4f033b7c8499651d0-0tgZJL_N.woff2 +0 -0
- package/dist/ui/assets/da8eba4acbf93048645043006de1c5f0-Ce0dmHn_.woff2 +0 -0
- package/dist/ui/assets/dc3f16892e20a85a8fcad2d3097e622b-8PRrFPJt.woff2 +0 -0
- package/dist/ui/assets/dd5f808ec691f911483a2eb60b5049bd-BhVupSbT.woff2 +0 -0
- package/dist/ui/assets/e2b04534cc697a76afd70e885119cb8c-DO303iCb.woff2 +0 -0
- package/dist/ui/assets/e307280eb2fb7809adee14ff0d247d32-DHMLXvSs.woff2 +0 -0
- package/dist/ui/assets/e3b484bec850c669094f713570870b3a-BI_9kRw8.woff2 +0 -0
- package/dist/ui/assets/e7b1d196949b7530cbda0ad956fbb2cc-i2m2Id0q.woff2 +0 -0
- package/dist/ui/assets/e7c2e5fb387880508811cadc2926e13a-BfROXX4U.woff2 +0 -0
- package/dist/ui/assets/e8a5fe283c8b9e173097bee0b5757ab6-BvfjDjYP.woff2 +0 -0
- package/dist/ui/assets/e8e3465d48435612e9c3509be3984188-BeUPU5-9.woff2 +0 -0
- package/dist/ui/assets/e93903f4604b007fc1b7134a74f5c534-B1M4h46T.woff2 +0 -0
- package/dist/ui/assets/eb0994906efd969102d522b6d8b3aff2-i9SHU5CJ.woff2 +0 -0
- package/dist/ui/assets/eb32eee23ceadd3fc4eb5763d3573f57-B3h9kgf3.woff2 +0 -0
- package/dist/ui/assets/ebadbc005027fb3e2b1c2d15be38c66b-qVQSBwBu.woff2 +0 -0
- package/dist/ui/assets/ecbbb464ff6934b5012cf33c80c7a907-CBEPqZfS.woff2 +0 -0
- package/dist/ui/assets/ed79ac25c014b6434f22fe1e2549bc26-mWhz7hkU.woff2 +0 -0
- package/dist/ui/assets/eed64f40443554cb9f65edaca176abb0-Cqw29XTm.woff2 +0 -0
- package/dist/ui/assets/efc749471476cce09e12e7038c757950-BhSWPf1Q.woff2 +0 -0
- package/dist/ui/assets/f2a7c2cb375662aa93d8f31116d39c20-D1zsMYCW.woff2 +0 -0
- package/dist/ui/assets/f5ba0fb4332ccceadc481e47134023d3-CAKMPiu5.woff2 +0 -0
- package/dist/ui/assets/f6326eb5031e18478aa4588598f96965-Dj3uu55n.woff2 +0 -0
- package/dist/ui/assets/f8678cbaab9caef95e1986731dcbd92f-CnIuR3EN.woff2 +0 -0
- package/dist/ui/assets/f8d9d55e2a811e9839c9af5f8f2d62ac-DIUQRBsH.woff2 +0 -0
- package/dist/ui/assets/f98b117e363eb51f4d7c7d1056da7688-O9MtqjIW.woff2 +0 -0
- package/dist/ui/assets/f9ab5d9163148ceb211e5b4f15a3d8e0-Dtqse-AT.woff2 +0 -0
- package/dist/ui/assets/fa69ce224ff0a6c0ff97b10a829779c2-pX45dCNY.woff2 +0 -0
- package/dist/ui/assets/fc38d1da3930d39dd8e286f708105765-DmbOHwTI.woff2 +0 -0
- package/dist/ui/assets/fe03c1dfaffc1cad3521fe55f1541d6b-BW8aEqCj.woff2 +0 -0
- package/dist/ui/assets/fe0b2421eb4d1f4f8949c602871a317c-BsBLV6jH.woff2 +0 -0
- package/dist/ui/assets/fe9c75d0cb3d808023412d26dfc9d388-eXjcgUPI.woff2 +0 -0
- package/dist/ui/assets/index-B61V60w_.js +19 -0
- package/dist/ui/assets/index-bkyxJG8J.css +1 -0
- package/dist/ui/assets/theme-BNq4FkXS.js +1 -0
- package/dist/ui/index.html +52 -0
- package/dist/webui.js +21 -0
- package/docs/backend.md +260 -0
- package/docs/frontend.md +125 -0
- package/docs/remix-icon-license.txt +186 -0
- package/docs/usage.md +227 -0
- package/package.json +82 -0
package/docs/backend.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Backend Architecture & Supervisor Design
|
|
2
|
+
|
|
3
|
+
Sash uses a loopback-only supervisor daemon (`sashd`) to own the Core process, generated configuration, profile state, system-proxy ownership and the HTTP/WebSocket gateway.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
CLI / WebUI
|
|
7
|
+
│ http://127.0.0.1:19090
|
|
8
|
+
▼
|
|
9
|
+
sashd
|
|
10
|
+
├── /sash/* daemon control, Core lifecycle, settings, profiles, system proxy
|
|
11
|
+
├── /core/api/* authenticated controller reverse proxy
|
|
12
|
+
├── ProfileService
|
|
13
|
+
├── RuntimeLifecycle
|
|
14
|
+
│ ├── CoreSupervisor
|
|
15
|
+
│ └── SystemProxyManager
|
|
16
|
+
└── state locks / atomic persistence
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The Core remains a non-detached child of `sashd`. Runtime transitions, disk mutations and daemon startup are serialized at separate boundaries instead of relying on PID files as locks.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. Module Boundaries
|
|
24
|
+
|
|
25
|
+
- `src/daemon.ts`: public facade re-exporting the daemon surface.
|
|
26
|
+
- `src/daemon/router.ts`: the single URLPattern route table plus match → auth → dispatch; `src/daemon/handlers/*` own the per-domain handlers, `src/daemon/errors.ts` maps domain errors onto the unified error envelope, and `src/daemon/context.ts` holds the shared `DaemonContext`/`DaemonGate`.
|
|
27
|
+
- `src/daemon/app.ts`: service assembly around one mutation queue; `src/daemon/server.ts` owns the HTTP/WebSocket listeners and listener close; `src/daemon/scheduler.ts` owns profile auto-update timers; `src/daemon/entry.ts` is the production entrypoint.
|
|
28
|
+
- `src/runtime-lifecycle.ts`: serialized Core/proxy state transitions.
|
|
29
|
+
- `src/supervisor.ts`: child ownership, readiness probes and verified termination.
|
|
30
|
+
- `src/daemon-lifecycle.ts`: daemon discovery, singleton startup, CLI shutdown and the maintenance boundary used by full restarts and Core updates.
|
|
31
|
+
- `src/state-lock.ts`: atomic file leases and cross-process mutation queues.
|
|
32
|
+
- `src/system-proxy-manager.ts`: durable proxy ownership journal, serialized asynchronous OS operations, generation-bound observation cache and conditional recovery.
|
|
33
|
+
- `src/sysproxy.ts` / `src/sysproxy/`: public system-proxy API plus focused Windows, macOS and GNOME asynchronous snapshot/apply backends.
|
|
34
|
+
- `src/profile-service.ts`: profile/config preparation and publication through one-shot opaque capabilities with strict optimistic rechecks.
|
|
35
|
+
- `src/core-install-record.ts`: the canonical install-record codec and release-tag validation shared by install and update paths.
|
|
36
|
+
- `src/core-update.ts`: the low-level executable/install-record transaction, force-repair quarantine and crash recovery.
|
|
37
|
+
- `src/core-update-coordination.ts`: one logical commit/rollback decision across retained managed state and the Core update journal.
|
|
38
|
+
- `src/core-update-service.ts`: staged update, runtime ownership, maintenance, publication and restoration orchestration.
|
|
39
|
+
- `src/offline-mutation.ts` / `src/runtime-recovery.ts`: daemon/offline ownership checks and the fixed legacy-proxy, journaled-proxy, stale-Core and update-recovery order.
|
|
40
|
+
- `src/http.ts` / `src/github.ts`: bounded networking and trusted release downloads, including one absolute asset budget shared across mirror attempts and redirects.
|
|
41
|
+
- `src/settings.ts`: versioned runtime schema, explicit public-field allowlist and candidate validation for `sash.json`.
|
|
42
|
+
- `src/settings-service.ts`: shared online/offline settings preparation, durable publication and runtime-transition orchestration behind a single typed `apply(patch)` transaction.
|
|
43
|
+
- `src/contracts.ts`: browser-safe API contracts and `unknown`-to-typed response parsers shared by the daemon client and WebUI.
|
|
44
|
+
- `src/sash-client.ts`: browser-safe typed client for the daemon-owned `/sash/*` API, built from those parsers; `src/daemon-client.ts` is the Node factory adding retries, deadlines and loopback-only dispatch.
|
|
45
|
+
- `src/runtime-owner.ts`: runtime ownership state machine and the start/stop/restart orchestration consumed by the thin `src/commands/*` wiring modules.
|
|
46
|
+
- `src/json-shape.ts` / `src/error-utils.ts`: domain-neutral JSON shape, canonical timestamp and unknown-error helpers; persistent readers retain their own size, missing and corruption policies.
|
|
47
|
+
- `src/status.ts`: stable CLI status/proxy observations, explicit unknown values and complete/incomplete exit semantics.
|
|
48
|
+
- `src/log-follow.ts`: bounded tail/follow cursors with creation, truncation, identity-rotation and cancellation handling.
|
|
49
|
+
- `web/src/stores/state.ts`: the single reactive WebUI state source, runtime ownership metadata and computed selectors. Focused runtime, profile, Core, telemetry and toast modules import it directly; `web/src/stores/index.ts` is only the stable public re-export facade.
|
|
50
|
+
- `web/src/views/OverviewView.vue`: page header, Core restart and responsive two-column composition. `OverviewGeneralPane.vue` owns common controls and traffic, `OverviewProxyPane.vue` owns mode-driven groups, and `useProxyLatency()` owns generation-bound latency requests.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 2. Ownership and Serialization
|
|
55
|
+
|
|
56
|
+
### Daemon ownership
|
|
57
|
+
|
|
58
|
+
`sashd` acquires `state/sashd.lock` before reading or migrating persistent state. The lock record contains a random token, PID, purpose and timestamp. A PID file is discovery metadata only; it is never the singleton authority.
|
|
59
|
+
|
|
60
|
+
- `state/sashd-start.lock` serializes concurrent CLI spawn attempts.
|
|
61
|
+
- `state/runtime.lock` serializes top-level `start`, `stop`, `restart` and Core update operations.
|
|
62
|
+
- `state/mutation.lock` separates daemon-owned mutations from offline CLI mutations.
|
|
63
|
+
- `state/settings.lock` prevents concurrent first-run secret generation and settings rewrites.
|
|
64
|
+
- `state/system-proxy.json.lock` serializes proxy journal operations.
|
|
65
|
+
|
|
66
|
+
Lock records are fully written and fsynced before an atomic hard-link publishes them. Synchronous and asynchronous callers share one acquisition decision and differ only in how they wait, so live-owner, dead-owner, corruption and deadline rules cannot drift. A live owner is never displaced. Dead owners can be reclaimed; corrupt records fail closed and require explicit repair. If a lock disappears between metadata inspection and bounded content read it is retried as a missing observation rather than mislabeled corrupt. Durable rename/remove operations retry Windows sharing violations without deleting a caller-owned source; an interrupted executable unlock probe is restored before Core consistency checks, while conflicting target/probe bytes are both preserved for explicit repair.
|
|
67
|
+
|
|
68
|
+
Offline commands reload settings after acquiring `mutation.lock`. Ordinary mutations refuse a live orphan Core or corrupt PID record. Lifecycle and update callers must explicitly request reconciliation, which restores legacy and journaled proxy ownership before terminating only a verified stale Core and then recovering coordinated update state.
|
|
69
|
+
|
|
70
|
+
CLI commands resolve one tagged runtime owner before choosing daemon or offline behavior. Only the healthy branch constructs a daemon client, and both control requests and displayed dashboard/API endpoints use the daemon's observed PID-record port rather than a potentially stale configured port. Confirmed-stopped and unresponsive owners remain distinct so an uncertain live daemon is never treated as offline.
|
|
71
|
+
|
|
72
|
+
### Runtime lifecycle
|
|
73
|
+
|
|
74
|
+
`RuntimeLifecycle` is the only daemon layer that combines Core and system-proxy transitions. Operations enter one promise queue and update a small phase model (`stopped`, `starting`, `running`, `stopping`, `restarting`, `failed`) with a monotonic generation.
|
|
75
|
+
|
|
76
|
+
Invariants:
|
|
77
|
+
|
|
78
|
+
1. A start prepares and Core-validates the exact active config before spawn.
|
|
79
|
+
2. The Core must pass two readiness probes before it is considered healthy.
|
|
80
|
+
3. After readiness, a bounded `/configs` probe records the actual `tun.enable` state; probe failure is represented as unknown instead of being mistaken for inactive.
|
|
81
|
+
4. Desired system proxy is applied only after readiness.
|
|
82
|
+
5. A deliberate stop restores the previous OS proxy before stopping the Core.
|
|
83
|
+
6. If proxy restoration cannot be proved, the healthy Core is left running.
|
|
84
|
+
7. Late child-exit events and delayed cleanup callbacks cannot clear a replacement child.
|
|
85
|
+
8. Controller status probes retain an owned-child generation snapshot and report stopped if that child exits or is replaced while the probe is pending.
|
|
86
|
+
9. System-proxy application verifies the same healthy owned child before and after OS changes; lost ownership immediately triggers proxy release.
|
|
87
|
+
10. Process ownership is revalidated before every graceful or force signal; failed verification or termination preserves PID ownership.
|
|
88
|
+
|
|
89
|
+
Unexpected Core exit retries proxy restoration and records failures in daemon error logs.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 3. API Namespaces
|
|
94
|
+
|
|
95
|
+
There are exactly two HTTP namespaces plus the static dashboard. Every `/sash/*` route is implemented by `sashd` itself; every `/core/api/*` route is reverse-proxied to the Core controller. There are no root-level aliases and no unprefixed duplicates.
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
/sash/* implemented by sashd
|
|
99
|
+
/core/api/* forwarded to the Core controller (HTTP and WebSocket share one rule)
|
|
100
|
+
/ui/* static dashboard assets
|
|
101
|
+
/ 302 redirect to /ui/
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `/sash/*`
|
|
105
|
+
|
|
106
|
+
| Endpoint | Method | Auth | Description |
|
|
107
|
+
| :--- | :--- | :--- | :--- |
|
|
108
|
+
| `/sash/daemon/health` | `GET` | public | Readiness, PID, start time and per-boot WebUI token. |
|
|
109
|
+
| `/sash/daemon/status` | `GET` | public | Daemon/Core/proxy/public-settings snapshot; `core.tunActive` is the verified runtime TUN state when available, while proxy `appliedKnown`/`stateKnown` and `queryError` preserve OS observation uncertainty. |
|
|
110
|
+
| `/sash/daemon/shutdown` | `POST` | control | Under the daemon mutation queue, snapshot whether Core was running, restore proxy/stop Core, return `{coreWasRunning}`, then close. Cleanup failure returns `500` and leaves the daemon available for retry. |
|
|
111
|
+
| `/sash/core/start` | `POST` | control | Rebuild config, start and wait for readiness; returns `{pid, version?, tunActive?}`. |
|
|
112
|
+
| `/sash/core/stop` | `POST` | control | Restore proxy, then stop the child; `204`. |
|
|
113
|
+
| `/sash/core/restart` | `POST` | control | Rebuild config and execute one serialized replacement; same body as start. |
|
|
114
|
+
| `/sash/core/reload` | `POST` | control | Re-render, validate and reload active config; returns `{proxyCount, source}`. |
|
|
115
|
+
| `/sash/proxy` | `GET` | public | Desired, Sash-owned and observed OS proxy state. |
|
|
116
|
+
| `/sash/settings` | `GET` | public | Public settings projection (secrets omitted). |
|
|
117
|
+
| `/sash/settings` | `PATCH` | control | Partial-object update (`SettingsPatch`); one transaction per apply, returns `{restartRequired, settings}`. Enabling `systemProxy` requires a healthy Core. |
|
|
118
|
+
| `/sash/settings/file` | `GET` | control | Raw canonical `sash.json` text, including secrets. |
|
|
119
|
+
| `/sash/settings/file` | `PUT` | control | Replace settings from raw text; parsed, diffed and applied through the same `SettingsService.apply` path. |
|
|
120
|
+
| `/sash/profiles` | `GET` / `POST` | public / control | List profiles; add a remote subscription. |
|
|
121
|
+
| `/sash/profiles/import` | `POST` | control | Import a local profile document. |
|
|
122
|
+
| `/sash/profiles/active` | `PUT` | control | Activate a profile id or `null`. |
|
|
123
|
+
| `/sash/profiles/update-all` | `POST` | control | Update every profile; always `200`, per-profile failures in `failed`. |
|
|
124
|
+
| `/sash/profiles/:id/content` | `GET` / `PUT` | control | Read or replace raw profile YAML. |
|
|
125
|
+
| `/sash/profiles/:id/update` | `POST` | control | Re-fetch one remote profile. |
|
|
126
|
+
| `/sash/profiles/:id` | `PATCH` / `DELETE` | control | Rename or remove one profile. |
|
|
127
|
+
|
|
128
|
+
Appending `?fresh=1` to status/proxy reads bypasses the short OS-state cache used by normal WebUI polling.
|
|
129
|
+
|
|
130
|
+
### Response envelope
|
|
131
|
+
|
|
132
|
+
Success responses return the resource body directly with no `ok` field; mutations with no content return `204`. Error responses always carry `{error: {code, message}}` where `code` is machine-readable: `invalid_input`, `not_found`, `conflict`, `core_unhealthy`, `shutting_down`, `unauthorized`, `http` or `internal`. A `500` never leaks internal error text. `update-all` reports per-profile business failures through its `failed` array at HTTP `200`.
|
|
133
|
+
|
|
134
|
+
The WebUI store has one runtime-refresh path. Normal polling keeps a coherent same-owner Core snapshot until it is missing, degraded or stale for the current profile revision, while explicit post-mutation refreshes force a complete snapshot. Connections, proxies and rules use the same generation-bound fetch/adopt/error template, so responses from an older runtime owner cannot overwrite current state.
|
|
135
|
+
|
|
136
|
+
The Node daemon client and browser WebUI share one browser-safe client built on `src/contracts.ts`: successful bodies are read as `unknown` and passed through per-resource parsers. Required nested fields, positive safe-integer PIDs, valid ports, nonnegative revisions, canonical timestamps, optional Core fields, proxy state and explicit public settings are validated before state changes. Unknown extra fields are tolerated for forward compatibility but discarded from the typed projection. `appliedKnown` and `stateKnown` are mandatory inside the current daemon; only the network parser normalizes flags omitted by a legacy daemon to `false`. A malformed `200` response is therefore an error, not trusted TypeScript data.
|
|
137
|
+
|
|
138
|
+
### `/core/api/*`
|
|
139
|
+
|
|
140
|
+
Every request in this namespace requires the persistent CLI bearer or per-boot WebUI token before an upstream connection is opened. `sashd` strips Sash credentials and the browser Host header, then injects the internal controller bearer.
|
|
141
|
+
|
|
142
|
+
HTTP and WebSocket routing consume one parsed origin-form request target. Absolute-form, authority-form, asterisk-form, network-path and cross-authority backslash targets are rejected with `400`. Route matching removes only trailing route slashes; WHATWG dot-segment normalization happens once, encoded slashes are not decoded again, and the same canonical pathname constructs the Core target. In particular, `/core/api?x=1` forwards as `/?x=1`, repeated namespace-root slashes collapse to `/`, and the query is appended exactly once.
|
|
143
|
+
|
|
144
|
+
Known paths with the wrong method return `405` plus an `Allow` header derived from the route table. Dashboard redirects accept only `GET`/`HEAD` and preserve the root query.
|
|
145
|
+
|
|
146
|
+
Traffic/log streams are authenticated `GET` WebSocket upgrades under `/core/api/traffic` and `/core/api/logs`. HTTP and WebSocket share the same gateway rule: only `/core/api/*` is forwarded, and WebSocket upgrades only accept `GET`.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 4. Control-Request Security
|
|
151
|
+
|
|
152
|
+
- The daemon listener binds only to `127.0.0.1`, rejects non-loopback Host headers, and only accepts loopback Core controller addresses.
|
|
153
|
+
- State-changing methods and every HTTP Core-gateway route require the persistent CLI bearer or per-boot WebUI token. Any request carrying a non-loopback Origin header is rejected outright, regardless of method.
|
|
154
|
+
- WebSocket upgrades validate loopback Origin, authentication and route boundaries.
|
|
155
|
+
- Public settings/status contracts omit controller and daemon secrets.
|
|
156
|
+
- Controller and daemon clients use a direct dispatcher with normal TLS verification; proxy environment variables apply only to remote downloads.
|
|
157
|
+
- Every managed runtime and OS/browser/package helper child removes GitHub/npm tokens, npm credential-file/auth variables and npm registry credentials. Fixed Windows/macOS system tools use trusted absolute paths; Linux desktop helpers are resolved only through absolute PATH entries.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 5. Settings, Profiles and Config Transactions
|
|
162
|
+
|
|
163
|
+
`sash.json` has explicit `schemaVersion: 1`. Loading validates the JSON root, every field type, nonblank control-character-free secrets, port range, loopback controller address, unknown keys and all three listener ports (`mixedPort`, controller and daemon) for collisions. Version-0 files and removed version metadata migrate to canonical v1. Invalid or future-version files are never overwritten.
|
|
164
|
+
|
|
165
|
+
After managed-state recovery, daemon and offline initialization first migrate a nonblank legacy `subscriptionUrl` into an active meta-only profile. That URL has priority over any pre-profile `config.yaml`. Only when `profiles/index.json` does not exist may Sash import `config.yaml` once as the active local `Imported config` profile. A present empty index is an explicit opt-out. The candidate must be bounded, regular, valid core-format YAML and contain non-default routing content after managed operational keys are stripped: nonempty proxies/providers, or nonempty rules/groups that differ from the Sash DIRECT-only default. Exact generated defaults are not imported. Invalid candidates fail initialization without changing `config.yaml`; successful import journals the profile YAML and index under `mutation.lock` while leaving `config.yaml` byte-for-byte in place. Later `ProfileService` preparation re-renders and, when Core is installed, validates the canonical profile-derived candidate before runtime use.
|
|
166
|
+
|
|
167
|
+
`SettingsService` snapshots committed settings, creates an immutable canonical candidate, then fetches/renders/Core-validates active profile configuration outside the mutation lock. Under the short commit boundary it rechecks settings/profile snapshots and journals settings plus generated config before publication. The daemon exposes only `committedSettings` to GET/status/auth handlers; Core spawn/restart can temporarily use `runtimeSettings` while a candidate transition is in progress. The committed in-memory snapshot changes only after the journaled transition succeeds; failure restores disk/config and the old runtime. Online TUN enable is additionally committed only when the restarted Core reports `tun.enable: true`; inactive or unverified results use the same disk/config/runtime compensation path. Inactive TUN errors direct every platform to rerun a full `sash restart` from an elevated shell, which replaces the daemon itself; they preserve the data root explicitly only where it was customized or `sudo` would change the default home. A Core-only restart (for example from the dashboard) cannot elevate `sashd`.
|
|
168
|
+
|
|
169
|
+
Prepared profile work is never exposed as a mutable internal transaction object. `ProfileService` issues WeakMap-backed, one-shot opaque capabilities that reject forgery, cross-instance use and repeated consumption. Settings publication deliberately binds a weak active-source snapshot (`activeId`, profile identity/URL and exact raw YAML digest), so unrelated metadata updates do not invalidate an otherwise safe settings change. A strict active reload instead binds the committed settings, complete active `ProfileMeta`, active selection and raw digest. Both paths prepare outside the mutation boundary and consume the capability only while rechecking and publishing; only a conflict raised before the publication callback is entered receives the single bounded automatic retry.
|
|
170
|
+
|
|
171
|
+
Profile application follows:
|
|
172
|
+
|
|
173
|
+
1. Parse the untrusted source profile.
|
|
174
|
+
2. Overlay Sash-owned operational keys.
|
|
175
|
+
3. Write an isolated candidate.
|
|
176
|
+
4. Run the installed Core with `-t -d <root> -f <candidate>`.
|
|
177
|
+
5. Enter the short profile commit boundary, re-read the bounded regular index/profile files and verify the target profile identity, URL, active selection, managed-settings snapshot and exact raw-profile SHA-256 captured during preparation.
|
|
178
|
+
6. Snapshot the affected fixed roles (`sash.json`, profile YAML, index and/or `config.yaml`), then atomically persist a `publishing` record in `state/managed-state-transaction.json` before publication.
|
|
179
|
+
7. Ordinary settings/profile publication reloads or restarts only after every file is published, marks the journal `committed`, then clears it. The same journal can include the canonical `sash.json` snapshot. Startup finalizes a committed journal without rolling the published state back.
|
|
180
|
+
8. Core update publication uses a version-3 `core-update` coordination record. After profile/index/config files publish, phase `retained` preserves their exact pre-update snapshots until the Core journal has a durable health/restoration outcome. Ordinary mutations cannot consume this retained state.
|
|
181
|
+
9. On any publication or reload failure, restore every snapshot while continuing after individual restore errors, then reload the prior config when one existed. A rollback reload failure is reported explicitly. Incomplete rollback retains the journal; daemon and offline initialization recover an ordinary `publishing` journal under `mutation.lock` before reading or migrating profile state.
|
|
182
|
+
|
|
183
|
+
The daemon parses profile request bodies before its mutation boundary. Remote fetch, YAML parsing, rendering and Core validation also occur before that boundary; only recheck, publication and runtime reload are serialized. Offline commands use the same split boundary after reloading settings, verifying daemon/orphan-Core ownership and migrating the legacy setting. Remote and stored profile YAML are capped at 8 MiB; `profiles/index.json` is capped at 2 MiB, and both must be regular files. Profile requests use an absolute deadline and explicit hop-by-hop redirects: HTTPS cannot downgrade to HTTP, restricted literal addresses cannot cross origins, and public origins cannot redirect to literal private/loopback targets. Scheduled network fetches use bounded concurrency; state commits remain serialized and recheck profile identity/URL and active selection before publication. Scheduler timers are retained when daemon cleanup or listener closure fails, and are cleared only after successful shutdown.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 6. System-Proxy Ownership
|
|
188
|
+
|
|
189
|
+
`settings.systemProxy` stores desired state. `state/system-proxy.json` separately records ownership:
|
|
190
|
+
|
|
191
|
+
```text
|
|
192
|
+
prepared: original snapshot + intended target persisted before OS writes
|
|
193
|
+
applied: target was written and read back exactly
|
|
194
|
+
restoring: restoration began and original/target-compatible partial state remains recoverable
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
All platform capture/apply work runs through asynchronous `execFile` children with the same scrubbed environment, absolute trusted-tool resolution, timeout and output bounds as other managed helpers. Commands remain explicitly sequential: Windows writes PAC/endpoints before `ProxyEnable` and awaits best-effort WinINet refresh; macOS writes data before states and turns unwanted modes off before enabling selected modes; GNOME writes every endpoint before changing `mode`. This keeps the daemon event loop responsive without weakening partial-write ordering.
|
|
198
|
+
|
|
199
|
+
`SystemProxyManager` serializes inspect, apply and release operations in one in-process queue in addition to the cross-process journal lock. Every mutation invalidates a monotonic observation generation when queued and again when complete. Same-generation inspections share one in-flight capture; ordinary polling can reuse a settled short-lived cache, while a fresh read bypasses only that settled cache. Inspection compares strict journal observations before and after OS capture and retries once if another process changes or removes the journal. Unstable observations are never cached. `/sash/health` does not enter this queue, so it remains responsive while a slow OS inspection is pending.
|
|
200
|
+
|
|
201
|
+
Enable flow:
|
|
202
|
+
|
|
203
|
+
1. Capture all fields Sash will modify.
|
|
204
|
+
2. Derive the target and persist `prepared` atomically.
|
|
205
|
+
3. Re-read the OS state to detect changes during preparation.
|
|
206
|
+
4. Apply and verify the target.
|
|
207
|
+
5. Mark the journal `applied`.
|
|
208
|
+
|
|
209
|
+
Release/recovery restores only when every managed value still equals either the original or Sash target. Before multi-field restoration Sash persists the `restoring` phase, so a crash can continue from an original/target-compatible partial state. A third-party value is never overwritten. Failed restoration retains the journal and blocks deliberate Core shutdown.
|
|
210
|
+
|
|
211
|
+
Platform scope:
|
|
212
|
+
|
|
213
|
+
- Windows: manual proxy, bypass list and PAC URL are managed; the legacy flat automatic-detection value is observed for status but intentionally neither written nor verified.
|
|
214
|
+
- macOS: HTTP, HTTPS, SOCKS and automatic proxy URL/state for every active network service. Existing authenticated proxy settings are not taken over because credentials cannot be restored safely.
|
|
215
|
+
- Linux: GNOME `gsettings` mode, automatic URL, HTTP authentication toggle and HTTP/HTTPS/SOCKS endpoints. Other desktop environments are reported unsupported.
|
|
216
|
+
|
|
217
|
+
A missing journal never authorizes Sash to disable an unrelated system proxy.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 7. Core Download and Update Trust
|
|
222
|
+
|
|
223
|
+
Release identity and asset metadata come only from official GitHub endpoints. Asset mirrors are byte transports, not trust roots.
|
|
224
|
+
|
|
225
|
+
- Release tags are restricted to safe path tokens.
|
|
226
|
+
- Every initial/redirect URL is HTTPS and host-allowlisted.
|
|
227
|
+
- Downloads are streamed with a 128 MiB archive cap and backpressure.
|
|
228
|
+
- The complete archive must match the SHA-256 digest published in GitHub release metadata.
|
|
229
|
+
- Extraction has a 512 MiB output cap; ZIP deflate is streamed rather than fully inflated in memory.
|
|
230
|
+
- The staged executable must report the exact requested release token via `-v`.
|
|
231
|
+
- The staged executable validates the freshly generated active configuration before publication.
|
|
232
|
+
|
|
233
|
+
First install publishes through `state/core-install-transaction.json`: after staging has passed digest/version checks, Sash records an empty pre-install binary/metadata snapshot, publishes the executable, publishes `state/install.json`, marks the transaction `committed`, then clears it. Startup/offline recovery removes binary and metadata for an interrupted `publishing` transaction, while a `committed` marker is only cleared. Transaction JSON uses a strict fixed schema with no stored paths.
|
|
234
|
+
|
|
235
|
+
Update flow downloads and validates outside runtime ownership. Once staging completes, it acquires `state/runtime.lock` before requesting the atomic maintenance shutdown snapshot and keeps that ownership through offline publication and runtime restoration. Under `mutation.lock`, Sash reloads committed settings, performs legacy/journaled proxy cleanup and verified stale-Core cleanup once, recovers earlier update state, prepares the active profile outside the short commit boundary, then rechecks both the profile capability and controller vacancy before publication.
|
|
236
|
+
|
|
237
|
+
The final mutation first writes a retained managed-state journal for profile/index/config snapshots, then starts `state/core-update-transaction.json`. Normal updates use the version-1 `prepared`, `swapped` and `health-verified` phases. A previously running Core is verified immediately; a stopped Core remains in `swapped` with the old install record and `.bak` until its next managed start proves the target controller version and health. Successful external restoration marks managed state committed before removing the Core rollback slot, so a crash cannot lose both rollback directions. Failure restores managed state before binary/install metadata and still attempts both sides if either rollback reports an error.
|
|
238
|
+
|
|
239
|
+
Forced repair uses the version-2 `repair-prepared` and `repair-restoring` phases in addition to the normal swap/health phases. Malformed executable and install-metadata entries are moved to fixed `.repair.bak` quarantine paths only after the journal is durable. Partial quarantine and partial restoration are resumable. Missing, extra or unowned quarantine entries fail closed. Daemon startup and offline commands use the same proxy, stale-Core and coordinated-journal recovery order before ordinary consistency checks.
|
|
240
|
+
|
|
241
|
+
Official digest metadata is mandatory. If the metadata API is unavailable, Sash refuses an unverifiable mirror download instead of falling back to executing unverified bytes.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 8. Persistence Safety
|
|
246
|
+
|
|
247
|
+
Atomic state writes use a same-directory temporary file, file `fsync`, rename and POSIX parent-directory `fsync`. Sensitive files use mode `0o600` on POSIX. Important files are:
|
|
248
|
+
|
|
249
|
+
- `sash.json`: versioned desired settings and local secrets.
|
|
250
|
+
- `profiles/index.json`, `profiles/<id>.yaml`: profile metadata/content.
|
|
251
|
+
- `config.yaml`: runtime config rendered from the active profile or the DIRECT-only default; a qualifying pre-profile file is preserved during its one-time local-profile import.
|
|
252
|
+
- `state/sash.pid`, `state/sashd.pid`: discovery records.
|
|
253
|
+
- `state/system-proxy.json`: durable proxy ownership transaction.
|
|
254
|
+
- `state/managed-state-transaction.json`: recoverable settings/profile/index/config snapshots, including retained Core-update coordination state.
|
|
255
|
+
- `state/install.json`: committed Core version metadata using the shared fixed-shape install-record codec.
|
|
256
|
+
- `state/core-install-transaction.json`: strict first-install publication journal.
|
|
257
|
+
- `state/core-update-transaction.json`: strict version-1 update journal or version-2 force-repair quarantine journal, including deferred managed-start rollback ownership.
|
|
258
|
+
- `state/*.lock`: local-filesystem ownership records.
|
|
259
|
+
|
|
260
|
+
`SASH_HOME` should reside on a local filesystem that supports atomic rename, hard links and normal per-user permissions.
|
package/docs/frontend.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Frontend Architecture & WebUI Design
|
|
2
|
+
|
|
3
|
+
The WebUI is a Vue 3 application built with Vite and bundled into `dist/ui/`. sashd serves it at `http://127.0.0.1:19090/ui/`; `<root>/ui/index.html` can override the bundled dashboard.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Build and Quality Gates
|
|
8
|
+
|
|
9
|
+
- `npm run build:ui` calls the Vite Node API through `scripts/build-ui.mjs`.
|
|
10
|
+
- `npm run typecheck:web` runs `vue-tsc`, including Vue templates.
|
|
11
|
+
- `npm test` runs server type checking, WebUI type checking, backend tests and WebUI TypeScript tests.
|
|
12
|
+
- Biome checks WebUI TypeScript and Vite configuration. Vue templates are type-checked by `vue-tsc` and compiled by Vite.
|
|
13
|
+
- Vite 6 builds the bundled dashboard on the declared Node.js 24 baseline.
|
|
14
|
+
- CI runs lint, server/WebUI type checks, all tests, production builds and actual tarball pack/install/CLI/UI smoke on Windows, macOS and Linux with Node.js 24.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 2. Source Layout
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
web/src/
|
|
22
|
+
├── api/index.ts typed REST client and WebSocket reconnect logic
|
|
23
|
+
├── components/
|
|
24
|
+
│ ├── AppSidebar.vue
|
|
25
|
+
│ ├── ConfirmDialog.vue
|
|
26
|
+
│ ├── Icon.vue semantic icon wrapper
|
|
27
|
+
│ ├── icons.ts tree-shaken Remix Icon component mapping
|
|
28
|
+
│ ├── ProxyGroupSection.vue reusable group header/node grid
|
|
29
|
+
│ ├── TrafficChart.vue
|
|
30
|
+
│ └── ...
|
|
31
|
+
├── i18n/ Chinese source messages and typed English mirror
|
|
32
|
+
├── stores/index.ts runtime state, refresh actions and polling
|
|
33
|
+
├── stores/state-ownership.ts pure generations, cleanup and sync helpers
|
|
34
|
+
├── styles/main.css design tokens and shared utility/component styles
|
|
35
|
+
├── types/index.ts core-controller response types; daemon types are shared
|
|
36
|
+
├── views/
|
|
37
|
+
│ ├── OverviewView.vue status, traffic, modes and proxy groups
|
|
38
|
+
│ ├── ProfilesView.vue download/import/update/select/delete profiles
|
|
39
|
+
│ ├── LogsView.vue
|
|
40
|
+
│ ├── ConnectionsView.vue
|
|
41
|
+
│ ├── RulesView.vue
|
|
42
|
+
│ └── SettingsView.vue
|
|
43
|
+
├── App.vue shell, view selection and stream lifecycle
|
|
44
|
+
├── theme.ts persisted system/light/dark theme state
|
|
45
|
+
└── router.ts small hash router
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The shell follows a classic compact desktop-console layout: a 25px title strip sits above a 170px text-navigation sidebar with live traffic, recessed active-item geometry, runtime and Core status. Below 900px it becomes a compact title strip plus safe-area-aware bottom navigation. Light and Dark themes use flat neutral surfaces, dense rows, restrained shadows and distinct mode/semantic state colors. Profiles use a full-width download toolbar and compact profile grid; Logs use an edge-to-edge stream; Connections use colored metadata tags; Settings use labeled flat sections. Overview intentionally remains a roughly one-third/two-thirds combined General + Proxies workspace: common runtime controls and the GLOBAL/RULE/DIRECT selector stay on the left while the mode-driven proxy groups or DIRECT state render on the right. `styles/main.css` owns theme tokens, component states and reduced-motion behavior without a runtime-loaded UI dependency. Functional icons use selected, tree-shaken `@remixicon/vue` line components behind the local semantic `Icon.vue` API; the Sash brand mark remains project-owned. `THIRD_PARTY_NOTICES.md` records the bundled Vue MIT and Remix Icon notices, and the complete Remix Icon License v1.0 remains in `docs/remix-icon-license.txt`; both are included in the npm tarball. Long connection and rule sets remain paginated and switch to narrow-screen layouts.
|
|
49
|
+
|
|
50
|
+
Daemon/profile contracts live in `src/contracts.ts` and are imported as types by both `src/daemon-client.ts` and the WebUI. This prevents manually duplicated `SashStatus` and profile response shapes.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 3. Runtime State Ownership
|
|
55
|
+
|
|
56
|
+
`web/src/stores/index.ts` owns status, profiles, proxy groups, rules, connections, traffic history, manual delay results, logs, operation flags and toasts. Pure ownership/generation helpers live in `web/src/stores/state-ownership.ts` and are covered by browser-free `node:test` tests. A pinned `happy-dom` dev dependency supplies the minimal DOM behavior harness for mounting a real Vue `createApp` render path and asserting reactive notice transitions; no second test runner or coverage-only gate is introduced.
|
|
57
|
+
|
|
58
|
+
Canonical actions include:
|
|
59
|
+
|
|
60
|
+
- `refreshStatus()`
|
|
61
|
+
- `refreshConnections()`
|
|
62
|
+
- `refreshProxies()`
|
|
63
|
+
- `refreshProfiles()`
|
|
64
|
+
- `refreshRuntimeState()`
|
|
65
|
+
- profile mutation actions such as `updateProfile()` and `activateProfile()`
|
|
66
|
+
- runtime intent actions such as `setOutboundMode()` and `selectGroupProxy()`
|
|
67
|
+
- `startRuntimePolling()`
|
|
68
|
+
|
|
69
|
+
Polling is self-scheduling with `setTimeout` after the previous cycle completes. It slows to a 15-second interval while the page is hidden and refreshes immediately after returning to the foreground. Domain request generations discard responses made stale by a newer refresh or user mutation. Core-specific API calls are made only after status reports `running && healthy`.
|
|
70
|
+
|
|
71
|
+
Daemon reachability, profile revision and Core snapshots have separate ownership. A successful `/sash/status` keeps the daemon online even when a downstream Core gateway request returns 502. Profiles track their last fetched daemon revision independently and refresh on revision changes even while Core is stopped. A daemon restart resets that revision comparison.
|
|
72
|
+
|
|
73
|
+
The Core owner is the daemon boot plus Core PID/start time. A stopped/unhealthy Core or unreachable daemon clears proxy groups, rules, connections/totals and traffic rates/history. A same-owner Core API failure preserves the last complete configs/proxies/rules/connections snapshot, marks it degraded and retries; a changed owner clears the old snapshot before fetching, so failed replacement data cannot be shown under the new owner. Profile revision changes request a new snapshot without prematurely discarding same-owner data.
|
|
74
|
+
|
|
75
|
+
Manual latency results are stored separately from polled proxy payloads. Normal proxy polling therefore preserves a test result; a successful profile snapshot generation or Core owner change invalidates stale delay results.
|
|
76
|
+
|
|
77
|
+
Views call store actions rather than maintaining separate copies of status/proxy/rule refresh orchestration. Same-domain controls use store operation flags, while independent group/node latency tests remain concurrent.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 4. Profiles and Overview Components
|
|
82
|
+
|
|
83
|
+
The Profiles page provides:
|
|
84
|
+
|
|
85
|
+
- URL download and clipboard paste
|
|
86
|
+
- local YAML import
|
|
87
|
+
- Update All and per-profile update
|
|
88
|
+
- active profile selection
|
|
89
|
+
- quota/expiry display from provider metadata
|
|
90
|
+
- persisted update errors
|
|
91
|
+
- delete confirmation
|
|
92
|
+
|
|
93
|
+
The Overview proxy panel uses `ProxyGroupSection.vue` for manual, automatic and GLOBAL groups. This keeps node-card rendering, delay labels, UDP/current markers and group test controls in one component. Group and node latency tests track independent in-flight sets, so tests for different groups/nodes can run concurrently. Mode and proxy-selection mutations are latest-request-wins and disable only their matching control domain.
|
|
94
|
+
|
|
95
|
+
System proxy controls are target-state based: enabling requires a running, healthy Core; disabling remains available when desired/applied/OS-observed state indicates cleanup may still be required, including while the Core is stopped.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 5. API and Streaming
|
|
100
|
+
|
|
101
|
+
`web/src/api/index.ts`:
|
|
102
|
+
|
|
103
|
+
- distinguishes JSON and `204 No Content` endpoints through overloads rather than `undefined as T`;
|
|
104
|
+
- reads an error response body once and surfaces JSON or plain-text messages;
|
|
105
|
+
- parses WebSocket frames separately from consumer callbacks, validates finite non-negative traffic counters and known textual log records, and silently drops malformed frames;
|
|
106
|
+
- maintains at most one reconnect timer per stream;
|
|
107
|
+
- binds stream callbacks to the current runtime generation and stops stream ownership when the daemon session or healthy Core is unavailable;
|
|
108
|
+
- resets stale traffic rates/history when the current traffic stream disconnects without allowing an older socket's close event to clear a replacement stream;
|
|
109
|
+
- sends `X-Sash-Token` on HTTP requests after initialization.
|
|
110
|
+
|
|
111
|
+
Streams:
|
|
112
|
+
|
|
113
|
+
- `api.connectTraffic(callback)` → `/core/api/traffic`
|
|
114
|
+
- `api.connectLogs(callback)` → `/core/api/logs`
|
|
115
|
+
|
|
116
|
+
The controller secret is never available to browser code; sashd injects it server-side.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 6. Interaction State
|
|
121
|
+
|
|
122
|
+
- Settings derive mixed-port dirty state by comparing the draft with the last committed value, preserve a genuinely edited draft across polling, and expose an explicit reset action. Successful `allow-lan` and TUN responses commit the returned settings snapshot directly before any follow-up refresh. TUN controls separately derive active, inactive, unverified, pending-start and desired/runtime-mismatch presentation from `settings.tun` plus `core.tunActive`; failed online activation never advances the committed switch, and the returned error explains that the whole Sash daemon—not only its Core child—must be restarted with elevated privileges.
|
|
123
|
+
- Logs receive monotonic IDs before entering the capped 600-row buffer, providing stable Vue keys and an update sequence even when length remains constant.
|
|
124
|
+
- The global confirm service settles a previous pending Promise before opening another dialog; Escape, route changes and component unmount cancel the active confirmation.
|
|
125
|
+
- The global banner distinguishes an unreachable daemon from a degraded same-owner Core snapshot and an unavailable new-owner snapshot.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
Remix Icon License v1.0
|
|
2
|
+
Version 1.0 – January 2026
|
|
3
|
+
Copyright (c) 2017–2026 Remix Design
|
|
4
|
+
|
|
5
|
+
This License governs the use of the Remix Icon library, including all icons,
|
|
6
|
+
SVG files, font files, and related assets (collectively, the "Icons").
|
|
7
|
+
|
|
8
|
+
1. Grant of License
|
|
9
|
+
|
|
10
|
+
Subject to the terms and conditions of this License, Remix Design grants you
|
|
11
|
+
a worldwide, royalty-free, non-exclusive license to use, copy, modify,
|
|
12
|
+
merge, and distribute the Icons, as permitted below.
|
|
13
|
+
|
|
14
|
+
2. Permitted Uses
|
|
15
|
+
|
|
16
|
+
You are permitted to:
|
|
17
|
+
|
|
18
|
+
2.1 Commercial and Non-Commercial Use
|
|
19
|
+
Use the Icons in personal, commercial, and client projects, including
|
|
20
|
+
but not limited to:
|
|
21
|
+
- Websites, web applications, and mobile applications
|
|
22
|
+
- Software products and SaaS platforms
|
|
23
|
+
- User interface designs, design systems, and UI kits
|
|
24
|
+
- Presentations, documentation, and marketing materials
|
|
25
|
+
- Physical or digital products where the Icons are decorative or
|
|
26
|
+
functional elements
|
|
27
|
+
|
|
28
|
+
2.2 Modification and Derivative Works
|
|
29
|
+
- Modify, adapt, or customize the Icons
|
|
30
|
+
- Change colors, sizes, stroke styles, or other visual attributes
|
|
31
|
+
to fit your project needs
|
|
32
|
+
|
|
33
|
+
2.3 Distribution as Part of a Larger Work
|
|
34
|
+
Include the Icons (modified or unmodified) as part of a larger product,
|
|
35
|
+
provided that:
|
|
36
|
+
- The Icons are functional or decorative components of the product, and
|
|
37
|
+
- The Icons are not marketed or sold as the primary value of the product
|
|
38
|
+
|
|
39
|
+
2.4 Attribution (Optional)
|
|
40
|
+
Attribution to Remix Icon is appreciated but not required.
|
|
41
|
+
Suggested attribution:
|
|
42
|
+
"Icons by Remix Icon (https://remixicon.com)"
|
|
43
|
+
|
|
44
|
+
3. Restrictions
|
|
45
|
+
|
|
46
|
+
3.1 Prohibited: Standalone Icon Sales
|
|
47
|
+
You may NOT:
|
|
48
|
+
- Sell, sublicense, or distribute the Icons as a standalone product
|
|
49
|
+
- Package the Icons as an independent icon pack, icon library, or
|
|
50
|
+
icon font for sale
|
|
51
|
+
- Offer the Icons as a downloadable asset collection where the Icons
|
|
52
|
+
themselves constitute the primary product value
|
|
53
|
+
|
|
54
|
+
Clarification:
|
|
55
|
+
A product is considered a prohibited standalone icon sale if:
|
|
56
|
+
- The Icons are the primary advertised feature, or
|
|
57
|
+
- Removing the Icons would eliminate the core value of the product
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
- Prohibited: "Premium Icon Pack – 2,000+ Icons for $29"
|
|
61
|
+
- Prohibited: "Extended Remix Icon Library (Paid)"
|
|
62
|
+
- Permitted: Website or app templates that include Icons as UI elements
|
|
63
|
+
- Permitted: Design systems or UI kits where Icons are a minor component
|
|
64
|
+
|
|
65
|
+
3.2 Prohibited: Competing Icon Libraries
|
|
66
|
+
You may NOT use the Icons to create, distribute, or sell a competing
|
|
67
|
+
icon library or icon set, even if the Icons are modified with only
|
|
68
|
+
minor or superficial changes (such as color, size, stroke width,
|
|
69
|
+
corner radius, or layout).
|
|
70
|
+
|
|
71
|
+
3.3 Prohibited: Logo, Trademark, and Brand Identity Use
|
|
72
|
+
You may NOT use the Icons, whether modified or unmodified, as:
|
|
73
|
+
- A logo, trademark, or brand identifier
|
|
74
|
+
- The primary visual identity of a company, product, or service
|
|
75
|
+
|
|
76
|
+
This includes, but is not limited to:
|
|
77
|
+
- Creating a logo by applying simple modifications such as color changes,
|
|
78
|
+
rotation, combination, or layout
|
|
79
|
+
- Using an Icon as a brand mark, app icon, or identity symbol where the
|
|
80
|
+
Icon itself represents the brand
|
|
81
|
+
|
|
82
|
+
Rationale:
|
|
83
|
+
Remix Icon is designed for functional and informational use. Using the
|
|
84
|
+
Icons as brand identifiers may impair their recognizability and neutrality
|
|
85
|
+
in other functional contexts.
|
|
86
|
+
|
|
87
|
+
4. Brand Icons and Trademarks
|
|
88
|
+
|
|
89
|
+
Some Icons represent third-party brands or services.
|
|
90
|
+
|
|
91
|
+
4.1 Permitted Use of Brand Icons
|
|
92
|
+
You may use brand icons solely to represent or link to the corresponding
|
|
93
|
+
brand, product, or service.
|
|
94
|
+
Examples:
|
|
95
|
+
- Using a GitHub icon to link to a GitHub repository
|
|
96
|
+
- Displaying social media icons in a website footer
|
|
97
|
+
|
|
98
|
+
4.2 Prohibited Use of Brand Icons
|
|
99
|
+
You may NOT:
|
|
100
|
+
- Use brand icons as your own logo or trademark
|
|
101
|
+
- Modify brand icons to create a new brand identity
|
|
102
|
+
- Use brand icons in a way that implies endorsement or affiliation
|
|
103
|
+
by the brand owner
|
|
104
|
+
- Use brand icons for purposes unrelated to the represented brand
|
|
105
|
+
|
|
106
|
+
4.3 Trademark Responsibility
|
|
107
|
+
All brand icons are subject to the trademark rights of their respective
|
|
108
|
+
owners. This License does not grant any trademark rights.
|
|
109
|
+
Users are responsible for ensuring that their use of brand icons complies
|
|
110
|
+
with applicable trademark laws.
|
|
111
|
+
|
|
112
|
+
5. Copyright and License Notice
|
|
113
|
+
|
|
114
|
+
When distributing the complete Icon library or substantial portions thereof:
|
|
115
|
+
|
|
116
|
+
- You must retain existing copyright notices in source files
|
|
117
|
+
- You should include a copy of this License or a link to it
|
|
118
|
+
|
|
119
|
+
For individual use of Icons within projects (e.g., compiled apps, websites,
|
|
120
|
+
presentations), no copyright notice is required.
|
|
121
|
+
|
|
122
|
+
6. No Trademark Rights
|
|
123
|
+
|
|
124
|
+
This License does not grant permission to use:
|
|
125
|
+
|
|
126
|
+
- The "Remix Icon" name, logo, or trademarks
|
|
127
|
+
- Any trademarks represented by brand icons
|
|
128
|
+
|
|
129
|
+
You may refer to Remix Icon by name solely for attribution purposes.
|
|
130
|
+
|
|
131
|
+
7. Disclaimer of Warranty
|
|
132
|
+
|
|
133
|
+
THE ICONS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
134
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY,
|
|
135
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
|
|
136
|
+
|
|
137
|
+
8. Limitation of Liability
|
|
138
|
+
|
|
139
|
+
IN NO EVENT SHALL REMIX DESIGN BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
|
|
140
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
|
|
141
|
+
FROM, OUT OF, OR IN CONNECTION WITH THE ICONS OR THEIR USE.
|
|
142
|
+
|
|
143
|
+
9. License Compatibility
|
|
144
|
+
|
|
145
|
+
You may integrate the Icons into projects under permissive licenses
|
|
146
|
+
(such as MIT, BSD, or Apache 2.0), provided that:
|
|
147
|
+
|
|
148
|
+
- The Icons themselves remain governed by this License
|
|
149
|
+
- The restrictions in Section 3 continue to apply
|
|
150
|
+
|
|
151
|
+
This License may be incompatible with licenses that require unrestricted
|
|
152
|
+
redistribution of assets as standalone products or mandatory application
|
|
153
|
+
of strong copyleft or share-alike terms.
|
|
154
|
+
|
|
155
|
+
10. Termination
|
|
156
|
+
|
|
157
|
+
Your rights under this License terminate automatically if you breach its
|
|
158
|
+
terms.
|
|
159
|
+
|
|
160
|
+
Upon termination:
|
|
161
|
+
|
|
162
|
+
- You must cease any distribution that violates this License
|
|
163
|
+
- You may continue to use the Icons in projects already distributed
|
|
164
|
+
prior to termination
|
|
165
|
+
- End users who received such projects are not affected
|
|
166
|
+
|
|
167
|
+
11. License Versioning
|
|
168
|
+
|
|
169
|
+
Remix Design may publish revised versions of this License.
|
|
170
|
+
|
|
171
|
+
You may choose to use the Icons under:
|
|
172
|
+
|
|
173
|
+
- The version of this License under which you received them, or
|
|
174
|
+
- Any later version published by Remix Design
|
|
175
|
+
|
|
176
|
+
Once chosen, that version may be used indefinitely.
|
|
177
|
+
|
|
178
|
+
How to Apply This License
|
|
179
|
+
|
|
180
|
+
Include the following notice in your documentation when appropriate:
|
|
181
|
+
|
|
182
|
+
"This project uses icons from Remix Icon (https://remixicon.com),
|
|
183
|
+
licensed under the Remix Icon License v1.0."
|
|
184
|
+
|
|
185
|
+
When distributing the complete Icon library or substantial portions of it,
|
|
186
|
+
include a copy of this License.
|