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.
Root is root
Section titled “Root is root”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.
Why the root SSH channel exists at all
Section titled “Why the root SSH channel exists at all”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 createCLI, never the Proxmox REST API, and each script’s ownroot_check()hard-exits on anything that isn’t root. - Proxmox’s REST API has no LXC equivalent of the QEMU guest-agent
execendpoint at all:pct execandpct pushare 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.
SSH is opt-in, and scoped to one job
Section titled “SSH is opt-in, and scoped to one job”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_keyson 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.
Host-key pinning
Section titled “Host-key pinning”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.
The API token is scoped, not root
Section titled “The API token is scoped, not root”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.
Update signature verification
Section titled “Update signature verification”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.
The repository is private
Section titled “The repository is private”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.