Split the proxy core into a shared library, add a Linux CLI - #9
Merged
Merged
Conversation
Splits Package.swift into ProxyLightCore (portable), the existing macOS app target, and a new proxylight-cli target, extracting a plain-Swift ProxyOrchestrator out of AppState so both frontends drive the same start/stop/mapping logic. The CLI is minimal scope: proxy + PAC + CA cert generation only, no system-proxy automation, no CA trust-store install, no login-at-boot, no self-update on Linux. Closes stuartshields#8. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sg1MPNVjkvn53s8bgYdJD9
svandragt
marked this pull request as draft
July 10, 2026 09:39
Contributor
Author
|
Owner
|
@svandragt Would this also work with Windows Powershell? I don't have a Windows laptop to try it out. |
Contributor
Author
|
I would presume not. Windows and Linux are very different. There are free Windows VMs available on Microsoft's site but I'd open a separate issue for that for now, it's not in scope. Powershell the terminal app can run Windows cli executables fine though. Powershell the scripting language has its own syntax so I don't think that's what you mean. If you mean WSL then it might work. Also worth testing in a separate issue |
Owner
|
@svandragt Yeah WSL is what I meant. I agree that we can test this in a different issue. |
DispatchSource.makeSignalSource reads a signalfd on Linux, which never sees a signal set to SIG_IGN, so Ctrl-C and `kill` left the proxy running until SIGKILL. Replace it with a self-pipe C handler that works on both platforms, and flush stdout after the startup banner so it reaches a log file or journald promptly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014b9yy159qjGz9hj1o1wWx4
Also replace the stale DispatchSource signal-handling note in CLAUDE.md with the self-pipe rationale. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014b9yy159qjGz9hj1o1wWx4
svandragt
marked this pull request as ready for review
September 7, 2026 13:51
Contributor
Author
|
@stuartshields ready. Tested successfully on a DDEV WP project! |
stuartshields
approved these changes
Sep 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #8.
Problem
ProxyLight only ran as a macOS menu bar app. Everything lived in one target, so it couldn't build on Linux at all, even though most of the proxy has nothing macOS-specific in it.
Solution
Split the package into a portable core library, the existing macOS app, and a new command-line tool that runs on Linux and macOS.1 The app behaves exactly as before; it now depends on the core instead of owning everything.2
The CLI covers starting the proxy and managing mappings, and nothing else.3 It prints the PAC URL and the CA certificate path; the user points their browser at the URL and imports the certificate by hand. No system-proxy automation, no trust-store install, no login-at-boot, no self-update, as agreed in #8.
Along the way this fixes a real bug: the CLI ignored Ctrl-C and
kill, so it couldn't be run under a process manager.4Tests are split the same way, and the portable suite passes on Linux.5 README and CLAUDE.md describe the new layout and the Linux build.6
Not in this PR
No CI job for Linux yet. Builds were checked by hand in Docker.
Test plan
swift buildandswift teston macOS: app unchanged, all tests passdocker run --rm -v "$PWD":/src -w /src swift:6.1-noble swift build --product proxylight-cli -c release --static-swift-stdlibproduces a binary that runs on the hostproxylight-cli mapping add … && proxylight-cli startserveshttp://127.0.0.1:9876/proxy.packill -TERM <pid>stops itFootnotes
Package.swiftnow has three targets.ProxyLightCore: mapping engine, PAC generation, CA and leaf certificate generation, the SwiftNIO proxy pipeline, and a newProxyOrchestratorextracted fromAppState.ProxyLight: the macOS app.proxylight-cli: the executable, built on swift-argument-parser. ↩macOS-only wrappers (
SystemProxyManager,CATrustManager,SelfUpdater,LoginItemManager,ProxyRestoreStore) moved toSources/ProxyLight/MacOS/.MappingStoregained an#if os(macOS)branch so the config directory is~/Library/Application Support/ProxyLighton macOS and$XDG_CONFIG_HOME/proxylight(default~/.config/proxylight) on Linux. ↩Subcommands:
start(default,--port),mapping add|list|remove(add --fallback-on-not-foundfor serve-local-first mode),import,export,ca-path. ↩The SIGINT/SIGTERM
DispatchSourcewas scheduled onDispatchQueue.main, which never drains because the CLI blocks the main thread on a semaphore instead of running a run loop. It now uses a dedicated queue. ↩ProxyLightCoreTests(portable) andProxyLightTests(macOS-only). The core suite is 86/86 against theswift:6.1Docker image. ↩The dependencies (swift-nio 2.101, swift-nio-ssl 2.37, swift-certificates 1.19) require Swift tools 6.1, so
swift:6.0images fail.--static-swift-stdlibis needed for the binary to run outside the build container. ↩