# Rotate a signing key

> Replace a service's Ed25519 signing key without stranding any receipt: what rotate_key does, what happens to old receipts, and what a leaked key can no longer do.

*LOPW documentation · Guides.* Canonical page: https://lopw.com/docs/guides/rotate-key.html · This Markdown twin: https://lopw.com/docs/guides/rotate-key.md · Index of all pages: https://lopw.com/llms.txt · Everything in one file: https://lopw.com/llms-full.txt

Keys are stored by `key_version`. Rotating stamps the previous key with a retirement time: receipts it signed before then stay claimable, and it cannot produce new ones that any claim would accept.

1. Generate a new Ed25519 seed and compute its public key (`signingKeyFromSeed`, `publicKeyHex`).
2. Call `rotate_key(service_id, signing_key)` with the seller’s account key. It returns the new `key_version` (previous + 1), stores the new key with `registered_at = now`, and sets the old key’s `retired_at = now`. A `KeyRotated` event is emitted.
3. Deploy the new seed to your server (`signingSeed`). New offers and receipts carry the new `key_version`; the middleware picks it up from the service state within `stateCacheSeconds`, or at once after `lopw.refresh()`.

*Bond Registry*

```text
rotate_key(service_id, signing_key) -> key_version      // seller signs; not allowed when Closed
get_key(service_id, key_version) -> { public_key, registered_at, retired_at }   // retired_at = 0 while current
```

| Receipt signed by | `served_at` | Claim result |
| --- | --- | --- |
| Old key | before `retired_at` | Verified normally; claimable until its window closes |
| Old key | at or after `retired_at` | `KeyRetired`: rejected, nothing recorded |
| New key | any time after rotation | Verified normally |

> **If a key leaks**
>
> Rotate at once. The attacker can only produce receipts with `served_at` before the retirement time, inside their claim windows, each capped by `max_claim` and all of them by the slash rate limit. Offers are checked the same way: a claim verifies the offer signature against the key at `offer.key_version`.
