[简体中文](./README.md) | English # carton [![Telegram Group](https://img.shields.io/badge/Telegram-Group-26A5E4?style=for-the-badge&logo=telegram&logoColor=white)](https://t.me/+fwutL7igOTk3ZmFl) `carton` is a desktop client powered by `sing-box`. It aims to stay close to the official SFM experience in interaction flow and information layout, while putting more weight on performance, responsiveness, and a few practical enhancements. The project currently targets `Windows` and `Linux`. There are no plans to publish a `macOS` version, because SFM already exists on macOS. Current focus: - Keep the experience close to official SFM to reduce migration cost - Prioritize UI responsiveness, startup speed, and long-running resource usage - Start `sing-box` with your own config and rules, only taking over a small set of toggle-style options - Add useful enhancements without disrupting the main workflow - Use a non-Electron / non-Tauri / non-web-tech desktop stack with lower memory usage and higher performance ## Config Override Behavior `carton` does not directly overwrite your entire `sing-box` config at startup. It builds a runtime config on top of your original file and only changes a small set of fields that are directly tied to desktop-side toggles. This is intentional because many users strongly dislike third-party GUIs overwriting carefully prepared configs and rules. `carton` tries to touch as little of your hand-written or subscription-generated content as possible. - `log`: only updates `log.level`; if the original config has no `log` object, `carton` adds a minimal one - `inbounds`: only touches `mixed` and `tun` inbounds - For an existing `mixed` inbound, it only updates `listen`, `listen_port`, and `set_system_proxy`; other fields stay as-is - For an existing `tun` inbound, it only updates `address`, `auto_route`, `strict_route`, and `route_exclude_address`; any extra fields you already configured are preserved - If the config does not already contain the corresponding `mixed` or `tun` inbound, `carton` adds it at runtime; if `tun` is turned off, the corresponding `tun` inbound is removed > `carton` is not an official SFM client and is not affiliated with the sing-box team. ## Screenshots | Dashboard | Groups | | --- | --- | | ![Dashboard](./docs/imgs/dashboard.png) | ![Groups](./docs/imgs/group.png) | | Connections | Profiles | | ![Connections](./docs/imgs/connection.png) | ![Profiles](./docs/imgs/profile.png) | ## Highlights ### Main workflow close to official SFM - Six core pages: Dashboard, Groups, Profiles, Connections, Logs, and Settings - Common actions such as start, stop, status check, and group switching are kept in the main workflow - Built-in Clash API / WebUI entry to match existing usage habits ### Performance-oriented - Built with `Avalonia` and `.NET 10` - Non-Electron, non-Tauri, and non-web-tech desktop framework approach - One direct motivation is that many real-world apps built on those stacks can easily land at `200MB+` memory usage after startup - Includes `NativeAOT` publish scripts for faster startup and lower runtime overhead - Uses on-demand page loading and background page release/refresh control to reduce long-running resource usage ### Config and subscription management - Create, import, and edit local configs - Import remote subscriptions with manual update and auto-update intervals - Save per-profile runtime options before startup - If you do not have a `sing-box` subscription URL, you can use [`sublink-worker`](https://github.com/7Sageer/sublink-worker); it provides the online tool [`app.sublink.works`](https://app.sublink.works) to convert various subscription formats or protocol links into `sing-box` configs. If you are using an airport subscription provider, set `User Agent` to `clash` or `xray` ### Node and group enhancements - Read and display outbound groups - Support node switching, latency testing, and URLTest refresh - View and switch groups directly from the tray menu - Optionally disconnect affected connections after node switching ### Practical extras - System proxy toggle - Runtime options for TUN, listen port, LAN access, and log level - Real-time traffic, memory usage, session duration, connections, and logs - sing-box kernel download, update, custom kernel installation, and kernel switching - App update channels, backup export/import, and portable data directory switching - Chinese and English UI with theme settings ## Tech Stack - `Avalonia UI` - `.NET 10` - `CommunityToolkit.Mvvm` - `sing-box` - `Velopack` ## Development and Build ### Platforms - `Windows` - `Linux` ### Requirements - `.NET 10 SDK` - Windows NativeAOT publishing requires `Desktop development with C++` or an equivalent MSVC / Windows SDK toolchain - If you want to generate the Windows installer, `NSIS` is also required and `makensis` must be available; GitHub Actions installs NSIS automatically ### Local build ```powershell dotnet build carton.slnx ``` ### Development run ```powershell dotnet run --project src\carton.GUI\carton.GUI.csproj ``` ### Windows NativeAOT publish ```powershell scripts\test-publish-win-aot.bat win-x64 Release ``` Or use the packaging script that also creates the installer: ```powershell scripts\build-release-win-x64.bat ``` In practice: - `scripts\test-publish-win-aot.bat` performs the NativeAOT publish only - `scripts\build-release-win-x64.bat` runs `scripts\build-release-win-x64.ps1` and additionally creates the portable archive and NSIS installer ### Linux NativeAOT publish ```bash ./scripts/test-publish-linux-aot.sh linux-x64 Release ``` This script writes output to `artifacts/publish/`. The repository already contains multiple runtime targets, while the current ready-to-use scripts are mainly organized around the Windows AOT build flow. ## Positioning If you care about: - an experience that stays close to official SFM - a more performance-oriented implementation - a few practical additions beyond the official client without turning the app into something else then `carton` is being built in that direction. ## License This project is released under the GNU General Public License v3.0. See [LICENSE](./LICENSE) for details.