Docs
Guidesince cloud@2026-09-10

A host that proves nothing

A plain VPS signs no metadata, so it holds a key instead and both sides count every login. A copied key locks both out.

Some hosts can attest nothing. Hetzner, DigitalOcean, Linode, Vultr and OVH all serve unsigned metadata over plain HTTP and expose no secure element, so there is no signature to check and nobody to vouch for the host. penv gives it a key of its own and counts every login instead.

How the counter works

The host generates an Ed25519 private key. Penv Cloud keeps the public half. Both sides remember a number, and every successful login advances it by one.

A stolen key still authenticates once. Doing so advances the number on the server, so the real host arrives with a stale one on its next login and both are locked out.

This detects a copy after the fact. It does not stop the copy from working the first time, and penv says so everywhere it offers this.

Enroll the host

Open Machine Identities then All Identities, open the identity this host should be, and enroll a bound keypair. Confirming it is you is required, and so is machine:issue. The console shows a one-time enrollment secret starting with pce_. It is shown once.

penv machine enroll pce_example

penv generates the key and sends the public half, then prints the credential id:

enrolled 3b1f...c204
the key stays in this host's keychain and is never printed

The private key lives in the host's operating system keychain and is never written to a file and never printed. A host with no keychain is refused, because an enrolled key that did not survive the process would be worse than no enrollment at all:

{
  "error": "no_keychain",
  "message": "this host has no keychain, so an enrolled key would not survive the process.",
  "fix": "Give the host a PENV_TOKEN, or run it where the OS keychain opens."
}

An empty secret is refused the same way, with no_secret and a fix naming the console.

The enrollment secret carries its own pce_ prefix so it can never be handed over as a bearer credential by mistake, and the server spends it in the statement that reads it.

What one login looks like

  1. penv asks for a nonce. The server issues it and it lives 120 seconds.
  2. penv signs four lines with the private key: a fixed prefix, the credential id, the nonce, and the current number.
  3. The server checks the nonce and burns it on its replay guard before it touches the database, so an immediate replay costs one hash.
  4. It checks the signature next, then the expiry, then the number.
  5. On success it advances the number and returns a credential that lasts fifteen minutes.
  6. penv writes the new number to the keychain before it uses the credential.

The nonce comes from the server rather than the host, so somebody with brief access cannot walk away with a stash of pre-signed challenges. The signature is checked before the expiry and before the number, so a caller with no key cannot lock out a host and cannot learn which credential ids exist.

Run one penv per host. Two processes re-authenticating at the same moment present the same number twice, which is what a copy looks like, and the server locks the credential rather than guess.

The two refusals, and how to tell them apart

AnswerWhat happenedWhat is revoked
409 clonedThe number the host presented is not the one the server holdsThe keypair, and every credential it minted
401 replayedThe same request arrived twice. The number is behind, and this is not a copyNothing

A clone stops the host. penv says so in one line:

{
  "error": "cloned",
  "message": "this host's key was seen on another machine, so the identity is locked.",
  "fix": "Issue a new enrolment secret in the console and run penv machine enroll again."
}

A replay reads as unauthorized at the command line, with the message that the credential is not accepted by this server. Nothing was revoked, and the next login with a fresh nonce works.

The lockout writes a credential.cloned entry naming the number the caller presented beside the number the server held, which is the only place anyone will read what happened. An owner also gets a security email.

After a lockout

There is no un-revoke. Find where the key was copied before you enroll again, because a fresh key on a host somebody still has access to buys you one more lockout and nothing else.

Do it in order

  1. Open the identity in Machine Identities then All Identities and enroll a bound keypair.
  2. Copy the pce_ secret. It is shown once.
  3. On the host, run penv machine enroll pce_... and read back the credential id.
  4. Run penv run -- <your command> and confirm it reads the environment the identity is bound to.
  5. Filter the audit log for credential.cloned when a host starts failing, and read both numbers.
  6. Find how the key was copied, then revoke the identity and enroll a new key on a clean host.

A credential leaked