Skip to content

literouter v0.5.0

Choose a tag to compare

@github-actions github-actions released this 25 Oct 07:48
· 16 commits to main since this release

v0.5.0 — The Desktop App

Highlights

  • literouter is now an application you install and double-click. It starts the gateway, opens the web console in your browser, and lives in the system tray. No window of its own — the console is the interface. Tray menu: Show opens the console, Exit stops the gateway.
  • The gateway runs inside the app's process. literouter.core is a library, so the app links it and calls start() / stop() directly instead of spawning literouter serve as a child. That is what makes one implementation instead of three: a supervised child would need posix_spawn/CreateProcessW, kill/GenerateConsoleCtrlEvent, waitpid/WaitForSingleObject and a pipe on every platform.
  • A gateway that is already running is adopted, not fought over. If you have literouter serve going, the app opens its console, keeps the icon, and leaves that gateway alone on exit — it did not start it. Detection asks the instance (GET /__literouter/status), so port: 0 works: only the instance knows what the kernel gave it.
  • A macOS .app bundle. Unzip, drag to Applications, double-click. Gets its own Dock icon and a Gatekeeper identity that can be granted per app. ditto -c -k --sequesterRsrc packs it, because a plain zip -r of a .app is a known way to ship a bundle that will not launch.
  • A Windows installer. Installs to Program Files, adds Start-menu entries, uninstalls from Apps & features. It does not edit PATH unless asked, does not add firewall rules, and does not register a service.
  • Autostart is opt-in and user-scoped. Nothing is written on first launch. literouter-tray --autostart adds a login entry in a location you can delete by hand without elevation — XDG autostart, ~/Library/LaunchAgents, or the Windows Startup folder. Never a system service.

Fixed

  • Streamed answers were paced by Nagle. Neither the server socket nor the upstream socket set TCP_NODELAY, which is httplib's default. Measured on loopback against a relay answering in 0.1 ms: a buffered request took 42 ms instead of 0.7 ms, and a streamed answer 42.15 ms instead of 0.90 ms. Both legs now set it. Group 4c guards the upstream leg against the burst-write relay that actually reproduces it — the existing Stream stub pauses 5 ms per chunk, which is longer than the delayed-ACK timer, so it passed even with the bug present.
  • TLS certificate paths were passed to OpenSSL as UTF-8 bytes. The config stores UTF-8; OpenSSL's PEM loaders decode through the ANSI code page on Windows, so a certificate under a directory named in Chinese failed at bind time with a message that named neither the path nor the reason. pathFromUtf8 and pathForCApi make the two conversions explicit — and they are not interchangeable, because pathToUtf8 produces exactly the bytes those APIs cannot read.
  • stdout was left in the CRT's text mode on Windows, turning every \n in --json output into \r\n. A CR inside a JSON string value is either invalid JSON or a silently different value, and the consumers are jq, CI, and the Docker healthcheck.

Changed

  • The raw-socket test groups run on Windows. Groups 12 (hanging relay), 23 (request deadline) and 20 (connection reuse) were wrapped in #ifndef _WIN32 because their stubs speak raw sockets. Those are the failover and pooling paths — the parts with the most to go wrong — so the platform with the least coverage was the one where a failover bug would be hardest to find. core/tests/lr_test_socket.h provides the same vocabulary with Winsock behind it.
  • tray is a workspace member. It has to be: core pins its dependencies with workspace = true, which mcpp only resolves inside the workspace that declares them. The cost is that a full workspace build now pulls eui-neo's graphics closure; mcpp build -p core -p cli still builds only the headless pair.

Upgrade Notes

  • The configuration schema is unchanged and needs no migration. Existing config.json files load and behave exactly as before.
  • If you use the command-line gateway, nothing about it changed. The install scripts still install literouter and still take --service / -Service for a headless service.
  • The desktop app and the CLI are independent: installing one does not touch the other, and both read the same config.
  • New artifacts this release adds: literouter-<version>-macos-arm64-app.zip, literouter-<version>-windows-x86_64-setup.exe, and literouter-tray inside the Linux tarball.
  • macOS Intel is still not published; install.sh refuses it with a pointer to Rosetta or building from source.

Known Limits

  • The tray menu has two entries. EUI-NEO hard-codes Show / separator / Exit in all three of its backends and exposes only two booleans. Starting and stopping follow the process lifetime instead, and autostart is a command-line flag rather than a menu item.
  • Show cannot report failure. EUI-NEO's openUrl runs xdg-open … & through std::system(), so it returns success as soon as the fork happens, whether or not a browser appears. The app calls it and does not pretend the result means anything.
  • The Windows and macOS packaging steps have not run on this machine. ISCC is Windows-only and ditto/codesign are macOS-only; they were verified structurally (the plist parses, the .iss has every required section and its staged path rewriting resolves) and execute for the first time in this release workflow.

Installation

Desktop app — download from the GitHub Release:

Platform File
macOS (Apple Silicon) literouter-0.5.0-macos-arm64-app.zip
Windows (x86_64) literouter-0.5.0-windows-x86_64-setup.exe
Linux (x86_64) literouter-0.5.0-linux-x86_64.tar.gz (run the literouter-tray inside)

Command-line gateway — unchanged:

curl -fsSL https://raw.githubusercontent.com/YangQi0408/literouter/main/scripts/install.sh | bash

Then open http://127.0.0.1:8787/ui/.