commit c4a76164888b4a4bd485428d89fe0e58abc352bf Author: proxyctl coordinator Date: Fri Jul 17 01:51:13 2026 +0800 chore: initialize proxyctl specification diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore new file mode 100644 index 0000000..d20c0fe --- /dev/null +++ b/.codegraph/.gitignore @@ -0,0 +1,5 @@ +# CodeGraph data files — local to each machine, not for committing. +# Ignore everything in .codegraph/ except this file itself, so transient +# files (the database, daemon.pid, sockets, logs) never show up in git. +* +!.gitignore diff --git a/.scratch/proxyctl/issues/01-persistent-proxy-profiles.md b/.scratch/proxyctl/issues/01-persistent-proxy-profiles.md new file mode 100644 index 0000000..e9927d5 --- /dev/null +++ b/.scratch/proxyctl/issues/01-persistent-proxy-profiles.md @@ -0,0 +1,18 @@ +# 01 — Create and activate persistent Proxy Profiles + +**What to build:** A runnable `proxyctl` TUI in which a user can manage validated Proxy Profiles and select an Active Profile, establishing the first complete path from keyboard interaction through the Application Module to durable configuration. + +**Blocked by:** None — can start immediately. + +**Status:** ready-for-agent + +- [ ] Running `proxyctl` without arguments opens a keyboard-driven TUI and shows the Active Profile or a clear no-Profile state. +- [ ] The Profile page lets the user create, view, edit, delete, and activate uniquely named Proxy Profiles. +- [ ] Single proxy mode accepts one URL and expands it for compatible protocol uses without losing the user's value when advanced editing is opened. +- [ ] Advanced mode supports distinct HTTP, HTTPS, and ALL/SOCKS URLs. +- [ ] URL validation accepts only HTTP, HTTPS, SOCKS5, and SOCKS5H with valid hosts and ports and presents actionable validation errors. +- [ ] Bypass validation accepts exact hostnames, domain suffixes, IPv4, IPv6, and CIDR, then normalizes and deduplicates entries. +- [ ] No Profile is guessed or created automatically; the main page remains accessible when no Active Profile exists. +- [ ] Profile configuration follows XDG conventions, is written atomically, and is restricted to the owning user. +- [ ] Proxy passwords are obscured in rendered output and redacted from all errors produced by this slice. +- [ ] Application-level tests drive Profile behavior through the Interface consumed by the TUI rather than private helpers. diff --git a/.scratch/proxyctl/issues/02-shell-target.md b/.scratch/proxyctl/issues/02-shell-target.md new file mode 100644 index 0000000..70bd61f --- /dev/null +++ b/.scratch/proxyctl/issues/02-shell-target.md @@ -0,0 +1,20 @@ +# 02 — Safely control the Shell Target + +**What to build:** The first complete Target Adapter, allowing Bash and Zsh users to enable or disable the Active Profile from the TUI and have the invoking parent Shell synchronize after exit, while establishing the reusable Target seam and safety model for later integrations. + +**Blocked by:** 01 — Create and activate persistent Proxy Profiles. + +**Status:** ready-for-agent + +- [ ] A shared Target Interface exposes description, inspection, and desired-state operations without leaking platform paths or commands to the TUI. +- [ ] The Shell Target is Available only for detected Bash or Zsh environments; unsupported Shells are explained outside the main Target list. +- [ ] Enabling writes the Active Profile as conventional upper- and lower-case HTTP, HTTPS, ALL, and bypass environment assignments. +- [ ] Disabling emits the required unsets and restores any prior recognized Shell proxy state rather than silently discarding it. +- [ ] Attempts to enable without an Active Profile fail immediately with a route to Profile management. +- [ ] Inspection distinguishes Enabled, Disabled, External, and Drifted according to the glossary. +- [ ] The TUI shows an applying state and changes the switch only after success; failures preserve or re-inspect the actual state and show a redacted explanation. +- [ ] Shell integration install and uninstall operations are explicit, idempotent, and preserve unrelated user startup configuration. +- [ ] After an integrated TUI invocation exits, the parent Bash or Zsh session loads the generated assignments or unsets. +- [ ] Recovery metadata follows XDG state conventions, uses atomic writes and owner-only permissions where secrets may be present. +- [ ] When invoked through `sudo`, Profile and user Target state resolve to the invoking user; a direct root login resolves to root. +- [ ] A reusable Target contract harness verifies availability, all four states, enable, disable, restoration, idempotency, validation failure, permission failure, partial failure, and secret redaction without touching the host Shell configuration. diff --git a/.scratch/proxyctl/issues/03-apt-target.md b/.scratch/proxyctl/issues/03-apt-target.md new file mode 100644 index 0000000..2c8a37a --- /dev/null +++ b/.scratch/proxyctl/issues/03-apt-target.md @@ -0,0 +1,18 @@ +# 03 — Control the APT Target + +**What to build:** An APT Target that persistently applies the Active Profile on Debian-family systems, preserves pre-existing APT proxy behavior, and reports real configuration and permission outcomes through the common Target experience. + +**Blocked by:** 02 — Safely control the Shell Target. + +**Status:** ready-for-agent + +- [ ] APT is Available only when the expected Debian-family tooling and configuration capability are detected. +- [ ] The Target explanation states that APT supports proxy environment variables and that this integration provides persistent, privilege-boundary-safe configuration. +- [ ] Enabling uses an isolated `proxyctl`-owned overlay wherever supported and does not rewrite unrelated APT configuration. +- [ ] Disabling removes only the managed overlay and restores any previously effective recognized configuration. +- [ ] Inspection reports Enabled when effective managed values match the Active Profile, External for recognized non-managed proxy settings, Drifted for mismatched managed settings, and Disabled otherwise. +- [ ] Supported Profile protocol values and Bypass entries are translated without silently omitting data. +- [ ] An incompatible protocol or Bypass entry prevents mutation and identifies the incompatible value. +- [ ] Missing write authority produces immediate permission feedback and leaves or re-inspects the actual state. +- [ ] Repeated enable and disable operations are idempotent. +- [ ] Adapter contract and Target-specific tests use an isolated filesystem and fake APT command behavior; they never modify host package-manager configuration. diff --git a/.scratch/proxyctl/issues/04-docker-containers-target.md b/.scratch/proxyctl/issues/04-docker-containers-target.md new file mode 100644 index 0000000..3c49da5 --- /dev/null +++ b/.scratch/proxyctl/issues/04-docker-containers-target.md @@ -0,0 +1,18 @@ +# 04 — Control the Docker containers/builds Target + +**What to build:** A Docker client Target that applies the Active Profile as defaults for new containers and builds while preserving unrelated client configuration and clearly distinguishing this behavior from Docker daemon proxying. + +**Blocked by:** 02 — Safely control the Shell Target. + +**Status:** ready-for-agent + +- [ ] The Target is Available when the standard Docker client configuration model can be used for the owning user. +- [ ] Its explanation states that it affects new containers and builds, not Docker daemon registry access or existing containers. +- [ ] Enabling safely merges managed proxy defaults without removing unrelated Docker client settings. +- [ ] Disabling restores recognized prior proxy defaults and retains all unrelated configuration. +- [ ] Inspection distinguishes Enabled, Disabled, External, and Drifted, including externally edited managed values. +- [ ] HTTP, HTTPS, ALL/SOCKS, and Bypass values are applied only where Docker's documented model can represent them. +- [ ] Unsupported Profile data prevents mutation and is reported rather than silently discarded. +- [ ] Secret-bearing configuration is owner-restricted and credentials are redacted from UI errors and logs. +- [ ] Writes are atomic and repeated operations are idempotent. +- [ ] Adapter contract and Target-specific tests use isolated Docker client configuration and never contact or modify a real Docker daemon. diff --git a/.scratch/proxyctl/issues/05-gnome-target.md b/.scratch/proxyctl/issues/05-gnome-target.md new file mode 100644 index 0000000..8cd4942 --- /dev/null +++ b/.scratch/proxyctl/issues/05-gnome-target.md @@ -0,0 +1,18 @@ +# 05 — Control the GNOME desktop proxy Target + +**What to build:** A GNOME desktop Target that applies the Active Profile through GNOME's official settings model, restores prior desktop proxy settings safely, and appears only when the required settings schema is available. + +**Blocked by:** 02 — Safely control the Shell Target. + +**Status:** ready-for-agent + +- [ ] The Target is Available only when the expected GNOME settings command and proxy schema are usable for the owning user. +- [ ] The explanation states that it affects applications consuming GNOME system network settings and does not promise coverage of every graphical application. +- [ ] Enabling records the recognized original proxy mode and values before applying the Active Profile. +- [ ] Disabling restores the recorded original settings when managed values remain under `proxyctl` ownership. +- [ ] Inspection distinguishes Enabled, Disabled, External, and Drifted using positive evidence from GNOME settings. +- [ ] HTTP, HTTPS, SOCKS, and Bypass values are translated only when their semantics can be preserved. +- [ ] Any unrepresentable protocol or Bypass rule rejects the operation before mutation with an actionable explanation. +- [ ] Running through `sudo` operates on the invoking user's GNOME settings rather than root's desktop settings. +- [ ] Command, session-bus, schema, and permission failures are reflected immediately without false success. +- [ ] Adapter contract and Target-specific tests use a fake settings command and never modify the active desktop session. diff --git a/.scratch/proxyctl/issues/06-docker-daemon-target.md b/.scratch/proxyctl/issues/06-docker-daemon-target.md new file mode 100644 index 0000000..1b26415 --- /dev/null +++ b/.scratch/proxyctl/issues/06-docker-daemon-target.md @@ -0,0 +1,19 @@ +# 06 — Control the Docker daemon Target + +**What to build:** A systemd-managed Docker daemon Target that configures daemon network proxying separately from container defaults and performs an explicit, confirmed restart with truthful post-operation state. + +**Blocked by:** 02 — Safely control the Shell Target. + +**Status:** ready-for-agent + +- [ ] The Target is Available only when a supported systemd-managed Docker daemon is detected. +- [ ] The explanation states that it affects daemon operations such as registry access, not container network traffic or container/build defaults. +- [ ] Pressing Space presents a restart warning before any configuration mutation. +- [ ] Cancelling the warning performs no file write, manager reload, or daemon restart. +- [ ] Confirming applies the compatible Active Profile through a managed daemon configuration surface, reloads manager configuration as required, and immediately restarts Docker. +- [ ] There is no Pending Restart state; success is reported only after restart and post-operation inspection succeed. +- [ ] Failure to write, reload, or restart produces immediate redacted feedback and re-inspects actual configuration as Enabled, Disabled, External, or Drifted. +- [ ] Existing recognized daemon proxy configuration is preserved and restored when the managed Target is disabled. +- [ ] Unsupported protocol or Bypass data rejects the operation before mutation rather than being omitted. +- [ ] Missing authority is reported as a normal permission error; the Adapter never initiates privilege escalation. +- [ ] Adapter contract and Target-specific tests fake systemd and Docker behavior and prove cancellation, command ordering, restart failure, restoration, and idempotency without touching the host daemon. diff --git a/.scratch/proxyctl/issues/07-diagnostics-and-reset.md b/.scratch/proxyctl/issues/07-diagnostics-and-reset.md new file mode 100644 index 0000000..3d6e9b7 --- /dev/null +++ b/.scratch/proxyctl/issues/07-diagnostics-and-reset.md @@ -0,0 +1,18 @@ +# 07 — Diagnose and reset managed Target state + +**What to build:** Operational maintenance behavior that lets users inspect configuration and permission problems, safely restore every managed Target before uninstalling, and troubleshoot failures from bounded redacted local logs. + +**Blocked by:** 02 — Safely control the Shell Target. + +**Status:** ready-for-agent + +- [ ] `proxyctl doctor` performs read-only checks of Profile validity, identity resolution, storage permissions, Target availability, detected state, required commands, and relevant write authority. +- [ ] Diagnostic output states the evidence behind External or Drifted results and redacts all credentials. +- [ ] `proxyctl reset` requires confirmation before changing any Target. +- [ ] A confirmed reset attempts to disable every managed Target through the same Target Interface used by the TUI and restores recorded original state where safe. +- [ ] Reset reports each Target result independently, preserves truthful state after partial failure, and exits unsuccessfully when any requested cleanup fails. +- [ ] Cancellation performs no Target mutation. +- [ ] Mutating operations are protected against concurrent `proxyctl` processes so recovery metadata cannot be corrupted. +- [ ] Local logs record operation type, Target, result, and redacted failure detail without credentials or full authenticated URLs. +- [ ] Log growth is bounded through rotation or equivalent retention behavior. +- [ ] The maintenance commands have process-level tests using fake Targets and isolated XDG locations; tests never modify the host system. diff --git a/.scratch/proxyctl/issues/08-coordinate-target-panel.md b/.scratch/proxyctl/issues/08-coordinate-target-panel.md new file mode 100644 index 0000000..23844b1 --- /dev/null +++ b/.scratch/proxyctl/issues/08-coordinate-target-panel.md @@ -0,0 +1,20 @@ +# 08 — Coordinate the Active Profile and complete the Target panel + +**What to build:** The complete everyday TUI experience across all first-release Targets, including dynamic discovery, contextual help, coordinated Active Profile changes, immediate per-Target progress, and safe handling of Profile deletion. + +**Blocked by:** 03 — Control the APT Target; 04 — Control the Docker containers/builds Target; 05 — Control the GNOME desktop proxy Target; 06 — Control the Docker daemon Target; 07 — Diagnose and reset managed Target state. + +**Status:** ready-for-agent + +- [ ] The main panel lists only Available Targets and refreshes availability and state on demand. +- [ ] The help page lists every built-in Target, including hidden Targets and the reason each was not detected. +- [ ] The selected Target's summary explains effects, non-effects, configuration ownership, expected permissions, and relevant side effects. +- [ ] Arrow keys and `j`/`k` navigate, Space toggles, `p` opens Profile management, `r` refreshes, `?` opens help, and `q`/Escape returns or exits consistently. +- [ ] The Active Profile is always visible on the main panel, including a clear absent state. +- [ ] Attempting to enable any Target without an Active Profile fails immediately and points to Profile management; disabling remains possible. +- [ ] Changing the Active Profile updates every Enabled Target one at a time and reflects progress and completion immediately on each row. +- [ ] A cross-Target partial failure keeps successful updates, re-inspects failed Targets, marks Drifted where appropriate, and presents a redacted summary without attempting global rollback. +- [ ] Deleting the final Proxy Profile requires confirmation, leaves existing managed configuration in place as Drifted, and prevents re-enabling until a new Active Profile exists. +- [ ] External configuration can be temporarily overlaid by the Active Profile and is restored to External when the managed Target is disabled. +- [ ] TUI tests cover key-to-intent mapping, page transitions, Active Profile rendering, all Target states, confirmation dialogs, progress, errors, and explanations without duplicating Application behavior tests. +- [ ] Full Application integration tests exercise multiple fake and real hermetic Adapters through Active Profile switching and partial failure. diff --git a/.scratch/proxyctl/issues/09-release-v1.md b/.scratch/proxyctl/issues/09-release-v1.md new file mode 100644 index 0000000..cdc84a7 --- /dev/null +++ b/.scratch/proxyctl/issues/09-release-v1.md @@ -0,0 +1,18 @@ +# 09 — Publish a verifiable proxyctl v1 + +**What to build:** A documented and reproducible first release that users on Linux amd64 and arm64 can install, verify, operate safely, diagnose, and remove without relying on distribution-specific packaging. + +**Blocked by:** 08 — Coordinate the Active Profile and complete the Target panel. + +**Status:** ready-for-agent + +- [ ] `proxyctl version` reports the build version in human-readable and script-friendly output. +- [ ] Process-level smoke tests cover default TUI startup, version, doctor, reset confirmation, and Shell integration install/uninstall using an isolated environment. +- [ ] The full automated suite proves no test modifies real Shell startup files, package-manager configuration, Docker state, GNOME settings, or system services. +- [ ] Reproducible release automation builds single Linux binaries for amd64 and arm64. +- [ ] Release automation emits SHA-256 checksums for every distributed binary or archive. +- [ ] User documentation explains installation, first Profile creation, Shell integration, each Target's effects, all status meanings, permission behavior, explicit `sudo` use, Docker restart risk, diagnostics, reset, and uninstall cleanup. +- [ ] Documentation states that `proxyctl` does not manage proxy processes, test connectivity, elevate privileges internally, collect telemetry, or check for updates automatically. +- [ ] Documentation identifies the supported Linux, Shell, APT, Docker, and GNOME boundaries and the intentionally deferred integrations. +- [ ] Source installation through Go tooling works without relying on deb, rpm, AUR, Snap, Flatpak, or another distribution package. +- [ ] A clean-machine verification demonstrates installation, a Profile workflow, at least one hermetic Target workflow, diagnosis, reset, and removal. diff --git a/.scratch/proxyctl/spec.md b/.scratch/proxyctl/spec.md new file mode 100644 index 0000000..b14b17b --- /dev/null +++ b/.scratch/proxyctl/spec.md @@ -0,0 +1,182 @@ +Status: ready-for-agent + +# proxyctl v1 specification + +## Problem Statement + +Linux users frequently need one proxy configuration to apply across several independent configuration surfaces. Changing Shell environment variables is insufficient for daemons, container defaults, desktop applications, and commands whose environment changes across privilege boundaries. Each surface has different configuration formats, state semantics, permissions, and restart requirements. + +Users currently have to remember which files and commands control each surface, repeat the same proxy details, distinguish Docker daemon behavior from container/build behavior, preserve pre-existing settings, and diagnose partial failures manually. This is error-prone and makes switching between locations or networks unnecessarily risky. + +## Solution + +Build `proxyctl`, a keyboard-driven Linux TUI that coordinates one Active Profile across independently switchable Targets. The default screen shows only Available Targets, their detected state, the Active Profile, a concise explanation of the selected Target, and immediate operation feedback. Proxy Profile management lives on a separate page. + +The first release supports Bash/Zsh Shell environments, APT, systemd-managed Docker daemon proxying, Docker container/build defaults, and GNOME desktop proxy settings. Every Target owns its detection, application, restoration, compatibility checks, and explanation behind one common Interface. + +`proxyctl` preserves user-owned configuration wherever possible. It prefers a tool-owned overlay; when a Target has only a single mutable configuration surface, it records enough original state to restore it safely. It never silently discards unsupported Proxy Profile fields or Bypass rules. + +## User Stories + +1. As a Linux user, I want to open `proxyctl` without arguments, so that I immediately see the proxy controls relevant to my machine. +2. As a Linux user, I want the main screen to show the Active Profile, so that I know which proxy settings an enable action will apply. +3. As a Linux user, I want only Available Targets on the main screen, so that absent software does not clutter routine use. +4. As a curious user, I want the help page to list every Target supported by `proxyctl`, so that I can discover integrations not available on my current system. +5. As a troubleshooting user, I want unavailable Target explanations to state what was not detected, so that I understand why an integration is hidden. +6. As a keyboard-focused user, I want to navigate with arrow keys or `j`/`k`, so that the TUI feels natural in a terminal. +7. As a keyboard-focused user, I want to toggle the selected Target with Space, so that routine switching is fast. +8. As a keyboard-focused user, I want `p` to open Proxy Profile management, so that configuration editing is separate from routine switching. +9. As a keyboard-focused user, I want `?` to open help, so that explanations are always discoverable. +10. As a keyboard-focused user, I want `r` to refresh detection, so that newly installed or removed software is reflected without restarting. +11. As a keyboard-focused user, I want `q` or Escape to leave the current page or exit, so that navigation is predictable. +12. As a user in a mouse-capable terminal, I want the keyboard Interface to remain complete without mouse support, so that behavior is consistent across terminals. +13. As a Linux user, I want the selected Target's summary visible on the main screen, so that I understand what its switch affects. +14. As a Linux user, I want each Target explanation to state what it does not affect, so that names such as Docker daemon are not misleading. +15. As a Linux user, I want each Target explanation to describe expected permissions and side effects, so that I can anticipate failures or restarts. +16. As a Linux user, I want detailed help for each Target, so that I can inspect configuration scope and detection evidence when needed. +17. As a user with several networks, I want to create named Proxy Profiles, so that I can reuse proxy settings without retyping them. +18. As a user with several Proxy Profiles, I want exactly one of them selected as the Active Profile, so that all Enabled Targets have one unambiguous source. +19. As a first-time user, I want the Target screen to remain usable when no Proxy Profiles exist, so that I am not forced through an onboarding wizard. +20. As a first-time user, I want an attempt to enable a Target without an Active Profile to explain the problem and point me to Profile management, so that the failed action is understandable. +21. As a user with no Active Profile, I want to remain able to disable previously managed Targets, so that stale system configuration can always be removed. +22. As a user editing a Proxy Profile, I want a default Single proxy mode, so that common local-proxy setups require only one URL and a Bypass list. +23. As an advanced user, I want separate HTTP, HTTPS, and ALL/SOCKS values, so that I can model proxies with protocol-specific endpoints. +24. As a user switching from Single proxy to advanced mode, I want my existing value preserved and expanded, so that changing modes does not discard input. +25. As a user entering a proxy endpoint, I want malformed URLs and unsupported schemes rejected before application, so that bad configuration never reaches a Target. +26. As a user with an HTTP proxy, I want `http` and `https` proxy URL schemes supported, so that standard enterprise proxies can be represented. +27. As a user with a SOCKS proxy, I want `socks5` and `socks5h` kept distinct, so that DNS resolution behavior remains explicit. +28. As a user with internal resources, I want Bypass entries for exact hostnames, domain suffixes, IPv4, IPv6, and CIDR ranges, so that internal traffic can avoid the proxy. +29. As a user editing Bypass rules, I want entries normalized and deduplicated, so that the Proxy Profile stays understandable. +30. As a security-conscious user, I want a Target to reject an incompatible Bypass rule rather than silently drop it, so that traffic is not unexpectedly sent through a proxy. +31. As a user with an authenticated proxy, I want credentials representable in proxy URLs, so that authenticated endpoints work. +32. As a user with an authenticated proxy, I want passwords obscured in the TUI and removed from logs and errors, so that credentials are not casually exposed. +33. As a user who values portable configuration, I want Proxy Profiles stored in a human-readable format under the standard user configuration area, so that I can inspect and back them up. +34. As a user storing credentials, I want the Profile configuration restricted to my user account, so that other local users cannot read it. +35. As a Linux user, I want Shell proxy variables to include conventional upper- and lower-case forms, so that common command-line tools inherit them reliably. +36. As a Bash or Zsh user, I want a one-time Shell integration installer, so that later TUI changes can update my current parent Shell after the TUI exits. +37. As a Bash or Zsh user, I want to uninstall the Shell integration cleanly, so that `proxyctl` leaves no stale startup hook. +38. As a user of an unsupported Shell, I want the Shell Target hidden and the help page to explain the supported shells, so that the limitation is explicit. +39. As an APT user, I want persistent APT proxy control, so that package operations remain consistent even when an interactive Shell environment is not propagated. +40. As an APT user with existing configuration, I want `proxyctl` to preserve that configuration beneath its own overlay, so that disabling the Target restores my prior setup. +41. As a Docker user, I want Docker daemon proxying separate from Docker container/build defaults, so that I can control registry access independently from injected container variables. +42. As a Docker user, I want the Docker daemon Target to explain that it affects daemon network operations, so that I do not mistake it for container networking. +43. As a Docker user, I want a warning before Docker daemon configuration triggers a restart, so that I can avoid disrupting running work unexpectedly. +44. As a Docker user, I want cancellation of the restart warning to make no changes, so that declining is safe. +45. As a Docker user, I want daemon configuration and restart failures reflected immediately, so that the TUI never implies a change succeeded when it did not. +46. As a Docker user, I want container/build defaults to affect only future containers and builds, so that the behavior matches Docker's documented model. +47. As a GNOME user, I want a desktop proxy Target, so that applications using GNOME system network settings receive the Active Profile. +48. As a non-GNOME user, I want the GNOME Target hidden, so that unsupported desktop settings are not offered. +49. As a user toggling a Target, I want an applying state followed immediately by success or a red error, so that every Space press has visible feedback. +50. As a user whose enable operation fails before writing anything, I want the previous Target state preserved, so that a failed operation does not move the switch. +51. As a user whose operation partially changes a Target, I want `proxyctl` to re-inspect the real configuration, so that the displayed state reflects what is actually present. +52. As a user switching the Active Profile, I want every Enabled Target updated to the new Profile, so that enabled configuration remains coordinated. +53. As a user switching the Active Profile, I want each Target row to update as its operation completes, so that progress and failures are visible immediately. +54. As a user facing a partial Active Profile switch, I want successful Targets to remain updated and failed Targets to become Drifted when appropriate, so that the tool does not hide partial success. +55. As a user, I want an Enabled Target to mean its actual managed configuration matches the Active Profile, so that the status has a precise meaning. +56. As a user, I want a Drifted Target to identify a managed configuration that no longer matches the Active Profile, so that I know it needs repair or removal. +57. As a user with pre-existing proxy settings, I want a recognized non-`proxyctl` configuration shown as External, so that the tool does not claim ownership of it. +58. As a user disabling a Target that previously had External configuration, I want that configuration restored and the state to return to External, so that my prior setup is preserved. +59. As a user with an unrecognized configuration source, I accept that absence of positive detection is displayed as Disabled, so that the first release avoids an Unknown state. +60. As a user inspecting an External state, I want the details to identify the evidence that was detected, so that I can locate the original configuration. +61. As a safety-conscious user, I want `proxyctl` to prefer dedicated tool-owned configuration fragments, so that it avoids rewriting unrelated user files. +62. As a safety-conscious user, I want original values recorded when an isolated fragment is impossible, so that disabling can restore the previous state. +63. As a safety-conscious user, I want configuration writes to be atomic, so that a crash does not leave a partially written file. +64. As a user, I want only one `proxyctl` operation to mutate state at a time, so that concurrent sessions cannot corrupt recovery metadata. +65. As an unprivileged user, I want writable Targets to work normally and permission failures to be reported plainly, so that the TUI does not surprise me with a password prompt. +66. As an unprivileged user, I want `proxyctl` never to initiate privilege escalation, so that authority changes remain under my control. +67. As a user who explicitly starts `proxyctl` through `sudo`, I want the invoking user to remain the Profile and user-configuration owner, so that I do not see an unrelated root configuration. +68. As a user who explicitly starts `proxyctl` through `sudo`, I want system Targets to use the available root authority while user Targets still operate on the invoking user's configuration, so that mixed-scope behavior is predictable. +69. As a root login user, I want root to be treated as the configuration owner when there is no invoking user, so that direct root sessions remain coherent. +70. As a troubleshooting user, I want `proxyctl doctor` to report configuration, permission, command, and Target detection problems, so that I can diagnose failures without guessing. +71. As a user removing `proxyctl`, I want `proxyctl reset` to disable all managed Targets after confirmation and restore prior configuration, so that uninstalling does not strand proxy settings. +72. As a Shell user, I want explicit Shell install and uninstall maintenance commands, so that startup-file changes are reversible. +73. As a user, I want a version command, so that I can report the exact build when seeking help. +74. As a user, I want local diagnostic logs with bounded growth, so that failures can be investigated without consuming unlimited disk space. +75. As a privacy-conscious user, I want no telemetry or automatic update checks, so that `proxyctl` never contacts the network on its own. +76. As a user, I want `proxyctl` not to test proxy connectivity automatically, so that toggling configuration has no hidden network side effects. +77. As a Linux amd64 user, I want a single-file release binary, so that installation is straightforward. +78. As a Linux arm64 user, I want an equivalent release binary, so that devices such as ARM development boards are supported. +79. As a release consumer, I want checksums for release artifacts, so that I can verify downloads. +80. As a developer, I want `go install` support, so that I can build and install directly from source. + +## Implementation Decisions + +- The product and executable are named `proxyctl`. +- The implementation uses Go and Bubble Tea. +- The default invocation opens the TUI. Maintenance commands cover Shell integration installation/removal, diagnostics, full managed-state reset, and version reporting. Scriptable Profile switching and Target toggling are not part of the first CLI Interface. +- The TUI is keyboard-only in the first release. Its default page is the Target switch panel; Proxy Profile management and help are separate pages. +- The Target panel shows only Available Targets. The help page lists all built-in Targets and explains failed detection. +- The supported first-release Target Adapters are Bash/Zsh Shell environment, APT, systemd-managed Docker daemon, Docker container/build defaults, and GNOME desktop proxy. +- The core TUI and Shell behavior target general Linux. APT support is limited to Debian-family systems, Docker daemon support to systemd-managed installations, Docker container/build support to the standard Docker client configuration model, and GNOME support to systems exposing the expected settings schema. +- Target integrations are compiled in. There is no plugin Interface, arbitrary-command Target, or user-authored executable hook. +- One Application Module coordinates Proxy Profiles, Active Profile transitions, Target operations, persistence, and user-facing results. Both TUI and maintenance commands consume its Interface. +- A single Target seam hides platform-specific behavior. Target Adapters expose description, inspection, and desired-state operations; callers do not know configuration paths or system commands. +- Target dependencies such as filesystem access and command execution are accepted at internal seams rather than created globally, permitting hermetic tests. +- A Proxy Profile has a simple Single proxy editing mode and an advanced HTTP/HTTPS/ALL-SOCKS mode. Single mode expands into protocol values without losing the original value when advanced editing is opened. +- Accepted URL schemes are HTTP, HTTPS, SOCKS5, and SOCKS5H. Scheme semantics are preserved, and each Target validates compatibility rather than converting unsupported schemes. +- Bypass rules support exact hostnames, domain suffixes, IPv4, IPv6, and CIDR. They are normalized and deduplicated. A Target must reject an incompatible rule instead of dropping it. +- Proxy URLs may contain credentials. The human-readable Profile configuration is stored under the XDG user configuration location with owner-only permissions. Secrets are obscured in the TUI and redacted from diagnostics and logs. +- User-authored configuration, generated Shell integration, runtime recovery metadata, generated Shell environment, and logs are separated according to XDG configuration and state conventions. +- Configuration and state writes use temporary output followed by atomic replacement. Cross-process mutation is serialized with a lock. +- `proxyctl` prefers tool-owned overlays or fragments. When a Target cannot provide an isolated overlay, recovery state records the original value and the last applied fingerprint. +- Target inspection distinguishes Enabled, Disabled, External, and Drifted. External is shown only when an Adapter positively recognizes a non-`proxyctl` proxy setting. If no recognized external setting is found, the state is Disabled; there is no Unknown state in the first release. +- An operation changes the displayed switch only after success. Failures are rendered immediately on the affected row with full redacted detail in the explanation area. +- After any partial write or command failure, the Adapter re-inspects the Target and returns the state implied by actual configuration. +- Changing the Active Profile attempts to update every Enabled Target and reports progress per Target. Cross-Target rollback is not attempted. Failed Targets may become Drifted while successful Targets remain Enabled on the new Profile. +- No Active Profile is permitted when no Proxy Profiles exist. Attempts to enable a Target in that state fail with a direct route to Profile management. Disabling remains available. +- Deleting the final Proxy Profile requires confirmation. Existing managed Target configuration is retained and becomes Drifted rather than being modified implicitly. +- Shell behavior uses a one-time Bash/Zsh integration because a child TUI process cannot mutate its parent Shell environment. The wrapper reloads generated exports/unsets after the TUI exits. Unsupported shells do not expose the Shell Target. +- Shell output includes conventional upper- and lower-case proxy environment names and explicit unsets when disabled. +- Docker daemon and Docker container/build defaults remain separate Targets because their documented effects and configuration surfaces differ. +- Docker daemon changes require an explicit warning before any mutation. Confirmation applies the configuration, reloads the manager as required, and restarts Docker immediately. Cancellation performs no mutation; there is no Pending Restart state. +- APT remains a dedicated Target for persistence and privilege-boundary reliability even though APT supports proxy environment variables. +- `proxyctl` never invokes `sudo`, `pkexec`, or another privilege escalation mechanism. Permission failures are normal operation results. +- When already running under `sudo`, `proxyctl` uses the invoking user's identity and XDG locations for Profiles and user Targets while allowing system Targets to use current authority. With no invoking user, the effective root user is the owner. +- Local logs contain redacted operation diagnostics, have bounded growth, and never contain unredacted proxy credentials. +- The program has no telemetry, automatic update requests, or automatic proxy connectivity checks. +- Release output consists of checksum-verifiable single binaries for Linux amd64 and arm64, plus source installation through Go tooling. Distribution-specific packages are deferred. + +## Testing Decisions + +- Good tests assert externally observable behavior through the highest practical seam: returned application state, rendered user-facing outcomes, managed configuration effects, restoration behavior, and invoked system operations. They do not assert private helper calls, internal data layout, or incidental implementation order. +- The primary test seam is the Application Module Interface used by both the TUI and maintenance commands. Tests submit the same intents a user can trigger and assert resulting snapshots and operation results. +- Application tests cover Proxy Profile CRUD, Active Profile selection and deletion, Single/Advanced validation, absence of an Active Profile, per-Target immediate feedback, partial Profile switching, reset, diagnostics, and credential redaction. +- The shared Target Interface is the second seam. Every real Target Adapter is run through common contract tests for availability detection, Disabled, Enabled, External, Drifted, enable, disable, restoration, idempotency, incompatible Proxy Profile rejection, permission failure, partial failure, and redaction. +- Adapter tests use isolated XDG directories, temporary filesystem roots, and a fake command runner. They must never modify the developer machine's Shell startup files, APT configuration, Docker daemon, Docker client configuration, GNOME settings, or systemd state. +- Target-specific tests cover Bash and Zsh integration behavior, APT overlay precedence and restoration, Docker daemon restart confirmation and command failures, Docker container/build semantics, and GNOME settings snapshot/restoration. +- `sudo` identity behavior is tested using injected process identity and environment data rather than requiring tests to run as root. +- TUI tests remain thin: key-to-intent mapping, page navigation, visible Active Profile, status/error rendering, and selected-Target explanation. Business behavior is not duplicated at the rendering layer. +- Process-level smoke tests cover default startup, Shell integration maintenance commands, doctor, reset confirmation, and version output. +- Filesystem tests verify owner-only secret-bearing configuration, atomic replacement, lock behavior, original-state preservation, and cleanup. +- Failure tests verify that no operation reports success before effects complete, cancellation mutates nothing, unsupported Bypass rules are never silently omitted, and logs/errors redact credentials. +- There is no existing codebase test prior art. The first tracer implementation must establish reusable application and Target contract harnesses for later Adapters. + +## Out of Scope + +- Starting, stopping, configuring, or supervising proxy programs such as Clash, sing-box, or Xray. +- Proxy subscriptions, node management, latency testing, automatic node selection, and routing-rule management. +- Transparent proxying, TUN devices, iptables, nftables, or other traffic interception. +- Automatic proxy connectivity checks. +- Arbitrary systemd service proxy configuration. +- Git, DNF, npm, pip, or other tools that already consume conventional Shell proxy variables in the intended first-release scenarios. +- Firefox-specific policy or profile modification. +- Chromium-specific launch-flag management. +- KDE or other desktop proxy integrations beyond GNOME. +- Snap/Ubuntu Core proxy configuration. +- Fish and other Shells beyond Bash and Zsh. +- Mouse interaction. +- Plugins, custom Targets, and arbitrary user-defined commands. +- A complete headless CLI for Profile and Target operations. +- Internal privilege escalation or graphical authorization agents. +- Cross-Target atomic transactions and rollback. +- Distribution-specific deb, rpm, AUR, Snap, Flatpak, or similar packages. +- Windows, macOS, and non-Linux Unix platforms. + +## Further Notes + +- Domain terminology is defined in the repository glossary and must be used consistently in code, tests, issues, and user-facing explanations. +- The supporting research corrects an important premise: APT, DNF, and Git do support conventional proxy environment variables. Dedicated integrations are justified by persistence, process, privilege, or desktop boundaries rather than a blanket lack of environment-variable support. +- Docker daemon and Docker container/build defaults are deliberately separate because Docker documents them as independent configuration surfaces with different effects. +- GNOME desktop proxy support is preferred over browser-specific modification because Chromium consumes system network settings and Firefox can use a system proxy mode. +- External detection is explicitly best-effort. The first release favors a simpler four-state Interface and documents detection evidence rather than claiming exhaustive discovery of every possible configuration source. +- No ADR is currently required: the decisions are contained within this product specification and do not yet meet the threshold of a surprising, costly-to-reverse architectural trade-off proven against an existing implementation. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e060511 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,9 @@ +## Agent skills + +### Issue tracker + +Issues are tracked as local Markdown files under `.scratch/`. See `docs/agents/issue-tracker.md`. + +### Domain docs + +This repo uses a single-context domain documentation layout. See `docs/agents/domain.md`. diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..414190c --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,33 @@ +# Proxy Configuration + +This context describes how one active proxy configuration is coordinated across independently switchable Linux configuration surfaces. + +## Language + +**Proxy Profile**: +A named, reusable set of proxy connection details and bypass rules. +_Avoid_: Proxy, configuration preset + +**Active Profile**: +The Proxy Profile currently selected as the source for every Enabled Target; it may be absent when no Proxy Profiles exist. +_Avoid_: Current proxy, selected preset + +**Target**: +A configuration surface that can independently receive proxy settings, such as the Shell environment, APT, or the Docker daemon. +_Avoid_: Application, service + +**Available Target**: +A Target detected as applicable to the current system and therefore shown on the main switch panel. +_Avoid_: Installed application, supported Target + +**Enabled Target**: +A Target whose actual configuration matches the Active Profile; a Target cannot be enabled when there is no Active Profile. +_Avoid_: Active application, proxied app + +**Drifted Target**: +A Target whose managed configuration exists but no longer matches the Active Profile, whether because an update failed or an external change occurred. +_Avoid_: Broken Target, partially enabled Target + +**External Target**: +A Target for which a recognized proxy configuration exists outside this tool's ownership; absence of detected external configuration is treated as Disabled. +_Avoid_: Unknown Target, unmanaged error diff --git a/docs/agents/domain.md b/docs/agents/domain.md new file mode 100644 index 0000000..cd31b02 --- /dev/null +++ b/docs/agents/domain.md @@ -0,0 +1,35 @@ +# Domain Docs + +How engineering skills should consume this repo's domain documentation. + +## Before exploring, read these + +- `CONTEXT.md` at the repo root +- Relevant ADRs under `docs/adr/` + +If these files do not exist, proceed silently. The domain-modeling flow creates +them lazily when terms or decisions are resolved. + +## File structure + +This is a single-context repo: + +``` +/ +├── CONTEXT.md +├── docs/adr/ +└── src/ +``` + +## Use the glossary's vocabulary + +Use terms as defined in `CONTEXT.md`. Do not drift to synonyms the glossary +explicitly avoids. + +If a needed concept is missing, reconsider whether new language is necessary or +note the gap for `/domain-modeling`. + +## Flag ADR conflicts + +If proposed work contradicts an existing ADR, surface the conflict explicitly +rather than silently overriding it. diff --git a/docs/agents/issue-tracker.md b/docs/agents/issue-tracker.md new file mode 100644 index 0000000..cb96da3 --- /dev/null +++ b/docs/agents/issue-tracker.md @@ -0,0 +1,30 @@ +# Issue tracker: Local Markdown + +Issues and specs for this repo live as Markdown files in `.scratch/`. + +## Conventions + +- One feature per directory: `.scratch//` +- The spec is `.scratch//spec.md` +- Implementation issues are one file per ticket at + `.scratch//issues/-.md` +- Tickets are numbered from `01` +- Comments and conversation history are appended under `## Comments` + +## When a skill says "publish to the issue tracker" + +Create a new file under `.scratch//`, creating the directory if needed. + +## When a skill says "fetch the relevant ticket" + +Read the referenced file. The user will normally provide its path or issue number. + +## Wayfinding operations + +- Map: `.scratch//map.md` +- Child ticket: `.scratch//issues/NN-.md` +- `Type:` records `research`, `prototype`, `grilling`, or `task` +- `Status:` records `claimed` or `resolved` +- `Blocked by: NN, NN` declares blocking relationships +- Claim a ticket by setting `Status: claimed` +- Resolve it by adding `## Answer`, setting `Status: resolved`, and updating the map diff --git a/docs/research/linux-proxy-targets.md b/docs/research/linux-proxy-targets.md new file mode 100644 index 0000000..fe61eec --- /dev/null +++ b/docs/research/linux-proxy-targets.md @@ -0,0 +1,92 @@ +# Linux proxy configuration targets + +## Question + +Which commonly used Linux tools need proxy configuration outside the interactive Shell's `HTTP_PROXY`/`HTTPS_PROXY`/`ALL_PROXY` environment? + +## Key distinction + +“Does not support proxy environment variables” and “does not inherit the current Shell environment” are different problems. Several tools support the variables when present in their own process, but daemons, desktop applications, and commands launched through privilege boundaries often do not receive the interactive Shell's values. + +## Findings + +### Strong candidates for dedicated Targets + +#### Docker daemon + +The daemon has its own startup environment and does not inherit later changes to an interactive Shell. Docker supports daemon proxy settings in `daemon.json`, CLI flags, or the daemon service environment, and recommends `daemon.json`. A dedicated Target is justified because it affects daemon operations such as registry access and normally requires a daemon restart. + +Source: [Docker — Configure the daemon to use a proxy](https://docs.docker.com/engine/daemon/proxy/) + +#### Docker container/build defaults + +Docker's client-side `~/.docker/config.json` proxy configuration injects proxy environment variables and build arguments into **new containers and builds**. Docker explicitly states that this does not configure the Docker CLI or daemon and does not affect existing containers. This is a distinct Target from Docker daemon. + +Source: [Docker — Configure Docker to use a proxy server](https://docs.docker.com/engine/cli/proxy/) + +#### GNOME desktop proxy + +GNOME exposes a system proxy setting with None, Manual, and Automatic modes. Its documentation says applications using the network connection use the selected setting. This is valuable for graphical applications that consume desktop settings rather than an interactive Shell's environment. + +Source: [GNOME Help — Define proxy settings](https://help.gnome.org/users/gnome-help/stable/net-proxy.html.en) + +#### systemd system services + +Processes launched by the system service manager generally do not inherit the manager's environment automatically. Their environment is assembled from sources including `Environment=`, `EnvironmentFile=`, and explicitly passed manager variables. Therefore changing the current Shell environment does not reconfigure already managed system services. + +This is a real configuration gap, but an unrestricted “all systemd services” Target would be too broad. Individual daemon integrations should be added only when there is a concrete use case. + +Sources: + +- [systemd.exec — Environment variables in spawned processes](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#Environment%20Variables%20in%20Spawned%20Processes) +- [systemd.exec — Environment= and EnvironmentFile=](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#Environment=) + +### Desktop applications + +#### Chromium + +Chromium documents use of system network settings, including system proxy settings, and supports explicit proxy command-line switches such as `--proxy-server` and `--proxy-bypass-list`. Its documented configuration surface is therefore the desktop/system setting or launch arguments, not the interactive Shell proxy variables. + +A separate Chromium Target is probably unnecessary if the tool supports the desktop's system proxy setting. + +Source: [Chromium — Network Settings](https://www.chromium.org/developers/design-documents/network-settings/) + +#### Firefox + +Firefox exposes its own proxy model with `none`, `system`, `manual`, `autoDetect`, and `autoConfig` modes, including HTTP, SSL, SOCKS, and bypass settings. The official enterprise policy schema provides a manageable configuration surface, but changing enterprise policy is intrusive for a personal switcher. Prefer desktop system proxy support first; consider a Firefox-specific Target only if users require manual Firefox profiles. + +Source: [Mozilla Policy Templates — Proxy](https://mozilla.github.io/policy-templates/#proxy) + +### Context-dependent candidate + +#### Snap on Ubuntu Core + +Snap's documented system options include `proxy.http`, `proxy.https`, and `proxy.ftp`, but the documentation marks these options as available only on Ubuntu Core. This is not a strong general-desktop MVP Target. + +Source: [Snapcraft — System options](https://snapcraft.io/docs/reference/administration/system-options#heading--proxy) + +## Common tools that already support Shell proxy variables + +These are not strong dedicated Targets merely because they use package-specific configuration too: + +- **APT** supports `http_proxy` and `no_proxy`; it also supports persistent `Acquire::http::Proxy` configuration. A dedicated APT Target can still improve persistence and avoid environments being lost across `sudo`, but APT is not accurately described as lacking environment-variable support. Source: [Debian apt-transport-http(1), Proxy Configuration](https://manpages.debian.org/bookworm/apt/apt-transport-http.1.en.html#Proxy_Configuration) +- **DNF** uses curl proxy environment variables when its `proxy` option is unset. Source: [DNF Configuration Reference — proxy](https://dnf.readthedocs.io/en/latest/conf_ref.html#proxy-label) +- **Git** normally uses `http_proxy`, `https_proxy`, and `all_proxy`; `http.proxy` overrides them. Source: [Git config — http.proxy](https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpproxy) + +## Recommended scope + +For the first release: + +1. Shell environment +2. APT — for persistent/sudo-safe configuration, not because APT lacks environment support +3. Docker daemon +4. Docker containers/builds +5. GNOME desktop proxy, **only if GNOME is detected** + +Defer: + +- Firefox-specific settings +- Chromium-specific launch flags +- arbitrary systemd services +- Snap/Ubuntu Core +- desktop integrations other than GNOME until their official configuration contracts are researched