docs: streamline README and split detailed guides
hermetic-ci / test (push) Canceled after 0s

This commit is contained in:
proxyctl coordinator
2026-08-30 11:35:52 +08:00
parent 2786b03b96
commit 416425a9bf
5 changed files with 248 additions and 105 deletions
+51 -88
View File
@@ -1,145 +1,108 @@
# proxyctl # proxyctl
`proxyctl` is a keyboard-driven Linux TUI for coordinating one **Active Profile** across independently switchable proxy **Targets**. It manages proxy configuration surfaces; it does not run a proxy server. `proxyctl` is a keyboard-driven Linux TUI that applies one **Active Profile** to independently switchable proxy **Targets**. It configures existing system components; it does not run a proxy server.
## Supported systems ## Supported systems
The v1 binaries target Linux on amd64 and arm64. General TUI behavior and the Shell Target are distribution-independent. The supported Target boundaries are: Linux amd64 and arm64 are supported.
- **Shell:** Bash and Zsh only. | Target | Scope |
- **APT:** Debian-family systems with `apt-config` and `apt.conf.d`. |---|---|
- **Docker daemon:** Docker installations managed by systemd. | Shell | Bash and Zsh sessions |
- **Docker containers/builds:** the standard per-user Docker client `config.json` model. | APT | Debian-family `apt` configuration |
- **GNOME:** sessions exposing the expected `org.gnome.system.proxy` settings schemas. | Docker daemon | systemd-managed Docker service |
| Docker containers/builds | Per-user Docker client defaults |
| GNOME | `org.gnome.system.proxy` settings |
Unavailable Targets are hidden from the main panel; `?` opens contextual, scrollable Help over the current page and includes every built-in Target and its detection evidence. Unavailable Targets are hidden by default. Press `u` to show them and `?` for contextual help.
## Install ## Install
### Verified release binary ### Release binary
Download the binary for your architecture together with `SHA256SUMS`, then verify it before installing: Download the binary for your architecture together with `SHA256SUMS`, then verify and install it:
```sh ```sh
grep 'proxyctl-linux-amd64$' SHA256SUMS | sha256sum -c - # substitute arm64 when needed grep 'proxyctl-linux-amd64$' SHA256SUMS | sha256sum -c - # use arm64 when needed
chmod +x proxyctl-linux-amd64 # use proxyctl-linux-arm64 on arm64 chmod +x proxyctl-linux-amd64
install -m 0755 proxyctl-linux-amd64 "$HOME/.local/bin/proxyctl" install -m 0755 proxyctl-linux-amd64 "$HOME/.local/bin/proxyctl"
proxyctl version proxyctl version
proxyctl version --short # exact value only, for scripts
``` ```
Release output is two single binaries, not packages or archives. The project does not publish deb, rpm, AUR, Snap, Flatpak, or similar packages. ### From source
### Install from source with Go Go 1.24 or newer is required:
Go 1.24 or newer is required. From a source checkout:
```sh ```sh
go install ./cmd/proxyctl go install ./cmd/proxyctl
``` ```
The executable is placed in `GOBIN`, or in `$(go env GOPATH)/bin` when `GOBIN` is unset. Ensure that directory is on `PATH`. Source builds report `dev` unless a release version is injected by release automation. ## Quick start
## First use and Profiles
Start the Target panel with no arguments:
```sh ```sh
proxyctl proxyctl
``` ```
Press `m` for Proxy Profile management, then `n` to create a Profile, enter a name and proxy URL, and press Ctrl+S to save. The editor supports cursor movement, Home, End, Delete, Backspace, insertion, paste, and horizontal scrolling; Tab or the arrow keys move between fields, and Escape cancels without saving. Validation stays beside each field. The management page supports reviewing obscured connection details, editing, and safe deletion; Active Profile switching remains a separate dashboard picker workflow rather than a management action. Single mode applies one URL to HTTP, HTTPS, and ALL/SOCKS. Ctrl+T selects advanced mode for separate values without discarding values entered in either mode. A bare `host:port` is accepted and normalized to `http://`. The Single proxy URL field visibly offers Protocol Probe with Ctrl+D; it runs only on that explicit keypress, and its credential-safe progress and HTTP/SOCKS5 result stay with the initiating URL context rather than global status. When one endpoint supports both protocols, the probe switches the form to advanced mode, retains a read-only Single URL probe source row, and fills HTTP/HTTPS with `http://` and ALL/SOCKS with `socks5://`. Bypass accepts comma- or newline-delimited hostnames, domain suffixes, IPv4, IPv6, and CIDR entries; the editor previews normalized, deduplicated values and identifies invalid items before save. 1. Press `m`, then `n`, to create a Proxy Profile.
2. Enter a name and proxy URL; press Ctrl+S to save.
3. Press `p` on the dashboard to select the Active Profile.
4. Select a Target with `↑`/`↓` or `j`/`k`.
5. Press Space to enable, disable, apply, or resolve that Target.
The dashboard shows the Active Profile. Press `p` to open the searchable Active Profile picker, type to filter, use arrows to select, and press Enter to activate; Escape cancels without changing the Active Profile. Use arrows or `j`/`k` to select an Available Target and Space to run its contextual action: Enable, Disable, Apply Profile, or Resolve. Drift Resolution explicitly offers Reapply Active Profile and Restore Original; restoration refuses safely if proxyctl no longer has sufficient ownership evidence. Press `r` to refresh detection, `u` to show unavailable Targets, and `?` on any page or dialog for contextual Help. Help scrolls with arrows, `j`/`k`, Page Up/Page Down, Home, and End; `q` or Escape closes it without changing the page, focus, selection, or viewport beneath it. Applying or reapplying requires an Active Profile, while Restore Original remains available without one. Common keys:
Escape closes the top dialog or returns one page level. `q` returns from child pages and exits only from the unobscured dashboard (in the searchable Profile picker it remains search input). Ctrl+C requests exit everywhere. If a system mutation is running, either dashboard `q` or Ctrl+C visibly defers exit until the mutation completes safely; Target browsing and Help remain available while conflicting changes are disabled. | Key | Action |
|---|---|
| `p` | Switch Active Profile |
| `m` | Manage Profiles |
| Space | Run the selected Target action |
| `r` | Refresh Target detection |
| `u` | Show unavailable Targets |
| `?` | Contextual help |
| `q` / Esc | Back or exit |
## TUI keyboard, responsive, and accessibility behavior For Profile modes, Protocol Probe, Target states, complete keyboard behavior, and accessibility details, see [docs/user-guide.md](docs/user-guide.md).
The dashboard is keyboard-only. Up/Down or `j`/`k` moves within the focused region, Tab moves between the Target list and details, Page Up/Page Down scrolls long details, and the fixed footer lists the actions currently accepted. Focus always has a text marker such as `[focused]`, not color alone. Confirmation dialogs start on Cancel; Left/Right or Tab changes the choice and Enter activates it. Errors remain visible until acknowledged or directly resolved and can be expanded with `e`; progress appears both on the affected Target row and in the status area.
At 100×28 and larger, Targets and details are side by side. From 60–99 columns, the same workflow uses a single-column layout. Below 60×16, proxyctl shows only a minimum-size warning; resizing back restores the current page, selection, focus, scroll position, dialog, and in-flight operation. The TUI uses the terminal alternate screen and restores the prior shell screen after normal or safely deferred exit.
State is always communicated by a standard Unicode/ASCII-safe symbol and an explicit word (`● Enabled`, `○ Disabled`, `◇ External`, `! Drifted`, `… Checking`); no Nerd Font is required. Styling adapts to light and dark terminal backgrounds. Set `NO_COLOR` to disable color without losing state, focus, progress, warning, disabled-action, or error meaning.
Profiles are human-readable under the user's XDG configuration directory (normally `~/.config/proxyctl/profiles.json`) and are stored owner-only because proxy URLs may contain credentials.
## Shell integration ## Shell integration
A child TUI cannot change its parent Shell. Install the one-time Bash/Zsh startup hook: A child TUI cannot modify its parent Shell. Install the Bash/Zsh startup hook once:
```sh ```sh
proxyctl shell install proxyctl shell install
``` ```
Restart or source the startup file once. Thereafter the wrapper reloads proxy exports and unsets after `proxyctl` exits. The Shell Target emits conventional upper- and lower-case `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, and `NO_PROXY` forms. Remove the hook with: Restart or source the startup file. Remove the hook with:
```sh ```sh
proxyctl shell uninstall proxyctl shell uninstall
``` ```
Fish and other Shells are intentionally unsupported in v1. ## System Targets and permissions
## What each Target affects APT and Docker daemon changes normally require root authority. `proxyctl` never invokes `sudo`, `pkexec`, or another elevation mechanism itself. Start it explicitly with `sudo proxyctl` when system Targets are required.
1. **Shell** — commands started by integrated Bash/Zsh sessions. It does not change existing processes, system services, Docker, or desktop settings. Docker daemon changes reload systemd and restart Docker. Review the confirmation before proceeding.
2. **APT** — persistent APT package operations across Shell and privilege boundaries through a tool-owned `apt.conf.d` overlay. It does not configure other package managers and requires no service restart.
3. **Docker daemon** — daemon operations such as registry access. It does not set container networking or container/build defaults. Every change reloads systemd and immediately restarts Docker; confirm only when disruption to running work is acceptable.
4. **Docker containers/builds** — proxy variables and build arguments for future containers and builds via the owning user's Docker client configuration. It does not affect existing containers, the Docker CLI connection, or the daemon.
5. **GNOME** — GNOME system proxy settings used by compatible desktop applications. It does not rewrite browser-specific profiles or configure non-GNOME desktops. GNOME settings cannot express `socks5h` remote-DNS semantics, and the Protocol Probe fills `socks5h` for SOCKS-only endpoints; switch to advanced mode and enter `socks5` when a SOCKS endpoint must work through the GNOME Target.
proxyctl prefers isolated, tool-owned overlays. Where that is impossible it records original values and a last-applied fingerprint so disable/reset can restore prior state safely. See [docs/security.md](docs/security.md) for privilege boundaries, credential handling, restoration guarantees, and project scope.
## Target states
- **Enabled:** actual managed configuration matches the Active Profile.
- **Disabled:** no managed or positively recognized external proxy configuration was detected.
- **External:** a recognized proxy configuration exists but is not owned by proxyctl.
- **Drifted:** managed configuration exists but no longer matches the Active Profile, including partial failures or configuration left when the final Profile is deleted.
External detection is best-effort; Disabled does not claim that every possible configuration source was exhaustively searched.
## Permissions, sudo, and restarts
User Targets normally need only user-file permissions. APT and Docker daemon files generally require authority to write system locations, and Docker daemon operations require authority to reload/restart its systemd service.
proxyctl **never** runs `sudo`, `pkexec`, or another elevation mechanism. Permission failures are reported normally. If system Targets are wanted, the user must explicitly start `sudo proxyctl`; under sudo, the invoking user remains the owner of Profiles, Shell, Docker client, and GNOME configuration while current root authority is available to system Targets. In a direct root login, root is the owner. Review the Docker restart warning before confirming any daemon change.
## Diagnose, reset, and remove ## Diagnose, reset, and remove
Run read-only diagnostics for Profile validity, storage permissions, required commands, Target availability/state, and Target-specific authority:
```sh ```sh
proxyctl doctor proxyctl doctor # read-only diagnostics
proxyctl reset # restore managed Targets
proxyctl shell uninstall # remove Shell startup integration
``` ```
Before removal, restore every managed Target. The command asks for confirmation and does nothing when declined: Complete a successful reset before deleting recovery state or uninstalling the executable.
```sh ## Privacy
proxyctl reset
proxyctl shell uninstall
```
Inspect failures and rerun until reset reports no failed managed Target. Then remove the installed executable and, after retaining any desired logs, remove empty proxyctl configuration/state directories. Do not delete recovery state before a successful reset because it may be needed to restore original settings. proxyctl has no telemetry, automatic update checks, or automatic proxy-connectivity tests. Network traffic is generated only by the user-initiated Protocol Probe. Diagnostic output and logs redact credentials; stored Profile and Target configuration can still contain credentials and is protected with restrictive file permissions.
## Privacy and safety boundaries ## Documentation
proxyctl has no telemetry, automatic update checks, or automatic proxy-connectivity tests. It does not contact the network on its own; the only network traffic it generates is the user-initiated Protocol Probe (Ctrl+D in the Profile form), a handshake check that never forwards traffic through the proxy and never contacts anything beyond the endpoint itself. Local diagnostic logs are credential-redacted and bounded in size. Every TUI frame defensively redacts credentials from Profile values, Target evidence, progress, notices, errors and command output, and confirmation copy immediately before rendering, including malformed credential-bearing URLs. Profile form frames mask the entire URL userinfo while retaining the underlying value for saving. Proxy credentials remain present where a selected Target requires them, so normal local file security still matters. - [User guide](docs/user-guide.md)
- [Security and operational boundaries](docs/security.md)
proxyctl does **not** manage or supervise proxy processes such as Clash, sing-box, or Xray. It does not manage subscriptions, nodes, latency, routing rules, transparent proxying, TUN/iptables/nftables, arbitrary services or commands, plugins, or a complete headless Profile/Target CLI. - [Development and release verification](docs/development.md)
- [Product specifications](docs/specs/)
Deferred integrations include DNF, Git-specific settings, npm/pip, arbitrary systemd services, Firefox policies, Chromium launch flags, KDE and other desktops, Snap/Ubuntu Core, Fish, Windows, macOS, distribution packages, mouse interaction, and cross-Target rollback. Conventional Shell variables may already cover some deferred command-line tools. - [Linux Target research](docs/research/linux-proxy-targets.md)
Support is limited to the Linux architectures and Target boundaries above. When reporting an issue, include `proxyctl version`, redacted `proxyctl doctor` output, distribution/desktop/Shell details, and the affected Target/state. Never include unredacted proxy credentials.
## Build and verification
Create reproducible Linux binaries and checksums without packages or archives:
```sh
VERSION=v1.0.0 ./scripts/build-release.sh
sha256sum -c dist/SHA256SUMS
file dist/proxyctl-linux-amd64 dist/proxyctl-linux-arm64
```
`./scripts/test-release.sh` builds twice, compares binaries and checksum manifests, verifies both checksums, and checks ELF architectures. `./scripts/verify-clean-machine.sh` uses an isolated identity and XDG roots to exercise `go install`, Profile and hermetic Target workflows, doctor, reset cancellation, Shell install/uninstall, removal, and unchanged host-state snapshots.
+36
View File
@@ -0,0 +1,36 @@
# Development and release verification
## Requirements
- Go 1.24 or newer
- Linux
- GNU `sha256sum`, `stat`, and `find`
- `file`
## Local verification
```sh
gofmt -w .
go test ./...
go vet ./...
./scripts/check-docs.sh
./scripts/test-hermetic.sh
./scripts/test-release.sh
./scripts/verify-clean-machine.sh
```
`test-hermetic.sh` runs the complete uncached Go suite as an unprivileged user and verifies that readable host configuration content and metadata remain unchanged.
`verify-clean-machine.sh` uses isolated identity and XDG roots to exercise installation, Profile workflows, hermetic Target workflows, diagnostics, reset confirmation, Shell integration, and removal.
## Release build
```sh
VERSION=v1.0.0 ./scripts/build-release.sh
sha256sum -c dist/SHA256SUMS
file dist/proxyctl-linux-amd64 dist/proxyctl-linux-arm64
```
The release consists of Linux amd64 and arm64 single binaries plus `SHA256SUMS`; it does not produce distribution packages or archives.
`test-release.sh` builds twice, compares binaries and checksum manifests, validates both architectures and checksums, and verifies that process-test seams cannot enter a release artifact.
+54
View File
@@ -0,0 +1,54 @@
# Security and operational boundaries
## Privileges
User Targets normally require only access to user-owned files. APT and Docker daemon configuration generally require root authority.
proxyctl never starts `sudo`, `pkexec`, or another elevation mechanism. Run `sudo proxyctl` explicitly when system Targets are needed. Under sudo, the invoking user remains the owner of Profiles, Shell integration, Docker client configuration, and GNOME settings; root authority is used for system Targets. A direct root login uses root as the owner.
Docker daemon changes reload systemd and restart Docker immediately after confirmation.
## Configuration recovery
proxyctl prefers isolated, tool-owned overlays. Where shared configuration must be edited, it records the original value and a fingerprint of the last applied state.
Restore and reset operations refuse to overwrite configuration that changed after proxyctl applied it. Recovery metadata must not be deleted before a successful reset.
Use:
```sh
proxyctl doctor
proxyctl reset
proxyctl shell uninstall
```
Inspect and resolve all failures before removing the executable or state directory.
## Credentials and privacy
Proxy URLs may contain credentials. Profile, recovery, and managed credential-bearing files use restrictive permissions. Docker daemon configuration is owner-only while proxyctl manages credential-bearing proxy values.
The TUI, diagnostics, errors, command output, and bounded local operation logs defensively redact credentials. Profile editor frames obscure URL user information. Credentials remain present in underlying configuration where a selected Target requires them, so host file security still matters.
proxyctl has:
- no telemetry
- no automatic update checks
- no automatic proxy-connectivity tests
The user-initiated Protocol Probe contacts only the entered endpoint, performs protocol handshakes, and never forwards traffic through the proxy.
## Scope
proxyctl configures proxy settings; it does **not** manage or supervise proxy processes such as Clash, sing-box, or Xray. It does not manage subscriptions, nodes, latency, routing rules, transparent proxying, TUN/iptables/nftables, arbitrary services, or plugins.
It currently excludes DNF, Git-specific settings, npm/pip settings, arbitrary systemd services, browser policies, KDE, Fish, Windows, macOS, distribution packages, mouse interaction, and cross-Target rollback.
Support is limited to the Linux architectures and Targets documented in the README. When reporting an issue, include:
- `proxyctl version`
- redacted `proxyctl doctor` output
- distribution and desktop/Shell details
- affected Target and state
Never include unredacted proxy credentials.
+75
View File
@@ -0,0 +1,75 @@
# User guide
## Profiles
A Proxy Profile contains proxy URLs and bypass rules. The Active Profile is the source applied to every Enabled Target.
Open Profile management with `m`:
- `n`: create a Profile
- `e` or Enter: edit the selected Profile
- `d`: safely delete the selected Profile
- Ctrl+S: save the editor
- Ctrl+T: switch between Single and Advanced modes
Single mode applies one URL to HTTP, HTTPS, and ALL/SOCKS. Advanced mode accepts separate values. A bare `host:port` is normalized to `http://`.
Bypass entries may be comma- or newline-delimited hostnames, domain suffixes, IPv4/IPv6 addresses, or CIDR ranges. The editor normalizes and deduplicates valid entries.
### Protocol Probe
Press Ctrl+D while editing the Single proxy URL to run an explicit handshake probe. It reports HTTP and SOCKS5 support without forwarding traffic or contacting another endpoint.
If both protocols are detected, the editor changes to Advanced mode and fills HTTP/HTTPS with `http://` and ALL/SOCKS with `socks5://`. GNOME cannot represent `socks5h` remote-DNS semantics; use Advanced mode with `socks5` when GNOME must use that endpoint.
## Dashboard
Press `p` to search and select the Active Profile. Select Targets with arrows or `j`/`k`; Space runs the contextual action:
- **Enable** a Disabled Target
- **Disable** an Enabled Target
- **Apply Profile** to an External Target
- **Resolve** a Drifted Target by reapplying the Active Profile or restoring the original configuration
Restoration refuses to overwrite configuration that changed after proxyctl applied it.
Other controls:
- `r`: refresh detection
- `u`: show unavailable Targets
- Tab: switch between Target list and details
- Page Up/Page Down: scroll details
- `e`: expand or collapse the current error
- `x`: acknowledge the current error
- `?`: contextual Help
- `q` or Esc: close the current page/dialog; `q` exits from the unobscured dashboard
- Ctrl+C: request exit
Conflicting actions are disabled while a mutation runs. Exit requests cancel cancellable work and wait for the operation to settle safely.
## Target states
- **Enabled:** managed configuration matches the Active Profile.
- **Disabled:** no managed or positively recognized external proxy configuration was detected.
- **External:** recognized proxy configuration exists outside proxyctl ownership.
- **Drifted:** managed configuration no longer matches the Active Profile, including partial failures.
External detection is best-effort. Disabled does not claim that every possible configuration source was exhaustively searched.
## What each Target affects
1. **Shell** — future commands in integrated Bash/Zsh sessions. It does not change existing processes or services.
2. **APT** — persistent Debian-family package operations through a tool-owned `apt.conf.d` overlay.
3. **Docker daemon** — registry and daemon traffic. Every change reloads systemd and restarts Docker.
4. **Docker containers/builds** — proxy defaults for future containers and builds in the owning user's Docker client configuration. It does not configure the daemon.
5. **GNOME** — compatible desktop applications using GNOME system proxy settings. It does not rewrite browser-specific profiles.
## Responsive and accessible TUI
At 100×28 and larger, Targets and details are side by side. At 60–99 columns they use one column. Below 60×16, proxyctl displays a minimum-size warning and restores interaction state after resize.
Focus and state never rely on color alone. States include symbols and words such as `● Enabled`, `○ Disabled`, `◇ External`, `! Drifted`, and `… Checking`. No Nerd Font is required. Set `NO_COLOR` to disable color.
Confirmation dialogs start on Cancel. The TUI uses the terminal alternate screen and restores the previous screen after exit.
Profiles are normally stored at `~/.config/proxyctl/profiles.json` with owner-only permissions.
+28 -13
View File
@@ -2,22 +2,37 @@
set -eu set -eu
repo=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd) repo=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
readme="$repo/README.md"
test -s "$readme" require_text() {
for text in \ file=$1
'## Install' 'go install ./cmd/proxyctl' '## First use and Profiles' \ shift
'## Shell integration' 'Shell' 'APT' 'Docker daemon' \ test -s "$file"
'Docker containers/builds' 'GNOME' '## Target states' 'Enabled' \ for text
'Disabled' 'External' 'Drifted' '## Permissions, sudo, and restarts' \
'proxyctl doctor' 'proxyctl reset' 'proxyctl shell uninstall' \
'no telemetry' 'automatic update checks' 'proxy-connectivity tests' \
'does **not** manage or supervise proxy processes' 'Support is limited'
do do
grep -Fq "$text" "$readme" || { grep -Fq "$text" "$file" || {
echo "README is missing required documentation: $text" >&2 echo "$file is missing required documentation: $text" >&2
exit 1 exit 1
} }
done done
}
printf 'README documentation contract passed\n' require_text "$repo/README.md" \
'## Install' 'go install ./cmd/proxyctl' '## Quick start' \
'## Shell integration' 'proxyctl doctor' 'proxyctl reset' \
'no telemetry' 'docs/user-guide.md' 'docs/security.md' 'docs/development.md'
require_text "$repo/docs/user-guide.md" \
'## Profiles' 'Protocol Probe' '## Target states' 'Enabled' 'Disabled' \
'External' 'Drifted' 'Shell' 'APT' 'Docker daemon' \
'Docker containers/builds' 'GNOME' 'NO_COLOR'
require_text "$repo/docs/security.md" \
'proxyctl never starts `sudo`' 'Configuration recovery' \
'no automatic update checks' 'does **not** manage or supervise proxy processes' \
'Support is limited'
require_text "$repo/docs/development.md" \
'go test ./...' 'go vet ./...' 'test-hermetic.sh' \
'test-release.sh' 'verify-clean-machine.sh' 'SHA256SUMS'
printf 'documentation contract passed\n'