{"id":49655547,"url":"https://github.com/fabriziosalmi/proxxx","last_synced_at":"2026-05-30T11:02:46.704Z","repository":{"id":355867667,"uuid":"1226652468","full_name":"fabriziosalmi/proxxx","owner":"fabriziosalmi","description":"Terminal cockpit for Proxmox VE \u0026 PBS — Rust TUI + CLI, single static binary, six-stage commit gate, no agent on the cluster.","archived":false,"fork":false,"pushed_at":"2026-05-28T09:32:29.000Z","size":3753,"stargazers_count":14,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-28T10:15:58.832Z","etag":null,"topics":["cli","devops","homelab","mcp","proxmox","proxmox-backup-server","proxmox-ve","ratatui","rust","terminal-ui"],"latest_commit_sha":null,"homepage":"https://fabriziosalmi.github.io/proxxx/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fabriziosalmi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":"THREAT_MODEL.md","audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-01T17:08:46.000Z","updated_at":"2026-05-28T09:18:01.000Z","dependencies_parsed_at":"2026-05-12T03:01:39.659Z","dependency_job_id":null,"html_url":"https://github.com/fabriziosalmi/proxxx","commit_stats":null,"previous_names":["fabriziosalmi/proxxx"],"tags_count":33,"template":false,"template_full_name":null,"purl":"pkg:github/fabriziosalmi/proxxx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fproxxx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fproxxx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fproxxx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fproxxx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fabriziosalmi","download_url":"https://codeload.github.com/fabriziosalmi/proxxx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabriziosalmi%2Fproxxx/sbom","scorecard":{"id":1247302,"data":{"date":"2026-05-12T00:54:47Z","repo":{"name":"github.com/fabriziosalmi/proxxx","commit":"c4d34cab0159f785e3bbf513f0efdcae6b21f4c2"},"scorecard":{"version":"v5.3.0","commit":"c22063e786c11f9dd714d777a687ff7c4599b600"},"score":4.6,"checks":[{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dependency-update-tool"}},{"name":"Maintained","score":0,"reason":"project was created within the last 90 days. Please review its contents carefully","details":["Warn: Repository was created within the last 90 days."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/scorecard.yml:42","Info: jobLevel 'actions' permission set to 'read': .github/workflows/scorecard.yml:43","Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/docs.yml:33","Warn: topLevel 'contents' permission set to 'write': .github/workflows/release.yml:38","Info: topLevel permissions set to 'read-all': .github/workflows/scorecard.yml:30","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#security-policy"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dangerous-workflow"}},{"name":"Pinned-Dependencies","score":8,"reason":"dependency not pinned by hash detected -- score normalized to 8","details":["Warn: downloadThenRun not pinned by hash: tests/live/setup_demo.sh:149","Warn: downloadThenRun not pinned by hash: tests/live/test_mutation.sh:521","Info:  15 out of  15 GitHub-owned GitHubAction dependencies pinned","Info:  13 out of  13 third-party GitHubAction dependencies pinned","Info:   0 out of   2 downloadThenRun dependencies pinned","Info:   1 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#cii-best-practices"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#license"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 1 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#sast"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v0.1.22 not signed: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320857580","Warn: release artifact v0.1.21 not signed: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320831421","Warn: release artifact v0.1.20 not signed: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320823880","Warn: release artifact v0.1.19 not signed: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320812974","Warn: release artifact v0.1.18 not signed: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320804349","Warn: release artifact v0.1.22 does not have provenance: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320857580","Warn: release artifact v0.1.21 does not have provenance: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320831421","Warn: release artifact v0.1.20 does not have provenance: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320823880","Warn: release artifact v0.1.19 does not have provenance: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320812974","Warn: release artifact v0.1.18 does not have provenance: https://api.github.com/repos/fabriziosalmi/proxxx/releases/320804349"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: RUSTSEC-2023-0071"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#vulnerabilities"}},{"name":"CI-Tests","score":10,"reason":"1 out of 1 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#ci-tests"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#branch-protection"}},{"name":"Contributors","score":0,"reason":"project has 0 contributing companies or organizations -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#contributors"}}]},"last_synced_at":"2026-05-12T03:04:02.377Z","repository_id":355867667,"created_at":"2026-05-12T03:04:02.377Z","updated_at":"2026-05-12T03:04:02.377Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33689564,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","devops","homelab","mcp","proxmox","proxmox-backup-server","proxmox-ve","ratatui","rust","terminal-ui"],"created_at":"2026-05-06T09:01:15.315Z","updated_at":"2026-05-30T11:02:46.697Z","avatar_url":"https://github.com/fabriziosalmi.png","language":"Rust","funding_links":[],"categories":["cli"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/icon/brand-square-512.png\" alt=\"proxxx\" width=\"120\" height=\"120\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eproxxx\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eTerminal cockpit for Proxmox VE \u0026 Proxmox Backup Server.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Rust · async · single static binary · no installer · no agent.\u003cbr\u003e\n  Talks to the things that already exist on your cluster — REST against PVE and PBS, SSH for the rest — instead of asking you to deploy a new daemon.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/fabriziosalmi/proxxx/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/fabriziosalmi/proxxx/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/fabriziosalmi/proxxx/actions/workflows/scorecard.yml\"\u003e\u003cimg src=\"https://api.securityscorecards.dev/projects/github.com/fabriziosalmi/proxxx/badge\" alt=\"OpenSSF Scorecard\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/fabriziosalmi/proxxx/releases/latest\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/fabriziosalmi/proxxx?sort=semver\u0026display_name=tag\" alt=\"Latest release\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/fabriziosalmi/proxxx/blob/main/rust-toolchain.toml\"\u003e\u003cimg src=\"https://img.shields.io/badge/MSRV-1.95-orange?logo=rust\" alt=\"MSRV 1.95\"\u003e\u003c/a\u003e\n  \u003ca href=\"#license\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"MIT\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/demo.svg\" alt=\"proxxx animated demo — a destructive command is typed, refused by the pre-flight risk gate, then approved via Telegram HITL and executed\" width=\"900\"\u003e\n\u003c/p\u003e\n\n---\n\n## Who is this for?\n\nPick the row that matches you and jump straight to the right page.\n\n| If you are… | …you'll care about | Start here |\n| :--- | :--- | :--- |\n| **Homelab solo** running 1-3 nodes | wizard, fast TUI, single binary, no daemons | [5-min homelab quickstart](https://fabriziosalmi.github.io/proxxx/guide/quickstart-homelab) |\n| **Platform / SRE** on 10-50 nodes with on-call | HITL Telegram gate, alert daemon, `--format json` for CI, `--profile` for multi-cluster | [Production checklist](https://fabriziosalmi.github.io/proxxx/guide/production-checklist) · [HITL](https://fabriziosalmi.github.io/proxxx/integrations/hitl) |\n| **DevOps** scripting Proxmox in pipelines | typed exit codes, deterministic JSON, pre-flight risk gate, batch ops with `--yes` | [CLI reference](https://fabriziosalmi.github.io/proxxx/reference/cli) · [Exit codes](https://fabriziosalmi.github.io/proxxx/reference/exit-codes) |\n| **LLM / agent integrator** wiring Claude/Cursor to a cluster | MCP server (stdio + Streamable HTTP), compile-time-fixed 25-tool registry, SHA-256 pinned for supply-chain audit | [LLM/MCP quickstart](https://fabriziosalmi.github.io/proxxx/guide/quickstart-llm-mcp) |\n| **Security / compliance** evaluating before deploy | typed errors, HITL replay protection, sigstore-signed releases, CycloneDX SBOM, gate on every commit | [`THREAT_MODEL.md`](THREAT_MODEL.md) · [`SECURITY.md`](SECURITY.md) · [Production checklist](https://fabriziosalmi.github.io/proxxx/guide/production-checklist) |\n| **EU-regulated ops** (NIS2 / ISO 27001 / GDPR) | append-only SQLite audit log with HMAC-SHA256 chain, `proxxx audit verify`, zero telemetry, fully self-hosted | [EU \u0026 compliance](#eu--compliance) |\n| **Contributor** sending a PR | 8-stage commit gate (fmt + clippy + audit + cargo-deny + test+proptest + live cluster + mutation lifecycle), no-skip-flags policy | [`ARCHITECTURE.md`](ARCHITECTURE.md) · [`CONTRIBUTING.md`](CONTRIBUTING.md) · [Pre-commit gate](https://fabriziosalmi.github.io/proxxx/guide/pre-commit-gate) |\n\n---\n\n## What you get\n\n- **One binary** — `proxxx`. CLI, TUI, MCP server, unified daemon (alerts + HITL + schedule) all in the same executable.\n- **Cluster-wide read in a second** — `proxxx ls nodes`, `proxxx ls guests`, fuzzy search across the whole cluster from `/`. `--all-profiles` fans the read across every configured cluster in parallel.\n- **GitOps for Proxmox** — `proxxx state {export,diff,apply}` produces a byte-stable TOML across **eight state families** (pools, ACL grants, storage definitions, backup jobs, cluster firewall, notification matchers, HA rules, HA resources), diffs against live, converges via dispatched API calls. Pre-flight risk gates refuse Severe changes (non-empty pool delete, root-role ACL delete, shared-storage delete, HA-rule strict-flip) unless `--allow-risk`; `--interactive` adds per-Severe `[y/N]` stdin prompts.\n- **Pipeline writes** — start, stop, migrate, snapshot, clone, backup, patch, disk-move, with `--format json` for jq. `migrate --stream` shows live per-disk progress.\n- **Pre-flight risk gate** — both per-guest (Locked/Running/LongUptime/TaggedProd/ActiveNetTraffic/HaManaged + 5 more) and state-change (PoolDeleteNonEmpty/AclDeleteRootRole/StorageDeleteShared/BackupJobDelete/NotificationMatcherDelete/HaRuleDelete/HaRuleStrictChange/HaResourceDelete/HaResourceStateChange/BulkChangeCount) — refuses destructive ops without explicit override.\n- **HITL** — Telegram-mediated human approval gate, deny-on-timeout (120 s), policy-driven by tag / vmid / wildcard.\n- **Incident lockdown** — `proxxx incident freeze --reason X --ttl 4h` halts every mutation cluster-wide (`POST`/`PUT`/`DELETE` refuse with exit 8); `thaw` lifts it. Reads keep working for investigators.\n- **Console handoff + recording** — SSH/serial/SPICE/noVNC, all from `proxxx \u003cverb\u003e \u003cvmid\u003e`. `proxxx serial --record` writes asciinema cast v2; `proxxx play-cast` replays.\n- **Cross-cluster** — `proxxx find \u003cvmid\u003e` answers \"which cluster owns this guest?\" without manual profile-switching.\n- **Fleet view** — `proxxx fleet` aggregates **every** configured profile (clusters + standalone hosts, mixed) into one read-only TUI: per-cluster health summary + an aggregated guest table. `↑↓` select a cluster, `Tab` toggle the guest pane (selected vs whole fleet), `Enter` drill into a cluster's full TUI, `q` quit.\n- **Read-only profiles** — `read_only = true` on a profile makes proxxx refuse *every* mutation on it client-side (reads still work); pair with a `PVEAuditor` PVE token for a server-enforced lock too. Observe production safely, write only on your test cluster.\n- **PBS browse + restore** — REST browse plus `proxmox-backup-client` restore with `kill_on_drop` supervision. `proxxx backup-verify` does metadata-level integrity probing.\n- **Observability** — `proxxx logs tail` (cross-node journalctl fanout via SSH), `proxxx heatmap` (per-node API RTT), `proxxx anomaly` (z-score outliers), `proxxx accounting --timeframe month` (CPU-hours/GiB·h/net-GiB from per-guest RRD).\n- **Upgrade pre-flight** — `proxxx upgrade-check --target 9.x` scans cluster + config against bundled rules; exit code 1 on any block-severity finding (CI-gateable).\n- **Bundled error knowledge base** — `proxxx explain \u003cerror-id\u003e` for every typed error proxxx can emit (15 entries; ships with the binary, no network needed).\n- **Cluster digest for LLMs** — `proxxx describe --output llm-context` emits a token-compact prose+key:value paste-pronto for AI chats.\n- **MCP server** — stdio JSON-RPC + HTTP/SSE for LLM agents, compile-time-fixed tool registry, surface SHA-256 pinned. Server-sent `notifications/cluster-event` on both transports (task lifecycle + freeze/thaw events).\n- **Verifiable releases** — every tarball ships with three layers: SHA-256 sidecar, sigstore keyless cosign signature pinned to this exact workflow path (offline-verifiable; transparency-log inclusion proof embedded), and a CycloneDX SBOM generated from `Cargo.lock`. Audit with `cosign verify-blob` + `grype` / `trivy`.\n\n## EU \u0026 compliance\n\nproxxx is designed for operators who need auditability, data sovereignty, and supply-chain transparency — requirements increasingly mandated under NIS2, ISO 27001, and GDPR in the EU.\n\n| Requirement | How proxxx addresses it |\n| :--- | :--- |\n| **No telemetry** | Zero outbound connections except to your configured PVE/PBS endpoints and, if you opt in, your own Telegram bot. No analytics, no crash-reporting, no version-check pings. |\n| **Data sovereignty** | All state — config, cache, audit log, HITL keys — lives on-prem, under paths you control (`~/.config/proxxx/` / `~/.local/share/proxxx/` on Linux). Nothing leaves your environment unless you push it there. |\n| **Append-only audit log** | `proxxx audit log` — every mutation (start, stop, delete, snapshot, patch, create) is written to a local SQLite database with a per-entry HMAC-SHA256 chain. Tampering any record breaks the chain. |\n| **Cryptographic chain verification** | `proxxx audit verify` walks every entry, recomputes the HMAC chain from the keyed root, and reports the first broken link. CI-friendly: exits 0 on pass, 1 on violation — wire it into your compliance pipeline. |\n| **Export for SIEM** | `proxxx audit export --format json` or `--format csv` — pipe into Splunk, Elastic, Wazuh, or any log aggregator without an agent. |\n| **Supply-chain** | Every release ships: SHA-256 sidecar, sigstore keyless cosign signature (pinned to the exact workflow path, offline-verifiable, transparency-log proof embedded), and a CycloneDX SBOM from `Cargo.lock`. Audit with `cosign verify-blob` + `grype` / `trivy`. |\n| **Self-diagnostic** | `proxxx doctor` validates config, cluster connectivity, auth, Telegram HITL, PBS, SSH key, and audit log integrity in one pass. Exits 0 if all critical checks pass. |\n| **Secrets hygiene** | All secret values live in `Zeroizing\u003cString\u003e` (heap-wiped on Drop). HMAC and audit keys are stored at 0600 paths; proxxx refuses to start if a key file has world-readable permissions. |\n\n\u003e **Note:** proxxx is a management tool, not a compliance product. `proxxx audit verify` provides integrity assurance for the local mutation log; it does not replace a SIEM or a formal audit trail required by a certification body. Use it as one control layer in a broader NIS2 / ISO 27001 implementation.\n\n---\n\n## Install\n\nPre-built binaries for **macOS Apple Silicon**, **Linux x86_64-musl**, and **Linux aarch64-musl** (Pi 4/5, Ampere, Graviton, Oracle Free Tier) are attached to each [tagged release](https://github.com/fabriziosalmi/proxxx/releases). All Linux artefacts are statically linked — no glibc, drops onto Alpine through RHEL.\n\nDownload + verify the full supply-chain trio:\n\n```bash\nTARGET=x86_64-unknown-linux-musl     # or aarch64-apple-darwin\nVERSION=0.2.0                        # latest at time of writing\n\ngh release download v${VERSION} --repo fabriziosalmi/proxxx \\\n  --pattern \"*-${TARGET}.tar.gz\" \\\n  --pattern \"*-${TARGET}.tar.gz.sha256\" \\\n  --pattern \"*-${TARGET}.tar.gz.cosign.bundle\"\n\n# 1. Checksum\nshasum -a 256 -c proxxx-${VERSION}-${TARGET}.tar.gz.sha256\n\n# 2. Sigstore keyless signature (offline; cert pinned to release.yml)\ncosign verify-blob \\\n  --bundle proxxx-${VERSION}-${TARGET}.tar.gz.cosign.bundle \\\n  --certificate-identity-regexp 'https://github.com/fabriziosalmi/proxxx/.github/workflows/release.yml@.*' \\\n  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \\\n  proxxx-${VERSION}-${TARGET}.tar.gz\n\n# 3. (optional) Audit the CycloneDX SBOM\ngh release download v${VERSION} --repo fabriziosalmi/proxxx \\\n  --pattern \"*.cdx.json\" --pattern \"*.cdx.json.sha256\"\nshasum -a 256 -c proxxx-${VERSION}.cdx.json.sha256\ngrype sbom:proxxx-${VERSION}.cdx.json   # or trivy / cyclonedx-cli\n\ntar xzf proxxx-${VERSION}-${TARGET}.tar.gz\n./proxxx-${VERSION}-${TARGET}/proxxx --version\n```\n\nIf you only want the binary fast (no verification), skip steps 2–3 and run just `shasum -a 256 -c …` from the snippet above. Production deployments should run all three — see the [Production checklist](https://fabriziosalmi.github.io/proxxx/guide/production-checklist).\n\nOr build from source (needs Rust 1.95+):\n\n```bash\ngit clone https://github.com/fabriziosalmi/proxxx.git\ncd proxxx \u0026\u0026 cargo build --release\n./target/release/proxxx --version\n```\n\nThe Linux musl artifact is statically linked — runs on every distro from RHEL 6 to Alpine 3.x without GLIBC drama.\n\n## Quick start\n\n```bash\nproxxx init --interactive               # 5-step wizard: prompts for URL, auth, TLS, optional\n                                        # SSH + Telegram, validates each input against the\n                                        # live cluster before write. Recommended for first\n                                        # run — wrong field caught here, never lands in TOML.\nproxxx init                             # non-interactive variant: writes a commented\n                                        # starter config.toml; refuses to overwrite — pass\n                                        # --force if you mean it. Edit url / user /\n                                        # token_id / token_secret manually after.\nproxxx ls nodes                         # validates the connection.\nproxxx                                  # TUI (no args). Press ? for the keymap; the\n                                        # bottom-row footer shows contextual binds always.\nproxxx --help                           # full subcommand list.\nproxxx version --json                   # build + capability metadata.\n```\n\nThe starter `config.toml` carries inline comments for every secret-resolution path (CLI flag → env var → 0600 file → OS keychain). Optional sections — HITL via Telegram, SSH layer, PBS, alerts, policies — are commented out so the API-only operator doesn't have to delete anything.\n\n## Daily-driver TUI\n\nRun with no arguments. Vim keys, fuzzy search across the cluster (`/`), command palette (`:`), quick-open palette (`Ctrl+K`). 18 views over the same Elm-pattern reducer:\n\n| `1` Dashboard | `2` Nodes | `3` Guests | `4` Storage |\n| :---: | :---: | :---: | :---: |\n| `H` Heatmap | `B` Backup board | `G` Config grep | `Q` Operation queue |\n| `T` Audit timeline | `Z` Snapshot tree | `D` Drift compare | `W` Hardware passthrough |\n\nPlus a read-only **fleet view** (`proxxx fleet`) that aggregates every configured profile into one screen — `↑↓` select cluster, `Tab` toggle guest pane, `Enter` drill into a cluster, `q` quit.\n\nMulti-select + bulk ops with pre-flight risk preview. Operation queue with dry-run, diff preview, replay-as-script export (proxxx CLI / pvesh / curl / Ansible), and HITL approval gate (Telegram, policy-driven).\n\nThe terminal is restored on every exit path — happy, `?` early-return, panic. RAII `TerminalGuard` plus a flight-recorder panic hook installed in `main()` before the runtime starts.\n\n## Pipeline-friendly CLI\n\n```bash\n# Read\nproxxx ls guests --format json | jq '.[] | select(.status == \"running\") | .vmid'\nproxxx ha preview --node pve1                   # failover what-if\nproxxx hw conflicts --node pve1                 # PCI passthrough audit\nproxxx perms root@pam --node pve1               # effective permissions\n```\n\n```bash\n# Write — every destructive op routes through the pre-flight risk gate\nproxxx start 100 101 102\nproxxx delete 100 --yes\nproxxx migrate 100 pve2 --yes\nproxxx snapshot create 100 --name pre-upgrade\nproxxx disk move 100 --disk scsi0 --storage ceph-rbd --yes\nproxxx patch apply --reboot=auto --dry-run\n```\n\n```bash\n# Console handoff\nproxxx ssh    100                               # interactive ssh into guest (system ssh +\n                                                # QGA / lxc-interfaces auto-discovery; falls\n                                                # back to [ssh.guests.\"100\"] when explicit)\nproxxx serial 100 --node pve1                   # raw termproxy WebSocket\nproxxx spice  100 --node pve1                   # writes 0600 .vv, launches remote-viewer\nproxxx novnc  100 --node pve1                   # opens browser to web UI's noVNC\n```\n\n```bash\n# GitOps loop — export, diff, apply with safety on top\nproxxx state export \u003e state.toml                  # snapshot cluster state\ngit add state.toml \u0026\u0026 git commit -m snapshot      # version it\n$EDITOR state.toml                                # declare intent\nproxxx state diff state.toml                      # preview drift, exit 2 if any\nproxxx state apply state.toml --dry-run           # rehearse, never mutates\nproxxx state apply state.toml --prune             # pre-flight refuses Severe (exit 6)\nproxxx state apply state.toml --prune --interactive  # per-Severe [y/N] prompt\n```\n\n```bash\n# Cross-cluster fanout + cluster digest\nproxxx find 100                                   # which profile owns VMID 100?\nproxxx ls guests --all-profiles                   # every guest across every cluster\nproxxx fleet                                      # read-only TUI: ALL profiles in one screen\nproxxx describe --output llm-context              # paste at top of an LLM chat\nproxxx accounting --group-by pool --timeframe month  # CPU-hours / GiB·h / net-GiB\nproxxx heatmap                                    # per-node API RTT, color-bucketed\nproxxx anomaly --threshold 3                      # z-score outliers on CPU + mem%\nproxxx logs tail --service pveproxy --since \"1 hour ago\"  # cross-node journalctl\n```\n\n```bash\n# Incident lockdown + upgrade pre-flight\nproxxx incident freeze --reason \"rotating token\" --ttl 4h   # halt every mutation cluster-wide\nproxxx incident status --output json\nproxxx incident thaw --reason \"rotation complete\"\nproxxx upgrade-check --target 9.x                 # exits 1 on any block-severity finding\nproxxx explain freeze-refusal                     # bundled error knowledge base\n```\n\n```bash\n# Console handoff + session recording\nproxxx ssh    100                                 # interactive ssh into guest (auto-discovery)\nproxxx serial 100 --node pve1 --record            # raw termproxy WebSocket + asciinema cast\nproxxx play-cast ~/.../sessions/\u003cts\u003e-100-serial.cast --speed 2\nproxxx spice  100 --node pve1                     # writes 0600 .vv, launches remote-viewer\nproxxx novnc  100 --node pve1                     # opens browser to web UI's noVNC\n```\n\n```bash\n# Long-running daemon — alerts + HITL + schedule under ONE process\nproxxx daemon serve                               # all three with one SIGTERM\nproxxx daemon serve --no-hitl                     # alerts + schedule only\nproxxx schedule add --name nightly-snap --every 1d --cmd \"vm snapshot 100 --yes\"\n\n# MCP for AI agents (stdio JSON-RPC, HTTP/SSE for streaming)\nproxxx mcp serve                                  # stdio + interleaved server-sent notifications\nproxxx mcp serve-http --bind 0.0.0.0:8080         # HTTP/SSE transport\nproxxx mcp tools --checksum                       # registry SHA-256 for audit pinning\n```\n\nExit codes are stable contract — see [`docs/reference/exit-codes.md`](docs/reference/exit-codes.md) for the full table.\n\n## Configuration\n\nDefault location follows the `directories` project-dirs convention:\n\n| Platform | Path |\n| :--- | :--- |\n| Linux | `~/.config/proxxx/config.toml` |\n| macOS | `~/Library/Application Support/dev.proxxx.proxxx/config.toml` |\n\nSecrets resolve in order: CLI flag → `PROXXX_TOKEN_SECRET` env → `token_secret_file` (0600 enforced) → inline TOML → OS keychain. Loaded values live in `Zeroizing\u003cString\u003e` and are wiped from the heap on `Drop`.\n\n| Optional section | Unlocks |\n| :--- | :--- |\n| `[telegram]`     | HITL approvals + alert routing |\n| `[ssh]`          | Patching orchestrator, `proxxx perms`, guest SSH |\n| `[ssh.guests.X]` | Per-guest SSH overrides (optional — `proxxx ssh \u003cvmid\u003e` auto-discovers via QGA / lxc-interfaces by default; pin only when the agent's off, only loopback/link-local IPs are returned, or you want a stable DNS name) |\n| `[pbs]`          | PBS browse + restore |\n| `[[alerts]]`     | Alerting daemon — `node_offline`, `storage_above`, `replication_failing` |\n| `[[policies]]`   | HITL gating rules — match by tag / vmid / wildcard |\n\nPer-profile, set `read_only = true` to refuse all mutations on that profile client-side (reads unaffected, exit 8) — pair with a `PVEAuditor` PVE token for a server-side lock. See the [configuration guide](docs/guide/configuration.md#read-only-profiles-read_only).\n\n## Quality gate\n\nEight stages, run as both a pre-commit hook and the CI contract in [`.github/workflows/ci.yml`](.github/workflows/ci.yml).\n\n| Stage | What | Time |\n| :---: | :--- | :---: |\n| 0 | secret regression scan | \u003c1 s |\n| 1 | `cargo fmt --all -- --check` | ~3 s |\n| 2 | `cargo clippy --release --all-targets` | 10–60 s |\n| 3 | `cargo audit --deny warnings` | 3–5 s |\n| 4 | `cargo deny check` (license / banned crates / sources / wildcards) | 2–4 s |\n| 5 | `cargo test --release --all-targets` (646 lib tests + 447 integration tests including ~25 proptest properties × 256 cases each) | 10–90 s |\n| 6 | `tests/live/test_run.sh` (67 read-only probes against the live cluster) | ~30 s |\n| 7 | `tests/live/test_mutation.sh` (34 mutation probes: LXC + cluster-level CRUD across all 8 state families + QEMU; opt-in QGA via `PROXXX_E2E_QGA_VMID=\u003cvmid\u003e`) | ~60 s |\n\nEnd-to-end wall time against a reachable cluster: **~340–480 s**.\n\n```bash\ngit config core.hooksPath .githooks\nchmod +x scripts/gate.sh .githooks/pre-commit .githooks/pre-push\ncargo install cargo-audit --locked\ncargo install cargo-deny --locked\n```\n\nThe clippy `[lints.clippy]` block in [`Cargo.toml`](Cargo.toml) denies `unwrap_used`, `expect_used`, `panic`, `todo`, `await_holding_lock` in production code.\n\n## Architecture\n\nPure Elm-pattern TUI over a typed REST client. The reducer is sync, total, and tested without a runtime.\n\n```\n        crossterm key            tokio::mpsc\u003cDataMsg\u003e\n  user ─────────────────► event::map_key ─► Action\n                                              │\n                                              ▼\n                                   app::update(state, action)\n                                              │\n                                ┌─────────────┴────────────┐\n                                ▼                          ▼\n                          AppState mutation      Option\u003cSideEffect\u003e\n                                                          │\n                                                          ▼\n                                       enforce_preflight  →  check_hitl\n                                       (risk gate)           (Telegram round-trip)\n                                                          │\n                                                          ▼\n                                       ProxmoxGateway / PbsGateway / SshPool\n```\n\n| Module | Responsibility |\n| :--- | :--- |\n| [`src/app.rs`](src/app.rs) | Pure reducer. No I/O, no async. ~70 `Action` variants, ~20 `SideEffect`. |\n| [`src/api/`](src/api) | `ProxmoxGateway` trait, typed `ApiError` enum (9 categorical variants), reqwest client with 32 MiB body cap and rate limiter. The `types/` directory holds ~3100 LOC of serde-typed PVE responses split across 16 submodules. |\n| [`src/api/error.rs`](src/api/error.rs) | `Unauthorized`, `Forbidden`, `NotFound`, `RateLimited`, `PayloadTooLarge`, `StorageHang`, `Transport`, `Parse`, `Other`. Callers `.downcast_ref()` for differentiated handling. |\n| [`src/state/`](src/state) | GitOps loop — model + export + diff + apply + preflight, eight state families (pools, ACL, storage, backup-jobs, firewall-cluster, notifications, HA rules, HA resources), byte-stable TOML. |\n| [`src/pbs/`](src/pbs) | PBS REST browse + `kill_on_drop(true)` supervision over `proxmox-backup-client restore`. |\n| [`src/ssh/`](src/ssh) | `russh`, publickey only, dedicated TOFU `known_hosts` (separate from `~/.ssh/`), per-node connection pool. |\n| [`src/app/cache.rs`](src/app/cache.rs) | SQLite-backed time-travel cache, drives `proxxx replay \u003ctimestamp\u003e`. |\n| [`src/app/preflight.rs`](src/app/preflight.rs) | 11 risk variants with per-op weighting and `--allow-risk` override. |\n| [`src/hitl/`](src/hitl) | Real Telegram round-trip via `HitlCoordinator` + a single shared `getUpdates` poller. Deny on 120 s timeout, deny when Telegram unconfigured but a policy matched. |\n| [`src/mcp/`](src/mcp) | JSON-RPC server with stdio + Streamable HTTP transports. Compile-time-fixed tool registry (25 tools). Surface SHA-256 pinned via `proxxx mcp tools --checksum`. |\n| [`src/util/`](src/util) | `panic_hook` (flight recorder), `terminal_guard` (RAII raw-mode), `shutdown` (SIGTERM / SIGINT for daemons). |\n\n## Documentation\n\n- **VitePress site** — [`docs/`](docs/) and [the live build](https://fabriziosalmi.github.io/proxxx/). Local preview: `cd docs \u0026\u0026 npm install \u0026\u0026 npx vitepress dev`.\n- [`CHANGELOG.md`](CHANGELOG.md) — what shipped, with the SemVer contract for CLI / JSON / config / MCP registry surfaces.\n- [`pre-commit/`](pre-commit/) — four matrices distinguishing *implemented* from *verified end-to-end*:\n    [`01-feature-coverage.md`](pre-commit/01-feature-coverage.md) ·\n    [`02-error-handling.md`](pre-commit/02-error-handling.md) ·\n    [`03-security-invariants.md`](pre-commit/03-security-invariants.md) ·\n    [`04-resiliency-and-chaos.md`](pre-commit/04-resiliency-and-chaos.md)\n- [`ARCHITECTURE.md`](ARCHITECTURE.md) — one-page module map + data flow + reducer/side-effect bus + process model.\n- [`THREAT_MODEL.md`](THREAT_MODEL.md) — attack surfaces, mitigations, accepted risks, verification ladder.\n- [`SECURITY.md`](SECURITY.md) — vulnerability reporting policy + scope + hardening snapshot.\n- [`CONTRIBUTING.md`](CONTRIBUTING.md) — onboarding, the gate, live-cluster verification format.\n- [`.cargo/audit.toml`](.cargo/audit.toml) — supply-chain advisory ignore policy.\n- [`deny.toml`](deny.toml) — cargo-deny policy: license whitelist + banned crates + source lock.\n\n## Live cluster harness\n\n[`tests/live/`](tests/live/) drives the release binary against a real PVE cluster — separate from the cargo integration tests in [`tests/`](tests/) which use `wiremock`.\n\n| File | Tracked | Purpose |\n| :--- | :---: | :--- |\n| [`test_run.sh`](tests/live/test_run.sh) | ✓ | 67 read-only probes covering the full CLI surface; logs to `test_run.log` |\n| [`test_mutation.sh`](tests/live/test_mutation.sh) | ✓ | 34 mutation probes with `trap EXIT` cleanup: LXC 9999 (create → start → snapshot → stop → delete), cluster-level CRUD across all 8 state families (pool / ACL / storage-defs / backup-job / firewall-cluster alias+group+ipset / notifications matcher / HA rules / HA resources), QEMU 9998 from alpine ISO, opt-in QGA round-trips via `PROXXX_E2E_QGA_VMID=\u003cvmid\u003e` |\n| `test_*.log` | — | Generated by the harness |\n\n## Honest non-goals\n\nDesign boundaries — proxxx will not ship these.\n\n- **No GUI.** Proxmox already has a web UI; proxxx is for terminal users who want CLI / TUI / scripting parity.\n- **No frame rendering** for graphical SPICE or VNC. proxxx hands off to `remote-viewer` / `virt-viewer` (SPICE) or the system browser (noVNC). It never holds pixel buffers.\n- **No re-implementation of Perl algorithms in Rust** where the Perl on the node is the ground truth. `proxxx perms` shells out to `pveum user permissions` over SSH and parses, since the `pve-access-control` evaluator is canonical. The API-side `proxxx access permissions` is also available — same typed tree from `/access/permissions`, no SSH dependency — for the common case where the evaluator's full expansion isn't needed.\n- **No new dependencies for trivial things.** Three-line per-platform `Command::new` beats pulling `opener` for a launcher.\n- **No multi-cluster aggregation in the TUI** — single profile per process by architectural decision; switch with `--profile`.\n- **No Ceph cluster writes.** Operators reach for the `ceph` CLI directly on the node where the kernel module is loaded; proxxx wraps Ceph reads (status, metadata, flags) but not destructive ops (osd add/down, mon create, pool prune).\n- **No SDN config writes.** PVE SDN is opt-in cluster config that few clusters enable, and the wire shape changes between PVE versions. Skipped rather than ship a fragile surface.\n- **No browser-only auth flows.** U2F/WebAuthn registration and OIDC's redirect-callback dance both need a browser to drive them. proxxx exposes the API-driven primitives (token CRUD, password change, ACL editing) but stays out of `/access/openid/*` and `/access/tfa/u2f` — there's no terminal UX for those that beats the web UI.\n- **No snapshot rollback as a destructive trigger.** The snapshot-tree TUI shows a read-only rollback impact preview (what would be discarded + time delta); the actual rollback runs through `qm rollback` / `pct rollback` or the PVE web UI. Read-only inspector views never expose destructive entry points by design.\n\n## License\n\nMIT. Copyright © 2026 Fabrizio Salmi. See [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabriziosalmi%2Fproxxx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabriziosalmi%2Fproxxx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabriziosalmi%2Fproxxx/lists"}