Skip to content

The trust model, stated plainly

You’re about to run curl -fsSL https://web.proxploy.dev/install.sh | bash on a machine that manages your virtualization infrastructure. That deserves a straight answer about what you’re granting, not a marketing page. This is that answer.

Proxploy’s App Store installs community scripts on your nodes as root, over SSH. Those scripts can do anything root can do, and Proxploy does not sandbox them:

Root is root. A malicious script owns the node, identical to running it yourself. Proxploy adds provenance and evidence, not sandboxing.

That’s not a limitation we’re working around; it’s the honest shape of the risk. Proxploy’s mitigations (a server-side catalog cache, script content pinned and diffed against upstream before every run with confirmation on drift, full output archived, an audit row with a script hash for every run) give you provenance and evidence for what ran and when. None of it turns a malicious script into a safe one. Reviewing a script before you approve running it remains the actual control, the same as it would be if you pasted it into a root shell yourself.

We looked for a way to avoid needing raw root SSH before deciding to ship with it. It doesn’t exist. Two independent facts rule it out:

  • Every community-scripts LXC installer creates its container with the host-local pct create CLI, never the Proxmox REST API, and each script’s own root_check() hard-exits on anything that isn’t root.
  • Proxmox’s REST API has no LXC equivalent of the QEMU guest-agent exec endpoint at all: pct exec and pct push are host-CLI-only, with no API-driven substitute.

So there’s no non-interactive, API-only path for either creating the container or running the install script inside it. A root shell on the node is structurally required for this feature to exist, not a design shortcut we took because it was easier.

Enrolling SSH for a host is optional. Skip it and Proxploy still gives you monitoring, lifecycle actions (start/stop/snapshot/clone/migrate), consoles, and backups; all of that runs over the scoped API token below, never SSH. The only things that need SSH are App Store installs, updates, and migrations.

The key itself is narrow by construction: one dedicated Ed25519 keypair per install, generated in-process (never shelled out to ssh-keygen), used by exactly one component, the SSH executor, for exactly one purpose: running install/update/migration scripts. There’s no console path and no general-purpose command runner riding on this key. When you authorize it, the wizard tells you exactly that:

This key gives Proxploy a root shell on the node, used only for App Store install/update/migration scripts, exactly as if you ran them yourself as root on the node. Every use is audit-logged and its full output archived. Authorize it by adding the line to /root/.ssh/authorized_keys on the node.

Every use of that key writes an audit row (actor, host, job id, script hash, result) and archives the full session output, not because logging makes a malicious script safe, but because after-the-fact evidence is the part sandboxing can’t give you and audit logging can.

The node’s SSH host key is recorded the first time Proxploy connects (trust-on-first-use, shown to you for confirmation) and pinned from then on. If it ever changes, the executor hard-fails rather than silently accepting the new key; you get an explicit re-verification flow, never an auto-accept. A changed host key is treated as a security event.

Everything that doesn’t need SSH goes through a Proxmox API token, and that token is never broader than what you asked for: least privilege by construction, one capability at a time. See The minimal-privilege API token for the exact roles and privileges. Proxploy never asks for your root@pam password, even transiently.

Every update is verified before a single unverified byte touches disk. The order is fixed and matters: an Ed25519 signature is checked over the raw manifest bytes, before any parsing (so a manifest can’t be massaged into something that parses differently than what was signed), then the tarball’s sha256 checksum is checked, and only then is anything unpacked. Downgrades are refused: a candidate version older than what’s running is never treated as an available update.

The release public key ships inside the install script itself, not fetched from anywhere at install time; there’s nothing unpacked yet for a fresh install to read a key out of. That’s sound because the script arrives over TLS from a host you already chose to trust, the same trust the curl already places. It also means rotating that key isn’t a live flag flip: it requires shipping a new release, signed by the old key, that carries the new one.

Crash reporting is off, and stays off unless you turn it on

Section titled “Crash reporting is off, and stays off unless you turn it on”

Proxploy sends us nothing about your install unless you ask it to. There is no telemetry, no usage analytics, and no crash reporting running by default. The installer does not enable any of it, so a stock install talks to Aspyre Labs for exactly two things: checking for updates, and license activation if you hold a license.

Crash reporting exists and is one setting: put PROXPLOY_SENTRY_DSN in /etc/proxploy/proxploy.env and restart. Unset, which is how it ships, the reporting code never initialises and nothing is sent.

If you do turn it on, what leaves your network is the exception type, its message, and the stack trace, plus the Proxploy version and whether you’re on dev or prod. What does not leave is anything Sentry-style SDKs would normally attach and we explicitly disable: request bodies, headers, cookies, session identifiers, and client IP addresses. Given what this app holds, Proxmox API tokens, SSH keys, your node names and LAN addresses, that distinction is the whole point, so we set it in code rather than trusting a default.

Reports go to a GlitchTip instance Aspyre Labs runs at errors.aspyrelabs.com. Nothing about this is on the entitlement path: crash reporting is not a paid feature, is not gated by your tier, and turning it off never costs you anything.

Proxploy is not source-available today. The repository is private, and staying private is Aspyre Labs’ current decision. Do not read “self-hosted” as “you can audit the source.” If that changes, this page will change with it.

A note on the entitlement test fixture, if you go looking

Section titled “A note on the entitlement test fixture, if you go looking”

If you inspect the repository history or a release artifact closely enough to notice backend/tests/contract/entitlement_token.fixture.json: it carries a real Ed25519 private key, used only to sign fixture tokens in tests. It cannot grant anything in a running install. Production only ever loads keys from BUNDLED_PUBLIC_KEYS, one entry, dev-2026-07; and an entitlement token whose kid isn’t in that set is hard-rejected with no fallback. build_release.sh builds the release tarball with --exclude='tests', so the fixture doesn’t ship in a release at all.