# Vultr Marketplace

**A VM you click into existence on Vultr is anonymous compute — every outbound request leaves as the same shared-pool IP as ten thousand other tenants, one abuse report from the whole range getting blocked.**

The Whisper Agent Node fixes that at the one moment you fully control a VM's fate: first boot. Deploy it from the Marketplace, paste your Whisper key as an optional deploy variable, and the instance comes up already holding a real, routable, reverse-resolvable IPv6 `/128` — with a persistent egress service binding every outbound packet to that address. No framework to install, no sidecar to wire up later, no snowflake box to remember.

## The pain this removes

Ask any team that has tried to run agents — or just outbound automation — from rented cloud compute:

- **The exit IP is shared and disposable.** It tells a remote service nothing about who's calling, and a neighbor's abuse gets you rate-limited or banned.
- **"Which VM did that request?" has no good answer.** You're grepping instance IDs against a spreadsheet, not resolving a name.
- **Identity setup is a snowflake.** Someone SSHes in, hand-installs a proxy, hardcodes a key in `.bashrc`, and six months later nobody knows how it was wired.
- **It doesn't scale past one box.** Every new VM repeats the manual dance, or worse, reuses the same baked-in credential everyone forgot was there.

Whisper's answer, on Vultr specifically: an **imageless, 1-click Marketplace app** — no custom snapshot to maintain — that ships completely empty (no secret, ever) and, if you hand it a key at deploy time, wires up a *named*, *routable*, *cryptographically verifiable* agent identity before you've finished your coffee.

## Two-tier by design

| | no key (default) | `whisper_api_key` supplied at deploy |
|---|---|---|
| `whisper` CLI installed from the signed repo | yes | yes |
| Keyless checks (`whisper verify`, `whisper rdap`) | yes | yes |
| Named agent `/128` minted (PTR + RDAP) | you run `whisper` later | **yes, automatic on first boot** |
| Persistent egress (`whisper-egress` service) | — | **yes** — Tier-1 WireGuard, auto-fallback to Tier-1.5 SOCKS5 |
| Shells egress from the `/128` by default | — | yes (`ALL_PROXY`) |

Leave the key field blank and you get a clean box with the CLI ready — `whisper verify` and `whisper rdap` work against any address with zero configuration, no account needed. Postel's Law applied to a marketplace listing: liberal in what it accepts (a key is optional, read from four different places — see below), conservative in what it ships (nothing baked in, ever).

## The mechanism: imageless Vendor Data, not a snapshot

Most Marketplace 1-click apps are a pre-baked disk snapshot — rebuild the image, resubmit for review, every time anything changes. The Whisper Agent Node is **script-based**: the entire deploy artifact is a POSIX `sh` script, `firstboot.sh`, submitted to Vultr as **cloud-init Vendor Data** and run once, idempotently, on first boot (guarded by a `/etc/whisper/.provisioned` stamp — `cloud-init`'s "boot" script type re-runs on every boot, so the script has to make itself a no-op after success). That means **a CLI release alone needs no resubmission** — the script always installs the current signed package.

Vendor Data runs before user data, as root, per the [cloud-init](https://cloudinit.readthedocs.io/) module contract. Whisper's script does three things in order:

1. **Install the CLI from a signed channel** — `apt`/`dnf` against `get.whisper.online` (GPG-verified repo metadata), falling back to the same sha256+PGP-verified `curl https://get.whisper.online | sh` installer used everywhere else. No unsigned binary ever touches the box.
2. **Read the optional deploy variables liberally.** Vultr's Metadata API is queried at `http://169.254.169.254/v1/internal/app-<name>` and the legacy `v1.json` `app_variables` document; if neither has a value, a cloud-init-seeded file under `/etc/whisper/` is tried; if that's empty too, an environment variable. Four channels, one variable — accept it however it arrives, because a marketplace portal, a raw API deploy, and a hand-written cloud-config all seed variables differently.
3. **If (and only if) a key showed up:** write it to the CLI's own key file (`~/.config/whisper-ns/key`, mode 600 — identical to what `whisper login <key>` does), verify it against the control plane, mint a **named** agent identity, and bring up systemd unit `whisper-egress.service` holding `whisper connect` open as a local `socks5h://127.0.0.1:1080` front. A companion `whisper-egress-watch.timer` polls a *functional* probe every minute — not `systemctl is-active`, an actual fetch through the proxy that must observe a `2a04:2a01::/32` source — and restarts the service on two consecutive misses. If Tier-1 WireGuard can't verify within 90 seconds (UDP filtered by the network, say), the script automatically retries at Tier-1.5 SOCKS5. Either way the front is the same `socks5h://127.0.0.1:1080`, so nothing downstream needs to know which tier won.

The deploy-time variables, all optional:

| Variable | Default | Meaning |
|---|---|---|
| `whisper_api_key` | *(none — keyless)* | Your Whisper key. Present ⇒ turnkey identity + egress on first boot. |
| `whisper_agent_name` | `vultr-<hostname>` | Name for the minted agent (sanitized to `[A-Za-z0-9-]`, ≤ 63 chars). |
| `whisper_egress_tier` | `wireguard` | `wireguard` (Tier-1, routed `/128`) or `socks5` (Tier-1.5). Auto-falls back if WireGuard can't verify. |

The seed file, if used, is deleted the instant it's read — the key lives only in the CLI's own 0600 key file from that point on.

## Deploying it

**With stock tools** — build the user-data yourself against the raw Vultr API (no Whisper software involved in the deploy step itself):

```sh
# cloud-config.yaml is the YAML wrapper around firstboot.sh; seed a key file too
UD=$(base64 -w0 userdata.yaml)
curl -s -X POST https://api.vultr.com/v2/instances \
  -H "Authorization: Bearer $VULTR_API_KEY" -H 'Content-Type: application/json' \
  -d "{\"region\":\"ams\",\"plan\":\"vc2-1c-1gb\",\"os_id\":2136,
       \"enable_ipv6\":true,\"label\":\"whisper-node\",\"user_data\":\"$UD\"}"
```

**With Whisper** — on the Marketplace, this is a form: pick **Whisper Agent Node**, paste your key into the optional *Whisper API key* field, deploy. Nothing to script. Either path lands on the same box running the same `firstboot.sh`.

## Verifying the identity — from outside, with nothing installed

Once it's up, the `/128` is a real address in `2a04:2a01::/32` (announced by `AS219419`) — provable with tools you already have.

**With stock tools:**

```sh
# reverse DNS
dig -x 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 +short
# → acef2002a323d40d4.<tenant>.agents.whisper.online.

# the TLS identity behind that name (DANE-anchored)
openssl s_client -connect acef2002a323d40d4.<tenant>.agents.whisper.online:443 \
  -dane_tlsa_domain acef2002a323d40d4.<tenant>.agents.whisper.online \
  -dane_tlsa_rrdata "3 1 1 b653a4ef…fcb82d1d" < /dev/null

# is the egress actually sourcing from that /128?
curl --socks5-hostname 127.0.0.1:1080 https://rdap.whisper.online/egress-ip
```

**With Whisper**, the same three checks collapse to one command run on the box:

```sh
whisper ip        # proves the live egress source is the node's own /128
whisper verify acef2002a323d40d4.<tenant>.agents.whisper.online   # DANE + DNSSEC + reverse-DNS + transparency, the full keyless chain
```

Both go through the identical public checks documented on [DANE](/docs/dane) and [reverse fCrDNS](/docs/reverse-fcrdns) — the CLI just runs the whole chain for you instead of you assembling `dig`/`openssl`/`curl` by hand.

## What's running on a connected node

```
whisper-egress.service        # Restart=always; holds `whisper connect` open, 127.0.0.1:1080
whisper-egress-watch.timer    # every 60s: functional probe → restart on 2 consecutive misses
/etc/whisper/identity.env     # agent name, /128, tier, proxy — no secret, world-readable
/etc/profile.d/whisper-egress.sh   # login shells export ALL_PROXY=socks5h://127.0.0.1:1080
```

`ALL_PROXY` is `socks5h`, deliberately — the `h` means hostnames resolve *through the proxy*, i.e. through Whisper policy DNS, not the VM's local resolver. Remove `/etc/profile.d/whisper-egress.sh` any time to opt a shell out.

This was e2e-proven on a real `vc2-1c-1gb` Debian 12 instance in Vultr's `ams` region: first boot installed the signed CLI, minted a named agent, brought up Tier-1 WireGuard egress, and passed the functional proxy probe, PTR lookup, and RDAP check — including a deliberate egress-kill test that self-healed via the watchdog in under 90 seconds.

> Submitting or re-submitting this listing is a two-gate process on Vultr's side — verified-vendor onboarding, then Marketplace QA after "Make Public" — both external reviews outside Whisper's control. Everything up to the portal, including the live e2e proof, is captured in the Vultr recipe (`firstboot.sh` as cloud-init Vendor Data), which you can run against your own Vultr account without waiting on the listing.

## Same recipe, other clouds

The identical two-tier, bring-your-own-key model ships for [DigitalOcean](/docs/digitalocean) (a Packer-built Marketplace snapshot rather than imageless Vendor Data — DigitalOcean's app model expects a pre-baked image) and for [Kubernetes](/docs/kubernetes), where a mutating admission webhook injects the same egress behavior as a sidecar into opted-in pods instead of a systemd unit on a VM. Pick whichever unit of deployment matches how you already run compute; the identity and egress guarantees are the same.

## Next

[DigitalOcean Marketplace](/docs/digitalocean) · [Connect & egress tiers](/docs/connect)
